ibapi/orders/
order_builder.rs

1use super::{Action, Order, OrderComboLeg, TagValue};
2
3/// An auction order is entered into the electronic trading system during the pre-market opening period for execution at the
4/// Calculated Opening Price (COP). If your order is not filled on the open, the order is re-submitted as a limit order with
5/// the limit price set to the COP or the best bid/ask after the market opens.
6/// Products: FUT, STK
7pub fn at_auction(action: Action, quantity: f64, price: f64) -> Order {
8    Order {
9        action,
10        tif: "AUC".to_owned(),
11        order_type: "MTL".to_owned(),
12        total_quantity: quantity,
13        limit_price: Some(price),
14        ..Order::default()
15    }
16}
17
18/// A Discretionary order is a limit order submitted with a hidden, specified 'discretionary' amount off the limit price which
19/// may be used to increase the price range over which the limit order is eligible to execute. The market sees only the limit price.
20/// Products: STK
21pub fn discretionary(action: Action, quantity: f64, price: f64, discretionary_amount: f64) -> Order {
22    Order {
23        action,
24        order_type: "LMT".to_owned(),
25        total_quantity: quantity,
26        limit_price: Some(price),
27        discretionary_amt: discretionary_amount,
28        ..Order::default()
29    }
30}
31
32/// A Market order is an order to buy or sell at the market bid or offer price. A market order may increase the likelihood of a fill
33/// and the speed of execution, but unlike the Limit order a Market order provides no price protection and may fill at a price far
34/// lower/higher than the current displayed bid/ask.
35/// Products: BOND, CFD, EFP, CASH, FUND, FUT, FOP, OPT, STK, WAR
36pub fn market_order(action: Action, quantity: f64) -> Order {
37    Order {
38        action,
39        order_type: "MKT".to_owned(),
40        total_quantity: quantity,
41        ..Order::default()
42    }
43}
44
45/// A Market if Touched (MIT) is an order to buy (or sell) a contract below (or above) the market. Its purpose is to take advantage
46/// of sudden or unexpected changes in share or other prices and provides investors with a trigger price to set an order in motion.
47/// Investors may be waiting for excessive strength (or weakness) to cease, which might be represented by a specific price point.
48/// MIT orders can be used to determine whether or not to enter the market once a specific price level has been achieved. This order
49/// is held in the system until the trigger price is touched, and is then submitted as a market order. An MIT order is similar to a
50/// stop order, except that an MIT sell order is placed above the current market price, and a stop sell order is placed below
51/// Products: BOND, CFD, CASH, FUT, FOP, OPT, STK, WAR
52pub fn market_if_touched(action: Action, quantity: f64, price: f64) -> Order {
53    Order {
54        action,
55        order_type: "MIT".to_owned(),
56        total_quantity: quantity,
57        aux_price: Some(price),
58        ..Order::default()
59    }
60}
61
62/// A Market-on-Close (MOC) order is a market order that is submitted to execute as close to the closing price as possible.
63/// Products: CFD, FUT, STK, WAR
64pub fn market_on_close(action: Action, quantity: f64) -> Order {
65    Order {
66        action,
67        order_type: "MOC".to_owned(),
68        total_quantity: quantity,
69        ..Order::default()
70    }
71}
72
73/// A Market-on-Open (MOO) order combines a market order with the OPG time in force to create an order that is automatically
74/// submitted at the market's open and fills at the market price.
75/// Products: CFD, STK, OPT, WAR
76pub fn market_on_open(action: Action, quantity: f64) -> Order {
77    Order {
78        action,
79        order_type: "MKT".to_owned(),
80        total_quantity: quantity,
81        tif: "OPG".to_owned(),
82        ..Order::default()
83    }
84}
85
86/// ISE Midpoint Match (MPM) orders always execute at the midpoint of the NBBO. You can submit market and limit orders direct-routed
87/// to ISE for MPM execution. Market orders execute at the midpoint whenever an eligible contra-order is available. Limit orders
88/// execute only when the midpoint price is better than the limit price. Standard MPM orders are completely anonymous.
89/// Products: STK
90pub fn midpoint_match(action: Action, quantity: f64) -> Order {
91    Order {
92        action,
93        order_type: "MKT".to_owned(),
94        total_quantity: quantity,
95        ..Order::default()
96    }
97}
98
99// A Midprice order is designed to split the difference between the bid and ask prices, and fill at the current midpoint of
100// the NBBO or better. Set an optional price cap to define the highest price (for a buy order) or the lowest price (for a sell
101// order) you are willing to accept. Requires TWS 975+. Smart-routing to US stocks only.
102pub fn midprice(action: Action, quantity: f64, price_cap: f64) -> Order {
103    Order {
104        action,
105        order_type: "MIDPRICE".to_owned(),
106        total_quantity: quantity,
107        limit_price: Some(price_cap),
108        ..Order::default()
109    }
110}
111
112/// A pegged-to-market order is designed to maintain a purchase price relative to the national best offer (NBO) or a sale price
113/// relative to the national best bid (NBB). Depending on the width of the quote, this order may be passive or aggressive.
114/// The trader creates the order by entering a limit price which defines the worst limit price that they are willing to accept.
115/// Next, the trader enters an offset amount which computes the active limit price as follows:
116///     Sell order price = Bid price + offset amount
117///     Buy order price = Ask price - offset amount
118/// Products: STK
119pub fn pegged_to_market(action: Action, quantity: f64, market_offset: f64) -> Order {
120    Order {
121        action,
122        order_type: "PEG MKT".to_owned(),
123        total_quantity: quantity,
124        aux_price: Some(market_offset),
125        ..Order::default()
126    }
127}
128
129/// A Pegged to Stock order continually adjusts the option order price by the product of a signed user-define delta and the change of
130/// the option's underlying stock price. The delta is entered as an absolute and assumed to be positive for calls and negative for puts.
131/// A buy or sell call order price is determined by adding the delta times a change in an underlying stock price to a specified starting
132/// price for the call. To determine the change in price, the stock reference price is subtracted from the current NBBO midpoint.
133/// The Stock Reference Price can be defined by the user, or defaults to the NBBO midpoint at the time of the order if no reference price
134/// is entered. You may also enter a high/low stock price range which cancels the order when reached. The delta times the change in stock
135/// price will be rounded to the nearest penny in favor of the order.
136/// Products: OPT
137pub fn pegged_to_stock(action: Action, quantity: f64, delta: f64, stock_reference_price: f64, starting_price: f64) -> Order {
138    Order {
139        action,
140        order_type: "PEG STK".to_owned(),
141        total_quantity: quantity,
142        delta: Some(delta),
143        stock_ref_price: Some(stock_reference_price),
144        starting_price: Some(starting_price),
145        ..Order::default()
146    }
147}
148
149/// Relative (a.k.a. Pegged-to-Primary) orders provide a means for traders to seek a more aggressive price than the National Best Bid
150/// and Offer (NBBO). By acting as liquidity providers, and placing more aggressive bids and offers than the current best bids and offers,
151/// traders increase their odds of filling their order. Quotes are automatically adjusted as the markets move, to remain aggressive.
152/// For a buy order, your bid is pegged to the NBB by a more aggressive offset, and if the NBB moves up, your bid will also move up.
153/// If the NBB moves down, there will be no adjustment because your bid will become even more aggressive and execute. For sales, your
154/// offer is pegged to the NBO by a more aggressive offset, and if the NBO moves down, your offer will also move down. If the NBO moves up,
155/// there will be no adjustment because your offer will become more aggressive and execute. In addition to the offset, you can define an
156/// absolute cap, which works like a limit price, and will prevent your order from being executed above or below a specified level.
157/// Stocks, Options and Futures - not available on paper trading
158/// Products: CFD, STK, OPT, FUT
159pub fn relative_pegged_to_primary(action: Action, quantity: f64, price_cap: f64, offset_amount: f64) -> Order {
160    Order {
161        action,
162        order_type: "REL".to_owned(),
163        total_quantity: quantity,
164        limit_price: Some(price_cap),
165        aux_price: Some(offset_amount),
166        ..Order::default()
167    }
168}
169
170/// Sweep-to-fill orders are useful when a trader values speed of execution over price. A sweep-to-fill order identifies the best price
171/// and the exact quantity offered/available at that price, and transmits the corresponding portion of your order for immediate execution.
172/// Simultaneously it identifies the next best price and quantity offered/available, and submits the matching quantity of your order for
173/// immediate execution.
174/// Products: CFD, STK, WAR
175pub fn sweep_to_fill(action: Action, quantity: f64, price: f64) -> Order {
176    Order {
177        action,
178        order_type: "LMT".to_owned(),
179        total_quantity: quantity,
180        limit_price: Some(price),
181        sweep_to_fill: true,
182        ..Order::default()
183    }
184}
185
186/// For option orders routed to the Boston Options Exchange (BOX) you may elect to participate in the BOX's price improvement auction in
187/// pennies. All BOX-directed price improvement orders are immediately sent from Interactive Brokers to the BOX order book, and when the
188/// terms allow, IB will evaluate it for inclusion in a price improvement auction based on price and volume priority. In the auction, your
189/// order will have priority over broker-dealer price improvement orders at the same price.
190/// An Auction Limit order at a specified price. Use of a limit order ensures that you will not receive an execution at a price less favorable
191/// than the limit price. Enter limit orders in penny increments with your auction improvement amount computed as the difference between your
192/// limit order price and the nearest listed increment.
193/// Products: OPT
194/// Supported Exchanges: BOX
195pub fn auction_limit(action: Action, quantity: f64, price: f64, auction_strategy: i32) -> Order {
196    Order {
197        action,
198        order_type: "LMT".to_owned(),
199        total_quantity: quantity,
200        limit_price: Some(price),
201        auction_strategy: Some(auction_strategy),
202        ..Order::default()
203    }
204}
205
206/// For option orders routed to the Boston Options Exchange (BOX) you may elect to participate in the BOX's price improvement auction in pennies.
207/// All BOX-directed price improvement orders are immediately sent from Interactive Brokers to the BOX order book, and when the terms allow,
208/// IB will evaluate it for inclusion in a price improvement auction based on price and volume priority. In the auction, your order will have
209/// priority over broker-dealer price improvement orders at the same price.
210/// An Auction Pegged to Stock order adjusts the order price by the product of a signed delta (which is entered as an absolute and assumed to be
211/// positive for calls, negative for puts) and the change of the option's underlying stock price. A buy or sell call order price is determined
212/// by adding the delta times a change in an underlying stock price change to a specified starting price for the call. To determine the change
213/// in price, a stock reference price (NBBO midpoint at the time of the order is assumed if no reference price is entered) is subtracted from
214/// the current NBBO midpoint. A stock range may also be entered that cancels an order when reached. The delta times the change in stock price
215/// will be rounded to the nearest penny in favor of the order and will be used as your auction improvement amount.
216/// Products: OPT
217/// Supported Exchanges: BOX
218pub fn auction_pegged_to_stock(action: Action, quantity: f64, starting_price: f64, delta: f64) -> Order {
219    Order {
220        action,
221        order_type: "PEG STK".to_owned(),
222        total_quantity: quantity,
223        delta: Some(delta),
224        starting_price: Some(starting_price),
225        ..Order::default()
226    }
227}
228
229/// For option orders routed to the Boston Options Exchange (BOX) you may elect to participate in the BOX's price improvement auction in pennies.
230/// All BOX-directed price improvement orders are immediately sent from Interactive Brokers to the BOX order book, and when the terms allow,
231/// IB will evaluate it for inclusion in a price improvement auction based on price and volume priority. In the auction, your order will have
232/// priority over broker-dealer price improvement orders at the same price.
233/// An Auction Relative order that adjusts the order price by the product of a signed delta (which is entered as an absolute and assumed to be
234/// positive for calls, negative for puts) and the change of the option's underlying stock price. A buy or sell call order price is determined
235/// by adding the delta times a change in an underlying stock price change to a specified starting price for the call. To determine the change
236/// in price, a stock reference price (NBBO midpoint at the time of the order is assumed if no reference price is entered) is subtracted from
237/// the current NBBO midpoint. A stock range may also be entered that cancels an order when reached. The delta times the change in stock price
238/// will be rounded to the nearest penny in favor of the order and will be used as your auction improvement amount.
239/// Products: OPT
240/// Supported Exchanges: BOX
241pub fn auction_relative(action: Action, quantity: f64, offset: f64) -> Order {
242    Order {
243        action,
244        order_type: "REL".to_owned(),
245        total_quantity: quantity,
246        aux_price: Some(offset),
247        ..Order::default()
248    }
249}
250
251/// The Block attribute is used for large volume option orders on ISE that consist of at least 50 contracts. To execute large-volume
252/// orders over time without moving the market, use the Accumulate/Distribute algorithm.
253/// Products: OPT
254pub fn block(action: Action, quantity: f64, price: f64) -> Order {
255    Order {
256        action,
257        order_type: "LMT".to_owned(),
258        total_quantity: quantity,
259        limit_price: Some(price),
260        block_order: true,
261        ..Order::default()
262    }
263}
264
265/// A Box Top order executes as a market order at the current best price. If the order is only partially filled, the remainder is submitted as
266/// a limit order with the limit price equal to the price at which the filled portion of the order executed.
267/// Products: OPT
268/// Supported Exchanges: BOX
269pub fn box_top(action: Action, quantity: f64) -> Order {
270    Order {
271        action,
272        order_type: "BOX TOP".to_owned(),
273        total_quantity: quantity,
274        ..Order::default()
275    }
276}
277
278/// A Limit order is an order to buy or sell at a specified price or better. The Limit order ensures that if the order fills,
279/// it will not fill at a price less favorable than your limit price, but it does not guarantee a fill.
280/// Products: BOND, CFD, CASH, FUT, FOP, OPT, STK, WAR
281pub fn limit_order(action: Action, quantity: f64, limit_price: f64) -> Order {
282    Order {
283        action,
284        order_type: "LMT".to_owned(),
285        total_quantity: quantity,
286        limit_price: Some(limit_price),
287        ..Order::default()
288    }
289}
290
291/// Forex orders can be placed in denomination of second currency in pair using cash_qty field
292/// Requires TWS or IBG 963+
293/// <https://www.interactivebrokers.com/en/index.php?f=23876#963-02>
294pub fn limit_order_with_cash_qty(action: Action, limit_price: f64, cash_qty: f64) -> Order {
295    Order {
296        action,
297        order_type: "LMT".to_owned(),
298        limit_price: Some(limit_price),
299        cash_qty: Some(cash_qty),
300        ..Order::default()
301    }
302}
303
304/// A Limit if Touched is an order to buy (or sell) a contract at a specified price or better, below (or above) the market. This order is
305/// held in the system until the trigger price is touched. An LIT order is similar to a stop limit order, except that an LIT sell order is
306/// placed above the current market price, and a stop limit sell order is placed below.
307/// Products: BOND, CFD, CASH, FUT, FOP, OPT, STK, WAR
308pub fn limit_if_touched(action: Action, quantity: f64, limit_price: f64, trigger_price: f64) -> Order {
309    Order {
310        action,
311        order_type: "LIT".to_owned(),
312        total_quantity: quantity,
313        limit_price: Some(limit_price),
314        aux_price: Some(trigger_price),
315        ..Order::default()
316    }
317}
318
319/// A Limit-on-close (LOC) order will be submitted at the close and will execute if the closing price is at or better than the submitted
320/// limit price.
321/// Products: CFD, FUT, STK, WAR
322pub fn limit_on_close(action: Action, quantity: f64, limit_price: f64) -> Order {
323    Order {
324        action,
325        order_type: "LOC".to_owned(),
326        total_quantity: quantity,
327        limit_price: Some(limit_price),
328        ..Order::default()
329    }
330}
331
332/// A Limit-on-Open (LOO) order combines a limit order with the OPG time in force to create an order that is submitted at the market's open,
333/// and that will only execute at the specified limit price or better. Orders are filled in accordance with specific exchange rules.
334/// Products: CFD, STK, OPT, WAR
335pub fn limit_on_open(action: Action, quantity: f64, limit_price: f64) -> Order {
336    Order {
337        action,
338        order_type: "LMT".to_owned(),
339        total_quantity: quantity,
340        limit_price: Some(limit_price),
341        tif: "OPG".to_owned(),
342        ..Order::default()
343    }
344}
345
346/// Passive Relative orders provide a means for traders to seek a less aggressive price than the National Best Bid and Offer (NBBO) while
347/// keeping the order pegged to the best bid (for a buy) or ask (for a sell). The order price is automatically adjusted as the markets move
348/// to keep the order less aggressive. For a buy order, your order price is pegged to the NBB by a less aggressive offset, and if the NBB
349/// moves up, your bid will also move up. If the NBB moves down, there will be no adjustment because your bid will become aggressive and execute.
350/// For a sell order, your price is pegged to the NBO by a less aggressive offset, and if the NBO moves down, your offer will also move down.
351/// If the NBO moves up, there will be no adjustment because your offer will become aggressive and execute. In addition to the offset, you can
352/// define an absolute cap, which works like a limit price, and will prevent your order from being executed above or below a specified level.
353/// The Passive Relative order is similar to the Relative/Pegged-to-Primary order, except that the Passive relative subtracts the offset from
354/// the bid and the Relative adds the offset to the bid.
355/// Products: STK, WAR
356pub fn passive_relative(action: Action, quantity: f64, offset: f64) -> Order {
357    Order {
358        action,
359        order_type: "PASSV REL".to_owned(),
360        total_quantity: quantity,
361        aux_price: Some(offset),
362        ..Order::default()
363    }
364}
365
366/// A pegged-to-midpoint order provides a means for traders to seek a price at the midpoint of the National Best Bid and Offer (NBBO).
367/// The price automatically adjusts to peg the midpoint as the markets move, to remain aggressive. For a buy order, your bid is pegged to
368/// the NBBO midpoint and the order price adjusts automatically to continue to peg the midpoint if the market moves. The price only adjusts
369/// to be more aggressive. If the market moves in the opposite direction, the order will execute.
370/// Products: STK
371pub fn pegged_to_midpoint(action: Action, quantity: f64, offset: f64, limit_price: f64) -> Order {
372    Order {
373        action,
374        order_type: "PEG MID".to_owned(),
375        total_quantity: quantity,
376        aux_price: Some(offset),
377        limit_price: Some(limit_price),
378        ..Order::default()
379    }
380}
381
382/// Bracket orders are designed to help limit your loss and lock in a profit by "bracketing" an order with two opposite-side orders.
383/// A BUY order is bracketed by a high-side sell limit order and a low-side sell stop order. A SELL order is bracketed by a high-side buy
384/// stop order and a low side buy limit order.
385/// Products: CFD, BAG, FOP, CASH, FUT, OPT, STK, WAR
386pub fn bracket_order(
387    parent_order_id: i32,
388    action: Action,
389    quantity: f64,
390    limit_price: f64,
391    take_profit_limit_price: f64,
392    stop_loss_price: f64,
393) -> Vec<Order> {
394    //This will be our main or "parent" order
395    let parent = Order {
396        order_id: parent_order_id,
397        action,
398        order_type: "LMT".to_owned(),
399        total_quantity: quantity,
400        limit_price: Some(limit_price),
401        transmit: false,
402        ..Order::default()
403    };
404
405    let take_profit = Order {
406        order_id: parent.order_id + 1,
407        action: action.reverse(),
408        order_type: "LMT".to_owned(),
409        total_quantity: quantity,
410        limit_price: Some(take_profit_limit_price),
411        parent_id: parent_order_id,
412        transmit: false,
413        ..Order::default()
414    };
415
416    let stop_loss = Order {
417        order_id: parent.order_id + 2,
418        action: action.reverse(),
419        order_type: "STP".to_owned(),
420        //Stop trigger price
421        aux_price: Some(stop_loss_price),
422        total_quantity: quantity,
423        parent_id: parent_order_id,
424        //In this case, the low side order will be the last child being sent. Therefore, it needs to set this attribute to true
425        //to activate all its predecessors
426        transmit: true,
427        ..Order::default()
428    };
429
430    vec![parent, take_profit, stop_loss]
431}
432
433/// Products:CFD, FUT, FOP, OPT, STK, WAR
434/// A Market-to-Limit (MTL) order is submitted as a market order to execute at the current best market price. If the order is only
435/// partially filled, the remainder of the order is canceled and re-submitted as a limit order with the limit price equal to the price
436/// at which the filled portion of the order executed.
437pub fn market_to_limit(action: Action, quantity: f64) -> Order {
438    Order {
439        action,
440        order_type: "MTL".to_owned(),
441        total_quantity: quantity,
442        ..Order::default()
443    }
444}
445
446/// This order type is useful for futures traders using Globex. A Market with Protection order is a market order that will be cancelled and
447/// resubmitted as a limit order if the entire order does not immediately execute at the market price. The limit price is set by Globex to be
448/// close to the current market price, slightly higher for a sell order and lower for a buy order.
449/// Products: FUT, FOP
450pub fn market_with_protection(action: Action, quantity: f64) -> Order {
451    Order {
452        action,
453        order_type: "MKT PRT".to_owned(),
454        total_quantity: quantity,
455        ..Order::default()
456    }
457}
458
459/// A Stop order is an instruction to submit a buy or sell market order if and when the user-specified stop trigger price is attained or
460/// penetrated. A Stop order is not guaranteed a specific execution price and may execute significantly away from its stop price. A Sell
461/// Stop order is always placed below the current market price and is typically used to limit a loss or protect a profit on a long stock
462/// position. A Buy Stop order is always placed above the current market price. It is typically used to limit a loss or help protect a
463/// profit on a short sale.
464/// Products: CFD, BAG, CASH, FUT, FOP, OPT, STK, WAR
465pub fn stop(action: Action, quantity: f64, stop_price: f64) -> Order {
466    Order {
467        action,
468        order_type: "STP".to_owned(),
469        total_quantity: quantity,
470        aux_price: Some(stop_price),
471        ..Order::default()
472    }
473}
474
475/// A Stop-Limit order is an instruction to submit a buy or sell limit order when the user-specified stop trigger price is attained or
476/// penetrated. The order has two basic components: the stop price and the limit price. When a trade has occurred at or through the stop
477/// price, the order becomes executable and enters the market as a limit order, which is an order to buy or sell at a specified price or better.
478/// Products: CFD, CASH, FUT, FOP, OPT, STK, WAR
479pub fn stop_limit(action: Action, quantity: f64, limit_price: f64, stop_price: f64) -> Order {
480    Order {
481        action,
482        order_type: "STP LMT".to_owned(),
483        total_quantity: quantity,
484        limit_price: Some(limit_price),
485        aux_price: Some(stop_price),
486        ..Order::default()
487    }
488}
489
490/// A Stop with Protection order combines the functionality of a stop limit order with a market with protection order. The order is set
491/// to trigger at a specified stop price. When the stop price is penetrated, the order is triggered as a market with protection order,
492/// which means that it will fill within a specified protected price range equal to the trigger price +/- the exchange-defined protection
493/// point range. Any portion of the order that does not fill within this protected range is submitted as a limit order at the exchange-defined
494/// trigger price +/- the protection points.
495/// Products: FUT
496pub fn stop_with_protection(action: Action, quantity: f64, stop_price: f64) -> Order {
497    Order {
498        action,
499        order_type: "STP PRT".to_owned(),
500        total_quantity: quantity,
501        aux_price: Some(stop_price),
502        ..Order::default()
503    }
504}
505
506/// A sell trailing stop order sets the stop price at a fixed amount below the market price with an attached "trailing" amount. As the
507/// market price rises, the stop price rises by the trail amount, but if the stock price falls, the stop loss price doesn't change,
508/// and a market order is submitted when the stop price is hit. This technique is designed to allow an investor to specify a limit on the
509/// maximum possible loss, without setting a limit on the maximum possible gain. "Buy" trailing stop orders are the mirror image of sell
510/// trailing stop orders, and are most appropriate for use in falling markets.
511/// Products: CFD, CASH, FOP, FUT, OPT, STK, WAR
512pub fn trailing_stop(action: Action, quantity: f64, trailing_percent: f64, trail_stop_price: f64) -> Order {
513    Order {
514        action,
515        order_type: "TRAIL".to_owned(),
516        total_quantity: quantity,
517        trailing_percent: Some(trailing_percent),
518        trail_stop_price: Some(trail_stop_price),
519        ..Order::default()
520    }
521}
522
523/// A trailing stop limit order is designed to allow an investor to specify a limit on the maximum possible loss, without setting a limit
524/// on the maximum possible gain. A SELL trailing stop limit moves with the market price, and continually recalculates the stop trigger
525/// price at a fixed amount below the market price, based on the user-defined "trailing" amount. The limit order price is also continually
526/// recalculated based on the limit offset. As the market price rises, both the stop price and the limit price rise by the trail amount and
527/// limit offset respectively, but if the stock price falls, the stop price remains unchanged, and when the stop price is hit a limit order
528/// is submitted at the last calculated limit price. A "Buy" trailing stop limit order is the mirror image of a sell trailing stop limit,
529/// and is generally used in falling markets.
530/// Products: BOND, CFD, CASH, FUT, FOP, OPT, STK, WAR
531pub fn trailing_stop_limit(action: Action, quantity: f64, lmt_price_offset: f64, trailing_amount: f64, trail_stop_price: f64) -> Order {
532    Order {
533        action,
534        order_type: "TRAIL LIMIT".to_owned(),
535        total_quantity: quantity,
536        trail_stop_price: Some(trail_stop_price),
537        limit_price_offset: Some(lmt_price_offset),
538        aux_price: Some(trailing_amount),
539        ..Order::default()
540    }
541}
542
543/// Create combination orders that include options, stock and futures legs (stock legs can be included if the order is routed
544/// through SmartRouting). Although a combination/spread order is constructed of separate legs, it is executed as a single transaction
545/// if it is routed directly to an exchange. For combination orders that are SmartRouted, each leg may be executed separately to ensure
546/// best execution.
547/// Products: OPT, STK, FUT
548pub fn combo_limit_order(action: Action, quantity: f64, limit_price: f64, non_guaranteed: bool) -> Order {
549    let mut order = Order {
550        action,
551        order_type: "LMT".to_owned(),
552        total_quantity: quantity,
553        limit_price: Some(limit_price),
554        ..Order::default()
555    };
556
557    if non_guaranteed {
558        order = tag_order_non_guaranteed(order)
559    }
560
561    order
562}
563
564fn tag_order_non_guaranteed(mut order: Order) -> Order {
565    order.smart_combo_routing_params = vec![];
566    order.smart_combo_routing_params.push(TagValue {
567        tag: "NonGuaranteed".to_owned(),
568        value: "1".to_owned(),
569    });
570    order
571}
572
573/// Create combination orders that include options, stock and futures legs (stock legs can be included if the order is routed
574/// through SmartRouting). Although a combination/spread order is constructed of separate legs, it is executed as a single transaction
575/// if it is routed directly to an exchange. For combination orders that are SmartRouted, each leg may be executed separately to ensure
576/// best execution.
577/// Products: OPT, STK, FUT
578pub fn combo_market_order(action: Action, quantity: f64, non_guaranteed: bool) -> Order {
579    let mut order = Order {
580        action,
581        order_type: "MKT".to_owned(),
582        total_quantity: quantity,
583        ..Order::default()
584    };
585
586    if non_guaranteed {
587        order = tag_order_non_guaranteed(order)
588    }
589
590    order
591}
592
593/// Create combination orders that include options, stock and futures legs (stock legs can be included if the order is routed
594/// through SmartRouting). Although a combination/spread order is constructed of separate legs, it is executed as a single transaction
595/// if it is routed directly to an exchange. For combination orders that are SmartRouted, each leg may be executed separately to ensure
596/// best execution.
597/// Products: OPT, STK, FUT
598pub fn limit_order_for_combo_with_leg_prices(action: Action, quantity: f64, leg_prices: Vec<f64>, non_guaranteed: bool) -> Order {
599    let mut order = Order {
600        action,
601        order_type: "LMT".to_owned(),
602        total_quantity: quantity,
603        order_combo_legs: vec![],
604        ..Order::default()
605    };
606
607    for price in leg_prices {
608        order.order_combo_legs.push(OrderComboLeg { price: Some(price) });
609    }
610
611    if non_guaranteed {
612        order = tag_order_non_guaranteed(order)
613    }
614
615    order
616}
617
618/// Create combination orders that include options, stock and futures legs (stock legs can be included if the order is routed
619/// through SmartRouting). Although a combination/spread order is constructed of separate legs, it is executed as a single transaction
620/// if it is routed directly to an exchange. For combination orders that are SmartRouted, each leg may be executed separately to ensure
621/// best execution.
622/// Products: OPT, STK, FUT
623pub fn relative_limit_combo(action: Action, quantity: f64, limit_price: f64, non_guaranteed: bool) -> Order {
624    let mut order = Order {
625        action,
626        order_type: "REL + LMT".to_owned(),
627        total_quantity: quantity,
628        limit_price: Some(limit_price),
629        ..Order::default()
630    };
631
632    if non_guaranteed {
633        order = tag_order_non_guaranteed(order)
634    }
635
636    order
637}
638
639/// Create combination orders that include options, stock and futures legs (stock legs can be included if the order is routed
640/// through SmartRouting). Although a combination/spread order is constructed of separate legs, it is executed as a single transaction
641/// if it is routed directly to an exchange. For combination orders that are SmartRouted, each leg may be executed separately to ensure
642/// best execution.
643/// Products: OPT, STK, FUT
644pub fn relative_market_combo(action: Action, quantity: f64, non_guaranteed: bool) -> Order {
645    let mut order = Order {
646        action,
647        order_type: "REL + MKT".to_owned(),
648        total_quantity: quantity,
649        ..Order::default()
650    };
651
652    if non_guaranteed {
653        order = tag_order_non_guaranteed(order)
654    }
655
656    order
657}
658
659/// One-Cancels All (OCA) order type allows an investor to place multiple and possibly unrelated orders assigned to a group. The aim is
660/// to complete just one of the orders, which in turn will cause TWS to cancel the remaining orders. The investor may submit several
661/// orders aimed at taking advantage of the most desirable price within the group. Completion of one piece of the group order causes
662/// cancellation of the remaining group orders while partial completion causes the group to rebalance. An investor might desire to sell
663/// 1000 shares of only ONE of three positions held above prevailing market prices. The OCA order group allows the investor to enter prices
664/// at specified target levels and if one is completed, the other two will automatically cancel. Alternatively, an investor may wish to take
665/// a LONG position in eMini S&P stock index futures in a falling market or else SELL US treasury futures at a more favorable price.
666/// Grouping the two orders using an OCA order type offers the investor two chance to enter a similar position, while only running the risk
667/// of taking on a single position.
668/// Products: BOND, CASH, FUT, FOP, STK, OPT, WAR
669pub fn one_cancels_all(oca_group: &str, mut oca_orders: Vec<Order>, oca_type: i32) -> Vec<Order> {
670    for order in &mut oca_orders {
671        order.oca_group = oca_group.to_owned();
672        order.oca_type = oca_type;
673    }
674
675    oca_orders
676}
677
678/// Specific to US options, investors are able to create and enter Volatility-type orders for options and combinations rather than price orders.
679/// Option traders may wish to trade and position for movements in the price of the option determined by its implied volatility. Because
680/// implied volatility is a key determinant of the premium on an option, traders position in specific contract months in an effort to take
681/// advantage of perceived changes in implied volatility arising before, during or after earnings or when company specific or broad market
682/// volatility is predicted to change. In order to create a Volatility order, clients must first create a Volatility Trader page from the
683/// Trading Tools menu and as they enter option contracts, premiums will display in percentage terms rather than premium. The buy/sell process
684/// is the same as for regular orders priced in premium terms except that the client can limit the volatility level they are willing to pay or
685/// receive.
686/// Products: FOP, OPT
687pub fn volatility(action: Action, quantity: f64, volatility_percent: f64, volatility_type: i32) -> Order {
688    Order {
689        action,
690        order_type: "VOL".to_owned(),
691        total_quantity: quantity,
692        volatility: Some(volatility_percent),   //Expressed in percentage (40%)
693        volatility_type: Some(volatility_type), // 1=daily, 2=annual
694        ..Order::default()
695    }
696}
697
698pub fn market_f_hedge(parent_order_id: i32, action: Action) -> Order {
699    //FX Hedge orders can only have a quantity of 0
700    let mut order = market_order(action, 0.0);
701    order.parent_id = parent_order_id;
702    order.hedge_type = "F".to_owned();
703
704    order
705}
706
707#[allow(clippy::too_many_arguments)]
708pub fn pegged_to_benchmark(
709    action: Action,
710    quantity: f64,
711    starting_price: f64,
712    pegged_change_amount_decrease: bool,
713    pegged_change_amount: f64,
714    reference_change_amount: f64,
715    reference_contract_id: i32,
716    reference_exchange: &str,
717    stock_reference_price: f64,
718    reference_contract_lower_range: f64,
719    reference_contract_upper_range: f64,
720) -> Order {
721    Order {
722        action,
723        order_type: "PEG BENCH".to_owned(),
724        total_quantity: quantity,
725        starting_price: Some(starting_price),
726        is_pegged_change_amount_decrease: pegged_change_amount_decrease,
727        pegged_change_amount: Some(pegged_change_amount), // by ... (and likewise for price moving in opposite direction)
728        reference_change_amount: Some(reference_change_amount), // whenever there is a price change of ...
729        reference_contract_id,                            // in the reference contract ...
730        reference_exchange: reference_exchange.to_owned(), // being traded at ...
731        stock_ref_price: Some(stock_reference_price),     // starting reference price is ...
732        //Keep order active as long as reference contract trades between ...
733        stock_range_lower: Some(reference_contract_lower_range),
734        stock_range_upper: Some(reference_contract_upper_range),
735        ..Order::default()
736    }
737}
738
739/// An attached order that turns the parent order (a conventional STP order) into a STP order
740/// in the opposite direction when the trigger is hit.
741pub fn attach_adjustable_to_stop(parent: &Order, attached_order_stop_price: f64, trigger_price: f64, adjusted_stop_price: f64) -> Order {
742    // Attached order is a conventional STP order
743    let mut order = stop(parent.action.reverse(), parent.total_quantity, attached_order_stop_price);
744
745    order.parent_id = parent.order_id;
746    order.trigger_price = Some(trigger_price); // When trigger price is penetrated
747    order.adjusted_order_type = "STP".to_owned(); // The parent order will be turned into a STP order
748    order.adjusted_stop_price = Some(adjusted_stop_price); // With the given STP price
749
750    order
751}
752
753/// An attached order that turns the parent order (a conventional STP order) into a STP LMT order
754/// in the opposite direction when the trigger is hit.
755pub fn attach_adjustable_to_stop_limit(
756    parent: &Order,
757    attached_order_stop_price: f64,
758    trigger_price: f64,
759    adjusted_stop_price: f64,
760    adjusted_stop_limit_price: f64,
761) -> Order {
762    // Attached order is a conventional STP order
763    let mut order = stop(parent.action.reverse(), parent.total_quantity, attached_order_stop_price);
764
765    order.parent_id = parent.order_id;
766    order.trigger_price = Some(trigger_price); // When trigger price is penetrated
767    order.adjusted_order_type = "STP LMT".to_owned(); // The parent order will be turned into a STP LMT order
768    order.adjusted_stop_price = Some(adjusted_stop_price); // With the given STP price
769    order.adjusted_stop_limit_price = Some(adjusted_stop_limit_price); // And the given limit price
770
771    order
772}
773
774/// An attached order that turns the parent order (a conventional STP order) into a
775/// TRAIL order in the opposite direction when the trigger is hit.
776pub fn attach_adjustable_to_trail(
777    parent: &Order,
778    attached_order_stop_price: f64,
779    trigger_price: f64,
780    adjusted_stop_price: f64,
781    adjusted_trail_amount: f64,
782    trail_unit: i32,
783) -> Order {
784    // Attached order is a conventional STP order
785    let mut order = stop(parent.action.reverse(), parent.total_quantity, attached_order_stop_price);
786
787    order.parent_id = parent.order_id;
788    order.trigger_price = Some(trigger_price); // When trigger price is penetrated
789    "TRAIL".clone_into(&mut order.adjusted_order_type); // The parent order will be turned into a TRAIL order
790    order.adjusted_stop_price = Some(adjusted_stop_price); // With a stop price of ...
791    order.adjustable_trailing_unit = trail_unit; // trailing by and amount (0) or a percent (100) ...
792    order.adjusted_trailing_amount = Some(adjusted_trail_amount); // of ...
793
794    order
795}
796
797pub fn what_if_limit_order(action: Action, quantity: f64, limit_price: f64) -> Order {
798    let mut order = limit_order(action, quantity, limit_price);
799    order.what_if = true;
800
801    order
802}
803
804// https://github.com/InteractiveBrokers/tws-api/blob/07e54ceecda2c9cbd6ffb5f524894f0c837a9ecb/source/csharpclient/client/ContractCondition.cs
805// pub fn price_condition(contract_id: i32, exchange: &str, price: f64, is_more: bool, is_conjunction: bool) -> PriceCondition
806// {
807//     //! [price_condition]
808//     //Conditions have to be created via the OrderCondition.Create
809//     PriceCondition priceCondition = (PriceCondition)OrderCondition.Create(OrderConditionType.Price);
810//     //When this contract...
811//     priceCondition.ConId = conId;
812//     //traded on this exchange
813//     priceCondition.Exchange = exchange;
814//     //has a price above/below
815//     priceCondition.IsMore = isMore;
816//     //this quantity
817//     priceCondition.Price = price;
818//     //AND | OR next condition (will be ignored if no more conditions are added)
819//     priceCondition.IsConjunctionConnection = isConjunction;
820//     //! [price_condition]
821//     return priceCondition;
822// }
823
824//     public static ExecutionCondition ExecutionCondition(string symbol, string secType, string exchange, bool isConjunction)
825//     {
826//         //! [execution_condition]
827//         ExecutionCondition execCondition = (ExecutionCondition)OrderCondition.Create(OrderConditionType.Execution);
828//         //When an execution on symbol
829//         execCondition.Symbol = symbol;
830//         //at exchange
831//         execCondition.Exchange = exchange;
832//         //for this secType
833//         execCondition.SecType = secType;
834//         //AND | OR next condition (will be ignored if no more conditions are added)
835//         execCondition.IsConjunctionConnection = isConjunction;
836//         //! [execution_condition]
837//         return execCondition;
838//     }
839
840//     public static MarginCondition MarginCondition(int percent, bool isMore, bool isConjunction)
841//     {
842//         //! [margin_condition]
843//         MarginCondition marginCondition = (MarginCondition)OrderCondition.Create(OrderConditionType.Margin);
844//         //If margin is above/below
845//         marginCondition.IsMore = isMore;
846//         //given percent
847//         marginCondition.Percent = percent;
848//         //AND | OR next condition (will be ignored if no more conditions are added)
849//         marginCondition.IsConjunctionConnection = isConjunction;
850//         //! [margin_condition]
851//         return marginCondition;
852//     }
853
854//     public static PercentChangeCondition PercentageChangeCondition(double pctChange, int conId, string exchange, bool isMore, bool isConjunction)
855//     {
856//         //! [percentage_condition]
857//         PercentChangeCondition pctChangeCondition = (PercentChangeCondition)OrderCondition.Create(OrderConditionType.PercentCange);
858//         //If there is a price percent change measured against last close price above or below...
859//         pctChangeCondition.IsMore = isMore;
860//         //this amount...
861//         pctChangeCondition.ChangePercent = pctChange;
862//         //on this contract
863//         pctChangeCondition.ConId = conId;
864//         //when traded on this exchange...
865//         pctChangeCondition.Exchange = exchange;
866//         //AND | OR next condition (will be ignored if no more conditions are added)
867//         pctChangeCondition.IsConjunctionConnection = isConjunction;
868//         //! [percentage_condition]
869//         return pctChangeCondition;
870//     }
871
872//     public static TimeCondition TimeCondition(string time, bool isMore, bool isConjunction)
873//     {
874//         //! [time_condition]
875//         TimeCondition timeCondition = (TimeCondition)OrderCondition.Create(OrderConditionType.Time);
876//         //Before or after...
877//         timeCondition.IsMore = isMore;
878//         //this time..
879//         timeCondition.Time = time;
880//         //AND | OR next condition (will be ignored if no more conditions are added)
881//         timeCondition.IsConjunctionConnection = isConjunction;
882//         //! [time_condition]
883//         return timeCondition;
884//     }
885
886//     public static VolumeCondition VolumeCondition(int conId, string exchange, bool isMore, int volume, bool isConjunction)
887//     {
888//         //! [volume_condition]
889//         VolumeCondition volCond = (VolumeCondition)OrderCondition.Create(OrderConditionType.Volume);
890//         //Whenever contract...
891//         volCond.ConId = conId;
892//         //When traded at
893//         volCond.Exchange = exchange;
894//         //reaches a volume higher/lower
895//         volCond.IsMore = isMore;
896//         //than this...
897//         volCond.Volume = volume;
898//         //AND | OR next condition (will be ignored if no more conditions are added)
899//         volCond.IsConjunctionConnection = isConjunction;
900//         //! [volume_condition]
901//         return volCond;
902
903//     }
904
905pub fn limit_ibkrats(action: Action, quantity: f64, limit_price: f64) -> Order {
906    Order {
907        action,
908        order_type: "LMT".to_owned(),
909        total_quantity: quantity,
910        limit_price: Some(limit_price),
911        not_held: true,
912        ..Order::default()
913    }
914}
915
916pub fn limit_order_with_manual_order_time(action: Action, quantity: f64, limit_price: f64, manual_order_time: &str) -> Order {
917    let mut order = limit_order(action, quantity, limit_price);
918    manual_order_time.clone_into(&mut order.manual_order_time);
919
920    order
921}
922
923pub fn peg_best_up_to_mid_order(
924    action: Action,
925    quantity: f64,
926    limit_price: f64,
927    min_trade_qty: i32,
928    min_compete_size: i32,
929    mid_offset_at_whole: f64,
930    mid_offset_at_half: f64,
931) -> Order {
932    Order {
933        action,
934        order_type: "PEG BEST".to_owned(),
935        total_quantity: quantity,
936        limit_price: Some(limit_price),
937        not_held: true,
938        min_trade_qty: Some(min_trade_qty),
939        min_compete_size: Some(min_compete_size),
940        compete_against_best_offset: super::COMPETE_AGAINST_BEST_OFFSET_UP_TO_MID,
941        mid_offset_at_whole: Some(mid_offset_at_whole),
942        mid_offset_at_half: Some(mid_offset_at_half),
943        ..Order::default()
944    }
945}
946
947pub fn peg_best_order(
948    action: Action,
949    quantity: f64,
950    limit_price: f64,
951    min_trade_qty: i32,
952    min_compete_size: i32,
953    compete_against_best_offset: f64,
954) -> Order {
955    Order {
956        action,
957        order_type: "PEG BEST".to_owned(),
958        total_quantity: quantity,
959        limit_price: Some(limit_price),
960        not_held: true,
961        min_trade_qty: Some(min_trade_qty),
962        min_compete_size: Some(min_compete_size),
963        compete_against_best_offset: Some(compete_against_best_offset),
964        ..Order::default()
965    }
966}
967
968pub fn peg_mid_order(
969    action: Action,
970    quantity: f64,
971    limit_price: f64,
972    min_trade_qty: i32,
973    mid_offset_at_whole: f64,
974    mid_offset_at_half: f64,
975) -> Order {
976    Order {
977        action,
978        order_type: "PEG MID".to_owned(),
979        total_quantity: quantity,
980        limit_price: Some(limit_price),
981        not_held: true,
982        min_trade_qty: Some(min_trade_qty),
983        mid_offset_at_whole: Some(mid_offset_at_whole),
984        mid_offset_at_half: Some(mid_offset_at_half),
985        ..Order::default()
986    }
987}