Skip to main content

oanda_rs/models/
order.rs

1//! Order models: the `Order` union, order request specifications with
2//! builders, and their supporting enums and detail structs.
3
4use serde::{Deserialize, Serialize};
5
6use super::macros::string_enum;
7use super::{
8    ClientId, DateTime, DecimalNumber, GuaranteedStopLossOrderLevelRestriction, InstrumentName,
9    OrderId, PriceValue, TradeId, TradeState, TransactionId,
10};
11
12string_enum! {
13    /// The type of the Order.
14    pub enum OrderType {
15        Market => "MARKET",
16        Limit => "LIMIT",
17        Stop => "STOP",
18        MarketIfTouched => "MARKET_IF_TOUCHED",
19        TakeProfit => "TAKE_PROFIT",
20        StopLoss => "STOP_LOSS",
21        TrailingStopLoss => "TRAILING_STOP_LOSS",
22        FixedPrice => "FIXED_PRICE",
23    }
24}
25
26string_enum! {
27    /// The type of the Order.
28    pub enum CancellableOrderType {
29        Limit => "LIMIT",
30        Stop => "STOP",
31        MarketIfTouched => "MARKET_IF_TOUCHED",
32        TakeProfit => "TAKE_PROFIT",
33        StopLoss => "STOP_LOSS",
34        TrailingStopLoss => "TRAILING_STOP_LOSS",
35    }
36}
37
38string_enum! {
39    /// The current state of the Order.
40    pub enum OrderState {
41        Pending => "PENDING",
42        Filled => "FILLED",
43        Triggered => "TRIGGERED",
44        Cancelled => "CANCELLED",
45    }
46}
47
48string_enum! {
49    /// The state to filter the requested Orders by.
50    pub enum OrderStateFilter {
51        Pending => "PENDING",
52        Filled => "FILLED",
53        Triggered => "TRIGGERED",
54        Cancelled => "CANCELLED",
55        All => "ALL",
56    }
57}
58
59string_enum! {
60    /// The time-in-force of an Order. TimeInForce describes how long an Order
61    /// should remain pending before being automatically cancelled by the
62    /// execution system.
63    pub enum TimeInForce {
64        Gtc => "GTC",
65        Gtd => "GTD",
66        Gfd => "GFD",
67        Fok => "FOK",
68        Ioc => "IOC",
69    }
70}
71
72string_enum! {
73    /// Specification of which price component should be used when determining
74    /// if an Order should be triggered and filled. This allows Orders to be
75    /// triggered based on the bid, ask, mid, default (ask for buy, bid for
76    /// sell) or inverse (ask for sell, bid for buy) price depending on the
77    /// desired behaviour. Orders are always filled using their default price
78    /// component. This feature is only provided through the REST API. Clients
79    /// who choose to specify a non-default trigger condition will not see it
80    /// reflected in any of OANDA's proprietary or partner trading platforms,
81    /// their transaction history or their account statements. OANDA platforms
82    /// always assume that an Order's trigger condition is set to the default
83    /// value when indicating the distance from an Order's trigger price, and
84    /// will always provide the default trigger condition when creating or
85    /// modifying an Order. A special restriction applies when creating a
86    /// guaranteed Stop Loss Order. In this case the TriggerCondition value must
87    /// either be "DEFAULT", or the "natural" trigger side "DEFAULT" results in.
88    /// So for a Stop Loss Order for a long trade valid values are "DEFAULT" and
89    /// "BID", and for short trades "DEFAULT" and "ASK" are valid.
90    pub enum OrderTriggerCondition {
91        Default => "DEFAULT",
92        Inverse => "INVERSE",
93        Bid => "BID",
94        Ask => "ASK",
95        Mid => "MID",
96    }
97}
98
99string_enum! {
100    /// Specification of how Positions in the Account are modified when the
101    /// Order is filled.
102    pub enum OrderPositionFill {
103        OpenOnly => "OPEN_ONLY",
104        ReduceFirst => "REDUCE_FIRST",
105        ReduceOnly => "REDUCE_ONLY",
106        Default => "DEFAULT",
107    }
108}
109
110string_enum! {
111    /// The time-in-force requested for the Market Order. Restricted to FOK or
112    /// IOC for a MarketOrder.
113    pub enum MarketOrderTimeInForce {
114        Gtc => "GTC",
115        Gtd => "GTD",
116        Gfd => "GFD",
117        Fok => "FOK",
118        Ioc => "IOC",
119    }
120}
121
122string_enum! {
123    /// The time-in-force requested for the Limit Order.
124    pub enum LimitOrderTimeInForce {
125        Gtc => "GTC",
126        Gtd => "GTD",
127        Gfd => "GFD",
128        Fok => "FOK",
129        Ioc => "IOC",
130    }
131}
132
133string_enum! {
134    /// The time-in-force requested for the Stop Order.
135    pub enum StopOrderTimeInForce {
136        Gtc => "GTC",
137        Gtd => "GTD",
138        Gfd => "GFD",
139        Fok => "FOK",
140        Ioc => "IOC",
141    }
142}
143
144string_enum! {
145    /// The time-in-force requested for the MarketIfTouched Order. Restricted to
146    /// "GTC", "GFD" and "GTD" for MarketIfTouched Orders.
147    pub enum MarketIfTouchedOrderTimeInForce {
148        Gtc => "GTC",
149        Gtd => "GTD",
150        Gfd => "GFD",
151        Fok => "FOK",
152        Ioc => "IOC",
153    }
154}
155
156string_enum! {
157    /// The time-in-force requested for the TakeProfit Order. Restricted to
158    /// "GTC", "GFD" and "GTD" for TakeProfit Orders.
159    pub enum TakeProfitOrderTimeInForce {
160        Gtc => "GTC",
161        Gtd => "GTD",
162        Gfd => "GFD",
163        Fok => "FOK",
164        Ioc => "IOC",
165    }
166}
167
168string_enum! {
169    /// The time-in-force requested for the StopLoss Order. Restricted to "GTC",
170    /// "GFD" and "GTD" for StopLoss Orders.
171    pub enum StopLossOrderTimeInForce {
172        Gtc => "GTC",
173        Gtd => "GTD",
174        Gfd => "GFD",
175        Fok => "FOK",
176        Ioc => "IOC",
177    }
178}
179
180string_enum! {
181    /// The time-in-force requested for the TrailingStopLoss Order. Restricted
182    /// to "GTC", "GFD" and "GTD" for TrailingStopLoss Orders.
183    pub enum TrailingStopLossOrderTimeInForce {
184        Gtc => "GTC",
185        Gtd => "GTD",
186        Gfd => "GFD",
187        Fok => "FOK",
188        Ioc => "IOC",
189    }
190}
191
192/// A ClientExtensions object allows a client to attach a clientID, tag and
193/// comment to Orders and Trades in their Account. Do not set, modify, or delete
194/// this field if your account is associated with MT4.
195#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
196#[non_exhaustive]
197pub struct ClientExtensions {
198    /// The Client ID of the Order/Trade
199    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
200    pub id: Option<String>,
201
202    /// A tag associated with the Order/Trade
203    #[serde(rename = "tag", skip_serializing_if = "Option::is_none")]
204    pub tag: Option<String>,
205
206    /// A comment associated with the Order/Trade
207    #[serde(rename = "comment", skip_serializing_if = "Option::is_none")]
208    pub comment: Option<String>,
209}
210
211/// TakeProfitDetails specifies the details of a Take Profit Order to be created
212/// on behalf of a client. This may happen when an Order is filled that opens a
213/// Trade requiring a Take Profit, or when a Trade's dependent Take Profit Order
214/// is modified directly through the Trade.
215#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
216#[non_exhaustive]
217pub struct TakeProfitDetails {
218    /// The price that the Take Profit Order will be triggered at. Only one of
219    /// the price and distance fields may be specified.
220    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
221    pub price: Option<PriceValue>,
222
223    /// The `timeInForce` field.
224    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
225    pub time_in_force: Option<TimeInForce>,
226
227    /// The date when the Take Profit Order will be cancelled on if timeInForce
228    /// is GTD.
229    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
230    pub gtd_time: Option<DateTime>,
231
232    /// The `clientExtensions` field.
233    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
234    pub client_extensions: Option<ClientExtensions>,
235}
236
237/// StopLossDetails specifies the details of a Stop Loss Order to be created on
238/// behalf of a client. This may happen when an Order is filled that opens a
239/// Trade requiring a Stop Loss, or when a Trade's dependent Stop Loss Order is
240/// modified directly through the Trade.
241#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
242#[non_exhaustive]
243pub struct StopLossDetails {
244    /// The price that the Stop Loss Order will be triggered at. Only one of the
245    /// price and distance fields may be specified.
246    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
247    pub price: Option<PriceValue>,
248
249    /// Specifies the distance (in price units) from the Trade's open price to
250    /// use as the Stop Loss Order price. Only one of the distance and price
251    /// fields may be specified.
252    #[serde(rename = "distance", skip_serializing_if = "Option::is_none")]
253    pub distance: Option<DecimalNumber>,
254
255    /// The `timeInForce` field.
256    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
257    pub time_in_force: Option<TimeInForce>,
258
259    /// The date when the Stop Loss Order will be cancelled on if timeInForce is
260    /// GTD.
261    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
262    pub gtd_time: Option<DateTime>,
263
264    /// The `clientExtensions` field.
265    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
266    pub client_extensions: Option<ClientExtensions>,
267
268    /// Flag indicating that the price for the Stop Loss Order is guaranteed.
269    /// The default value depends on the GuaranteedStopLossOrderMode of the
270    /// account, if it is REQUIRED, the default will be true, for DISABLED or
271    /// ENABLED the default is false.
272    #[serde(rename = "guaranteed", skip_serializing_if = "Option::is_none")]
273    pub guaranteed: Option<bool>,
274
275    /// The price trigger mode of the Stop Loss Order to be created. Observed
276    /// value: "TOP_OF_BOOK". This field is returned by the live v20 API but is
277    /// not present in OANDA's official documentation.
278    #[serde(rename = "triggerMode", skip_serializing_if = "Option::is_none")]
279    pub trigger_mode: Option<String>,
280}
281
282/// TrailingStopLossDetails specifies the details of a Trailing Stop Loss Order
283/// to be created on behalf of a client. This may happen when an Order is filled
284/// that opens a Trade requiring a Trailing Stop Loss, or when a Trade's
285/// dependent Trailing Stop Loss Order is modified directly through the Trade.
286#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
287#[non_exhaustive]
288pub struct TrailingStopLossDetails {
289    /// The distance (in price units) from the Trade's fill price that the
290    /// Trailing Stop Loss Order will be triggered at.
291    #[serde(rename = "distance", skip_serializing_if = "Option::is_none")]
292    pub distance: Option<DecimalNumber>,
293
294    /// The `timeInForce` field.
295    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
296    pub time_in_force: Option<TimeInForce>,
297
298    /// The date when the Trailing Stop Loss Order will be cancelled on if
299    /// timeInForce is GTD.
300    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
301    pub gtd_time: Option<DateTime>,
302
303    /// The `clientExtensions` field.
304    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
305    pub client_extensions: Option<ClientExtensions>,
306}
307
308/// Details required by clients creating a Guaranteed Stop Loss Order
309#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
310#[non_exhaustive]
311pub struct GuaranteedStopLossOrderEntryData {
312    /// The minimum distance allowed between the Trade's fill price and the
313    /// configured price for guaranteed Stop Loss Orders created for this
314    /// instrument. Specified in price units.
315    #[serde(rename = "minimumDistance", skip_serializing_if = "Option::is_none")]
316    pub minimum_distance: Option<DecimalNumber>,
317
318    /// The amount that is charged to the account if a guaranteed Stop Loss
319    /// Order is triggered and filled. The value is in price units and is
320    /// charged for each unit of the Trade.
321    #[serde(rename = "premium", skip_serializing_if = "Option::is_none")]
322    pub premium: Option<DecimalNumber>,
323
324    /// The `levelRestriction` field.
325    #[serde(rename = "levelRestriction", skip_serializing_if = "Option::is_none")]
326    pub level_restriction: Option<GuaranteedStopLossOrderLevelRestriction>,
327}
328
329/// The dynamic state of an Order. This is only relevant to TrailingStopLoss
330/// Orders, as no other Order type has dynamic state.
331#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
332#[non_exhaustive]
333pub struct DynamicOrderState {
334    /// The Order's ID.
335    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
336    pub id: Option<OrderId>,
337
338    /// The Order's calculated trailing stop value.
339    #[serde(rename = "trailingStopValue", skip_serializing_if = "Option::is_none")]
340    pub trailing_stop_value: Option<PriceValue>,
341
342    /// The distance between the Trailing Stop Loss Order's trailingStopValue
343    /// and the current Market Price. This represents the distance (in price
344    /// units) of the Order from a triggering price. If the distance could not
345    /// be determined, this value will not be set.
346    #[serde(rename = "triggerDistance", skip_serializing_if = "Option::is_none")]
347    pub trigger_distance: Option<PriceValue>,
348
349    /// True if an exact trigger distance could be calculated. If false, it
350    /// means the provided trigger distance is a best estimate. If the distance
351    /// could not be determined, this value will not be set.
352    #[serde(
353        rename = "isTriggerDistanceExact",
354        skip_serializing_if = "Option::is_none"
355    )]
356    pub is_trigger_distance_exact: Option<bool>,
357}
358
359/// An OrderIdentifier is used to refer to an Order, and contains both the
360/// OrderID and the ClientOrderID.
361#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
362#[non_exhaustive]
363pub struct OrderIdentifier {
364    /// The OANDA-assigned Order ID
365    #[serde(rename = "orderID", skip_serializing_if = "Option::is_none")]
366    pub order_id: Option<OrderId>,
367
368    /// The client-provided client Order ID
369    #[serde(rename = "clientOrderID", skip_serializing_if = "Option::is_none")]
370    pub client_order_id: Option<String>,
371}
372
373/// Representation of how many units of an Instrument are available to be traded
374/// by an Order depending on its postionFill option.
375#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
376#[non_exhaustive]
377pub struct UnitsAvailable {
378    /// The `default` field.
379    #[serde(rename = "default", skip_serializing_if = "Option::is_none")]
380    pub default: Option<UnitsAvailableDetails>,
381
382    /// The `reduceFirst` field.
383    #[serde(rename = "reduceFirst", skip_serializing_if = "Option::is_none")]
384    pub reduce_first: Option<UnitsAvailableDetails>,
385
386    /// The `reduceOnly` field.
387    #[serde(rename = "reduceOnly", skip_serializing_if = "Option::is_none")]
388    pub reduce_only: Option<UnitsAvailableDetails>,
389
390    /// The `openOnly` field.
391    #[serde(rename = "openOnly", skip_serializing_if = "Option::is_none")]
392    pub open_only: Option<UnitsAvailableDetails>,
393}
394
395/// Representation of many units of an Instrument are available to be traded for
396/// both long and short Orders.
397#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
398#[non_exhaustive]
399pub struct UnitsAvailableDetails {
400    /// The units available for long Orders.
401    #[serde(rename = "long", skip_serializing_if = "Option::is_none")]
402    pub long: Option<DecimalNumber>,
403
404    /// The units available for short Orders.
405    #[serde(rename = "short", skip_serializing_if = "Option::is_none")]
406    pub short: Option<DecimalNumber>,
407}
408
409/// A MarketOrderTradeClose specifies the extensions to a Market Order that has
410/// been created specifically to close a Trade.
411#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
412#[non_exhaustive]
413pub struct MarketOrderTradeClose {
414    /// The ID of the Trade requested to be closed
415    #[serde(rename = "tradeID", skip_serializing_if = "Option::is_none")]
416    pub trade_id: Option<TradeId>,
417
418    /// The client ID of the Trade requested to be closed
419    #[serde(rename = "clientTradeID", skip_serializing_if = "Option::is_none")]
420    pub client_trade_id: Option<String>,
421
422    /// Indication of how much of the Trade to close. Either "ALL", or a
423    /// DecimalNumber reflection a partial close of the Trade.
424    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
425    pub units: Option<String>,
426}
427
428/// A MarketOrderPositionCloseout specifies the extensions to a Market Order
429/// when it has been created to closeout a specific Position.
430#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
431#[non_exhaustive]
432pub struct MarketOrderPositionCloseout {
433    /// The instrument of the Position being closed out.
434    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
435    pub instrument: Option<InstrumentName>,
436
437    /// Indication of how much of the Position to close. Either "ALL", or a
438    /// DecimalNumber reflection a partial close of the Trade. The DecimalNumber
439    /// must always be positive, and represent a number that doesn't exceed the
440    /// absolute size of the Position.
441    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
442    pub units: Option<String>,
443}
444
445/// Details for the Market Order extensions specific to a Market Order placed
446/// that is part of a Market Order Margin Closeout in a client's account
447#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
448#[non_exhaustive]
449pub struct MarketOrderMarginCloseout {
450    /// The reason the Market Order was created to perform a margin closeout
451    #[serde(rename = "reason", skip_serializing_if = "Option::is_none")]
452    pub reason: Option<MarketOrderMarginCloseoutReason>,
453}
454
455/// Details for the Market Order extensions specific to a Market Order placed
456/// with the intent of fully closing a specific open trade that should have
457/// already been closed but wasn't due to halted market conditions
458#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
459#[non_exhaustive]
460pub struct MarketOrderDelayedTradeClose {
461    /// The ID of the Trade being closed
462    #[serde(rename = "tradeID", skip_serializing_if = "Option::is_none")]
463    pub trade_id: Option<TradeId>,
464
465    /// The Client ID of the Trade being closed
466    #[serde(rename = "clientTradeID", skip_serializing_if = "Option::is_none")]
467    pub client_trade_id: Option<TradeId>,
468
469    /// The Transaction ID of the DelayedTradeClosure transaction to which this
470    /// Delayed Trade Close belongs to
471    #[serde(
472        rename = "sourceTransactionID",
473        skip_serializing_if = "Option::is_none"
474    )]
475    pub source_transaction_id: Option<TransactionId>,
476}
477
478/// A MarketOrder is an order that is filled immediately upon creation using the
479/// current market price.
480#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
481#[non_exhaustive]
482pub struct MarketOrder {
483    /// The Order's identifier, unique within the Order's Account.
484    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
485    pub id: Option<OrderId>,
486
487    /// The time when the Order was created.
488    #[serde(rename = "createTime", skip_serializing_if = "Option::is_none")]
489    pub create_time: Option<DateTime>,
490
491    /// The `state` field.
492    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
493    pub state: Option<OrderState>,
494
495    /// The `clientExtensions` field.
496    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
497    pub client_extensions: Option<ClientExtensions>,
498
499    // type is pinned to "MARKET" by the enum wrapper
500    /// The Market Order's Instrument.
501    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
502    pub instrument: Option<InstrumentName>,
503
504    /// The quantity requested to be filled by the Market Order. A posititive
505    /// number of units results in a long Order, and a negative number of units
506    /// results in a short Order.
507    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
508    pub units: Option<DecimalNumber>,
509
510    /// The `timeInForce` field.
511    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
512    pub time_in_force: Option<MarketOrderTimeInForce>,
513
514    /// The worst price that the client is willing to have the Market Order
515    /// filled at.
516    #[serde(rename = "priceBound", skip_serializing_if = "Option::is_none")]
517    pub price_bound: Option<PriceValue>,
518
519    /// The `positionFill` field.
520    #[serde(rename = "positionFill", skip_serializing_if = "Option::is_none")]
521    pub position_fill: Option<OrderPositionFill>,
522
523    /// The `tradeClose` field.
524    #[serde(rename = "tradeClose", skip_serializing_if = "Option::is_none")]
525    pub trade_close: Option<MarketOrderTradeClose>,
526
527    /// The `longPositionCloseout` field.
528    #[serde(
529        rename = "longPositionCloseout",
530        skip_serializing_if = "Option::is_none"
531    )]
532    pub long_position_closeout: Option<MarketOrderPositionCloseout>,
533
534    /// The `shortPositionCloseout` field.
535    #[serde(
536        rename = "shortPositionCloseout",
537        skip_serializing_if = "Option::is_none"
538    )]
539    pub short_position_closeout: Option<MarketOrderPositionCloseout>,
540
541    /// The `marginCloseout` field.
542    #[serde(rename = "marginCloseout", skip_serializing_if = "Option::is_none")]
543    pub margin_closeout: Option<MarketOrderMarginCloseout>,
544
545    /// The `delayedTradeClose` field.
546    #[serde(rename = "delayedTradeClose", skip_serializing_if = "Option::is_none")]
547    pub delayed_trade_close: Option<MarketOrderDelayedTradeClose>,
548
549    /// The `takeProfitOnFill` field.
550    #[serde(rename = "takeProfitOnFill", skip_serializing_if = "Option::is_none")]
551    pub take_profit_on_fill: Option<TakeProfitDetails>,
552
553    /// The `stopLossOnFill` field.
554    #[serde(rename = "stopLossOnFill", skip_serializing_if = "Option::is_none")]
555    pub stop_loss_on_fill: Option<StopLossDetails>,
556
557    /// The `trailingStopLossOnFill` field.
558    #[serde(
559        rename = "trailingStopLossOnFill",
560        skip_serializing_if = "Option::is_none"
561    )]
562    pub trailing_stop_loss_on_fill: Option<TrailingStopLossDetails>,
563
564    /// The `tradeClientExtensions` field.
565    #[serde(
566        rename = "tradeClientExtensions",
567        skip_serializing_if = "Option::is_none"
568    )]
569    pub trade_client_extensions: Option<ClientExtensions>,
570
571    /// ID of the Transaction that filled this Order (only provided when the
572    /// Order's state is FILLED)
573    #[serde(
574        rename = "fillingTransactionID",
575        skip_serializing_if = "Option::is_none"
576    )]
577    pub filling_transaction_id: Option<TransactionId>,
578
579    /// Date/time when the Order was filled (only provided when the Order's
580    /// state is FILLED)
581    #[serde(rename = "filledTime", skip_serializing_if = "Option::is_none")]
582    pub filled_time: Option<DateTime>,
583
584    /// Trade ID of Trade opened when the Order was filled (only provided when
585    /// the Order's state is FILLED and a Trade was opened as a result of the
586    /// fill)
587    #[serde(rename = "tradeOpenedID", skip_serializing_if = "Option::is_none")]
588    pub trade_opened_id: Option<TradeId>,
589
590    /// Trade ID of Trade reduced when the Order was filled (only provided when
591    /// the Order's state is FILLED and a Trade was reduced as a result of the
592    /// fill)
593    #[serde(rename = "tradeReducedID", skip_serializing_if = "Option::is_none")]
594    pub trade_reduced_id: Option<TradeId>,
595
596    /// Trade IDs of Trades closed when the Order was filled (only provided when
597    /// the Order's state is FILLED and one or more Trades were closed as a
598    /// result of the fill)
599    #[serde(
600        rename = "tradeClosedIDs",
601        default,
602        skip_serializing_if = "Vec::is_empty"
603    )]
604    pub trade_closed_ids: Vec<TradeId>,
605
606    /// ID of the Transaction that cancelled the Order (only provided when the
607    /// Order's state is CANCELLED)
608    #[serde(
609        rename = "cancellingTransactionID",
610        skip_serializing_if = "Option::is_none"
611    )]
612    pub cancelling_transaction_id: Option<TransactionId>,
613
614    /// Date/time when the Order was cancelled (only provided when the state of
615    /// the Order is CANCELLED)
616    #[serde(rename = "cancelledTime", skip_serializing_if = "Option::is_none")]
617    pub cancelled_time: Option<DateTime>,
618}
619
620/// A LimitOrder is an order that is created with a price threshold, and will
621/// only be filled by a price that is equal to or better than the threshold.
622#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
623#[non_exhaustive]
624pub struct LimitOrder {
625    /// The Order's identifier, unique within the Order's Account.
626    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
627    pub id: Option<OrderId>,
628
629    /// The time when the Order was created.
630    #[serde(rename = "createTime", skip_serializing_if = "Option::is_none")]
631    pub create_time: Option<DateTime>,
632
633    /// The `state` field.
634    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
635    pub state: Option<OrderState>,
636
637    /// The `clientExtensions` field.
638    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
639    pub client_extensions: Option<ClientExtensions>,
640
641    // type is pinned to "LIMIT" by the enum wrapper
642    /// The Limit Order's Instrument.
643    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
644    pub instrument: Option<InstrumentName>,
645
646    /// The quantity requested to be filled by the Limit Order. A posititive
647    /// number of units results in a long Order, and a negative number of units
648    /// results in a short Order.
649    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
650    pub units: Option<DecimalNumber>,
651
652    /// The price threshold specified for the Limit Order. The Limit Order will
653    /// only be filled by a market price that is equal to or better than this
654    /// price.
655    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
656    pub price: Option<PriceValue>,
657
658    /// The `timeInForce` field.
659    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
660    pub time_in_force: Option<LimitOrderTimeInForce>,
661
662    /// The date/time when the Limit Order will be cancelled if its timeInForce
663    /// is "GTD".
664    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
665    pub gtd_time: Option<DateTime>,
666
667    /// The `positionFill` field.
668    #[serde(rename = "positionFill", skip_serializing_if = "Option::is_none")]
669    pub position_fill: Option<OrderPositionFill>,
670
671    /// The `triggerCondition` field.
672    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
673    pub trigger_condition: Option<OrderTriggerCondition>,
674
675    /// The `takeProfitOnFill` field.
676    #[serde(rename = "takeProfitOnFill", skip_serializing_if = "Option::is_none")]
677    pub take_profit_on_fill: Option<TakeProfitDetails>,
678
679    /// The `stopLossOnFill` field.
680    #[serde(rename = "stopLossOnFill", skip_serializing_if = "Option::is_none")]
681    pub stop_loss_on_fill: Option<StopLossDetails>,
682
683    /// The `trailingStopLossOnFill` field.
684    #[serde(
685        rename = "trailingStopLossOnFill",
686        skip_serializing_if = "Option::is_none"
687    )]
688    pub trailing_stop_loss_on_fill: Option<TrailingStopLossDetails>,
689
690    /// The `tradeClientExtensions` field.
691    #[serde(
692        rename = "tradeClientExtensions",
693        skip_serializing_if = "Option::is_none"
694    )]
695    pub trade_client_extensions: Option<ClientExtensions>,
696
697    /// ID of the Transaction that filled this Order (only provided when the
698    /// Order's state is FILLED)
699    #[serde(
700        rename = "fillingTransactionID",
701        skip_serializing_if = "Option::is_none"
702    )]
703    pub filling_transaction_id: Option<TransactionId>,
704
705    /// Date/time when the Order was filled (only provided when the Order's
706    /// state is FILLED)
707    #[serde(rename = "filledTime", skip_serializing_if = "Option::is_none")]
708    pub filled_time: Option<DateTime>,
709
710    /// Trade ID of Trade opened when the Order was filled (only provided when
711    /// the Order's state is FILLED and a Trade was opened as a result of the
712    /// fill)
713    #[serde(rename = "tradeOpenedID", skip_serializing_if = "Option::is_none")]
714    pub trade_opened_id: Option<TradeId>,
715
716    /// Trade ID of Trade reduced when the Order was filled (only provided when
717    /// the Order's state is FILLED and a Trade was reduced as a result of the
718    /// fill)
719    #[serde(rename = "tradeReducedID", skip_serializing_if = "Option::is_none")]
720    pub trade_reduced_id: Option<TradeId>,
721
722    /// Trade IDs of Trades closed when the Order was filled (only provided when
723    /// the Order's state is FILLED and one or more Trades were closed as a
724    /// result of the fill)
725    #[serde(
726        rename = "tradeClosedIDs",
727        default,
728        skip_serializing_if = "Vec::is_empty"
729    )]
730    pub trade_closed_ids: Vec<TradeId>,
731
732    /// ID of the Transaction that cancelled the Order (only provided when the
733    /// Order's state is CANCELLED)
734    #[serde(
735        rename = "cancellingTransactionID",
736        skip_serializing_if = "Option::is_none"
737    )]
738    pub cancelling_transaction_id: Option<TransactionId>,
739
740    /// Date/time when the Order was cancelled (only provided when the state of
741    /// the Order is CANCELLED)
742    #[serde(rename = "cancelledTime", skip_serializing_if = "Option::is_none")]
743    pub cancelled_time: Option<DateTime>,
744
745    /// The ID of the Order that was replaced by this Order (only provided if
746    /// this Order was created as part of a cancel/replace).
747    #[serde(rename = "replacesOrderID", skip_serializing_if = "Option::is_none")]
748    pub replaces_order_id: Option<OrderId>,
749
750    /// The ID of the Order that replaced this Order (only provided if this
751    /// Order was cancelled as part of a cancel/replace).
752    #[serde(rename = "replacedByOrderID", skip_serializing_if = "Option::is_none")]
753    pub replaced_by_order_id: Option<OrderId>,
754
755    /// How the Order may be partially filled. Observed value: "DEFAULT_FILL".
756    /// This field is returned by the live v20 API but is not present in OANDA's
757    /// official documentation.
758    #[serde(rename = "partialFill", skip_serializing_if = "Option::is_none")]
759    pub partial_fill: Option<String>,
760}
761
762/// A StopOrder is an order that is created with a price threshold, and will
763/// only be filled by a price that is equal to or worse than the threshold.
764#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
765#[non_exhaustive]
766pub struct StopOrder {
767    /// The Order's identifier, unique within the Order's Account.
768    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
769    pub id: Option<OrderId>,
770
771    /// The time when the Order was created.
772    #[serde(rename = "createTime", skip_serializing_if = "Option::is_none")]
773    pub create_time: Option<DateTime>,
774
775    /// The `state` field.
776    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
777    pub state: Option<OrderState>,
778
779    /// The `clientExtensions` field.
780    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
781    pub client_extensions: Option<ClientExtensions>,
782
783    // type is pinned to "STOP" by the enum wrapper
784    /// The Stop Order's Instrument.
785    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
786    pub instrument: Option<InstrumentName>,
787
788    /// The quantity requested to be filled by the Stop Order. A posititive
789    /// number of units results in a long Order, and a negative number of units
790    /// results in a short Order.
791    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
792    pub units: Option<DecimalNumber>,
793
794    /// The price threshold specified for the Stop Order. The Stop Order will
795    /// only be filled by a market price that is equal to or worse than this
796    /// price.
797    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
798    pub price: Option<PriceValue>,
799
800    /// The worst market price that may be used to fill this Stop Order. If the
801    /// market gaps and crosses through both the price and the priceBound, the
802    /// Stop Order will be cancelled instead of being filled.
803    #[serde(rename = "priceBound", skip_serializing_if = "Option::is_none")]
804    pub price_bound: Option<PriceValue>,
805
806    /// The `timeInForce` field.
807    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
808    pub time_in_force: Option<StopOrderTimeInForce>,
809
810    /// The date/time when the Stop Order will be cancelled if its timeInForce
811    /// is "GTD".
812    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
813    pub gtd_time: Option<DateTime>,
814
815    /// The `positionFill` field.
816    #[serde(rename = "positionFill", skip_serializing_if = "Option::is_none")]
817    pub position_fill: Option<OrderPositionFill>,
818
819    /// The `triggerCondition` field.
820    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
821    pub trigger_condition: Option<OrderTriggerCondition>,
822
823    /// The `takeProfitOnFill` field.
824    #[serde(rename = "takeProfitOnFill", skip_serializing_if = "Option::is_none")]
825    pub take_profit_on_fill: Option<TakeProfitDetails>,
826
827    /// The `stopLossOnFill` field.
828    #[serde(rename = "stopLossOnFill", skip_serializing_if = "Option::is_none")]
829    pub stop_loss_on_fill: Option<StopLossDetails>,
830
831    /// The `trailingStopLossOnFill` field.
832    #[serde(
833        rename = "trailingStopLossOnFill",
834        skip_serializing_if = "Option::is_none"
835    )]
836    pub trailing_stop_loss_on_fill: Option<TrailingStopLossDetails>,
837
838    /// The `tradeClientExtensions` field.
839    #[serde(
840        rename = "tradeClientExtensions",
841        skip_serializing_if = "Option::is_none"
842    )]
843    pub trade_client_extensions: Option<ClientExtensions>,
844
845    /// ID of the Transaction that filled this Order (only provided when the
846    /// Order's state is FILLED)
847    #[serde(
848        rename = "fillingTransactionID",
849        skip_serializing_if = "Option::is_none"
850    )]
851    pub filling_transaction_id: Option<TransactionId>,
852
853    /// Date/time when the Order was filled (only provided when the Order's
854    /// state is FILLED)
855    #[serde(rename = "filledTime", skip_serializing_if = "Option::is_none")]
856    pub filled_time: Option<DateTime>,
857
858    /// Trade ID of Trade opened when the Order was filled (only provided when
859    /// the Order's state is FILLED and a Trade was opened as a result of the
860    /// fill)
861    #[serde(rename = "tradeOpenedID", skip_serializing_if = "Option::is_none")]
862    pub trade_opened_id: Option<TradeId>,
863
864    /// Trade ID of Trade reduced when the Order was filled (only provided when
865    /// the Order's state is FILLED and a Trade was reduced as a result of the
866    /// fill)
867    #[serde(rename = "tradeReducedID", skip_serializing_if = "Option::is_none")]
868    pub trade_reduced_id: Option<TradeId>,
869
870    /// Trade IDs of Trades closed when the Order was filled (only provided when
871    /// the Order's state is FILLED and one or more Trades were closed as a
872    /// result of the fill)
873    #[serde(
874        rename = "tradeClosedIDs",
875        default,
876        skip_serializing_if = "Vec::is_empty"
877    )]
878    pub trade_closed_ids: Vec<TradeId>,
879
880    /// ID of the Transaction that cancelled the Order (only provided when the
881    /// Order's state is CANCELLED)
882    #[serde(
883        rename = "cancellingTransactionID",
884        skip_serializing_if = "Option::is_none"
885    )]
886    pub cancelling_transaction_id: Option<TransactionId>,
887
888    /// Date/time when the Order was cancelled (only provided when the state of
889    /// the Order is CANCELLED)
890    #[serde(rename = "cancelledTime", skip_serializing_if = "Option::is_none")]
891    pub cancelled_time: Option<DateTime>,
892
893    /// The ID of the Order that was replaced by this Order (only provided if
894    /// this Order was created as part of a cancel/replace).
895    #[serde(rename = "replacesOrderID", skip_serializing_if = "Option::is_none")]
896    pub replaces_order_id: Option<OrderId>,
897
898    /// The ID of the Order that replaced this Order (only provided if this
899    /// Order was cancelled as part of a cancel/replace).
900    #[serde(rename = "replacedByOrderID", skip_serializing_if = "Option::is_none")]
901    pub replaced_by_order_id: Option<OrderId>,
902
903    /// How the Order may be partially filled. Observed value: "DEFAULT_FILL".
904    /// This field is returned by the live v20 API but is not present in OANDA's
905    /// official documentation.
906    #[serde(rename = "partialFill", skip_serializing_if = "Option::is_none")]
907    pub partial_fill: Option<String>,
908
909    /// The price trigger mode of the Order. Observed value: "TOP_OF_BOOK". This
910    /// field is returned by the live v20 API but is not present in OANDA's
911    /// official documentation.
912    #[serde(rename = "triggerMode", skip_serializing_if = "Option::is_none")]
913    pub trigger_mode: Option<String>,
914}
915
916/// A MarketIfTouchedOrder is an order that is created with a price threshold,
917/// and will only be filled by a market price that is touches or crosses the
918/// threshold.
919#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
920#[non_exhaustive]
921pub struct MarketIfTouchedOrder {
922    /// The Order's identifier, unique within the Order's Account.
923    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
924    pub id: Option<OrderId>,
925
926    /// The time when the Order was created.
927    #[serde(rename = "createTime", skip_serializing_if = "Option::is_none")]
928    pub create_time: Option<DateTime>,
929
930    /// The `state` field.
931    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
932    pub state: Option<OrderState>,
933
934    /// The `clientExtensions` field.
935    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
936    pub client_extensions: Option<ClientExtensions>,
937
938    // type is pinned to "MARKET_IF_TOUCHED" by the enum wrapper
939    /// The MarketIfTouched Order's Instrument.
940    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
941    pub instrument: Option<InstrumentName>,
942
943    /// The quantity requested to be filled by the MarketIfTouched Order. A
944    /// posititive number of units results in a long Order, and a negative
945    /// number of units results in a short Order.
946    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
947    pub units: Option<DecimalNumber>,
948
949    /// The price threshold specified for the MarketIfTouched Order. The
950    /// MarketIfTouched Order will only be filled by a market price that crosses
951    /// this price from the direction of the market price at the time when the
952    /// Order was created (the initialMarketPrice). Depending on the value of
953    /// the Order's price and initialMarketPrice, the MarketIfTouchedOrder will
954    /// behave like a Limit or a Stop Order.
955    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
956    pub price: Option<PriceValue>,
957
958    /// The worst market price that may be used to fill this MarketIfTouched
959    /// Order.
960    #[serde(rename = "priceBound", skip_serializing_if = "Option::is_none")]
961    pub price_bound: Option<PriceValue>,
962
963    /// The `timeInForce` field.
964    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
965    pub time_in_force: Option<MarketIfTouchedOrderTimeInForce>,
966
967    /// The date/time when the MarketIfTouched Order will be cancelled if its
968    /// timeInForce is "GTD".
969    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
970    pub gtd_time: Option<DateTime>,
971
972    /// The `positionFill` field.
973    #[serde(rename = "positionFill", skip_serializing_if = "Option::is_none")]
974    pub position_fill: Option<OrderPositionFill>,
975
976    /// The `triggerCondition` field.
977    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
978    pub trigger_condition: Option<OrderTriggerCondition>,
979
980    /// The Market price at the time when the MarketIfTouched Order was created.
981    #[serde(rename = "initialMarketPrice", skip_serializing_if = "Option::is_none")]
982    pub initial_market_price: Option<PriceValue>,
983
984    /// The `takeProfitOnFill` field.
985    #[serde(rename = "takeProfitOnFill", skip_serializing_if = "Option::is_none")]
986    pub take_profit_on_fill: Option<TakeProfitDetails>,
987
988    /// The `stopLossOnFill` field.
989    #[serde(rename = "stopLossOnFill", skip_serializing_if = "Option::is_none")]
990    pub stop_loss_on_fill: Option<StopLossDetails>,
991
992    /// The `trailingStopLossOnFill` field.
993    #[serde(
994        rename = "trailingStopLossOnFill",
995        skip_serializing_if = "Option::is_none"
996    )]
997    pub trailing_stop_loss_on_fill: Option<TrailingStopLossDetails>,
998
999    /// The `tradeClientExtensions` field.
1000    #[serde(
1001        rename = "tradeClientExtensions",
1002        skip_serializing_if = "Option::is_none"
1003    )]
1004    pub trade_client_extensions: Option<ClientExtensions>,
1005
1006    /// ID of the Transaction that filled this Order (only provided when the
1007    /// Order's state is FILLED)
1008    #[serde(
1009        rename = "fillingTransactionID",
1010        skip_serializing_if = "Option::is_none"
1011    )]
1012    pub filling_transaction_id: Option<TransactionId>,
1013
1014    /// Date/time when the Order was filled (only provided when the Order's
1015    /// state is FILLED)
1016    #[serde(rename = "filledTime", skip_serializing_if = "Option::is_none")]
1017    pub filled_time: Option<DateTime>,
1018
1019    /// Trade ID of Trade opened when the Order was filled (only provided when
1020    /// the Order's state is FILLED and a Trade was opened as a result of the
1021    /// fill)
1022    #[serde(rename = "tradeOpenedID", skip_serializing_if = "Option::is_none")]
1023    pub trade_opened_id: Option<TradeId>,
1024
1025    /// Trade ID of Trade reduced when the Order was filled (only provided when
1026    /// the Order's state is FILLED and a Trade was reduced as a result of the
1027    /// fill)
1028    #[serde(rename = "tradeReducedID", skip_serializing_if = "Option::is_none")]
1029    pub trade_reduced_id: Option<TradeId>,
1030
1031    /// Trade IDs of Trades closed when the Order was filled (only provided when
1032    /// the Order's state is FILLED and one or more Trades were closed as a
1033    /// result of the fill)
1034    #[serde(
1035        rename = "tradeClosedIDs",
1036        default,
1037        skip_serializing_if = "Vec::is_empty"
1038    )]
1039    pub trade_closed_ids: Vec<TradeId>,
1040
1041    /// ID of the Transaction that cancelled the Order (only provided when the
1042    /// Order's state is CANCELLED)
1043    #[serde(
1044        rename = "cancellingTransactionID",
1045        skip_serializing_if = "Option::is_none"
1046    )]
1047    pub cancelling_transaction_id: Option<TransactionId>,
1048
1049    /// Date/time when the Order was cancelled (only provided when the state of
1050    /// the Order is CANCELLED)
1051    #[serde(rename = "cancelledTime", skip_serializing_if = "Option::is_none")]
1052    pub cancelled_time: Option<DateTime>,
1053
1054    /// The ID of the Order that was replaced by this Order (only provided if
1055    /// this Order was created as part of a cancel/replace).
1056    #[serde(rename = "replacesOrderID", skip_serializing_if = "Option::is_none")]
1057    pub replaces_order_id: Option<OrderId>,
1058
1059    /// The ID of the Order that replaced this Order (only provided if this
1060    /// Order was cancelled as part of a cancel/replace).
1061    #[serde(rename = "replacedByOrderID", skip_serializing_if = "Option::is_none")]
1062    pub replaced_by_order_id: Option<OrderId>,
1063
1064    /// How the Order may be partially filled. Observed value: "DEFAULT_FILL".
1065    /// This field is returned by the live v20 API but is not present in OANDA's
1066    /// official documentation.
1067    #[serde(rename = "partialFill", skip_serializing_if = "Option::is_none")]
1068    pub partial_fill: Option<String>,
1069}
1070
1071/// A TakeProfitOrder is an order that is linked to an open Trade and created
1072/// with a price threshold. The Order will be filled (closing the Trade) by the
1073/// first price that is equal to or better than the threshold. A TakeProfitOrder
1074/// cannot be used to open a new Position.
1075#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1076#[non_exhaustive]
1077pub struct TakeProfitOrder {
1078    /// The Order's identifier, unique within the Order's Account.
1079    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
1080    pub id: Option<OrderId>,
1081
1082    /// The time when the Order was created.
1083    #[serde(rename = "createTime", skip_serializing_if = "Option::is_none")]
1084    pub create_time: Option<DateTime>,
1085
1086    /// The `state` field.
1087    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
1088    pub state: Option<OrderState>,
1089
1090    /// The `clientExtensions` field.
1091    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
1092    pub client_extensions: Option<ClientExtensions>,
1093
1094    // type is pinned to "TAKE_PROFIT" by the enum wrapper
1095    /// The ID of the Trade to close when the price threshold is breached.
1096    #[serde(rename = "tradeID", skip_serializing_if = "Option::is_none")]
1097    pub trade_id: Option<TradeId>,
1098
1099    /// The client ID of the Trade to be closed when the price threshold is
1100    /// breached.
1101    #[serde(rename = "clientTradeID", skip_serializing_if = "Option::is_none")]
1102    pub client_trade_id: Option<String>,
1103
1104    /// The price threshold specified for the TakeProfit Order. The associated
1105    /// Trade will be closed by a market price that is equal to or better than
1106    /// this threshold.
1107    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
1108    pub price: Option<PriceValue>,
1109
1110    /// The `timeInForce` field.
1111    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
1112    pub time_in_force: Option<TakeProfitOrderTimeInForce>,
1113
1114    /// The date/time when the TakeProfit Order will be cancelled if its
1115    /// timeInForce is "GTD".
1116    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
1117    pub gtd_time: Option<DateTime>,
1118
1119    /// The `triggerCondition` field.
1120    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
1121    pub trigger_condition: Option<OrderTriggerCondition>,
1122
1123    /// ID of the Transaction that filled this Order (only provided when the
1124    /// Order's state is FILLED)
1125    #[serde(
1126        rename = "fillingTransactionID",
1127        skip_serializing_if = "Option::is_none"
1128    )]
1129    pub filling_transaction_id: Option<TransactionId>,
1130
1131    /// Date/time when the Order was filled (only provided when the Order's
1132    /// state is FILLED)
1133    #[serde(rename = "filledTime", skip_serializing_if = "Option::is_none")]
1134    pub filled_time: Option<DateTime>,
1135
1136    /// Trade ID of Trade opened when the Order was filled (only provided when
1137    /// the Order's state is FILLED and a Trade was opened as a result of the
1138    /// fill)
1139    #[serde(rename = "tradeOpenedID", skip_serializing_if = "Option::is_none")]
1140    pub trade_opened_id: Option<TradeId>,
1141
1142    /// Trade ID of Trade reduced when the Order was filled (only provided when
1143    /// the Order's state is FILLED and a Trade was reduced as a result of the
1144    /// fill)
1145    #[serde(rename = "tradeReducedID", skip_serializing_if = "Option::is_none")]
1146    pub trade_reduced_id: Option<TradeId>,
1147
1148    /// Trade IDs of Trades closed when the Order was filled (only provided when
1149    /// the Order's state is FILLED and one or more Trades were closed as a
1150    /// result of the fill)
1151    #[serde(
1152        rename = "tradeClosedIDs",
1153        default,
1154        skip_serializing_if = "Vec::is_empty"
1155    )]
1156    pub trade_closed_ids: Vec<TradeId>,
1157
1158    /// ID of the Transaction that cancelled the Order (only provided when the
1159    /// Order's state is CANCELLED)
1160    #[serde(
1161        rename = "cancellingTransactionID",
1162        skip_serializing_if = "Option::is_none"
1163    )]
1164    pub cancelling_transaction_id: Option<TransactionId>,
1165
1166    /// Date/time when the Order was cancelled (only provided when the state of
1167    /// the Order is CANCELLED)
1168    #[serde(rename = "cancelledTime", skip_serializing_if = "Option::is_none")]
1169    pub cancelled_time: Option<DateTime>,
1170
1171    /// The ID of the Order that was replaced by this Order (only provided if
1172    /// this Order was created as part of a cancel/replace).
1173    #[serde(rename = "replacesOrderID", skip_serializing_if = "Option::is_none")]
1174    pub replaces_order_id: Option<OrderId>,
1175
1176    /// The ID of the Order that replaced this Order (only provided if this
1177    /// Order was cancelled as part of a cancel/replace).
1178    #[serde(rename = "replacedByOrderID", skip_serializing_if = "Option::is_none")]
1179    pub replaced_by_order_id: Option<OrderId>,
1180}
1181
1182/// A StopLossOrder is an order that is linked to an open Trade and created with
1183/// a price threshold. The Order will be filled (closing the Trade) by the first
1184/// price that is equal to or worse than the threshold. A StopLossOrder cannot
1185/// be used to open a new Position.
1186#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1187#[non_exhaustive]
1188pub struct StopLossOrder {
1189    /// The Order's identifier, unique within the Order's Account.
1190    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
1191    pub id: Option<OrderId>,
1192
1193    /// The time when the Order was created.
1194    #[serde(rename = "createTime", skip_serializing_if = "Option::is_none")]
1195    pub create_time: Option<DateTime>,
1196
1197    /// The `state` field.
1198    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
1199    pub state: Option<OrderState>,
1200
1201    /// The `clientExtensions` field.
1202    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
1203    pub client_extensions: Option<ClientExtensions>,
1204
1205    // type is pinned to "STOP_LOSS" by the enum wrapper
1206    /// The premium that will be charged if the Stop Loss Order is guaranteed
1207    /// and the Order is filled at the guaranteed price. It is in price units
1208    /// and is charged for each unit of the Trade.
1209    #[serde(
1210        rename = "guaranteedExecutionPremium",
1211        skip_serializing_if = "Option::is_none"
1212    )]
1213    pub guaranteed_execution_premium: Option<DecimalNumber>,
1214
1215    /// The ID of the Trade to close when the price threshold is breached.
1216    #[serde(rename = "tradeID", skip_serializing_if = "Option::is_none")]
1217    pub trade_id: Option<TradeId>,
1218
1219    /// The client ID of the Trade to be closed when the price threshold is
1220    /// breached.
1221    #[serde(rename = "clientTradeID", skip_serializing_if = "Option::is_none")]
1222    pub client_trade_id: Option<String>,
1223
1224    /// The price threshold specified for the Stop Loss Order. If the guaranteed
1225    /// flag is false, the associated Trade will be closed by a market price
1226    /// that is equal to or worse than this threshold. If the flag is true the
1227    /// associated Trade will be closed at this price.
1228    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
1229    pub price: Option<PriceValue>,
1230
1231    /// Specifies the distance (in price units) from the Account's current price
1232    /// to use as the Stop Loss Order price. If the Trade is short the
1233    /// Instrument's bid price is used, and for long Trades the ask is used.
1234    #[serde(rename = "distance", skip_serializing_if = "Option::is_none")]
1235    pub distance: Option<DecimalNumber>,
1236
1237    /// The `timeInForce` field.
1238    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
1239    pub time_in_force: Option<StopLossOrderTimeInForce>,
1240
1241    /// The date/time when the StopLoss Order will be cancelled if its
1242    /// timeInForce is "GTD".
1243    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
1244    pub gtd_time: Option<DateTime>,
1245
1246    /// The `triggerCondition` field.
1247    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
1248    pub trigger_condition: Option<OrderTriggerCondition>,
1249
1250    /// Flag indicating that the Stop Loss Order is guaranteed. The default
1251    /// value depends on the GuaranteedStopLossOrderMode of the account, if it
1252    /// is REQUIRED, the default will be true, for DISABLED or ENABLED the
1253    /// default is false.
1254    #[serde(rename = "guaranteed", skip_serializing_if = "Option::is_none")]
1255    pub guaranteed: Option<bool>,
1256
1257    /// ID of the Transaction that filled this Order (only provided when the
1258    /// Order's state is FILLED)
1259    #[serde(
1260        rename = "fillingTransactionID",
1261        skip_serializing_if = "Option::is_none"
1262    )]
1263    pub filling_transaction_id: Option<TransactionId>,
1264
1265    /// Date/time when the Order was filled (only provided when the Order's
1266    /// state is FILLED)
1267    #[serde(rename = "filledTime", skip_serializing_if = "Option::is_none")]
1268    pub filled_time: Option<DateTime>,
1269
1270    /// Trade ID of Trade opened when the Order was filled (only provided when
1271    /// the Order's state is FILLED and a Trade was opened as a result of the
1272    /// fill)
1273    #[serde(rename = "tradeOpenedID", skip_serializing_if = "Option::is_none")]
1274    pub trade_opened_id: Option<TradeId>,
1275
1276    /// Trade ID of Trade reduced when the Order was filled (only provided when
1277    /// the Order's state is FILLED and a Trade was reduced as a result of the
1278    /// fill)
1279    #[serde(rename = "tradeReducedID", skip_serializing_if = "Option::is_none")]
1280    pub trade_reduced_id: Option<TradeId>,
1281
1282    /// Trade IDs of Trades closed when the Order was filled (only provided when
1283    /// the Order's state is FILLED and one or more Trades were closed as a
1284    /// result of the fill)
1285    #[serde(
1286        rename = "tradeClosedIDs",
1287        default,
1288        skip_serializing_if = "Vec::is_empty"
1289    )]
1290    pub trade_closed_ids: Vec<TradeId>,
1291
1292    /// ID of the Transaction that cancelled the Order (only provided when the
1293    /// Order's state is CANCELLED)
1294    #[serde(
1295        rename = "cancellingTransactionID",
1296        skip_serializing_if = "Option::is_none"
1297    )]
1298    pub cancelling_transaction_id: Option<TransactionId>,
1299
1300    /// Date/time when the Order was cancelled (only provided when the state of
1301    /// the Order is CANCELLED)
1302    #[serde(rename = "cancelledTime", skip_serializing_if = "Option::is_none")]
1303    pub cancelled_time: Option<DateTime>,
1304
1305    /// The ID of the Order that was replaced by this Order (only provided if
1306    /// this Order was created as part of a cancel/replace).
1307    #[serde(rename = "replacesOrderID", skip_serializing_if = "Option::is_none")]
1308    pub replaces_order_id: Option<OrderId>,
1309
1310    /// The ID of the Order that replaced this Order (only provided if this
1311    /// Order was cancelled as part of a cancel/replace).
1312    #[serde(rename = "replacedByOrderID", skip_serializing_if = "Option::is_none")]
1313    pub replaced_by_order_id: Option<OrderId>,
1314
1315    /// The price trigger mode of the Order. Observed value: "TOP_OF_BOOK". This
1316    /// field is returned by the live v20 API but is not present in OANDA's
1317    /// official documentation.
1318    #[serde(rename = "triggerMode", skip_serializing_if = "Option::is_none")]
1319    pub trigger_mode: Option<String>,
1320}
1321
1322/// A TrailingStopLossOrder is an order that is linked to an open Trade and
1323/// created with a price distance. The price distance is used to calculate a
1324/// trailing stop value for the order that is in the losing direction from the
1325/// market price at the time of the order's creation. The trailing stop value
1326/// will follow the market price as it moves in the winning direction, and the
1327/// order will filled (closing the Trade) by the first price that is equal to or
1328/// worse than the trailing stop value. A TrailingStopLossOrder cannot be used
1329/// to open a new Position.
1330#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1331#[non_exhaustive]
1332pub struct TrailingStopLossOrder {
1333    /// The Order's identifier, unique within the Order's Account.
1334    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
1335    pub id: Option<OrderId>,
1336
1337    /// The time when the Order was created.
1338    #[serde(rename = "createTime", skip_serializing_if = "Option::is_none")]
1339    pub create_time: Option<DateTime>,
1340
1341    /// The `state` field.
1342    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
1343    pub state: Option<OrderState>,
1344
1345    /// The `clientExtensions` field.
1346    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
1347    pub client_extensions: Option<ClientExtensions>,
1348
1349    // type is pinned to "TRAILING_STOP_LOSS" by the enum wrapper
1350    /// The ID of the Trade to close when the price threshold is breached.
1351    #[serde(rename = "tradeID", skip_serializing_if = "Option::is_none")]
1352    pub trade_id: Option<TradeId>,
1353
1354    /// The client ID of the Trade to be closed when the price threshold is
1355    /// breached.
1356    #[serde(rename = "clientTradeID", skip_serializing_if = "Option::is_none")]
1357    pub client_trade_id: Option<String>,
1358
1359    /// The price distance (in price units) specified for the TrailingStopLoss
1360    /// Order.
1361    #[serde(rename = "distance", skip_serializing_if = "Option::is_none")]
1362    pub distance: Option<DecimalNumber>,
1363
1364    /// The `timeInForce` field.
1365    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
1366    pub time_in_force: Option<TrailingStopLossOrderTimeInForce>,
1367
1368    /// The date/time when the StopLoss Order will be cancelled if its
1369    /// timeInForce is "GTD".
1370    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
1371    pub gtd_time: Option<DateTime>,
1372
1373    /// The `triggerCondition` field.
1374    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
1375    pub trigger_condition: Option<OrderTriggerCondition>,
1376
1377    /// The trigger price for the Trailing Stop Loss Order. The trailing stop
1378    /// value will trail (follow) the market price by the TSL order's configured
1379    /// "distance" as the market price moves in the winning direction. If the
1380    /// market price moves to a level that is equal to or worse than the
1381    /// trailing stop value, the order will be filled and the Trade will be
1382    /// closed.
1383    #[serde(rename = "trailingStopValue", skip_serializing_if = "Option::is_none")]
1384    pub trailing_stop_value: Option<PriceValue>,
1385
1386    /// ID of the Transaction that filled this Order (only provided when the
1387    /// Order's state is FILLED)
1388    #[serde(
1389        rename = "fillingTransactionID",
1390        skip_serializing_if = "Option::is_none"
1391    )]
1392    pub filling_transaction_id: Option<TransactionId>,
1393
1394    /// Date/time when the Order was filled (only provided when the Order's
1395    /// state is FILLED)
1396    #[serde(rename = "filledTime", skip_serializing_if = "Option::is_none")]
1397    pub filled_time: Option<DateTime>,
1398
1399    /// Trade ID of Trade opened when the Order was filled (only provided when
1400    /// the Order's state is FILLED and a Trade was opened as a result of the
1401    /// fill)
1402    #[serde(rename = "tradeOpenedID", skip_serializing_if = "Option::is_none")]
1403    pub trade_opened_id: Option<TradeId>,
1404
1405    /// Trade ID of Trade reduced when the Order was filled (only provided when
1406    /// the Order's state is FILLED and a Trade was reduced as a result of the
1407    /// fill)
1408    #[serde(rename = "tradeReducedID", skip_serializing_if = "Option::is_none")]
1409    pub trade_reduced_id: Option<TradeId>,
1410
1411    /// Trade IDs of Trades closed when the Order was filled (only provided when
1412    /// the Order's state is FILLED and one or more Trades were closed as a
1413    /// result of the fill)
1414    #[serde(
1415        rename = "tradeClosedIDs",
1416        default,
1417        skip_serializing_if = "Vec::is_empty"
1418    )]
1419    pub trade_closed_ids: Vec<TradeId>,
1420
1421    /// ID of the Transaction that cancelled the Order (only provided when the
1422    /// Order's state is CANCELLED)
1423    #[serde(
1424        rename = "cancellingTransactionID",
1425        skip_serializing_if = "Option::is_none"
1426    )]
1427    pub cancelling_transaction_id: Option<TransactionId>,
1428
1429    /// Date/time when the Order was cancelled (only provided when the state of
1430    /// the Order is CANCELLED)
1431    #[serde(rename = "cancelledTime", skip_serializing_if = "Option::is_none")]
1432    pub cancelled_time: Option<DateTime>,
1433
1434    /// The ID of the Order that was replaced by this Order (only provided if
1435    /// this Order was created as part of a cancel/replace).
1436    #[serde(rename = "replacesOrderID", skip_serializing_if = "Option::is_none")]
1437    pub replaces_order_id: Option<OrderId>,
1438
1439    /// The ID of the Order that replaced this Order (only provided if this
1440    /// Order was cancelled as part of a cancel/replace).
1441    #[serde(rename = "replacedByOrderID", skip_serializing_if = "Option::is_none")]
1442    pub replaced_by_order_id: Option<OrderId>,
1443}
1444
1445/// A FixedPriceOrder is an order that is filled immediately upon creation using
1446/// a fixed price.
1447#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1448#[non_exhaustive]
1449pub struct FixedPriceOrder {
1450    /// The Order's identifier, unique within the Order's Account.
1451    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
1452    pub id: Option<OrderId>,
1453
1454    /// The time when the Order was created.
1455    #[serde(rename = "createTime", skip_serializing_if = "Option::is_none")]
1456    pub create_time: Option<DateTime>,
1457
1458    /// The `state` field.
1459    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
1460    pub state: Option<OrderState>,
1461
1462    /// The `clientExtensions` field.
1463    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
1464    pub client_extensions: Option<ClientExtensions>,
1465
1466    // type is pinned to "FIXED_PRICE" by the enum wrapper
1467    /// The Fixed Price Order's Instrument.
1468    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
1469    pub instrument: Option<InstrumentName>,
1470
1471    /// The quantity requested to be filled by the Fixed Price Order. A
1472    /// posititive number of units results in a long Order, and a negative
1473    /// number of units results in a short Order.
1474    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
1475    pub units: Option<DecimalNumber>,
1476
1477    /// The price specified for the Fixed Price Order. This price is the exact
1478    /// price that the Fixed Price Order will be filled at.
1479    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
1480    pub price: Option<PriceValue>,
1481
1482    /// The `positionFill` field.
1483    #[serde(rename = "positionFill", skip_serializing_if = "Option::is_none")]
1484    pub position_fill: Option<OrderPositionFill>,
1485
1486    /// The `tradeState` field.
1487    #[serde(rename = "tradeState", skip_serializing_if = "Option::is_none")]
1488    pub trade_state: Option<TradeState>,
1489
1490    /// The `takeProfitOnFill` field.
1491    #[serde(rename = "takeProfitOnFill", skip_serializing_if = "Option::is_none")]
1492    pub take_profit_on_fill: Option<TakeProfitDetails>,
1493
1494    /// The `stopLossOnFill` field.
1495    #[serde(rename = "stopLossOnFill", skip_serializing_if = "Option::is_none")]
1496    pub stop_loss_on_fill: Option<StopLossDetails>,
1497
1498    /// The `trailingStopLossOnFill` field.
1499    #[serde(
1500        rename = "trailingStopLossOnFill",
1501        skip_serializing_if = "Option::is_none"
1502    )]
1503    pub trailing_stop_loss_on_fill: Option<TrailingStopLossDetails>,
1504
1505    /// The `tradeClientExtensions` field.
1506    #[serde(
1507        rename = "tradeClientExtensions",
1508        skip_serializing_if = "Option::is_none"
1509    )]
1510    pub trade_client_extensions: Option<ClientExtensions>,
1511
1512    /// ID of the Transaction that filled this Order (only provided when the
1513    /// Order's state is FILLED)
1514    #[serde(
1515        rename = "fillingTransactionID",
1516        skip_serializing_if = "Option::is_none"
1517    )]
1518    pub filling_transaction_id: Option<TransactionId>,
1519
1520    /// Date/time when the Order was filled (only provided when the Order's
1521    /// state is FILLED)
1522    #[serde(rename = "filledTime", skip_serializing_if = "Option::is_none")]
1523    pub filled_time: Option<DateTime>,
1524
1525    /// Trade ID of Trade opened when the Order was filled (only provided when
1526    /// the Order's state is FILLED and a Trade was opened as a result of the
1527    /// fill)
1528    #[serde(rename = "tradeOpenedID", skip_serializing_if = "Option::is_none")]
1529    pub trade_opened_id: Option<TradeId>,
1530
1531    /// Trade ID of Trade reduced when the Order was filled (only provided when
1532    /// the Order's state is FILLED and a Trade was reduced as a result of the
1533    /// fill)
1534    #[serde(rename = "tradeReducedID", skip_serializing_if = "Option::is_none")]
1535    pub trade_reduced_id: Option<TradeId>,
1536
1537    /// Trade IDs of Trades closed when the Order was filled (only provided when
1538    /// the Order's state is FILLED and one or more Trades were closed as a
1539    /// result of the fill)
1540    #[serde(
1541        rename = "tradeClosedIDs",
1542        default,
1543        skip_serializing_if = "Vec::is_empty"
1544    )]
1545    pub trade_closed_ids: Vec<TradeId>,
1546
1547    /// ID of the Transaction that cancelled the Order (only provided when the
1548    /// Order's state is CANCELLED)
1549    #[serde(
1550        rename = "cancellingTransactionID",
1551        skip_serializing_if = "Option::is_none"
1552    )]
1553    pub cancelling_transaction_id: Option<TransactionId>,
1554
1555    /// Date/time when the Order was cancelled (only provided when the state of
1556    /// the Order is CANCELLED)
1557    #[serde(rename = "cancelledTime", skip_serializing_if = "Option::is_none")]
1558    pub cancelled_time: Option<DateTime>,
1559}
1560
1561string_enum! {
1562    /// The reason that the Market Order was created to perform a margin
1563    /// closeout.
1564    pub enum MarketOrderMarginCloseoutReason {
1565        MarginCheckViolation => "MARGIN_CHECK_VIOLATION",
1566        RegulatoryMarginCallViolation => "REGULATORY_MARGIN_CALL_VIOLATION",
1567        RegulatoryMarginCheckViolation => "REGULATORY_MARGIN_CHECK_VIOLATION",
1568    }
1569}
1570
1571/// Internal helper running the same expression against the inner value of
1572/// every known [`Order`] variant.
1573macro_rules! for_each_order {
1574    ($order:expr, $inner:ident => $body:expr, $unknown:expr) => {
1575        match $order {
1576            Order::Market($inner) => $body,
1577            Order::Limit($inner) => $body,
1578            Order::Stop($inner) => $body,
1579            Order::MarketIfTouched($inner) => $body,
1580            Order::TakeProfit($inner) => $body,
1581            Order::StopLoss($inner) => $body,
1582            Order::TrailingStopLoss($inner) => $body,
1583            Order::FixedPrice($inner) => $body,
1584            Order::Unknown(_) => $unknown,
1585        }
1586    };
1587}
1588
1589/// An order in an account, discriminated by its `type` field.
1590///
1591/// Orders of a type unknown to this SDK deserialize into
1592/// [`Order::Unknown`] with the raw JSON preserved, so new order types on
1593/// OANDA's side never break deserialization.
1594#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1595#[serde(tag = "type")]
1596#[non_exhaustive]
1597pub enum Order {
1598    /// A market order (`MARKET`).
1599    #[serde(rename = "MARKET")]
1600    Market(MarketOrder),
1601    /// A limit order (`LIMIT`).
1602    #[serde(rename = "LIMIT")]
1603    Limit(LimitOrder),
1604    /// A stop order (`STOP`).
1605    #[serde(rename = "STOP")]
1606    Stop(StopOrder),
1607    /// A market-if-touched order (`MARKET_IF_TOUCHED`).
1608    #[serde(rename = "MARKET_IF_TOUCHED")]
1609    MarketIfTouched(MarketIfTouchedOrder),
1610    /// A take-profit order (`TAKE_PROFIT`).
1611    #[serde(rename = "TAKE_PROFIT")]
1612    TakeProfit(TakeProfitOrder),
1613    /// A stop-loss order (`STOP_LOSS`).
1614    #[serde(rename = "STOP_LOSS")]
1615    StopLoss(StopLossOrder),
1616    /// A trailing stop-loss order (`TRAILING_STOP_LOSS`).
1617    #[serde(rename = "TRAILING_STOP_LOSS")]
1618    TrailingStopLoss(TrailingStopLossOrder),
1619    /// A fixed-price order (`FIXED_PRICE`).
1620    #[serde(rename = "FIXED_PRICE")]
1621    FixedPrice(FixedPriceOrder),
1622    /// An order of a type not (yet) known to this SDK; the raw JSON object
1623    /// is preserved.
1624    #[serde(untagged)]
1625    Unknown(serde_json::Value),
1626}
1627
1628impl Order {
1629    /// The order's identifier, when known.
1630    pub fn id(&self) -> Option<&OrderId> {
1631        for_each_order!(self, o => o.id.as_ref(), None)
1632    }
1633
1634    /// The time the order was created, when known.
1635    pub fn create_time(&self) -> Option<&DateTime> {
1636        for_each_order!(self, o => o.create_time.as_ref(), None)
1637    }
1638
1639    /// The current state of the order, when known.
1640    pub fn state(&self) -> Option<&OrderState> {
1641        for_each_order!(self, o => o.state.as_ref(), None)
1642    }
1643
1644    /// The client extensions of the order, when known.
1645    pub fn client_extensions(&self) -> Option<&ClientExtensions> {
1646        for_each_order!(self, o => o.client_extensions.as_ref(), None)
1647    }
1648
1649    /// The wire name of the order's type (e.g. `MARKET`), or the raw
1650    /// `type` value for unknown orders.
1651    pub fn type_name(&self) -> Option<&str> {
1652        match self {
1653            Order::Market(_) => Some("MARKET"),
1654            Order::Limit(_) => Some("LIMIT"),
1655            Order::Stop(_) => Some("STOP"),
1656            Order::MarketIfTouched(_) => Some("MARKET_IF_TOUCHED"),
1657            Order::TakeProfit(_) => Some("TAKE_PROFIT"),
1658            Order::StopLoss(_) => Some("STOP_LOSS"),
1659            Order::TrailingStopLoss(_) => Some("TRAILING_STOP_LOSS"),
1660            Order::FixedPrice(_) => Some("FIXED_PRICE"),
1661            Order::Unknown(value) => value.get("type").and_then(serde_json::Value::as_str),
1662        }
1663    }
1664}
1665
1666/// The specification of an order to create, discriminated by its `type`
1667/// field. Built from one of the typed request structs:
1668///
1669/// ```
1670/// use oanda_rs::models::{MarketOrderRequest, OrderRequest, TakeProfitDetails};
1671///
1672/// let order: OrderRequest = MarketOrderRequest::new("EUR_USD", 100)
1673///     .take_profit_on_fill(TakeProfitDetails::at_price("1.1050".parse().unwrap()))
1674///     .into();
1675/// ```
1676#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1677#[serde(tag = "type")]
1678#[non_exhaustive]
1679pub enum OrderRequest {
1680    /// Create a market order (`MARKET`).
1681    #[serde(rename = "MARKET")]
1682    Market(MarketOrderRequest),
1683    /// Create a limit order (`LIMIT`).
1684    #[serde(rename = "LIMIT")]
1685    Limit(LimitOrderRequest),
1686    /// Create a stop order (`STOP`).
1687    #[serde(rename = "STOP")]
1688    Stop(StopOrderRequest),
1689    /// Create a market-if-touched order (`MARKET_IF_TOUCHED`).
1690    #[serde(rename = "MARKET_IF_TOUCHED")]
1691    MarketIfTouched(MarketIfTouchedOrderRequest),
1692    /// Create a take-profit order (`TAKE_PROFIT`).
1693    #[serde(rename = "TAKE_PROFIT")]
1694    TakeProfit(TakeProfitOrderRequest),
1695    /// Create a stop-loss order (`STOP_LOSS`).
1696    #[serde(rename = "STOP_LOSS")]
1697    StopLoss(StopLossOrderRequest),
1698    /// Create a trailing stop-loss order (`TRAILING_STOP_LOSS`).
1699    #[serde(rename = "TRAILING_STOP_LOSS")]
1700    TrailingStopLoss(TrailingStopLossOrderRequest),
1701}
1702
1703macro_rules! order_request_from {
1704    ($($struct:ident => $variant:ident,)+) => {
1705        $(
1706            impl From<$struct> for OrderRequest {
1707                fn from(request: $struct) -> Self {
1708                    OrderRequest::$variant(request)
1709                }
1710            }
1711        )+
1712    };
1713}
1714
1715order_request_from! {
1716    MarketOrderRequest => Market,
1717    LimitOrderRequest => Limit,
1718    StopOrderRequest => Stop,
1719    MarketIfTouchedOrderRequest => MarketIfTouched,
1720    TakeProfitOrderRequest => TakeProfit,
1721    StopLossOrderRequest => StopLoss,
1722    TrailingStopLossOrderRequest => TrailingStopLoss,
1723}
1724
1725/// Generates `with`-style optional-field setters for request builders.
1726macro_rules! setters {
1727    ($($(#[$meta:meta])* $name:ident: $ty:ty,)+) => {
1728        $(
1729            $(#[$meta])*
1730            pub fn $name(mut self, value: impl Into<$ty>) -> Self {
1731                self.$name = Some(value.into());
1732                self
1733            }
1734        )+
1735    };
1736}
1737
1738/// Specification of a market order. Required fields are set by
1739/// [`MarketOrderRequest::new`]; everything else is optional.
1740#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1741pub struct MarketOrderRequest {
1742    /// The market order's instrument.
1743    #[serde(rename = "instrument")]
1744    pub instrument: InstrumentName,
1745    /// The quantity requested to be filled. A positive number creates a
1746    /// long order, a negative number a short order.
1747    #[serde(rename = "units")]
1748    pub units: DecimalNumber,
1749    /// The time-in-force for the order (default `FOK`).
1750    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
1751    pub time_in_force: Option<MarketOrderTimeInForce>,
1752    /// The worst price the client is willing to have the order filled at.
1753    #[serde(rename = "priceBound", skip_serializing_if = "Option::is_none")]
1754    pub price_bound: Option<PriceValue>,
1755    /// How positions in the account are modified when the order fills
1756    /// (default `DEFAULT`).
1757    #[serde(rename = "positionFill", skip_serializing_if = "Option::is_none")]
1758    pub position_fill: Option<OrderPositionFill>,
1759    /// The client extensions to add to the order.
1760    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
1761    pub client_extensions: Option<ClientExtensions>,
1762    /// A take-profit order to create for a trade opened when the order
1763    /// fills.
1764    #[serde(rename = "takeProfitOnFill", skip_serializing_if = "Option::is_none")]
1765    pub take_profit_on_fill: Option<TakeProfitDetails>,
1766    /// A stop-loss order to create for a trade opened when the order fills.
1767    #[serde(rename = "stopLossOnFill", skip_serializing_if = "Option::is_none")]
1768    pub stop_loss_on_fill: Option<StopLossDetails>,
1769    /// A trailing stop-loss order to create for a trade opened when the
1770    /// order fills.
1771    #[serde(
1772        rename = "trailingStopLossOnFill",
1773        skip_serializing_if = "Option::is_none"
1774    )]
1775    pub trailing_stop_loss_on_fill: Option<TrailingStopLossDetails>,
1776    /// The client extensions to add to a trade opened when the order fills.
1777    #[serde(
1778        rename = "tradeClientExtensions",
1779        skip_serializing_if = "Option::is_none"
1780    )]
1781    pub trade_client_extensions: Option<ClientExtensions>,
1782}
1783
1784impl MarketOrderRequest {
1785    /// A market order for `units` of `instrument` (positive units = long,
1786    /// negative = short).
1787    pub fn new(instrument: impl Into<InstrumentName>, units: impl Into<DecimalNumber>) -> Self {
1788        MarketOrderRequest {
1789            instrument: instrument.into(),
1790            units: units.into(),
1791            time_in_force: None,
1792            price_bound: None,
1793            position_fill: None,
1794            client_extensions: None,
1795            take_profit_on_fill: None,
1796            stop_loss_on_fill: None,
1797            trailing_stop_loss_on_fill: None,
1798            trade_client_extensions: None,
1799        }
1800    }
1801
1802    setters! {
1803        /// Sets the time-in-force (default `FOK`).
1804        time_in_force: MarketOrderTimeInForce,
1805        /// Sets the worst acceptable fill price.
1806        price_bound: PriceValue,
1807        /// Sets how positions are modified on fill (default `DEFAULT`).
1808        position_fill: OrderPositionFill,
1809        /// Attaches client extensions to the order.
1810        client_extensions: ClientExtensions,
1811        /// Attaches a take-profit to the trade opened on fill.
1812        take_profit_on_fill: TakeProfitDetails,
1813        /// Attaches a stop-loss to the trade opened on fill.
1814        stop_loss_on_fill: StopLossDetails,
1815        /// Attaches a trailing stop-loss to the trade opened on fill.
1816        trailing_stop_loss_on_fill: TrailingStopLossDetails,
1817        /// Attaches client extensions to the trade opened on fill.
1818        trade_client_extensions: ClientExtensions,
1819    }
1820}
1821
1822/// Specification of a limit order. Required fields are set by
1823/// [`LimitOrderRequest::new`]; everything else is optional.
1824#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1825pub struct LimitOrderRequest {
1826    /// The limit order's instrument.
1827    #[serde(rename = "instrument")]
1828    pub instrument: InstrumentName,
1829    /// The quantity requested to be filled. A positive number creates a
1830    /// long order, a negative number a short order.
1831    #[serde(rename = "units")]
1832    pub units: DecimalNumber,
1833    /// The price threshold: the order will only be filled at this price or
1834    /// better.
1835    #[serde(rename = "price")]
1836    pub price: PriceValue,
1837    /// The time-in-force for the order (default `GTC`).
1838    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
1839    pub time_in_force: Option<LimitOrderTimeInForce>,
1840    /// The date/time when the order is cancelled if `time_in_force` is
1841    /// `GTD`.
1842    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
1843    pub gtd_time: Option<DateTime>,
1844    /// How positions in the account are modified when the order fills
1845    /// (default `DEFAULT`).
1846    #[serde(rename = "positionFill", skip_serializing_if = "Option::is_none")]
1847    pub position_fill: Option<OrderPositionFill>,
1848    /// Which price component is used to trigger the order (default
1849    /// `DEFAULT`).
1850    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
1851    pub trigger_condition: Option<OrderTriggerCondition>,
1852    /// The client extensions to add to the order.
1853    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
1854    pub client_extensions: Option<ClientExtensions>,
1855    /// A take-profit order to create for a trade opened when the order
1856    /// fills.
1857    #[serde(rename = "takeProfitOnFill", skip_serializing_if = "Option::is_none")]
1858    pub take_profit_on_fill: Option<TakeProfitDetails>,
1859    /// A stop-loss order to create for a trade opened when the order fills.
1860    #[serde(rename = "stopLossOnFill", skip_serializing_if = "Option::is_none")]
1861    pub stop_loss_on_fill: Option<StopLossDetails>,
1862    /// A trailing stop-loss order to create for a trade opened when the
1863    /// order fills.
1864    #[serde(
1865        rename = "trailingStopLossOnFill",
1866        skip_serializing_if = "Option::is_none"
1867    )]
1868    pub trailing_stop_loss_on_fill: Option<TrailingStopLossDetails>,
1869    /// The client extensions to add to a trade opened when the order fills.
1870    #[serde(
1871        rename = "tradeClientExtensions",
1872        skip_serializing_if = "Option::is_none"
1873    )]
1874    pub trade_client_extensions: Option<ClientExtensions>,
1875}
1876
1877impl LimitOrderRequest {
1878    /// A limit order for `units` of `instrument` at `price` or better.
1879    pub fn new(
1880        instrument: impl Into<InstrumentName>,
1881        units: impl Into<DecimalNumber>,
1882        price: impl Into<PriceValue>,
1883    ) -> Self {
1884        LimitOrderRequest {
1885            instrument: instrument.into(),
1886            units: units.into(),
1887            price: price.into(),
1888            time_in_force: None,
1889            gtd_time: None,
1890            position_fill: None,
1891            trigger_condition: None,
1892            client_extensions: None,
1893            take_profit_on_fill: None,
1894            stop_loss_on_fill: None,
1895            trailing_stop_loss_on_fill: None,
1896            trade_client_extensions: None,
1897        }
1898    }
1899
1900    setters! {
1901        /// Sets the time-in-force (default `GTC`).
1902        time_in_force: LimitOrderTimeInForce,
1903        /// Sets the cancellation time used with `GTD`.
1904        gtd_time: DateTime,
1905        /// Sets how positions are modified on fill (default `DEFAULT`).
1906        position_fill: OrderPositionFill,
1907        /// Sets the price component used for triggering.
1908        trigger_condition: OrderTriggerCondition,
1909        /// Attaches client extensions to the order.
1910        client_extensions: ClientExtensions,
1911        /// Attaches a take-profit to the trade opened on fill.
1912        take_profit_on_fill: TakeProfitDetails,
1913        /// Attaches a stop-loss to the trade opened on fill.
1914        stop_loss_on_fill: StopLossDetails,
1915        /// Attaches a trailing stop-loss to the trade opened on fill.
1916        trailing_stop_loss_on_fill: TrailingStopLossDetails,
1917        /// Attaches client extensions to the trade opened on fill.
1918        trade_client_extensions: ClientExtensions,
1919    }
1920}
1921
1922/// Specification of a stop order. Required fields are set by
1923/// [`StopOrderRequest::new`]; everything else is optional.
1924#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1925pub struct StopOrderRequest {
1926    /// The stop order's instrument.
1927    #[serde(rename = "instrument")]
1928    pub instrument: InstrumentName,
1929    /// The quantity requested to be filled. A positive number creates a
1930    /// long order, a negative number a short order.
1931    #[serde(rename = "units")]
1932    pub units: DecimalNumber,
1933    /// The price threshold: the order will only be filled at this price or
1934    /// worse.
1935    #[serde(rename = "price")]
1936    pub price: PriceValue,
1937    /// The worst market price that may be used to fill the order (only
1938    /// with `TimeInForce` `FOK` or `IOC`).
1939    #[serde(rename = "priceBound", skip_serializing_if = "Option::is_none")]
1940    pub price_bound: Option<PriceValue>,
1941    /// The time-in-force for the order (default `GTC`).
1942    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
1943    pub time_in_force: Option<StopOrderTimeInForce>,
1944    /// The date/time when the order is cancelled if `time_in_force` is
1945    /// `GTD`.
1946    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
1947    pub gtd_time: Option<DateTime>,
1948    /// How positions in the account are modified when the order fills
1949    /// (default `DEFAULT`).
1950    #[serde(rename = "positionFill", skip_serializing_if = "Option::is_none")]
1951    pub position_fill: Option<OrderPositionFill>,
1952    /// Which price component is used to trigger the order (default
1953    /// `DEFAULT`).
1954    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
1955    pub trigger_condition: Option<OrderTriggerCondition>,
1956    /// The client extensions to add to the order.
1957    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
1958    pub client_extensions: Option<ClientExtensions>,
1959    /// A take-profit order to create for a trade opened when the order
1960    /// fills.
1961    #[serde(rename = "takeProfitOnFill", skip_serializing_if = "Option::is_none")]
1962    pub take_profit_on_fill: Option<TakeProfitDetails>,
1963    /// A stop-loss order to create for a trade opened when the order fills.
1964    #[serde(rename = "stopLossOnFill", skip_serializing_if = "Option::is_none")]
1965    pub stop_loss_on_fill: Option<StopLossDetails>,
1966    /// A trailing stop-loss order to create for a trade opened when the
1967    /// order fills.
1968    #[serde(
1969        rename = "trailingStopLossOnFill",
1970        skip_serializing_if = "Option::is_none"
1971    )]
1972    pub trailing_stop_loss_on_fill: Option<TrailingStopLossDetails>,
1973    /// The client extensions to add to a trade opened when the order fills.
1974    #[serde(
1975        rename = "tradeClientExtensions",
1976        skip_serializing_if = "Option::is_none"
1977    )]
1978    pub trade_client_extensions: Option<ClientExtensions>,
1979}
1980
1981impl StopOrderRequest {
1982    /// A stop order for `units` of `instrument` triggered at `price`.
1983    pub fn new(
1984        instrument: impl Into<InstrumentName>,
1985        units: impl Into<DecimalNumber>,
1986        price: impl Into<PriceValue>,
1987    ) -> Self {
1988        StopOrderRequest {
1989            instrument: instrument.into(),
1990            units: units.into(),
1991            price: price.into(),
1992            price_bound: None,
1993            time_in_force: None,
1994            gtd_time: None,
1995            position_fill: None,
1996            trigger_condition: None,
1997            client_extensions: None,
1998            take_profit_on_fill: None,
1999            stop_loss_on_fill: None,
2000            trailing_stop_loss_on_fill: None,
2001            trade_client_extensions: None,
2002        }
2003    }
2004
2005    setters! {
2006        /// Sets the worst acceptable fill price (with `FOK`/`IOC`).
2007        price_bound: PriceValue,
2008        /// Sets the time-in-force (default `GTC`).
2009        time_in_force: StopOrderTimeInForce,
2010        /// Sets the cancellation time used with `GTD`.
2011        gtd_time: DateTime,
2012        /// Sets how positions are modified on fill (default `DEFAULT`).
2013        position_fill: OrderPositionFill,
2014        /// Sets the price component used for triggering.
2015        trigger_condition: OrderTriggerCondition,
2016        /// Attaches client extensions to the order.
2017        client_extensions: ClientExtensions,
2018        /// Attaches a take-profit to the trade opened on fill.
2019        take_profit_on_fill: TakeProfitDetails,
2020        /// Attaches a stop-loss to the trade opened on fill.
2021        stop_loss_on_fill: StopLossDetails,
2022        /// Attaches a trailing stop-loss to the trade opened on fill.
2023        trailing_stop_loss_on_fill: TrailingStopLossDetails,
2024        /// Attaches client extensions to the trade opened on fill.
2025        trade_client_extensions: ClientExtensions,
2026    }
2027}
2028
2029/// Specification of a market-if-touched order. Required fields are set by
2030/// [`MarketIfTouchedOrderRequest::new`]; everything else is optional.
2031#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2032pub struct MarketIfTouchedOrderRequest {
2033    /// The order's instrument.
2034    #[serde(rename = "instrument")]
2035    pub instrument: InstrumentName,
2036    /// The quantity requested to be filled. A positive number creates a
2037    /// long order, a negative number a short order.
2038    #[serde(rename = "units")]
2039    pub units: DecimalNumber,
2040    /// The price threshold: the order will only be filled by a market
2041    /// price that crosses this price from the direction of the market
2042    /// price at creation time.
2043    #[serde(rename = "price")]
2044    pub price: PriceValue,
2045    /// The worst market price that may be used to fill the order.
2046    #[serde(rename = "priceBound", skip_serializing_if = "Option::is_none")]
2047    pub price_bound: Option<PriceValue>,
2048    /// The time-in-force for the order (default `GTC`; only `GTC`, `GFD`
2049    /// and `GTD` are supported).
2050    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
2051    pub time_in_force: Option<MarketIfTouchedOrderTimeInForce>,
2052    /// The date/time when the order is cancelled if `time_in_force` is
2053    /// `GTD`.
2054    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
2055    pub gtd_time: Option<DateTime>,
2056    /// How positions in the account are modified when the order fills
2057    /// (default `DEFAULT`).
2058    #[serde(rename = "positionFill", skip_serializing_if = "Option::is_none")]
2059    pub position_fill: Option<OrderPositionFill>,
2060    /// Which price component is used to trigger the order (default
2061    /// `DEFAULT`).
2062    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
2063    pub trigger_condition: Option<OrderTriggerCondition>,
2064    /// The client extensions to add to the order.
2065    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
2066    pub client_extensions: Option<ClientExtensions>,
2067    /// A take-profit order to create for a trade opened when the order
2068    /// fills.
2069    #[serde(rename = "takeProfitOnFill", skip_serializing_if = "Option::is_none")]
2070    pub take_profit_on_fill: Option<TakeProfitDetails>,
2071    /// A stop-loss order to create for a trade opened when the order fills.
2072    #[serde(rename = "stopLossOnFill", skip_serializing_if = "Option::is_none")]
2073    pub stop_loss_on_fill: Option<StopLossDetails>,
2074    /// A trailing stop-loss order to create for a trade opened when the
2075    /// order fills.
2076    #[serde(
2077        rename = "trailingStopLossOnFill",
2078        skip_serializing_if = "Option::is_none"
2079    )]
2080    pub trailing_stop_loss_on_fill: Option<TrailingStopLossDetails>,
2081    /// The client extensions to add to a trade opened when the order fills.
2082    #[serde(
2083        rename = "tradeClientExtensions",
2084        skip_serializing_if = "Option::is_none"
2085    )]
2086    pub trade_client_extensions: Option<ClientExtensions>,
2087}
2088
2089impl MarketIfTouchedOrderRequest {
2090    /// A market-if-touched order for `units` of `instrument` triggered at
2091    /// `price`.
2092    pub fn new(
2093        instrument: impl Into<InstrumentName>,
2094        units: impl Into<DecimalNumber>,
2095        price: impl Into<PriceValue>,
2096    ) -> Self {
2097        MarketIfTouchedOrderRequest {
2098            instrument: instrument.into(),
2099            units: units.into(),
2100            price: price.into(),
2101            price_bound: None,
2102            time_in_force: None,
2103            gtd_time: None,
2104            position_fill: None,
2105            trigger_condition: None,
2106            client_extensions: None,
2107            take_profit_on_fill: None,
2108            stop_loss_on_fill: None,
2109            trailing_stop_loss_on_fill: None,
2110            trade_client_extensions: None,
2111        }
2112    }
2113
2114    setters! {
2115        /// Sets the worst acceptable fill price.
2116        price_bound: PriceValue,
2117        /// Sets the time-in-force (default `GTC`).
2118        time_in_force: MarketIfTouchedOrderTimeInForce,
2119        /// Sets the cancellation time used with `GTD`.
2120        gtd_time: DateTime,
2121        /// Sets how positions are modified on fill (default `DEFAULT`).
2122        position_fill: OrderPositionFill,
2123        /// Sets the price component used for triggering.
2124        trigger_condition: OrderTriggerCondition,
2125        /// Attaches client extensions to the order.
2126        client_extensions: ClientExtensions,
2127        /// Attaches a take-profit to the trade opened on fill.
2128        take_profit_on_fill: TakeProfitDetails,
2129        /// Attaches a stop-loss to the trade opened on fill.
2130        stop_loss_on_fill: StopLossDetails,
2131        /// Attaches a trailing stop-loss to the trade opened on fill.
2132        trailing_stop_loss_on_fill: TrailingStopLossDetails,
2133        /// Attaches client extensions to the trade opened on fill.
2134        trade_client_extensions: ClientExtensions,
2135    }
2136}
2137
2138/// Specification of a take-profit order attached to an existing trade.
2139#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2140pub struct TakeProfitOrderRequest {
2141    /// The ID of the trade to close when the order is filled.
2142    #[serde(rename = "tradeID")]
2143    pub trade_id: TradeId,
2144    /// The client ID of the trade to be closed when the order is filled.
2145    #[serde(rename = "clientTradeID", skip_serializing_if = "Option::is_none")]
2146    pub client_trade_id: Option<ClientId>,
2147    /// The price threshold: the associated trade will be closed at this
2148    /// price or better.
2149    #[serde(rename = "price")]
2150    pub price: PriceValue,
2151    /// The time-in-force for the order (default `GTC`).
2152    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
2153    pub time_in_force: Option<TakeProfitOrderTimeInForce>,
2154    /// The date/time when the order is cancelled if `time_in_force` is
2155    /// `GTD`.
2156    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
2157    pub gtd_time: Option<DateTime>,
2158    /// Which price component is used to trigger the order (default
2159    /// `DEFAULT`).
2160    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
2161    pub trigger_condition: Option<OrderTriggerCondition>,
2162    /// The client extensions to add to the order.
2163    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
2164    pub client_extensions: Option<ClientExtensions>,
2165}
2166
2167impl TakeProfitOrderRequest {
2168    /// A take-profit order closing `trade_id` at `price` or better.
2169    pub fn new(trade_id: impl Into<TradeId>, price: impl Into<PriceValue>) -> Self {
2170        TakeProfitOrderRequest {
2171            trade_id: trade_id.into(),
2172            client_trade_id: None,
2173            price: price.into(),
2174            time_in_force: None,
2175            gtd_time: None,
2176            trigger_condition: None,
2177            client_extensions: None,
2178        }
2179    }
2180
2181    setters! {
2182        /// Targets the trade by its client-provided ID instead.
2183        client_trade_id: ClientId,
2184        /// Sets the time-in-force (default `GTC`).
2185        time_in_force: TakeProfitOrderTimeInForce,
2186        /// Sets the cancellation time used with `GTD`.
2187        gtd_time: DateTime,
2188        /// Sets the price component used for triggering.
2189        trigger_condition: OrderTriggerCondition,
2190        /// Attaches client extensions to the order.
2191        client_extensions: ClientExtensions,
2192    }
2193}
2194
2195/// Specification of a stop-loss order attached to an existing trade.
2196/// Exactly one of `price` or `distance` must be set (see
2197/// [`StopLossOrderRequest::at_price`] and
2198/// [`StopLossOrderRequest::at_distance`]).
2199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2200pub struct StopLossOrderRequest {
2201    /// The ID of the trade to close when the order is filled.
2202    #[serde(rename = "tradeID")]
2203    pub trade_id: TradeId,
2204    /// The client ID of the trade to be closed when the order is filled.
2205    #[serde(rename = "clientTradeID", skip_serializing_if = "Option::is_none")]
2206    pub client_trade_id: Option<ClientId>,
2207    /// The price threshold: the associated trade will be closed by a
2208    /// market price that is equal to or worse than this threshold.
2209    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
2210    pub price: Option<PriceValue>,
2211    /// The distance (in price units) from the trade's open price to use as
2212    /// the stop-loss price instead of an absolute `price`.
2213    #[serde(rename = "distance", skip_serializing_if = "Option::is_none")]
2214    pub distance: Option<DecimalNumber>,
2215    /// The time-in-force for the order (default `GTC`).
2216    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
2217    pub time_in_force: Option<StopLossOrderTimeInForce>,
2218    /// The date/time when the order is cancelled if `time_in_force` is
2219    /// `GTD`.
2220    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
2221    pub gtd_time: Option<DateTime>,
2222    /// Which price component is used to trigger the order (default
2223    /// `DEFAULT`).
2224    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
2225    pub trigger_condition: Option<OrderTriggerCondition>,
2226    /// Deprecated: whether the order is guaranteed (only allowed when the
2227    /// account's `guaranteedStopLossOrderMode` permits it).
2228    #[serde(rename = "guaranteed", skip_serializing_if = "Option::is_none")]
2229    pub guaranteed: Option<bool>,
2230    /// The client extensions to add to the order.
2231    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
2232    pub client_extensions: Option<ClientExtensions>,
2233}
2234
2235impl StopLossOrderRequest {
2236    /// A stop-loss order closing `trade_id` at the absolute price
2237    /// threshold `price`.
2238    pub fn at_price(trade_id: impl Into<TradeId>, price: impl Into<PriceValue>) -> Self {
2239        StopLossOrderRequest {
2240            trade_id: trade_id.into(),
2241            client_trade_id: None,
2242            price: Some(price.into()),
2243            distance: None,
2244            time_in_force: None,
2245            gtd_time: None,
2246            trigger_condition: None,
2247            guaranteed: None,
2248            client_extensions: None,
2249        }
2250    }
2251
2252    /// A stop-loss order closing `trade_id` at `distance` price units from
2253    /// the trade's open price.
2254    pub fn at_distance(trade_id: impl Into<TradeId>, distance: impl Into<DecimalNumber>) -> Self {
2255        StopLossOrderRequest {
2256            trade_id: trade_id.into(),
2257            client_trade_id: None,
2258            price: None,
2259            distance: Some(distance.into()),
2260            time_in_force: None,
2261            gtd_time: None,
2262            trigger_condition: None,
2263            guaranteed: None,
2264            client_extensions: None,
2265        }
2266    }
2267
2268    setters! {
2269        /// Targets the trade by its client-provided ID instead.
2270        client_trade_id: ClientId,
2271        /// Sets the time-in-force (default `GTC`).
2272        time_in_force: StopLossOrderTimeInForce,
2273        /// Sets the cancellation time used with `GTD`.
2274        gtd_time: DateTime,
2275        /// Sets the price component used for triggering.
2276        trigger_condition: OrderTriggerCondition,
2277        /// Attaches client extensions to the order.
2278        client_extensions: ClientExtensions,
2279    }
2280}
2281
2282/// Specification of a trailing stop-loss order attached to an existing
2283/// trade.
2284#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2285pub struct TrailingStopLossOrderRequest {
2286    /// The ID of the trade to close when the order is filled.
2287    #[serde(rename = "tradeID")]
2288    pub trade_id: TradeId,
2289    /// The client ID of the trade to be closed when the order is filled.
2290    #[serde(rename = "clientTradeID", skip_serializing_if = "Option::is_none")]
2291    pub client_trade_id: Option<ClientId>,
2292    /// The distance (in price units) from the trade's fill price that the
2293    /// trailing stop-loss trails.
2294    #[serde(rename = "distance")]
2295    pub distance: DecimalNumber,
2296    /// The time-in-force for the order (default `GTC`).
2297    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
2298    pub time_in_force: Option<TrailingStopLossOrderTimeInForce>,
2299    /// The date/time when the order is cancelled if `time_in_force` is
2300    /// `GTD`.
2301    #[serde(rename = "gtdTime", skip_serializing_if = "Option::is_none")]
2302    pub gtd_time: Option<DateTime>,
2303    /// Which price component is used to trigger the order (default
2304    /// `DEFAULT`).
2305    #[serde(rename = "triggerCondition", skip_serializing_if = "Option::is_none")]
2306    pub trigger_condition: Option<OrderTriggerCondition>,
2307    /// The client extensions to add to the order.
2308    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
2309    pub client_extensions: Option<ClientExtensions>,
2310}
2311
2312impl TrailingStopLossOrderRequest {
2313    /// A trailing stop-loss order for `trade_id` trailing at `distance`
2314    /// price units.
2315    pub fn new(trade_id: impl Into<TradeId>, distance: impl Into<DecimalNumber>) -> Self {
2316        TrailingStopLossOrderRequest {
2317            trade_id: trade_id.into(),
2318            client_trade_id: None,
2319            distance: distance.into(),
2320            time_in_force: None,
2321            gtd_time: None,
2322            trigger_condition: None,
2323            client_extensions: None,
2324        }
2325    }
2326
2327    setters! {
2328        /// Targets the trade by its client-provided ID instead.
2329        client_trade_id: ClientId,
2330        /// Sets the time-in-force (default `GTC`).
2331        time_in_force: TrailingStopLossOrderTimeInForce,
2332        /// Sets the cancellation time used with `GTD`.
2333        gtd_time: DateTime,
2334        /// Sets the price component used for triggering.
2335        trigger_condition: OrderTriggerCondition,
2336        /// Attaches client extensions to the order.
2337        client_extensions: ClientExtensions,
2338    }
2339}
2340
2341impl ClientExtensions {
2342    /// Empty client extensions to fill via the setters.
2343    pub fn new() -> Self {
2344        ClientExtensions {
2345            id: None,
2346            tag: None,
2347            comment: None,
2348        }
2349    }
2350
2351    /// Sets the client-provided ID.
2352    pub fn id(mut self, id: impl Into<String>) -> Self {
2353        self.id = Some(id.into());
2354        self
2355    }
2356
2357    /// Sets the client-provided tag.
2358    pub fn tag(mut self, tag: impl Into<String>) -> Self {
2359        self.tag = Some(tag.into());
2360        self
2361    }
2362
2363    /// Sets the client-provided comment.
2364    pub fn comment(mut self, comment: impl Into<String>) -> Self {
2365        self.comment = Some(comment.into());
2366        self
2367    }
2368}
2369
2370impl Default for ClientExtensions {
2371    fn default() -> Self {
2372        ClientExtensions::new()
2373    }
2374}
2375
2376impl TakeProfitDetails {
2377    /// Take-profit details closing the trade at `price` or better.
2378    pub fn at_price(price: PriceValue) -> Self {
2379        TakeProfitDetails {
2380            price: Some(price),
2381            time_in_force: None,
2382            gtd_time: None,
2383            client_extensions: None,
2384        }
2385    }
2386}
2387
2388impl StopLossDetails {
2389    /// Stop-loss details closing the trade at the absolute `price`
2390    /// threshold.
2391    pub fn at_price(price: PriceValue) -> Self {
2392        StopLossDetails {
2393            price: Some(price),
2394            distance: None,
2395            time_in_force: None,
2396            gtd_time: None,
2397            client_extensions: None,
2398            guaranteed: None,
2399            trigger_mode: None,
2400        }
2401    }
2402
2403    /// Stop-loss details closing the trade at `distance` price units from
2404    /// its open price.
2405    pub fn at_distance(distance: DecimalNumber) -> Self {
2406        StopLossDetails {
2407            price: None,
2408            distance: Some(distance),
2409            time_in_force: None,
2410            gtd_time: None,
2411            client_extensions: None,
2412            guaranteed: None,
2413            trigger_mode: None,
2414        }
2415    }
2416}
2417
2418impl TrailingStopLossDetails {
2419    /// Trailing stop-loss details trailing at `distance` price units.
2420    pub fn at_distance(distance: DecimalNumber) -> Self {
2421        TrailingStopLossDetails {
2422            distance: Some(distance),
2423            time_in_force: None,
2424            gtd_time: None,
2425            client_extensions: None,
2426        }
2427    }
2428}