Skip to main content

oanda_client/
trade.rs

1//! OANDA trade endpoint contracts.
2
3use crate::client::RequestParts;
4use crate::ids::{AccountID, InstrumentName, TradeID, TradeSpecifier, TransactionID};
5use crate::models::{
6    ClientExtensions, GuaranteedStopLossDetails, GuaranteedStopLossOrderRejectTransaction,
7    GuaranteedStopLossOrderTransaction, MarketOrderRejectTransaction, MarketOrderTransaction,
8    OrderCancelRejectTransaction, OrderCancelTransaction, OrderFillTransaction, StopLossDetails,
9    StopLossOrderRejectTransaction, StopLossOrderTransaction, TakeProfitDetails,
10    TakeProfitOrderRejectTransaction, TakeProfitOrderTransaction, Trade,
11    TradeClientExtensionsModifyRejectTransaction, TradeClientExtensionsModifyTransaction,
12    TradeStateFilter, TrailingStopLossDetails, TrailingStopLossOrderRejectTransaction,
13    TrailingStopLossOrderTransaction,
14};
15use crate::validation::Validate;
16use crate::{Client, Error, Result};
17use reqwest::Method;
18use serde::{Deserialize, Serialize};
19
20/// Query parameters for `list_trades`.
21///
22/// The default uses the provider’s trade filters and result count.
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
24#[serde(rename_all = "camelCase")]
25pub struct ListTradesQuery {
26    /// List of Trade IDs to retrieve.
27    #[serde(rename = "ids", default, skip_serializing_if = "Option::is_none")]
28    pub ids: Option<Vec<TradeID>>,
29    /// The state to filter the requested Trades by. \[default=OPEN\]
30    #[serde(rename = "state", default, skip_serializing_if = "Option::is_none")]
31    pub state: Option<TradeStateFilter>,
32    /// The instrument to filter the requested Trades by.
33    #[serde(
34        rename = "instrument",
35        default,
36        skip_serializing_if = "Option::is_none"
37    )]
38    pub instrument: Option<InstrumentName>,
39    /// The maximum number of Trades to return. \[default=50, maximum=500\]
40    #[serde(rename = "count", default, skip_serializing_if = "Option::is_none")]
41    pub count: Option<i64>,
42    /// The maximum Trade ID to return. If not provided the most recent Trades in the Account are returned.
43    #[serde(rename = "beforeID", default, skip_serializing_if = "Option::is_none")]
44    pub before_id: Option<TradeID>,
45}
46
47impl Validate for ListTradesQuery {
48    fn validate(&self) -> Result<()> {
49        if let Some(ids) = &self.ids {
50            crate::validation::nonempty(ids, "ids")?;
51        }
52        crate::validation::count(self.count, 500)
53    }
54}
55
56/// Successful response for `list_trades`.
57#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
58#[serde(rename_all = "camelCase")]
59pub struct ListTradesResponse {
60    /// The list of Trade detail objects
61    #[serde(rename = "trades", default, skip_serializing_if = "Option::is_none")]
62    pub trades: Option<Vec<Trade>>,
63    /// The ID of the most recent Transaction created for the Account
64    #[serde(
65        rename = "lastTransactionID",
66        default,
67        skip_serializing_if = "Option::is_none"
68    )]
69    pub last_transaction_id: Option<TransactionID>,
70}
71
72impl Client {
73    /// List trades.
74    ///
75    /// # Errors
76    ///
77    /// Returns [`crate::OperationError::Client`] for invalid input, admission, transport, or
78    /// malformed response failures. Provider refusals return [`crate::OperationError::Rejected`]
79    /// or [`crate::OperationError::RejectedUnreadable`] when the rejection body cannot be read.
80    pub async fn list_trades(
81        &self,
82        account_id: &AccountID,
83        query: Option<&ListTradesQuery>,
84    ) -> std::result::Result<
85        crate::ApiResponse<ListTradesResponse>,
86        crate::OperationError<crate::GenericRejection>,
87    > {
88        let account_id_encoded = crate::client::segment(account_id.as_str());
89        let path = format!("/v3/accounts/{account_id_encoded}/trades");
90        let request = RequestParts::new(Method::GET, &path);
91        let request = match query {
92            Some(query) => request.query(query)?,
93            None => request,
94        };
95        self.execute(request).await
96    }
97}
98
99/// Successful response for `list_open_trades`.
100#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101#[serde(rename_all = "camelCase")]
102pub struct ListOpenTradesResponse {
103    /// The Account’s list of open Trades
104    #[serde(rename = "trades", default, skip_serializing_if = "Option::is_none")]
105    pub trades: Option<Vec<Trade>>,
106    /// The ID of the most recent Transaction created for the Account
107    #[serde(
108        rename = "lastTransactionID",
109        default,
110        skip_serializing_if = "Option::is_none"
111    )]
112    pub last_transaction_id: Option<TransactionID>,
113}
114
115impl Client {
116    /// List open trades.
117    ///
118    /// # Errors
119    ///
120    /// Returns [`crate::OperationError::Client`] for invalid input, admission, transport, or
121    /// malformed response failures. Provider refusals return [`crate::OperationError::Rejected`]
122    /// or [`crate::OperationError::RejectedUnreadable`] when the rejection body cannot be read.
123    pub async fn list_open_trades(
124        &self,
125        account_id: &AccountID,
126    ) -> std::result::Result<
127        crate::ApiResponse<ListOpenTradesResponse>,
128        crate::OperationError<crate::GenericRejection>,
129    > {
130        let account_id_encoded = crate::client::segment(account_id.as_str());
131        let path = format!("/v3/accounts/{account_id_encoded}/openTrades");
132        self.execute(RequestParts::new(Method::GET, &path)).await
133    }
134}
135
136/// Successful response for `trade_details`.
137#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
138#[serde(rename_all = "camelCase")]
139pub struct TradeDetailsResponse {
140    /// The details of the requested trade
141    #[serde(rename = "trade", default, skip_serializing_if = "Option::is_none")]
142    pub trade: Option<Trade>,
143    /// The ID of the most recent Transaction created for the Account
144    #[serde(
145        rename = "lastTransactionID",
146        default,
147        skip_serializing_if = "Option::is_none"
148    )]
149    pub last_transaction_id: Option<TransactionID>,
150}
151
152impl Client {
153    /// Trade details.
154    ///
155    /// # Errors
156    ///
157    /// Returns [`crate::OperationError::Client`] for invalid input, admission, transport, or
158    /// malformed response failures. Provider refusals return [`crate::OperationError::Rejected`]
159    /// or [`crate::OperationError::RejectedUnreadable`] when the rejection body cannot be read.
160    pub async fn trade_details(
161        &self,
162        account_id: &AccountID,
163        trade_specifier: &TradeSpecifier,
164    ) -> std::result::Result<
165        crate::ApiResponse<TradeDetailsResponse>,
166        crate::OperationError<crate::GenericRejection>,
167    > {
168        let account_id_encoded = crate::client::segment(account_id.as_str());
169        let trade_specifier_encoded = crate::client::segment(trade_specifier.as_str());
170        let path = format!("/v3/accounts/{account_id_encoded}/trades/{trade_specifier_encoded}");
171        self.execute(RequestParts::new(Method::GET, &path)).await
172    }
173}
174
175/// Request body for `close_trade`.
176#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
177#[serde(rename_all = "camelCase")]
178pub struct CloseTradeBody {
179    /// Indication of how much of the Trade to close. Either the string “ALL” (indicating that all of the Trade
180    /// should be closed), or a `DecimalNumber` representing the number of units of the open Trade to Close using
181    /// a `TradeClose` `MarketOrder`. The units specified must always be positive, and the magnitude of the value
182    /// cannot exceed the magnitude of the Trade’s open units. Omitting this field closes all units
183    /// (the provider default is `ALL`).
184    #[serde(rename = "units", default, skip_serializing_if = "Option::is_none")]
185    pub units: Option<crate::TradeCloseoutUnits>,
186}
187
188impl Validate for CloseTradeBody {
189    fn validate(&self) -> Result<()> {
190        if let Some(units) = &self.units {
191            units.validate()?;
192        }
193        Ok(())
194    }
195}
196
197/// Successful response for `close_trade`.
198#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct CloseTradeResponse {
201    /// The `MarketOrder` Transaction created to close the Trade.
202    #[serde(
203        rename = "orderCreateTransaction",
204        default,
205        skip_serializing_if = "Option::is_none"
206    )]
207    pub order_create_transaction: Option<MarketOrderTransaction>,
208    /// The `OrderFill` Transaction that fills the Trade-closing `MarketOrder` and closes the Trade.
209    #[serde(
210        rename = "orderFillTransaction",
211        default,
212        skip_serializing_if = "Option::is_none"
213    )]
214    pub order_fill_transaction: Option<OrderFillTransaction>,
215    /// The `OrderCancel` Transaction that immediately cancelled the Trade-closing `MarketOrder`.
216    #[serde(
217        rename = "orderCancelTransaction",
218        default,
219        skip_serializing_if = "Option::is_none"
220    )]
221    pub order_cancel_transaction: Option<OrderCancelTransaction>,
222    /// The IDs of all Transactions that were created while satisfying the request.
223    #[serde(
224        rename = "relatedTransactionIDs",
225        default,
226        skip_serializing_if = "Option::is_none"
227    )]
228    pub related_transaction_ids: Option<Vec<TransactionID>>,
229    /// The ID of the most recent Transaction created for the Account
230    #[serde(
231        rename = "lastTransactionID",
232        default,
233        skip_serializing_if = "Option::is_none"
234    )]
235    pub last_transaction_id: Option<TransactionID>,
236}
237
238/// Documented rejection fields for `close_trade`.
239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240#[serde(rename_all = "camelCase")]
241pub struct CloseTradeRejection {
242    /// The `MarketOrderReject` Transaction that rejects the creation of the Trade- closing `MarketOrder`.
243    #[serde(
244        rename = "orderRejectTransaction",
245        default,
246        skip_serializing_if = "Option::is_none"
247    )]
248    pub order_reject_transaction: Option<MarketOrderRejectTransaction>,
249    /// The code of the error that has occurred. This field may not be returned for some errors.
250    #[serde(rename = "errorCode", default, skip_serializing_if = "Option::is_none")]
251    pub error_code: Option<String>,
252    /// The human-readable description of the error that has occurred.
253    #[serde(rename = "errorMessage")]
254    pub error_message: String,
255    /// The ID of the most recent Transaction created for the Account. Only present if the Account exists.
256    #[serde(
257        rename = "lastTransactionID",
258        default,
259        skip_serializing_if = "Option::is_none"
260    )]
261    pub last_transaction_id: Option<TransactionID>,
262    /// The IDs of all Transactions that were created while satisfying the request. Only present if the Account
263    /// exists.
264    #[serde(
265        rename = "relatedTransactionIDs",
266        default,
267        skip_serializing_if = "Option::is_none"
268    )]
269    pub related_transaction_ids: Option<Vec<TransactionID>>,
270}
271
272impl Client {
273    /// Close trade.
274    ///
275    /// # Errors
276    ///
277    /// Returns [`crate::OperationError::Client`] for invalid input, admission, transport, or
278    /// malformed response failures. Provider refusals return [`crate::OperationError::Rejected`]
279    /// or [`crate::OperationError::RejectedUnreadable`] when the rejection body cannot be read.
280    /// An uncertain sent mutation fences this account until caller-acknowledged reconciliation.
281    pub async fn close_trade(
282        &self,
283        account_id: &AccountID,
284        trade_specifier: &TradeSpecifier,
285        body: &CloseTradeBody,
286    ) -> std::result::Result<
287        crate::ApiResponse<CloseTradeResponse>,
288        crate::OperationError<CloseTradeRejection>,
289    > {
290        let account_id_encoded = crate::client::segment(account_id.as_str());
291        let trade_specifier_encoded = crate::client::segment(trade_specifier.as_str());
292        let path =
293            format!("/v3/accounts/{account_id_encoded}/trades/{trade_specifier_encoded}/close");
294        self.execute(
295            RequestParts::new(Method::PUT, &path)
296                .body(body)?
297                .mutation(crate::client::MutationAdmission::Exit(account_id)),
298        )
299        .await
300    }
301}
302
303/// Request body for `update_trade_client_extensions`.
304#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
305#[serde(rename_all = "camelCase")]
306pub struct UpdateTradeClientExtensionsBody {
307    /// The Client Extensions to update the Trade with. Do not add, update, or delete the Client Extensions if
308    /// your account is associated with MT4.
309    #[serde(
310        rename = "clientExtensions",
311        default,
312        skip_serializing_if = "Option::is_none"
313    )]
314    pub client_extensions: Option<ClientExtensions>,
315}
316
317impl Validate for UpdateTradeClientExtensionsBody {
318    fn validate(&self) -> Result<()> {
319        if self.client_extensions.is_none() {
320            return Err(Error::InvalidInput(
321                "mutation body must not be empty".into(),
322            ));
323        }
324        Ok(())
325    }
326}
327
328/// Successful response for `update_trade_client_extensions`.
329#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
330#[serde(rename_all = "camelCase")]
331pub struct UpdateTradeClientExtensionsResponse {
332    /// The Transaction that updates the Trade’s Client Extensions.
333    #[serde(
334        rename = "tradeClientExtensionsModifyTransaction",
335        default,
336        skip_serializing_if = "Option::is_none"
337    )]
338    pub trade_client_extensions_modify_transaction: Option<TradeClientExtensionsModifyTransaction>,
339    /// The IDs of all Transactions that were created while satisfying the request.
340    #[serde(
341        rename = "relatedTransactionIDs",
342        default,
343        skip_serializing_if = "Option::is_none"
344    )]
345    pub related_transaction_ids: Option<Vec<TransactionID>>,
346    /// The ID of the most recent Transaction created for the Account
347    #[serde(
348        rename = "lastTransactionID",
349        default,
350        skip_serializing_if = "Option::is_none"
351    )]
352    pub last_transaction_id: Option<TransactionID>,
353}
354
355/// Documented rejection fields for `update_trade_client_extensions`.
356#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
357#[serde(rename_all = "camelCase")]
358pub struct UpdateTradeClientExtensionsRejection {
359    /// The Transaction that rejects the modification of the Trade’s Client Extensions.
360    #[serde(
361        rename = "tradeClientExtensionsModifyRejectTransaction",
362        default,
363        skip_serializing_if = "Option::is_none"
364    )]
365    pub trade_client_extensions_modify_reject_transaction:
366        Option<TradeClientExtensionsModifyRejectTransaction>,
367    /// The ID of the most recent Transaction created for the Account.
368    #[serde(
369        rename = "lastTransactionID",
370        default,
371        skip_serializing_if = "Option::is_none"
372    )]
373    pub last_transaction_id: Option<TransactionID>,
374    /// The IDs of all Transactions that were created while satisfying the request.
375    #[serde(
376        rename = "relatedTransactionIDs",
377        default,
378        skip_serializing_if = "Option::is_none"
379    )]
380    pub related_transaction_ids: Option<Vec<TransactionID>>,
381    /// The code of the error that has occurred. This field may not be returned for some errors.
382    #[serde(rename = "errorCode", default, skip_serializing_if = "Option::is_none")]
383    pub error_code: Option<String>,
384    /// The human-readable description of the error that has occurred.
385    #[serde(rename = "errorMessage")]
386    pub error_message: String,
387}
388
389impl Client {
390    /// Update trade client extensions.
391    ///
392    /// # Errors
393    ///
394    /// Returns [`crate::OperationError::Client`] for invalid input, admission, transport, or
395    /// malformed response failures. Provider refusals return [`crate::OperationError::Rejected`]
396    /// or [`crate::OperationError::RejectedUnreadable`] when the rejection body cannot be read.
397    /// An uncertain sent mutation fences this account until caller-acknowledged reconciliation.
398    pub async fn update_trade_client_extensions(
399        &self,
400        account_id: &AccountID,
401        trade_specifier: &TradeSpecifier,
402        body: &UpdateTradeClientExtensionsBody,
403    ) -> std::result::Result<
404        crate::ApiResponse<UpdateTradeClientExtensionsResponse>,
405        crate::OperationError<UpdateTradeClientExtensionsRejection>,
406    > {
407        let account_id_encoded = crate::client::segment(account_id.as_str());
408        let trade_specifier_encoded = crate::client::segment(trade_specifier.as_str());
409        let path = format!(
410            "/v3/accounts/{account_id_encoded}/trades/{trade_specifier_encoded}/clientExtensions"
411        );
412        self.execute(
413            RequestParts::new(Method::PUT, &path)
414                .body(body)?
415                .mutation(crate::client::MutationAdmission::Ordinary(account_id)),
416        )
417        .await
418    }
419}
420
421/// Request body for `set_trade_dependent_orders`.
422#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
423#[serde(rename_all = "camelCase")]
424pub struct SetTradeDependentOrdersBody {
425    /// The specification of the Take Profit to create/modify/cancel. If takeProfit is set to null, the Take
426    /// Profit Order will be cancelled if it exists. If takeProfit is not provided, the existing Take Profit
427    /// Order will not be modified. If a sub-field of takeProfit is not specified, that field will be set to a
428    /// default value on create, and be inherited by the replacing order on modify.
429    #[serde(
430        rename = "takeProfit",
431        default,
432        skip_serializing_if = "crate::Patch::is_unchanged"
433    )]
434    pub take_profit: crate::Patch<TakeProfitDetails>,
435    /// The specification of the Stop Loss to create/modify/cancel. If stopLoss is set to null, the Stop Loss
436    /// Order will be cancelled if it exists. If stopLoss is not provided, the existing Stop Loss Order will not
437    /// be modified. If a sub-field of stopLoss is not specified, that field will be set to a default value on
438    /// create, and be inherited by the replacing order on modify.
439    #[serde(
440        rename = "stopLoss",
441        default,
442        skip_serializing_if = "crate::Patch::is_unchanged"
443    )]
444    pub stop_loss: crate::Patch<StopLossDetails>,
445    /// The specification of the Trailing Stop Loss to create/modify/cancel. If trailingStopLoss is set to null,
446    /// the Trailing Stop Loss Order will be cancelled if it exists. If trailingStopLoss is not provided, the
447    /// existing Trailing Stop Loss Order will not be modified. If a sub-field of trailingStopLoss is not
448    /// specified, that field will be set to a default value on create, and be inherited by the replacing order
449    /// on modify.
450    #[serde(
451        rename = "trailingStopLoss",
452        default,
453        skip_serializing_if = "crate::Patch::is_unchanged"
454    )]
455    pub trailing_stop_loss: crate::Patch<TrailingStopLossDetails>,
456    /// The specification of the Guaranteed Stop Loss to create/modify/cancel. If guaranteedStopLoss is set to
457    /// null, the Guaranteed Stop Loss Order will be cancelled if it exists. If guaranteedStopLoss is not
458    /// provided, the existing Guaranteed Stop Loss Order will not be modified. If a sub-field of
459    /// guaranteedStopLoss is not specified, that field will be set to a default value on create, and be
460    /// inherited by the replacing order on modify.
461    #[serde(
462        rename = "guaranteedStopLoss",
463        default,
464        skip_serializing_if = "crate::Patch::is_unchanged"
465    )]
466    pub guaranteed_stop_loss: crate::Patch<GuaranteedStopLossDetails>,
467}
468
469impl Validate for SetTradeDependentOrdersBody {
470    fn validate(&self) -> Result<()> {
471        if self.take_profit.is_unchanged()
472            && self.stop_loss.is_unchanged()
473            && self.trailing_stop_loss.is_unchanged()
474            && self.guaranteed_stop_loss.is_unchanged()
475        {
476            return Err(Error::InvalidInput(
477                "mutation body must not be empty".into(),
478            ));
479        }
480        if let crate::Patch::Set(details) = &self.take_profit {
481            details.validate()?;
482        }
483        if let crate::Patch::Set(details) = &self.stop_loss {
484            details.validate()?;
485        }
486        if let crate::Patch::Set(details) = &self.trailing_stop_loss {
487            details.validate()?;
488        }
489        if let crate::Patch::Set(details) = &self.guaranteed_stop_loss {
490            details.validate()?;
491        }
492        Ok(())
493    }
494}
495
496/// Successful response for `set_trade_dependent_orders`.
497#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
498#[serde(rename_all = "camelCase")]
499pub struct SetTradeDependentOrdersResponse {
500    /// The Transaction created that cancels the Trade’s existing Take Profit Order.
501    #[serde(
502        rename = "takeProfitOrderCancelTransaction",
503        default,
504        skip_serializing_if = "Option::is_none"
505    )]
506    pub take_profit_order_cancel_transaction: Option<OrderCancelTransaction>,
507    /// The Transaction created that creates a new Take Profit Order for the Trade.
508    #[serde(
509        rename = "takeProfitOrderTransaction",
510        default,
511        skip_serializing_if = "Option::is_none"
512    )]
513    pub take_profit_order_transaction: Option<TakeProfitOrderTransaction>,
514    /// The Transaction created that immediately fills the Trade’s new Take Profit Order. Only provided if the
515    /// new Take Profit Order was immediately filled.
516    #[serde(
517        rename = "takeProfitOrderFillTransaction",
518        default,
519        skip_serializing_if = "Option::is_none"
520    )]
521    pub take_profit_order_fill_transaction: Option<OrderFillTransaction>,
522    /// The Transaction created that immediately cancels the Trade’s new Take Profit Order. Only provided if the
523    /// new Take Profit Order was immediately cancelled.
524    #[serde(
525        rename = "takeProfitOrderCreatedCancelTransaction",
526        default,
527        skip_serializing_if = "Option::is_none"
528    )]
529    pub take_profit_order_created_cancel_transaction: Option<OrderCancelTransaction>,
530    /// The Transaction created that cancels the Trade’s existing Stop Loss Order.
531    #[serde(
532        rename = "stopLossOrderCancelTransaction",
533        default,
534        skip_serializing_if = "Option::is_none"
535    )]
536    pub stop_loss_order_cancel_transaction: Option<OrderCancelTransaction>,
537    /// The Transaction created that creates a new Stop Loss Order for the Trade.
538    #[serde(
539        rename = "stopLossOrderTransaction",
540        default,
541        skip_serializing_if = "Option::is_none"
542    )]
543    pub stop_loss_order_transaction: Option<StopLossOrderTransaction>,
544    /// The Transaction created that immediately fills the Trade’s new Stop Order. Only provided if the new Stop
545    /// Loss Order was immediately filled.
546    #[serde(
547        rename = "stopLossOrderFillTransaction",
548        default,
549        skip_serializing_if = "Option::is_none"
550    )]
551    pub stop_loss_order_fill_transaction: Option<OrderFillTransaction>,
552    /// The Transaction created that immediately cancels the Trade’s new Stop Loss Order. Only provided if the
553    /// new Stop Loss Order was immediately cancelled.
554    #[serde(
555        rename = "stopLossOrderCreatedCancelTransaction",
556        default,
557        skip_serializing_if = "Option::is_none"
558    )]
559    pub stop_loss_order_created_cancel_transaction: Option<OrderCancelTransaction>,
560    /// The Transaction created that cancels the Trade’s existing Trailing Stop Loss Order.
561    #[serde(
562        rename = "trailingStopLossOrderCancelTransaction",
563        default,
564        skip_serializing_if = "Option::is_none"
565    )]
566    pub trailing_stop_loss_order_cancel_transaction: Option<OrderCancelTransaction>,
567    /// The Transaction created that creates a new Trailing Stop Loss Order for the Trade.
568    #[serde(
569        rename = "trailingStopLossOrderTransaction",
570        default,
571        skip_serializing_if = "Option::is_none"
572    )]
573    pub trailing_stop_loss_order_transaction: Option<TrailingStopLossOrderTransaction>,
574    /// The Transaction created that cancels the Trade’s existing Guaranteed Stop Loss Order.
575    #[serde(
576        rename = "guaranteedStopLossOrderCancelTransaction",
577        default,
578        skip_serializing_if = "Option::is_none"
579    )]
580    pub guaranteed_stop_loss_order_cancel_transaction: Option<OrderCancelTransaction>,
581    /// The Transaction created that creates a new Guaranteed Stop Loss Order for the Trade.
582    #[serde(
583        rename = "guaranteedStopLossOrderTransaction",
584        default,
585        skip_serializing_if = "Option::is_none"
586    )]
587    pub guaranteed_stop_loss_order_transaction: Option<GuaranteedStopLossOrderTransaction>,
588    /// The IDs of all Transactions that were created while satisfying the request.
589    #[serde(
590        rename = "relatedTransactionIDs",
591        default,
592        skip_serializing_if = "Option::is_none"
593    )]
594    pub related_transaction_ids: Option<Vec<TransactionID>>,
595    /// The ID of the most recent Transaction created for the Account
596    #[serde(
597        rename = "lastTransactionID",
598        default,
599        skip_serializing_if = "Option::is_none"
600    )]
601    pub last_transaction_id: Option<TransactionID>,
602}
603
604/// Documented rejection fields for `set_trade_dependent_orders`.
605#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
606#[serde(rename_all = "camelCase")]
607pub struct SetTradeDependentOrdersRejection {
608    /// An `OrderCancelRejectTransaction` represents the rejection of the cancellation of an Order in the client’s
609    /// Account.
610    #[serde(
611        rename = "takeProfitOrderCancelRejectTransaction",
612        default,
613        skip_serializing_if = "Option::is_none"
614    )]
615    pub take_profit_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
616    /// A `TakeProfitOrderRejectTransaction` represents the rejection of the creation of a `TakeProfit` Order.
617    #[serde(
618        rename = "takeProfitOrderRejectTransaction",
619        default,
620        skip_serializing_if = "Option::is_none"
621    )]
622    pub take_profit_order_reject_transaction: Option<TakeProfitOrderRejectTransaction>,
623    /// An `OrderCancelRejectTransaction` represents the rejection of the cancellation of an Order in the client’s
624    /// Account.
625    #[serde(
626        rename = "stopLossOrderCancelRejectTransaction",
627        default,
628        skip_serializing_if = "Option::is_none"
629    )]
630    pub stop_loss_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
631    /// A `StopLossOrderRejectTransaction` represents the rejection of the creation of a `StopLoss` Order.
632    #[serde(
633        rename = "stopLossOrderRejectTransaction",
634        default,
635        skip_serializing_if = "Option::is_none"
636    )]
637    pub stop_loss_order_reject_transaction: Option<StopLossOrderRejectTransaction>,
638    /// An `OrderCancelRejectTransaction` represents the rejection of the cancellation of an Order in the client’s
639    /// Account.
640    #[serde(
641        rename = "trailingStopLossOrderCancelRejectTransaction",
642        default,
643        skip_serializing_if = "Option::is_none"
644    )]
645    pub trailing_stop_loss_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
646    /// A `TrailingStopLossOrderRejectTransaction` represents the rejection of the creation of a `TrailingStopLoss`
647    /// Order.
648    #[serde(
649        rename = "trailingStopLossOrderRejectTransaction",
650        default,
651        skip_serializing_if = "Option::is_none"
652    )]
653    pub trailing_stop_loss_order_reject_transaction: Option<TrailingStopLossOrderRejectTransaction>,
654    /// An `OrderCancelRejectTransaction` represents the rejection of the cancellation of an Order in the client’s
655    /// Account.
656    #[serde(
657        rename = "guaranteedStopLossOrderCancelRejectTransaction",
658        default,
659        skip_serializing_if = "Option::is_none"
660    )]
661    pub guaranteed_stop_loss_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
662    /// A `GuaranteedStopLossOrderRejectTransaction` represents the rejection of the creation of a
663    /// `GuaranteedStopLoss` Order.
664    #[serde(
665        rename = "guaranteedStopLossOrderRejectTransaction",
666        default,
667        skip_serializing_if = "Option::is_none"
668    )]
669    pub guaranteed_stop_loss_order_reject_transaction:
670        Option<GuaranteedStopLossOrderRejectTransaction>,
671    /// The ID of the most recent Transaction created for the Account.
672    #[serde(
673        rename = "lastTransactionID",
674        default,
675        skip_serializing_if = "Option::is_none"
676    )]
677    pub last_transaction_id: Option<TransactionID>,
678    /// The IDs of all Transactions that were created while satisfying the request.
679    #[serde(
680        rename = "relatedTransactionIDs",
681        default,
682        skip_serializing_if = "Option::is_none"
683    )]
684    pub related_transaction_ids: Option<Vec<TransactionID>>,
685    /// The code of the error that has occurred. This field may not be returned for some errors.
686    #[serde(rename = "errorCode", default, skip_serializing_if = "Option::is_none")]
687    pub error_code: Option<String>,
688    /// The human-readable description of the error that has occurred.
689    #[serde(rename = "errorMessage")]
690    pub error_message: String,
691}
692
693impl Client {
694    /// Set trade dependent orders.
695    ///
696    /// # Errors
697    ///
698    /// Returns [`crate::OperationError::Client`] for invalid input, admission, transport, or
699    /// malformed response failures. Provider refusals return [`crate::OperationError::Rejected`]
700    /// or [`crate::OperationError::RejectedUnreadable`] when the rejection body cannot be read.
701    /// An uncertain sent mutation fences this account until caller-acknowledged reconciliation.
702    pub async fn set_trade_dependent_orders(
703        &self,
704        account_id: &AccountID,
705        trade_specifier: &TradeSpecifier,
706        body: &SetTradeDependentOrdersBody,
707    ) -> std::result::Result<
708        crate::ApiResponse<SetTradeDependentOrdersResponse>,
709        crate::OperationError<SetTradeDependentOrdersRejection>,
710    > {
711        let account_id_encoded = crate::client::segment(account_id.as_str());
712        let trade_specifier_encoded = crate::client::segment(trade_specifier.as_str());
713        let path =
714            format!("/v3/accounts/{account_id_encoded}/trades/{trade_specifier_encoded}/orders");
715        self.execute(
716            RequestParts::new(Method::PUT, &path)
717                .body(body)?
718                .mutation(crate::client::MutationAdmission::Ordinary(account_id)),
719        )
720        .await
721    }
722}