1use serde::{Deserialize, Serialize};
4
5use crate::client::Client;
6use crate::error::Error;
7use crate::models::transaction::{
8 MarketOrderRejectTransaction, MarketOrderTransaction, OrderCancelRejectTransaction,
9 OrderCancelTransaction, OrderFillTransaction, StopLossOrderRejectTransaction,
10 StopLossOrderTransaction, TakeProfitOrderRejectTransaction, TakeProfitOrderTransaction,
11 TradeClientExtensionsModifyRejectTransaction, TradeClientExtensionsModifyTransaction,
12 TrailingStopLossOrderRejectTransaction, TrailingStopLossOrderTransaction,
13};
14use crate::models::{
15 AccountId, ClientExtensions, InstrumentName, StopLossDetails, TakeProfitDetails, Trade,
16 TradeId, TradeSpecifier, TradeStateFilter, TrailingStopLossDetails, TransactionId,
17};
18
19impl Client {
20 pub fn list_trades(&self, account_id: impl Into<AccountId>) -> ListTradesRequest {
24 ListTradesRequest {
25 client: self.clone(),
26 account_id: account_id.into(),
27 params: Vec::new(),
28 }
29 }
30
31 pub async fn list_open_trades(
35 &self,
36 account_id: impl Into<AccountId>,
37 ) -> Result<ListTradesResponse, Error> {
38 let account_id = account_id.into();
39 self.execute(self.get(&["accounts", account_id.as_str(), "openTrades"]))
40 .await
41 }
42
43 pub async fn trade(
47 &self,
48 account_id: impl Into<AccountId>,
49 trade: impl Into<TradeSpecifier>,
50 ) -> Result<TradeResponse, Error> {
51 let account_id = account_id.into();
52 let trade = trade.into();
53 self.execute(self.get(&["accounts", account_id.as_str(), "trades", trade.as_str()]))
54 .await
55 }
56
57 pub fn close_trade(
78 &self,
79 account_id: impl Into<AccountId>,
80 trade: impl Into<TradeSpecifier>,
81 ) -> CloseTradeRequest {
82 CloseTradeRequest {
83 client: self.clone(),
84 account_id: account_id.into(),
85 trade: trade.into(),
86 units: None,
87 }
88 }
89
90 pub async fn set_trade_client_extensions(
97 &self,
98 account_id: impl Into<AccountId>,
99 trade: impl Into<TradeSpecifier>,
100 extensions: ClientExtensions,
101 ) -> Result<SetTradeClientExtensionsResponse, Error> {
102 let account_id = account_id.into();
103 let trade = trade.into();
104 let request = self
105 .put(&[
106 "accounts",
107 account_id.as_str(),
108 "trades",
109 trade.as_str(),
110 "clientExtensions",
111 ])
112 .json(&SetTradeClientExtensionsBody {
113 client_extensions: extensions,
114 });
115 self.execute(request).await
116 }
117
118 pub fn set_trade_dependent_orders(
127 &self,
128 account_id: impl Into<AccountId>,
129 trade: impl Into<TradeSpecifier>,
130 ) -> SetTradeDependentOrdersRequest {
131 SetTradeDependentOrdersRequest {
132 client: self.clone(),
133 account_id: account_id.into(),
134 trade: trade.into(),
135 body: SetTradeDependentOrdersBody {
136 take_profit: None,
137 stop_loss: None,
138 trailing_stop_loss: None,
139 },
140 }
141 }
142}
143
144#[derive(Debug)]
146pub struct ListTradesRequest {
147 client: Client,
148 account_id: AccountId,
149 params: Vec<(&'static str, String)>,
150}
151
152impl ListTradesRequest {
153 pub fn ids<I>(mut self, ids: I) -> Self
155 where
156 I: IntoIterator,
157 I::Item: Into<TradeId>,
158 {
159 let joined = ids
160 .into_iter()
161 .map(|id| id.into().0)
162 .collect::<Vec<_>>()
163 .join(",");
164 self.params.push(("ids", joined));
165 self
166 }
167
168 pub fn state(mut self, state: TradeStateFilter) -> Self {
170 self.params.push(("state", state.to_string()));
171 self
172 }
173
174 pub fn instrument(mut self, instrument: impl Into<InstrumentName>) -> Self {
176 self.params
177 .push(("instrument", instrument.into().as_str().to_owned()));
178 self
179 }
180
181 pub fn count(mut self, count: u32) -> Self {
183 self.params.push(("count", count.to_string()));
184 self
185 }
186
187 pub fn before_id(mut self, before_id: impl Into<TradeId>) -> Self {
190 self.params.push(("beforeID", before_id.into().0));
191 self
192 }
193
194 pub async fn send(self) -> Result<ListTradesResponse, Error> {
196 let request = self
197 .client
198 .get(&["accounts", self.account_id.as_str(), "trades"])
199 .query(&self.params);
200 self.client.execute(request).await
201 }
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
206#[non_exhaustive]
207pub struct ListTradesResponse {
208 #[serde(rename = "trades", default, skip_serializing_if = "Vec::is_empty")]
210 pub trades: Vec<Trade>,
211 #[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
213 pub last_transaction_id: Option<TransactionId>,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218#[non_exhaustive]
219pub struct TradeResponse {
220 #[serde(rename = "trade")]
222 pub trade: Trade,
223 #[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
225 pub last_transaction_id: Option<TransactionId>,
226}
227
228#[derive(Debug)]
230pub struct CloseTradeRequest {
231 client: Client,
232 account_id: AccountId,
233 trade: TradeSpecifier,
234 units: Option<String>,
235}
236
237#[derive(Debug, Serialize)]
238struct CloseTradeBody {
239 #[serde(skip_serializing_if = "Option::is_none")]
240 units: Option<String>,
241}
242
243impl CloseTradeRequest {
244 pub fn units(mut self, units: impl Into<String>) -> Self {
247 self.units = Some(units.into());
248 self
249 }
250
251 pub async fn send(self) -> Result<CloseTradeResponse, Error> {
253 let request = self
254 .client
255 .put(&[
256 "accounts",
257 self.account_id.as_str(),
258 "trades",
259 self.trade.as_str(),
260 "close",
261 ])
262 .json(&CloseTradeBody { units: self.units });
263 self.client.execute(request).await
264 }
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
269#[non_exhaustive]
270pub struct CloseTradeResponse {
271 #[serde(
273 rename = "orderCreateTransaction",
274 skip_serializing_if = "Option::is_none"
275 )]
276 pub order_create_transaction: Option<MarketOrderTransaction>,
277 #[serde(
279 rename = "orderFillTransaction",
280 skip_serializing_if = "Option::is_none"
281 )]
282 pub order_fill_transaction: Option<OrderFillTransaction>,
283 #[serde(
286 rename = "orderCancelTransaction",
287 skip_serializing_if = "Option::is_none"
288 )]
289 pub order_cancel_transaction: Option<OrderCancelTransaction>,
290 #[serde(
292 rename = "relatedTransactionIDs",
293 default,
294 skip_serializing_if = "Vec::is_empty"
295 )]
296 pub related_transaction_ids: Vec<TransactionId>,
297 #[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
299 pub last_transaction_id: Option<TransactionId>,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
306#[non_exhaustive]
307pub struct CloseTradeRejectBody {
308 #[serde(
310 rename = "orderRejectTransaction",
311 skip_serializing_if = "Option::is_none"
312 )]
313 pub order_reject_transaction: Option<MarketOrderRejectTransaction>,
314 #[serde(
316 rename = "relatedTransactionIDs",
317 default,
318 skip_serializing_if = "Vec::is_empty"
319 )]
320 pub related_transaction_ids: Vec<TransactionId>,
321 #[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
323 pub last_transaction_id: Option<TransactionId>,
324}
325
326#[derive(Debug, Serialize)]
327struct SetTradeClientExtensionsBody {
328 #[serde(rename = "clientExtensions")]
329 client_extensions: ClientExtensions,
330}
331
332#[derive(Debug, Clone, Serialize, Deserialize)]
334#[non_exhaustive]
335pub struct SetTradeClientExtensionsResponse {
336 #[serde(
338 rename = "tradeClientExtensionsModifyTransaction",
339 skip_serializing_if = "Option::is_none"
340 )]
341 pub trade_client_extensions_modify_transaction: Option<TradeClientExtensionsModifyTransaction>,
342 #[serde(
344 rename = "relatedTransactionIDs",
345 default,
346 skip_serializing_if = "Vec::is_empty"
347 )]
348 pub related_transaction_ids: Vec<TransactionId>,
349 #[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
351 pub last_transaction_id: Option<TransactionId>,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
358#[non_exhaustive]
359pub struct SetTradeClientExtensionsRejectBody {
360 #[serde(
362 rename = "tradeClientExtensionsModifyRejectTransaction",
363 skip_serializing_if = "Option::is_none"
364 )]
365 pub trade_client_extensions_modify_reject_transaction:
366 Option<TradeClientExtensionsModifyRejectTransaction>,
367 #[serde(
369 rename = "relatedTransactionIDs",
370 default,
371 skip_serializing_if = "Vec::is_empty"
372 )]
373 pub related_transaction_ids: Vec<TransactionId>,
374 #[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
376 pub last_transaction_id: Option<TransactionId>,
377}
378
379#[derive(Debug)]
381pub struct SetTradeDependentOrdersRequest {
382 client: Client,
383 account_id: AccountId,
384 trade: TradeSpecifier,
385 body: SetTradeDependentOrdersBody,
386}
387
388#[derive(Debug, Serialize)]
389struct SetTradeDependentOrdersBody {
390 #[serde(rename = "takeProfit", skip_serializing_if = "Option::is_none")]
391 take_profit: Option<Option<TakeProfitDetails>>,
392 #[serde(rename = "stopLoss", skip_serializing_if = "Option::is_none")]
393 stop_loss: Option<Option<StopLossDetails>>,
394 #[serde(rename = "trailingStopLoss", skip_serializing_if = "Option::is_none")]
395 trailing_stop_loss: Option<Option<TrailingStopLossDetails>>,
396}
397
398impl SetTradeDependentOrdersRequest {
399 pub fn take_profit(mut self, details: TakeProfitDetails) -> Self {
401 self.body.take_profit = Some(Some(details));
402 self
403 }
404
405 pub fn cancel_take_profit(mut self) -> Self {
407 self.body.take_profit = Some(None);
408 self
409 }
410
411 pub fn stop_loss(mut self, details: StopLossDetails) -> Self {
413 self.body.stop_loss = Some(Some(details));
414 self
415 }
416
417 pub fn cancel_stop_loss(mut self) -> Self {
419 self.body.stop_loss = Some(None);
420 self
421 }
422
423 pub fn trailing_stop_loss(mut self, details: TrailingStopLossDetails) -> Self {
425 self.body.trailing_stop_loss = Some(Some(details));
426 self
427 }
428
429 pub fn cancel_trailing_stop_loss(mut self) -> Self {
431 self.body.trailing_stop_loss = Some(None);
432 self
433 }
434
435 pub async fn send(self) -> Result<SetTradeDependentOrdersResponse, Error> {
437 let request = self
438 .client
439 .put(&[
440 "accounts",
441 self.account_id.as_str(),
442 "trades",
443 self.trade.as_str(),
444 "orders",
445 ])
446 .json(&self.body);
447 self.client.execute(request).await
448 }
449}
450
451#[derive(Debug, Clone, Serialize, Deserialize)]
453#[non_exhaustive]
454pub struct SetTradeDependentOrdersResponse {
455 #[serde(
457 rename = "takeProfitOrderCancelTransaction",
458 skip_serializing_if = "Option::is_none"
459 )]
460 pub take_profit_order_cancel_transaction: Option<OrderCancelTransaction>,
461 #[serde(
463 rename = "takeProfitOrderTransaction",
464 skip_serializing_if = "Option::is_none"
465 )]
466 pub take_profit_order_transaction: Option<TakeProfitOrderTransaction>,
467 #[serde(
470 rename = "takeProfitOrderFillTransaction",
471 skip_serializing_if = "Option::is_none"
472 )]
473 pub take_profit_order_fill_transaction: Option<OrderFillTransaction>,
474 #[serde(
477 rename = "takeProfitOrderCreatedCancelTransaction",
478 skip_serializing_if = "Option::is_none"
479 )]
480 pub take_profit_order_created_cancel_transaction: Option<OrderCancelTransaction>,
481 #[serde(
483 rename = "stopLossOrderCancelTransaction",
484 skip_serializing_if = "Option::is_none"
485 )]
486 pub stop_loss_order_cancel_transaction: Option<OrderCancelTransaction>,
487 #[serde(
489 rename = "stopLossOrderTransaction",
490 skip_serializing_if = "Option::is_none"
491 )]
492 pub stop_loss_order_transaction: Option<StopLossOrderTransaction>,
493 #[serde(
496 rename = "stopLossOrderFillTransaction",
497 skip_serializing_if = "Option::is_none"
498 )]
499 pub stop_loss_order_fill_transaction: Option<OrderFillTransaction>,
500 #[serde(
503 rename = "stopLossOrderCreatedCancelTransaction",
504 skip_serializing_if = "Option::is_none"
505 )]
506 pub stop_loss_order_created_cancel_transaction: Option<OrderCancelTransaction>,
507 #[serde(
510 rename = "trailingStopLossOrderCancelTransaction",
511 skip_serializing_if = "Option::is_none"
512 )]
513 pub trailing_stop_loss_order_cancel_transaction: Option<OrderCancelTransaction>,
514 #[serde(
516 rename = "trailingStopLossOrderTransaction",
517 skip_serializing_if = "Option::is_none"
518 )]
519 pub trailing_stop_loss_order_transaction: Option<TrailingStopLossOrderTransaction>,
520 #[serde(
522 rename = "relatedTransactionIDs",
523 default,
524 skip_serializing_if = "Vec::is_empty"
525 )]
526 pub related_transaction_ids: Vec<TransactionId>,
527 #[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
529 pub last_transaction_id: Option<TransactionId>,
530}
531
532#[derive(Debug, Clone, Serialize, Deserialize)]
536#[non_exhaustive]
537pub struct SetTradeDependentOrdersRejectBody {
538 #[serde(
540 rename = "takeProfitOrderCancelRejectTransaction",
541 skip_serializing_if = "Option::is_none"
542 )]
543 pub take_profit_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
544 #[serde(
546 rename = "takeProfitOrderRejectTransaction",
547 skip_serializing_if = "Option::is_none"
548 )]
549 pub take_profit_order_reject_transaction: Option<TakeProfitOrderRejectTransaction>,
550 #[serde(
552 rename = "stopLossOrderCancelRejectTransaction",
553 skip_serializing_if = "Option::is_none"
554 )]
555 pub stop_loss_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
556 #[serde(
558 rename = "stopLossOrderRejectTransaction",
559 skip_serializing_if = "Option::is_none"
560 )]
561 pub stop_loss_order_reject_transaction: Option<StopLossOrderRejectTransaction>,
562 #[serde(
565 rename = "trailingStopLossOrderCancelRejectTransaction",
566 skip_serializing_if = "Option::is_none"
567 )]
568 pub trailing_stop_loss_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
569 #[serde(
571 rename = "trailingStopLossOrderRejectTransaction",
572 skip_serializing_if = "Option::is_none"
573 )]
574 pub trailing_stop_loss_order_reject_transaction: Option<TrailingStopLossOrderRejectTransaction>,
575 #[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
577 pub last_transaction_id: Option<TransactionId>,
578}