Skip to main content

oanda_rs/models/
trade.rs

1//! Trade models.
2
3use serde::{Deserialize, Serialize};
4
5use super::macros::string_enum;
6use super::{
7    AccountUnits, ClientExtensions, DateTime, DecimalNumber, InstrumentName, OrderId, PriceValue,
8    StopLossOrder, TakeProfitOrder, TradeId, TrailingStopLossOrder, TransactionId,
9};
10
11/// The specification of a Trade within an Account. This includes the full
12/// representation of the Trade's dependent Orders in addition to the IDs of
13/// those Orders.
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15#[non_exhaustive]
16pub struct Trade {
17    /// The Trade's identifier, unique within the Trade's Account.
18    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
19    pub id: Option<TradeId>,
20
21    /// The Trade's Instrument.
22    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
23    pub instrument: Option<InstrumentName>,
24
25    /// The execution price of the Trade.
26    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
27    pub price: Option<PriceValue>,
28
29    /// The date/time when the Trade was opened.
30    #[serde(rename = "openTime", skip_serializing_if = "Option::is_none")]
31    pub open_time: Option<DateTime>,
32
33    /// The `state` field.
34    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
35    pub state: Option<TradeState>,
36
37    /// The initial size of the Trade. Negative values indicate a short Trade,
38    /// and positive values indicate a long Trade.
39    #[serde(rename = "initialUnits", skip_serializing_if = "Option::is_none")]
40    pub initial_units: Option<DecimalNumber>,
41
42    /// The margin required at the time the Trade was created. Note, this is the
43    /// 'pure' margin required, it is not the 'effective' margin used that
44    /// factors in the trade risk if a GSLO is attached to the trade.
45    #[serde(
46        rename = "initialMarginRequired",
47        skip_serializing_if = "Option::is_none"
48    )]
49    pub initial_margin_required: Option<AccountUnits>,
50
51    /// The number of units currently open for the Trade. This value is reduced
52    /// to 0.0 as the Trade is closed.
53    #[serde(rename = "currentUnits", skip_serializing_if = "Option::is_none")]
54    pub current_units: Option<DecimalNumber>,
55
56    /// The total profit/loss realized on the closed portion of the Trade.
57    #[serde(rename = "realizedPL", skip_serializing_if = "Option::is_none")]
58    pub realized_pl: Option<AccountUnits>,
59
60    /// The unrealized profit/loss on the open portion of the Trade.
61    #[serde(rename = "unrealizedPL", skip_serializing_if = "Option::is_none")]
62    pub unrealized_pl: Option<AccountUnits>,
63
64    /// Margin currently used by the Trade.
65    #[serde(rename = "marginUsed", skip_serializing_if = "Option::is_none")]
66    pub margin_used: Option<AccountUnits>,
67
68    /// The average closing price of the Trade. Only present if the Trade has
69    /// been closed or reduced at least once.
70    #[serde(rename = "averageClosePrice", skip_serializing_if = "Option::is_none")]
71    pub average_close_price: Option<PriceValue>,
72
73    /// The IDs of the Transactions that have closed portions of this Trade.
74    #[serde(
75        rename = "closingTransactionIDs",
76        default,
77        skip_serializing_if = "Vec::is_empty"
78    )]
79    pub closing_transaction_ids: Vec<TransactionId>,
80
81    /// The financing paid/collected for this Trade.
82    #[serde(rename = "financing", skip_serializing_if = "Option::is_none")]
83    pub financing: Option<AccountUnits>,
84
85    /// The date/time when the Trade was fully closed. Only provided for Trades
86    /// whose state is CLOSED.
87    #[serde(rename = "closeTime", skip_serializing_if = "Option::is_none")]
88    pub close_time: Option<DateTime>,
89
90    /// The `clientExtensions` field.
91    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
92    pub client_extensions: Option<ClientExtensions>,
93
94    /// The `takeProfitOrder` field.
95    #[serde(rename = "takeProfitOrder", skip_serializing_if = "Option::is_none")]
96    pub take_profit_order: Option<TakeProfitOrder>,
97
98    /// The `stopLossOrder` field.
99    #[serde(rename = "stopLossOrder", skip_serializing_if = "Option::is_none")]
100    pub stop_loss_order: Option<StopLossOrder>,
101
102    /// The `trailingStopLossOrder` field.
103    #[serde(
104        rename = "trailingStopLossOrder",
105        skip_serializing_if = "Option::is_none"
106    )]
107    pub trailing_stop_loss_order: Option<TrailingStopLossOrder>,
108
109    /// The total amount of dividend adjustment paid. This field is returned by
110    /// the live v20 API but is not present in OANDA's official documentation.
111    #[serde(rename = "dividendAdjustment", skip_serializing_if = "Option::is_none")]
112    pub dividend_adjustment: Option<DecimalNumber>,
113
114    /// The unrealized profit/loss inclusive of unsettled amounts. This field is
115    /// returned by the live v20 API but is not present in OANDA's official
116    /// documentation.
117    #[serde(rename = "trueUnrealizedPL", skip_serializing_if = "Option::is_none")]
118    pub true_unrealized_pl: Option<DecimalNumber>,
119}
120
121/// The summary of a Trade within an Account. This representation does not
122/// provide the full details of the Trade's dependent Orders.
123#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
124#[non_exhaustive]
125pub struct TradeSummary {
126    /// The Trade's identifier, unique within the Trade's Account.
127    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
128    pub id: Option<TradeId>,
129
130    /// The Trade's Instrument.
131    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
132    pub instrument: Option<InstrumentName>,
133
134    /// The execution price of the Trade.
135    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
136    pub price: Option<PriceValue>,
137
138    /// The date/time when the Trade was opened.
139    #[serde(rename = "openTime", skip_serializing_if = "Option::is_none")]
140    pub open_time: Option<DateTime>,
141
142    /// The `state` field.
143    #[serde(rename = "state", skip_serializing_if = "Option::is_none")]
144    pub state: Option<TradeState>,
145
146    /// The initial size of the Trade. Negative values indicate a short Trade,
147    /// and positive values indicate a long Trade.
148    #[serde(rename = "initialUnits", skip_serializing_if = "Option::is_none")]
149    pub initial_units: Option<DecimalNumber>,
150
151    /// The margin required at the time the Trade was created. Note, this is the
152    /// 'pure' margin required, it is not the 'effective' margin used that
153    /// factors in the trade risk if a GSLO is attached to the trade.
154    #[serde(
155        rename = "initialMarginRequired",
156        skip_serializing_if = "Option::is_none"
157    )]
158    pub initial_margin_required: Option<AccountUnits>,
159
160    /// The number of units currently open for the Trade. This value is reduced
161    /// to 0.0 as the Trade is closed.
162    #[serde(rename = "currentUnits", skip_serializing_if = "Option::is_none")]
163    pub current_units: Option<DecimalNumber>,
164
165    /// The total profit/loss realized on the closed portion of the Trade.
166    #[serde(rename = "realizedPL", skip_serializing_if = "Option::is_none")]
167    pub realized_pl: Option<AccountUnits>,
168
169    /// The unrealized profit/loss on the open portion of the Trade.
170    #[serde(rename = "unrealizedPL", skip_serializing_if = "Option::is_none")]
171    pub unrealized_pl: Option<AccountUnits>,
172
173    /// Margin currently used by the Trade.
174    #[serde(rename = "marginUsed", skip_serializing_if = "Option::is_none")]
175    pub margin_used: Option<AccountUnits>,
176
177    /// The average closing price of the Trade. Only present if the Trade has
178    /// been closed or reduced at least once.
179    #[serde(rename = "averageClosePrice", skip_serializing_if = "Option::is_none")]
180    pub average_close_price: Option<PriceValue>,
181
182    /// The IDs of the Transactions that have closed portions of this Trade.
183    #[serde(
184        rename = "closingTransactionIDs",
185        default,
186        skip_serializing_if = "Vec::is_empty"
187    )]
188    pub closing_transaction_ids: Vec<TransactionId>,
189
190    /// The financing paid/collected for this Trade.
191    #[serde(rename = "financing", skip_serializing_if = "Option::is_none")]
192    pub financing: Option<AccountUnits>,
193
194    /// The date/time when the Trade was fully closed. Only provided for Trades
195    /// whose state is CLOSED.
196    #[serde(rename = "closeTime", skip_serializing_if = "Option::is_none")]
197    pub close_time: Option<DateTime>,
198
199    /// The `clientExtensions` field.
200    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
201    pub client_extensions: Option<ClientExtensions>,
202
203    /// ID of the Trade's Take Profit Order, only provided if such an Order
204    /// exists.
205    #[serde(rename = "takeProfitOrderID", skip_serializing_if = "Option::is_none")]
206    pub take_profit_order_id: Option<OrderId>,
207
208    /// ID of the Trade's Stop Loss Order, only provided if such an Order
209    /// exists.
210    #[serde(rename = "stopLossOrderID", skip_serializing_if = "Option::is_none")]
211    pub stop_loss_order_id: Option<OrderId>,
212
213    /// ID of the Trade's Trailing Stop Loss Order, only provided if such an
214    /// Order exists.
215    #[serde(
216        rename = "trailingStopLossOrderID",
217        skip_serializing_if = "Option::is_none"
218    )]
219    pub trailing_stop_loss_order_id: Option<OrderId>,
220
221    /// The total amount of dividend adjustment paid. This field is returned by
222    /// the live v20 API but is not present in OANDA's official documentation.
223    #[serde(rename = "dividendAdjustment", skip_serializing_if = "Option::is_none")]
224    pub dividend_adjustment: Option<DecimalNumber>,
225
226    /// The unrealized profit/loss inclusive of unsettled amounts. This field is
227    /// returned by the live v20 API but is not present in OANDA's official
228    /// documentation.
229    #[serde(rename = "trueUnrealizedPL", skip_serializing_if = "Option::is_none")]
230    pub true_unrealized_pl: Option<DecimalNumber>,
231}
232
233/// The dynamic (calculated) state of an open Trade
234#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
235#[non_exhaustive]
236pub struct CalculatedTradeState {
237    /// The Trade's ID.
238    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
239    pub id: Option<TradeId>,
240
241    /// The Trade's unrealized profit/loss.
242    #[serde(rename = "unrealizedPL", skip_serializing_if = "Option::is_none")]
243    pub unrealized_pl: Option<AccountUnits>,
244
245    /// Margin currently used by the Trade.
246    #[serde(rename = "marginUsed", skip_serializing_if = "Option::is_none")]
247    pub margin_used: Option<AccountUnits>,
248}
249
250string_enum! {
251    /// The classification of TradePLs.
252    pub enum TradePL {
253        Positive => "POSITIVE",
254        Negative => "NEGATIVE",
255        Zero => "ZERO",
256    }
257}
258
259string_enum! {
260    /// The current state of the Trade.
261    pub enum TradeState {
262        Open => "OPEN",
263        Closed => "CLOSED",
264        CloseWhenTradeable => "CLOSE_WHEN_TRADEABLE",
265    }
266}
267
268string_enum! {
269    /// The state to filter the Trades by
270    pub enum TradeStateFilter {
271        Open => "OPEN",
272        Closed => "CLOSED",
273        CloseWhenTradeable => "CLOSE_WHEN_TRADEABLE",
274        All => "ALL",
275    }
276}
277
278/// A TradeOpen object represents a Trade for an instrument that was opened in
279/// an Account. It is found embedded in Transactions that affect the position of
280/// an instrument in the Account, specifically the OrderFill Transaction.
281#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
282#[non_exhaustive]
283pub struct TradeOpen {
284    /// The ID of the Trade that was opened
285    #[serde(rename = "tradeID", skip_serializing_if = "Option::is_none")]
286    pub trade_id: Option<TradeId>,
287
288    /// The number of units opened by the Trade
289    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
290    pub units: Option<DecimalNumber>,
291
292    /// The average price that the units were opened at.
293    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
294    pub price: Option<PriceValue>,
295
296    /// This is the fee charged for opening the trade if it has a guaranteed
297    /// Stop Loss Order attached to it.
298    #[serde(
299        rename = "guaranteedExecutionFee",
300        skip_serializing_if = "Option::is_none"
301    )]
302    pub guaranteed_execution_fee: Option<AccountUnits>,
303
304    /// The `clientExtensions` field.
305    #[serde(rename = "clientExtensions", skip_serializing_if = "Option::is_none")]
306    pub client_extensions: Option<ClientExtensions>,
307
308    /// The half spread cost for the trade open. This can be a positive or
309    /// negative value and is represented in the home currency of the Account.
310    #[serde(rename = "halfSpreadCost", skip_serializing_if = "Option::is_none")]
311    pub half_spread_cost: Option<AccountUnits>,
312
313    /// The margin required at the time the Trade was created. Note, this is the
314    /// 'pure' margin required, it is not the 'effective' margin used that
315    /// factors in the trade risk if a GSLO is attached to the trade.
316    #[serde(
317        rename = "initialMarginRequired",
318        skip_serializing_if = "Option::is_none"
319    )]
320    pub initial_margin_required: Option<AccountUnits>,
321
322    /// The guaranteed execution fee expressed in the Instrument's quote
323    /// currency. This field is returned by the live v20 API but is not present
324    /// in OANDA's official documentation.
325    #[serde(
326        rename = "quoteGuaranteedExecutionFee",
327        skip_serializing_if = "Option::is_none"
328    )]
329    pub quote_guaranteed_execution_fee: Option<DecimalNumber>,
330
331    /// The cost of converting the guaranteed execution fee to the Account's
332    /// home currency. This field is returned by the live v20 API but is not
333    /// present in OANDA's official documentation.
334    #[serde(
335        rename = "guaranteedExecutionFeeHomeConversionCost",
336        skip_serializing_if = "Option::is_none"
337    )]
338    pub guaranteed_execution_fee_home_conversion_cost: Option<DecimalNumber>,
339}
340
341/// A TradeReduce object represents a Trade for an instrument that was reduced
342/// (either partially or fully) in an Account. It is found embedded in
343/// Transactions that affect the position of an instrument in the account,
344/// specifically the OrderFill Transaction.
345#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
346#[non_exhaustive]
347pub struct TradeReduce {
348    /// The ID of the Trade that was reduced or closed
349    #[serde(rename = "tradeID", skip_serializing_if = "Option::is_none")]
350    pub trade_id: Option<TradeId>,
351
352    /// The number of units that the Trade was reduced by
353    #[serde(rename = "units", skip_serializing_if = "Option::is_none")]
354    pub units: Option<DecimalNumber>,
355
356    /// The average price that the units were closed at. This price may be
357    /// clamped for guaranteed Stop Loss Orders.
358    #[serde(rename = "price", skip_serializing_if = "Option::is_none")]
359    pub price: Option<PriceValue>,
360
361    /// The PL realized when reducing the Trade
362    #[serde(rename = "realizedPL", skip_serializing_if = "Option::is_none")]
363    pub realized_pl: Option<AccountUnits>,
364
365    /// The financing paid/collected when reducing the Trade
366    #[serde(rename = "financing", skip_serializing_if = "Option::is_none")]
367    pub financing: Option<AccountUnits>,
368
369    /// This is the fee that is charged for closing the Trade if it has a
370    /// guaranteed Stop Loss Order attached to it.
371    #[serde(
372        rename = "guaranteedExecutionFee",
373        skip_serializing_if = "Option::is_none"
374    )]
375    pub guaranteed_execution_fee: Option<AccountUnits>,
376
377    /// The half spread cost for the trade reduce/close. This can be a positive
378    /// or negative value and is represented in the home currency of the
379    /// Account.
380    #[serde(rename = "halfSpreadCost", skip_serializing_if = "Option::is_none")]
381    pub half_spread_cost: Option<AccountUnits>,
382
383    /// The financing paid or collected in the Instrument's base currency. This
384    /// field is returned by the live v20 API but is not present in OANDA's
385    /// official documentation.
386    #[serde(rename = "baseFinancing", skip_serializing_if = "Option::is_none")]
387    pub base_financing: Option<DecimalNumber>,
388
389    /// The guaranteed execution fee expressed in the Instrument's quote
390    /// currency. This field is returned by the live v20 API but is not present
391    /// in OANDA's official documentation.
392    #[serde(
393        rename = "quoteGuaranteedExecutionFee",
394        skip_serializing_if = "Option::is_none"
395    )]
396    pub quote_guaranteed_execution_fee: Option<DecimalNumber>,
397
398    /// The financing rate in effect for the reduced Trade. This field is
399    /// returned by the live v20 API but is not present in OANDA's official
400    /// documentation.
401    #[serde(rename = "financingRate", skip_serializing_if = "Option::is_none")]
402    pub financing_rate: Option<DecimalNumber>,
403
404    /// The total cost of currency conversions, in the Account's home currency.
405    /// This field is returned by the live v20 API but is not present in OANDA's
406    /// official documentation.
407    #[serde(rename = "homeConversionCost", skip_serializing_if = "Option::is_none")]
408    pub home_conversion_cost: Option<DecimalNumber>,
409
410    /// The cost of converting the realized profit/loss to the Account's home
411    /// currency. This field is returned by the live v20 API but is not present
412    /// in OANDA's official documentation.
413    #[serde(
414        rename = "plHomeConversionCost",
415        skip_serializing_if = "Option::is_none"
416    )]
417    pub pl_home_conversion_cost: Option<DecimalNumber>,
418
419    /// The cost of converting the base financing to the Account's home
420    /// currency. This field is returned by the live v20 API but is not present
421    /// in OANDA's official documentation.
422    #[serde(
423        rename = "baseFinancingHomeConversionCost",
424        skip_serializing_if = "Option::is_none"
425    )]
426    pub base_financing_home_conversion_cost: Option<DecimalNumber>,
427
428    /// The cost of converting the guaranteed execution fee to the Account's
429    /// home currency. This field is returned by the live v20 API but is not
430    /// present in OANDA's official documentation.
431    #[serde(
432        rename = "guaranteedExecutionFeeHomeConversionCost",
433        skip_serializing_if = "Option::is_none"
434    )]
435    pub guaranteed_execution_fee_home_conversion_cost: Option<DecimalNumber>,
436}
437
438/// OpenTradeFinancing is used to pay/collect daily financing charge for an open
439/// Trade within an Account
440#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
441#[non_exhaustive]
442pub struct OpenTradeFinancing {
443    /// The ID of the Trade that financing is being paid/collected for.
444    #[serde(rename = "tradeID", skip_serializing_if = "Option::is_none")]
445    pub trade_id: Option<TradeId>,
446
447    /// The amount of financing paid/collected for the Trade.
448    #[serde(rename = "financing", skip_serializing_if = "Option::is_none")]
449    pub financing: Option<AccountUnits>,
450
451    /// The amount of financing paid/collected in the Instrument's base
452    /// currency. This field is returned by the live v20 API but is not present
453    /// in OANDA's official documentation.
454    #[serde(rename = "baseFinancing", skip_serializing_if = "Option::is_none")]
455    pub base_financing: Option<DecimalNumber>,
456
457    /// The financing rate in effect for the Trade. This field is returned by
458    /// the live v20 API but is not present in OANDA's official documentation.
459    #[serde(rename = "financingRate", skip_serializing_if = "Option::is_none")]
460    pub financing_rate: Option<DecimalNumber>,
461
462    /// The total cost of currency conversions for the Trade's financing, in the
463    /// Account's home currency. This field is returned by the live v20 API but
464    /// is not present in OANDA's official documentation.
465    #[serde(rename = "homeConversionCost", skip_serializing_if = "Option::is_none")]
466    pub home_conversion_cost: Option<DecimalNumber>,
467
468    /// The cost of converting the base financing to the Account's home
469    /// currency. This field is returned by the live v20 API but is not present
470    /// in OANDA's official documentation.
471    #[serde(
472        rename = "baseHomeConversionCost",
473        skip_serializing_if = "Option::is_none"
474    )]
475    pub base_home_conversion_cost: Option<DecimalNumber>,
476}