Skip to main content

oanda_rs/models/transaction/
misc_tx.rs

1//! Margin-call, financing, dividend, and housekeeping transaction subtypes,
2//! plus stream heartbeats and transaction filters.
3
4use serde::{Deserialize, Serialize};
5
6use crate::models::macros::string_enum;
7use crate::models::transaction::MarketOrderReason;
8use crate::models::{
9    AccountFinancingMode, AccountId, AccountUnits, DateTime, DecimalNumber, HomeConversionFactors,
10    InstrumentName, OpenTradeDividendAdjustment, PositionFinancing, RequestId, TradeId,
11    TransactionId,
12};
13
14/// A MarginCallEnterTransaction is created when an Account enters the margin
15/// call state.
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17#[non_exhaustive]
18pub struct MarginCallEnterTransaction {
19    /// The Transaction's Identifier.
20    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
21    pub id: Option<TransactionId>,
22
23    /// The date/time when the Transaction was created.
24    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
25    pub time: Option<DateTime>,
26
27    /// The ID of the user that initiated the creation of the Transaction.
28    #[serde(rename = "userID", skip_serializing_if = "Option::is_none")]
29    pub user_id: Option<i64>,
30
31    /// The ID of the Account the Transaction was created for.
32    #[serde(rename = "accountID", skip_serializing_if = "Option::is_none")]
33    pub account_id: Option<AccountId>,
34
35    /// The ID of the "batch" that the Transaction belongs to. Transactions in
36    /// the same batch are applied to the Account simultaneously.
37    #[serde(rename = "batchID", skip_serializing_if = "Option::is_none")]
38    pub batch_id: Option<TransactionId>,
39
40    /// The Request ID of the request which generated the transaction.
41    #[serde(rename = "requestID", skip_serializing_if = "Option::is_none")]
42    pub request_id: Option<RequestId>,
43    // type is pinned to "MARGIN_CALL_ENTER" by the enum wrapper
44}
45
46/// A MarginCallExtendTransaction is created when the margin call state for an
47/// Account has been extended.
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49#[non_exhaustive]
50pub struct MarginCallExtendTransaction {
51    /// The Transaction's Identifier.
52    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
53    pub id: Option<TransactionId>,
54
55    /// The date/time when the Transaction was created.
56    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
57    pub time: Option<DateTime>,
58
59    /// The ID of the user that initiated the creation of the Transaction.
60    #[serde(rename = "userID", skip_serializing_if = "Option::is_none")]
61    pub user_id: Option<i64>,
62
63    /// The ID of the Account the Transaction was created for.
64    #[serde(rename = "accountID", skip_serializing_if = "Option::is_none")]
65    pub account_id: Option<AccountId>,
66
67    /// The ID of the "batch" that the Transaction belongs to. Transactions in
68    /// the same batch are applied to the Account simultaneously.
69    #[serde(rename = "batchID", skip_serializing_if = "Option::is_none")]
70    pub batch_id: Option<TransactionId>,
71
72    /// The Request ID of the request which generated the transaction.
73    #[serde(rename = "requestID", skip_serializing_if = "Option::is_none")]
74    pub request_id: Option<RequestId>,
75
76    // type is pinned to "MARGIN_CALL_EXTEND" by the enum wrapper
77    /// The number of the extensions to the Account's current margin call that
78    /// have been applied. This value will be set to 1 for the first
79    /// MarginCallExtend Transaction
80    #[serde(rename = "extensionNumber", skip_serializing_if = "Option::is_none")]
81    pub extension_number: Option<i64>,
82}
83
84/// A MarginCallExitnterTransaction is created when an Account leaves the margin
85/// call state.
86#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
87#[non_exhaustive]
88pub struct MarginCallExitTransaction {
89    /// The Transaction's Identifier.
90    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
91    pub id: Option<TransactionId>,
92
93    /// The date/time when the Transaction was created.
94    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
95    pub time: Option<DateTime>,
96
97    /// The ID of the user that initiated the creation of the Transaction.
98    #[serde(rename = "userID", skip_serializing_if = "Option::is_none")]
99    pub user_id: Option<i64>,
100
101    /// The ID of the Account the Transaction was created for.
102    #[serde(rename = "accountID", skip_serializing_if = "Option::is_none")]
103    pub account_id: Option<AccountId>,
104
105    /// The ID of the "batch" that the Transaction belongs to. Transactions in
106    /// the same batch are applied to the Account simultaneously.
107    #[serde(rename = "batchID", skip_serializing_if = "Option::is_none")]
108    pub batch_id: Option<TransactionId>,
109
110    /// The Request ID of the request which generated the transaction.
111    #[serde(rename = "requestID", skip_serializing_if = "Option::is_none")]
112    pub request_id: Option<RequestId>,
113
114    // type is pinned to "MARGIN_CALL_EXIT" by the enum wrapper
115    /// The reason the Margin Call was exited. This field is returned by the
116    /// live v20 API but is not present in OANDA's official documentation.
117    #[serde(rename = "reason", skip_serializing_if = "Option::is_none")]
118    pub reason: Option<String>,
119}
120
121/// A DelayedTradeClosure Transaction is created administratively to indicate
122/// open trades that should have been closed but weren't because the open
123/// trades' instruments were untradeable at the time. Open trades listed in this
124/// transaction will be closed once their respective instruments become
125/// tradeable.
126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
127#[non_exhaustive]
128pub struct DelayedTradeClosureTransaction {
129    /// The Transaction's Identifier.
130    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
131    pub id: Option<TransactionId>,
132
133    /// The date/time when the Transaction was created.
134    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
135    pub time: Option<DateTime>,
136
137    /// The ID of the user that initiated the creation of the Transaction.
138    #[serde(rename = "userID", skip_serializing_if = "Option::is_none")]
139    pub user_id: Option<i64>,
140
141    /// The ID of the Account the Transaction was created for.
142    #[serde(rename = "accountID", skip_serializing_if = "Option::is_none")]
143    pub account_id: Option<AccountId>,
144
145    /// The ID of the "batch" that the Transaction belongs to. Transactions in
146    /// the same batch are applied to the Account simultaneously.
147    #[serde(rename = "batchID", skip_serializing_if = "Option::is_none")]
148    pub batch_id: Option<TransactionId>,
149
150    /// The Request ID of the request which generated the transaction.
151    #[serde(rename = "requestID", skip_serializing_if = "Option::is_none")]
152    pub request_id: Option<RequestId>,
153
154    // type is pinned to "DELAYED_TRADE_CLOSURE" by the enum wrapper
155    /// The `reason` field.
156    #[serde(rename = "reason", skip_serializing_if = "Option::is_none")]
157    pub reason: Option<MarketOrderReason>,
158
159    /// List of Trade ID's identifying the open trades that will be closed when
160    /// their respective instruments become tradeable
161    #[serde(rename = "tradeIDs", skip_serializing_if = "Option::is_none")]
162    pub trade_ids: Option<TradeId>,
163}
164
165/// A DailyFinancingTransaction represents the daily payment/collection of
166/// financing for an Account.
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
168#[non_exhaustive]
169pub struct DailyFinancingTransaction {
170    /// The Transaction's Identifier.
171    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
172    pub id: Option<TransactionId>,
173
174    /// The date/time when the Transaction was created.
175    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
176    pub time: Option<DateTime>,
177
178    /// The ID of the user that initiated the creation of the Transaction.
179    #[serde(rename = "userID", skip_serializing_if = "Option::is_none")]
180    pub user_id: Option<i64>,
181
182    /// The ID of the Account the Transaction was created for.
183    #[serde(rename = "accountID", skip_serializing_if = "Option::is_none")]
184    pub account_id: Option<AccountId>,
185
186    /// The ID of the "batch" that the Transaction belongs to. Transactions in
187    /// the same batch are applied to the Account simultaneously.
188    #[serde(rename = "batchID", skip_serializing_if = "Option::is_none")]
189    pub batch_id: Option<TransactionId>,
190
191    /// The Request ID of the request which generated the transaction.
192    #[serde(rename = "requestID", skip_serializing_if = "Option::is_none")]
193    pub request_id: Option<RequestId>,
194
195    // type is pinned to "DAILY_FINANCING" by the enum wrapper
196    /// The amount of financing paid/collected for the Account.
197    #[serde(rename = "financing", skip_serializing_if = "Option::is_none")]
198    pub financing: Option<AccountUnits>,
199
200    /// The Account's balance after daily financing.
201    #[serde(rename = "accountBalance", skip_serializing_if = "Option::is_none")]
202    pub account_balance: Option<AccountUnits>,
203
204    /// The `accountFinancingMode` field.
205    #[serde(
206        rename = "accountFinancingMode",
207        skip_serializing_if = "Option::is_none"
208    )]
209    pub account_financing_mode: Option<AccountFinancingMode>,
210
211    /// The financing paid/collected for each Position in the Account.
212    #[serde(
213        rename = "positionFinancings",
214        default,
215        skip_serializing_if = "Vec::is_empty"
216    )]
217    pub position_financings: Vec<PositionFinancing>,
218
219    /// The total cost of currency conversions for the financing, in the
220    /// Account's home currency. This field is returned by the live v20 API but
221    /// is not present in OANDA's official documentation.
222    #[serde(rename = "homeConversionCost", skip_serializing_if = "Option::is_none")]
223    pub home_conversion_cost: Option<DecimalNumber>,
224
225    /// The cost of converting the base financing to the Account's home
226    /// currency. This field is returned by the live v20 API but is not present
227    /// in OANDA's official documentation.
228    #[serde(
229        rename = "baseHomeConversionCost",
230        skip_serializing_if = "Option::is_none"
231    )]
232    pub base_home_conversion_cost: Option<DecimalNumber>,
233}
234
235/// A DividendAdjustment Transaction is created administratively to pay or
236/// collect dividend adjustment amounts to or from an Account.
237#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
238#[non_exhaustive]
239pub struct DividendAdjustmentTransaction {
240    /// The Transaction's Identifier.
241    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
242    pub id: Option<TransactionId>,
243
244    /// The date/time when the Transaction was created.
245    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
246    pub time: Option<DateTime>,
247
248    /// The ID of the user that initiated the creation of the Transaction.
249    #[serde(rename = "userID", skip_serializing_if = "Option::is_none")]
250    pub user_id: Option<i64>,
251
252    /// The ID of the Account the Transaction was created for.
253    #[serde(rename = "accountID", skip_serializing_if = "Option::is_none")]
254    pub account_id: Option<AccountId>,
255
256    /// The ID of the "batch" that the Transaction belongs to. Transactions in
257    /// the same batch are applied to the Account simultaneously.
258    #[serde(rename = "batchID", skip_serializing_if = "Option::is_none")]
259    pub batch_id: Option<TransactionId>,
260
261    /// The Request ID of the request which generated the transaction.
262    #[serde(rename = "requestID", skip_serializing_if = "Option::is_none")]
263    pub request_id: Option<RequestId>,
264
265    // type is pinned to "DIVIDEND_ADJUSTMENT" by the enum wrapper
266    /// The name of the instrument for the dividendAdjustment transaction.
267    #[serde(rename = "instrument", skip_serializing_if = "Option::is_none")]
268    pub instrument: Option<InstrumentName>,
269
270    /// The total dividend adjustment amount paid or collected in the Account's
271    /// home currency for the Account as a result of applying the
272    /// DividendAdjustment Transaction.
273    #[serde(rename = "dividendAdjustment", skip_serializing_if = "Option::is_none")]
274    pub dividend_adjustment: Option<DecimalNumber>,
275
276    /// The total dividend adjustment amount paid or collected in the
277    /// Instrument's quote currency.
278    #[serde(
279        rename = "quoteDividendAdjustment",
280        skip_serializing_if = "Option::is_none"
281    )]
282    pub quote_dividend_adjustment: Option<DecimalNumber>,
283
284    /// The `homeConversionFactors` field.
285    #[serde(
286        rename = "homeConversionFactors",
287        skip_serializing_if = "Option::is_none"
288    )]
289    pub home_conversion_factors: Option<HomeConversionFactors>,
290
291    /// The Account balance after applying the DividendAdjustment Transaction.
292    #[serde(rename = "accountBalance", skip_serializing_if = "Option::is_none")]
293    pub account_balance: Option<DecimalNumber>,
294
295    /// The Open Trades being adjusted.
296    #[serde(
297        rename = "openTradeDividendAdjustments",
298        default,
299        skip_serializing_if = "Vec::is_empty"
300    )]
301    pub open_trade_dividend_adjustments: Vec<OpenTradeDividendAdjustment>,
302
303    /// The cost of converting the dividend adjustment to the Account's home
304    /// currency. This field is returned by the live v20 API but is not present
305    /// in OANDA's official documentation.
306    #[serde(rename = "homeConversionCost", skip_serializing_if = "Option::is_none")]
307    pub home_conversion_cost: Option<DecimalNumber>,
308}
309
310/// A ResetResettablePLTransaction represents the resetting of the Account's
311/// resettable PL counters.
312#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
313#[non_exhaustive]
314pub struct ResetResettablePLTransaction {
315    /// The Transaction's Identifier.
316    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
317    pub id: Option<TransactionId>,
318
319    /// The date/time when the Transaction was created.
320    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
321    pub time: Option<DateTime>,
322
323    /// The ID of the user that initiated the creation of the Transaction.
324    #[serde(rename = "userID", skip_serializing_if = "Option::is_none")]
325    pub user_id: Option<i64>,
326
327    /// The ID of the Account the Transaction was created for.
328    #[serde(rename = "accountID", skip_serializing_if = "Option::is_none")]
329    pub account_id: Option<AccountId>,
330
331    /// The ID of the "batch" that the Transaction belongs to. Transactions in
332    /// the same batch are applied to the Account simultaneously.
333    #[serde(rename = "batchID", skip_serializing_if = "Option::is_none")]
334    pub batch_id: Option<TransactionId>,
335
336    /// The Request ID of the request which generated the transaction.
337    #[serde(rename = "requestID", skip_serializing_if = "Option::is_none")]
338    pub request_id: Option<RequestId>,
339    // type is pinned to "RESET_RESETTABLE_PL" by the enum wrapper
340}
341
342/// A LiquidityRegenerationSchedule indicates how liquidity that is used when
343/// filling an Order for an instrument is regenerated following the fill. A
344/// liquidity regeneration schedule will be in effect until the timestamp of its
345/// final step, but may be replaced by a schedule created for an Order of the
346/// same instrument that is filled while it is still in effect.
347#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
348#[non_exhaustive]
349pub struct LiquidityRegenerationSchedule {
350    /// The steps in the Liquidity Regeneration Schedule
351    #[serde(rename = "steps", default, skip_serializing_if = "Vec::is_empty")]
352    pub steps: Vec<LiquidityRegenerationScheduleStep>,
353}
354
355/// A liquidity regeneration schedule Step indicates the amount of bid and ask
356/// liquidity that is used by the Account at a certain time. These amounts will
357/// only change at the timestamp of the following step.
358#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
359#[non_exhaustive]
360pub struct LiquidityRegenerationScheduleStep {
361    /// The timestamp of the schedule step.
362    #[serde(rename = "timestamp", skip_serializing_if = "Option::is_none")]
363    pub timestamp: Option<DateTime>,
364
365    /// The amount of bid liquidity used at this step in the schedule.
366    #[serde(rename = "bidLiquidityUsed", skip_serializing_if = "Option::is_none")]
367    pub bid_liquidity_used: Option<DecimalNumber>,
368
369    /// The amount of ask liquidity used at this step in the schedule.
370    #[serde(rename = "askLiquidityUsed", skip_serializing_if = "Option::is_none")]
371    pub ask_liquidity_used: Option<DecimalNumber>,
372}
373
374string_enum! {
375    /// The possible types of a Transaction
376    pub enum TransactionType {
377        Create => "CREATE",
378        Close => "CLOSE",
379        Reopen => "REOPEN",
380        ClientConfigure => "CLIENT_CONFIGURE",
381        ClientConfigureReject => "CLIENT_CONFIGURE_REJECT",
382        TransferFunds => "TRANSFER_FUNDS",
383        TransferFundsReject => "TRANSFER_FUNDS_REJECT",
384        MarketOrder => "MARKET_ORDER",
385        MarketOrderReject => "MARKET_ORDER_REJECT",
386        FixedPriceOrder => "FIXED_PRICE_ORDER",
387        LimitOrder => "LIMIT_ORDER",
388        LimitOrderReject => "LIMIT_ORDER_REJECT",
389        StopOrder => "STOP_ORDER",
390        StopOrderReject => "STOP_ORDER_REJECT",
391        MarketIfTouchedOrder => "MARKET_IF_TOUCHED_ORDER",
392        MarketIfTouchedOrderReject => "MARKET_IF_TOUCHED_ORDER_REJECT",
393        TakeProfitOrder => "TAKE_PROFIT_ORDER",
394        TakeProfitOrderReject => "TAKE_PROFIT_ORDER_REJECT",
395        StopLossOrder => "STOP_LOSS_ORDER",
396        StopLossOrderReject => "STOP_LOSS_ORDER_REJECT",
397        TrailingStopLossOrder => "TRAILING_STOP_LOSS_ORDER",
398        TrailingStopLossOrderReject => "TRAILING_STOP_LOSS_ORDER_REJECT",
399        OrderFill => "ORDER_FILL",
400        OrderCancel => "ORDER_CANCEL",
401        OrderCancelReject => "ORDER_CANCEL_REJECT",
402        OrderClientExtensionsModify => "ORDER_CLIENT_EXTENSIONS_MODIFY",
403        OrderClientExtensionsModifyReject => "ORDER_CLIENT_EXTENSIONS_MODIFY_REJECT",
404        TradeClientExtensionsModify => "TRADE_CLIENT_EXTENSIONS_MODIFY",
405        TradeClientExtensionsModifyReject => "TRADE_CLIENT_EXTENSIONS_MODIFY_REJECT",
406        MarginCallEnter => "MARGIN_CALL_ENTER",
407        MarginCallExtend => "MARGIN_CALL_EXTEND",
408        MarginCallExit => "MARGIN_CALL_EXIT",
409        DelayedTradeClosure => "DELAYED_TRADE_CLOSURE",
410        DailyFinancing => "DAILY_FINANCING",
411        ResetResettablePl => "RESET_RESETTABLE_PL",
412        DividendAdjustment => "DIVIDEND_ADJUSTMENT",
413    }
414}
415
416string_enum! {
417    /// A filter that can be used when fetching Transactions
418    pub enum TransactionFilter {
419        Order => "ORDER",
420        Funding => "FUNDING",
421        Admin => "ADMIN",
422        Create => "CREATE",
423        Close => "CLOSE",
424        Reopen => "REOPEN",
425        ClientConfigure => "CLIENT_CONFIGURE",
426        ClientConfigureReject => "CLIENT_CONFIGURE_REJECT",
427        TransferFunds => "TRANSFER_FUNDS",
428        TransferFundsReject => "TRANSFER_FUNDS_REJECT",
429        MarketOrder => "MARKET_ORDER",
430        MarketOrderReject => "MARKET_ORDER_REJECT",
431        LimitOrder => "LIMIT_ORDER",
432        LimitOrderReject => "LIMIT_ORDER_REJECT",
433        StopOrder => "STOP_ORDER",
434        StopOrderReject => "STOP_ORDER_REJECT",
435        MarketIfTouchedOrder => "MARKET_IF_TOUCHED_ORDER",
436        MarketIfTouchedOrderReject => "MARKET_IF_TOUCHED_ORDER_REJECT",
437        TakeProfitOrder => "TAKE_PROFIT_ORDER",
438        TakeProfitOrderReject => "TAKE_PROFIT_ORDER_REJECT",
439        StopLossOrder => "STOP_LOSS_ORDER",
440        StopLossOrderReject => "STOP_LOSS_ORDER_REJECT",
441        TrailingStopLossOrder => "TRAILING_STOP_LOSS_ORDER",
442        TrailingStopLossOrderReject => "TRAILING_STOP_LOSS_ORDER_REJECT",
443        OneCancelsAllOrder => "ONE_CANCELS_ALL_ORDER",
444        OneCancelsAllOrderReject => "ONE_CANCELS_ALL_ORDER_REJECT",
445        OneCancelsAllOrderTriggered => "ONE_CANCELS_ALL_ORDER_TRIGGERED",
446        OrderFill => "ORDER_FILL",
447        OrderCancel => "ORDER_CANCEL",
448        OrderCancelReject => "ORDER_CANCEL_REJECT",
449        OrderClientExtensionsModify => "ORDER_CLIENT_EXTENSIONS_MODIFY",
450        OrderClientExtensionsModifyReject => "ORDER_CLIENT_EXTENSIONS_MODIFY_REJECT",
451        TradeClientExtensionsModify => "TRADE_CLIENT_EXTENSIONS_MODIFY",
452        TradeClientExtensionsModifyReject => "TRADE_CLIENT_EXTENSIONS_MODIFY_REJECT",
453        MarginCallEnter => "MARGIN_CALL_ENTER",
454        MarginCallExtend => "MARGIN_CALL_EXTEND",
455        MarginCallExit => "MARGIN_CALL_EXIT",
456        DelayedTradeClosure => "DELAYED_TRADE_CLOSURE",
457        DailyFinancing => "DAILY_FINANCING",
458        ResetResettablePl => "RESET_RESETTABLE_PL",
459    }
460}
461
462/// A TransactionHeartbeat object is injected into the Transaction stream to
463/// ensure that the HTTP connection remains active.
464#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
465#[non_exhaustive]
466pub struct TransactionHeartbeat {
467    /// The type discriminator, always `HEARTBEAT`.
468    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
469    pub r#type: Option<String>,
470
471    /// The ID of the most recent Transaction created for the Account
472    #[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
473    pub last_transaction_id: Option<TransactionId>,
474
475    /// The date/time when the TransactionHeartbeat was created.
476    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
477    pub time: Option<DateTime>,
478}
479
480/// A TransactionHeartbeat object is injected into the Transaction stream to
481/// ensure that the HTTP connection remains active.
482#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
483#[non_exhaustive]
484pub struct MT4TransactionHeartbeat {
485    /// The string "HEARTBEAT"
486    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
487    pub r#type: Option<String>,
488
489    /// The date/time when the TransactionHeartbeat was created.
490    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
491    pub time: Option<DateTime>,
492}