1use crate::client::RequestParts;
4use crate::ids::{
5 AccountID, ClientRequestID, InstrumentName, OrderID, OrderSpecifier, TransactionID,
6};
7use crate::models::{
8 ClientExtensions, Order, OrderCancelRejectTransaction, OrderCancelTransaction,
9 OrderClientExtensionsModifyRejectTransaction, OrderClientExtensionsModifyTransaction,
10 OrderFillTransaction, OrderRequest, OrderStateFilter, Transaction,
11};
12use crate::validation::Validate;
13use crate::{Client, Error, Result};
14use reqwest::Method;
15use serde::{Deserialize, Serialize};
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct CreateOrderBody {
21 #[serde(rename = "order", default, skip_serializing_if = "Option::is_none")]
23 pub order: Option<OrderRequest>,
24}
25
26impl Validate for CreateOrderBody {
27 fn validate(&self) -> Result<()> {
28 self.order
29 .as_ref()
30 .ok_or_else(|| Error::InvalidInput("order request is required".into()))?
31 .validate()
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct CreateOrderResponse {
39 #[serde(
41 rename = "orderCreateTransaction",
42 default,
43 skip_serializing_if = "Option::is_none"
44 )]
45 pub order_create_transaction: Option<Transaction>,
46 #[serde(
48 rename = "orderFillTransaction",
49 default,
50 skip_serializing_if = "Option::is_none"
51 )]
52 pub order_fill_transaction: Option<OrderFillTransaction>,
53 #[serde(
56 rename = "orderCancelTransaction",
57 default,
58 skip_serializing_if = "Option::is_none"
59 )]
60 pub order_cancel_transaction: Option<OrderCancelTransaction>,
61 #[serde(
64 rename = "orderReissueTransaction",
65 default,
66 skip_serializing_if = "Option::is_none"
67 )]
68 pub order_reissue_transaction: Option<Transaction>,
69 #[serde(
72 rename = "orderReissueRejectTransaction",
73 default,
74 skip_serializing_if = "Option::is_none"
75 )]
76 pub order_reissue_reject_transaction: Option<Transaction>,
77 #[serde(
79 rename = "relatedTransactionIDs",
80 default,
81 skip_serializing_if = "Option::is_none"
82 )]
83 pub related_transaction_ids: Option<Vec<TransactionID>>,
84 #[serde(
86 rename = "lastTransactionID",
87 default,
88 skip_serializing_if = "Option::is_none"
89 )]
90 pub last_transaction_id: Option<TransactionID>,
91}
92
93#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct CreateOrderRejection {
97 #[serde(
99 rename = "orderRejectTransaction",
100 default,
101 skip_serializing_if = "Option::is_none"
102 )]
103 pub order_reject_transaction: Option<Transaction>,
104 #[serde(
106 rename = "relatedTransactionIDs",
107 default,
108 skip_serializing_if = "Option::is_none"
109 )]
110 pub related_transaction_ids: Option<Vec<TransactionID>>,
111 #[serde(
113 rename = "lastTransactionID",
114 default,
115 skip_serializing_if = "Option::is_none"
116 )]
117 pub last_transaction_id: Option<TransactionID>,
118 #[serde(rename = "errorCode", default, skip_serializing_if = "Option::is_none")]
120 pub error_code: Option<String>,
121 #[serde(rename = "errorMessage")]
123 pub error_message: String,
124}
125
126impl Client {
127 pub async fn create_order(
136 &self,
137 account_id: &AccountID,
138 body: &CreateOrderBody,
139 ) -> std::result::Result<
140 crate::ApiResponse<CreateOrderResponse>,
141 crate::OperationError<CreateOrderRejection>,
142 > {
143 let account_id_encoded = crate::client::segment(account_id.as_str());
144 let path = format!("/v3/accounts/{account_id_encoded}/orders");
145 self.execute(
146 RequestParts::new(Method::POST, &path)
147 .body(body)?
148 .mutation(crate::client::MutationAdmission::Ordinary(account_id)),
149 )
150 .await
151 }
152}
153
154#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
156#[serde(rename_all = "camelCase")]
157pub struct ListOrdersQuery {
158 #[serde(rename = "ids", default, skip_serializing_if = "Option::is_none")]
160 pub ids: Option<Vec<OrderID>>,
161 #[serde(rename = "state", default, skip_serializing_if = "Option::is_none")]
163 pub state: Option<OrderStateFilter>,
164 #[serde(
166 rename = "instrument",
167 default,
168 skip_serializing_if = "Option::is_none"
169 )]
170 pub instrument: Option<InstrumentName>,
171 #[serde(rename = "count", default, skip_serializing_if = "Option::is_none")]
173 pub count: Option<i64>,
174 #[serde(rename = "beforeID", default, skip_serializing_if = "Option::is_none")]
176 pub before_id: Option<OrderID>,
177}
178
179impl Validate for ListOrdersQuery {
180 fn validate(&self) -> Result<()> {
181 if let Some(ids) = &self.ids {
182 crate::validation::nonempty(ids, "ids")?;
183 }
184 crate::validation::count(self.count, 500)
185 }
186}
187
188#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct ListOrdersResponse {
192 #[serde(rename = "orders", default, skip_serializing_if = "Option::is_none")]
194 pub orders: Option<Vec<Order>>,
195 #[serde(
197 rename = "lastTransactionID",
198 default,
199 skip_serializing_if = "Option::is_none"
200 )]
201 pub last_transaction_id: Option<TransactionID>,
202}
203
204impl Client {
205 pub async fn list_orders(
213 &self,
214 account_id: &AccountID,
215 query: Option<&ListOrdersQuery>,
216 ) -> std::result::Result<
217 crate::ApiResponse<ListOrdersResponse>,
218 crate::OperationError<crate::GenericRejection>,
219 > {
220 let account_id_encoded = crate::client::segment(account_id.as_str());
221 let path = format!("/v3/accounts/{account_id_encoded}/orders");
222 let request = RequestParts::new(Method::GET, &path);
223 let request = match query {
224 Some(query) => request.query(query)?,
225 None => request,
226 };
227 self.execute(request).await
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
233#[serde(rename_all = "camelCase")]
234pub struct ListPendingOrdersResponse {
235 #[serde(rename = "orders", default, skip_serializing_if = "Option::is_none")]
237 pub orders: Option<Vec<Order>>,
238 #[serde(
240 rename = "lastTransactionID",
241 default,
242 skip_serializing_if = "Option::is_none"
243 )]
244 pub last_transaction_id: Option<TransactionID>,
245}
246
247impl Client {
248 pub async fn list_pending_orders(
256 &self,
257 account_id: &AccountID,
258 ) -> std::result::Result<
259 crate::ApiResponse<ListPendingOrdersResponse>,
260 crate::OperationError<crate::GenericRejection>,
261 > {
262 let account_id_encoded = crate::client::segment(account_id.as_str());
263 let path = format!("/v3/accounts/{account_id_encoded}/pendingOrders");
264 self.execute(RequestParts::new(Method::GET, &path)).await
265 }
266}
267
268#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
270#[serde(rename_all = "camelCase")]
271pub struct OrderDetailsResponse {
272 #[serde(rename = "order", default, skip_serializing_if = "Option::is_none")]
274 pub order: Option<Order>,
275 #[serde(
277 rename = "lastTransactionID",
278 default,
279 skip_serializing_if = "Option::is_none"
280 )]
281 pub last_transaction_id: Option<TransactionID>,
282}
283
284impl Client {
285 pub async fn order_details(
293 &self,
294 account_id: &AccountID,
295 order_specifier: &OrderSpecifier,
296 ) -> std::result::Result<
297 crate::ApiResponse<OrderDetailsResponse>,
298 crate::OperationError<crate::GenericRejection>,
299 > {
300 let account_id_encoded = crate::client::segment(account_id.as_str());
301 let order_specifier_encoded = crate::client::segment(order_specifier.as_str());
302 let path = format!("/v3/accounts/{account_id_encoded}/orders/{order_specifier_encoded}");
303 self.execute(RequestParts::new(Method::GET, &path)).await
304 }
305}
306
307#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
309#[serde(rename_all = "camelCase")]
310pub struct ReplaceOrderBody {
311 #[serde(rename = "order", default, skip_serializing_if = "Option::is_none")]
313 pub order: Option<OrderRequest>,
314}
315
316impl Validate for ReplaceOrderBody {
317 fn validate(&self) -> Result<()> {
318 self.order
319 .as_ref()
320 .ok_or_else(|| Error::InvalidInput("order request is required".into()))?
321 .validate()
322 }
323}
324
325#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
327#[serde(rename_all = "camelCase")]
328pub struct ReplaceOrderResponse {
329 #[serde(
331 rename = "orderCancelTransaction",
332 default,
333 skip_serializing_if = "Option::is_none"
334 )]
335 pub order_cancel_transaction: Option<OrderCancelTransaction>,
336 #[serde(
338 rename = "orderCreateTransaction",
339 default,
340 skip_serializing_if = "Option::is_none"
341 )]
342 pub order_create_transaction: Option<Transaction>,
343 #[serde(
346 rename = "orderFillTransaction",
347 default,
348 skip_serializing_if = "Option::is_none"
349 )]
350 pub order_fill_transaction: Option<OrderFillTransaction>,
351 #[serde(
354 rename = "orderReissueTransaction",
355 default,
356 skip_serializing_if = "Option::is_none"
357 )]
358 pub order_reissue_transaction: Option<Transaction>,
359 #[serde(
362 rename = "orderReissueRejectTransaction",
363 default,
364 skip_serializing_if = "Option::is_none"
365 )]
366 pub order_reissue_reject_transaction: Option<Transaction>,
367 #[serde(
370 rename = "replacingOrderCancelTransaction",
371 default,
372 skip_serializing_if = "Option::is_none"
373 )]
374 pub replacing_order_cancel_transaction: Option<OrderCancelTransaction>,
375 #[serde(
377 rename = "relatedTransactionIDs",
378 default,
379 skip_serializing_if = "Option::is_none"
380 )]
381 pub related_transaction_ids: Option<Vec<TransactionID>>,
382 #[serde(
384 rename = "lastTransactionID",
385 default,
386 skip_serializing_if = "Option::is_none"
387 )]
388 pub last_transaction_id: Option<TransactionID>,
389}
390
391#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
393#[serde(rename_all = "camelCase")]
394pub struct ReplaceOrderRejection {
395 #[serde(
397 rename = "orderRejectTransaction",
398 default,
399 skip_serializing_if = "Option::is_none"
400 )]
401 pub order_reject_transaction: Option<Transaction>,
402 #[serde(
404 rename = "relatedTransactionIDs",
405 default,
406 skip_serializing_if = "Option::is_none"
407 )]
408 pub related_transaction_ids: Option<Vec<TransactionID>>,
409 #[serde(
411 rename = "lastTransactionID",
412 default,
413 skip_serializing_if = "Option::is_none"
414 )]
415 pub last_transaction_id: Option<TransactionID>,
416 #[serde(rename = "errorCode", default, skip_serializing_if = "Option::is_none")]
418 pub error_code: Option<String>,
419 #[serde(rename = "errorMessage")]
421 pub error_message: String,
422 #[serde(
425 rename = "orderCancelRejectTransaction",
426 default,
427 skip_serializing_if = "Option::is_none"
428 )]
429 pub order_cancel_reject_transaction: Option<Transaction>,
430}
431
432impl Client {
433 pub async fn replace_order(
442 &self,
443 account_id: &AccountID,
444 order_specifier: &OrderSpecifier,
445 body: &ReplaceOrderBody,
446 client_request_id: Option<&ClientRequestID>,
447 ) -> std::result::Result<
448 crate::ApiResponse<ReplaceOrderResponse>,
449 crate::OperationError<ReplaceOrderRejection>,
450 > {
451 let account_id_encoded = crate::client::segment(account_id.as_str());
452 let order_specifier_encoded = crate::client::segment(order_specifier.as_str());
453 let path = format!("/v3/accounts/{account_id_encoded}/orders/{order_specifier_encoded}");
454 self.execute(
455 RequestParts::new(Method::PUT, &path)
456 .body(body)?
457 .mutation(crate::client::MutationAdmission::Ordinary(account_id))
458 .client_request_id(client_request_id),
459 )
460 .await
461 }
462}
463
464#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
466#[serde(rename_all = "camelCase")]
467pub struct CancelOrderResponse {
468 #[serde(
470 rename = "orderCancelTransaction",
471 default,
472 skip_serializing_if = "Option::is_none"
473 )]
474 pub order_cancel_transaction: Option<OrderCancelTransaction>,
475 #[serde(
477 rename = "relatedTransactionIDs",
478 default,
479 skip_serializing_if = "Option::is_none"
480 )]
481 pub related_transaction_ids: Option<Vec<TransactionID>>,
482 #[serde(
484 rename = "lastTransactionID",
485 default,
486 skip_serializing_if = "Option::is_none"
487 )]
488 pub last_transaction_id: Option<TransactionID>,
489}
490
491#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
493#[serde(rename_all = "camelCase")]
494pub struct CancelOrderRejection {
495 #[serde(
497 rename = "orderCancelRejectTransaction",
498 default,
499 skip_serializing_if = "Option::is_none"
500 )]
501 pub order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
502 #[serde(
505 rename = "relatedTransactionIDs",
506 default,
507 skip_serializing_if = "Option::is_none"
508 )]
509 pub related_transaction_ids: Option<Vec<TransactionID>>,
510 #[serde(
512 rename = "lastTransactionID",
513 default,
514 skip_serializing_if = "Option::is_none"
515 )]
516 pub last_transaction_id: Option<TransactionID>,
517 #[serde(rename = "errorCode", default, skip_serializing_if = "Option::is_none")]
519 pub error_code: Option<String>,
520 #[serde(rename = "errorMessage")]
522 pub error_message: String,
523}
524
525impl Client {
526 pub async fn cancel_order(
535 &self,
536 account_id: &AccountID,
537 order_specifier: &OrderSpecifier,
538 client_request_id: Option<&ClientRequestID>,
539 ) -> std::result::Result<
540 crate::ApiResponse<CancelOrderResponse>,
541 crate::OperationError<CancelOrderRejection>,
542 > {
543 let account_id_encoded = crate::client::segment(account_id.as_str());
544 let order_specifier_encoded = crate::client::segment(order_specifier.as_str());
545 let path =
546 format!("/v3/accounts/{account_id_encoded}/orders/{order_specifier_encoded}/cancel");
547 self.execute(
548 RequestParts::new(Method::PUT, &path)
549 .mutation(crate::client::MutationAdmission::Exit(account_id))
550 .client_request_id(client_request_id),
551 )
552 .await
553 }
554}
555
556#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
558#[serde(rename_all = "camelCase")]
559pub struct UpdateOrderClientExtensionsBody {
560 #[serde(
563 rename = "clientExtensions",
564 default,
565 skip_serializing_if = "Option::is_none"
566 )]
567 pub client_extensions: Option<ClientExtensions>,
568 #[serde(
571 rename = "tradeClientExtensions",
572 default,
573 skip_serializing_if = "Option::is_none"
574 )]
575 pub trade_client_extensions: Option<ClientExtensions>,
576}
577
578impl Validate for UpdateOrderClientExtensionsBody {
579 fn validate(&self) -> Result<()> {
580 if self.client_extensions.is_none() && self.trade_client_extensions.is_none() {
581 return Err(Error::InvalidInput(
582 "mutation body must not be empty".into(),
583 ));
584 }
585 Ok(())
586 }
587}
588
589#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
591#[serde(rename_all = "camelCase")]
592pub struct UpdateOrderClientExtensionsResponse {
593 #[serde(
595 rename = "orderClientExtensionsModifyTransaction",
596 default,
597 skip_serializing_if = "Option::is_none"
598 )]
599 pub order_client_extensions_modify_transaction: Option<OrderClientExtensionsModifyTransaction>,
600 #[serde(
602 rename = "lastTransactionID",
603 default,
604 skip_serializing_if = "Option::is_none"
605 )]
606 pub last_transaction_id: Option<TransactionID>,
607 #[serde(
609 rename = "relatedTransactionIDs",
610 default,
611 skip_serializing_if = "Option::is_none"
612 )]
613 pub related_transaction_ids: Option<Vec<TransactionID>>,
614}
615
616#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
618#[serde(rename_all = "camelCase")]
619pub struct UpdateOrderClientExtensionsRejection {
620 #[serde(
622 rename = "orderClientExtensionsModifyRejectTransaction",
623 default,
624 skip_serializing_if = "Option::is_none"
625 )]
626 pub order_client_extensions_modify_reject_transaction:
627 Option<OrderClientExtensionsModifyRejectTransaction>,
628 #[serde(
630 rename = "lastTransactionID",
631 default,
632 skip_serializing_if = "Option::is_none"
633 )]
634 pub last_transaction_id: Option<TransactionID>,
635 #[serde(
637 rename = "relatedTransactionIDs",
638 default,
639 skip_serializing_if = "Option::is_none"
640 )]
641 pub related_transaction_ids: Option<Vec<TransactionID>>,
642 #[serde(rename = "errorCode", default, skip_serializing_if = "Option::is_none")]
644 pub error_code: Option<String>,
645 #[serde(rename = "errorMessage")]
647 pub error_message: String,
648}
649
650impl Client {
651 pub async fn update_order_client_extensions(
660 &self,
661 account_id: &AccountID,
662 order_specifier: &OrderSpecifier,
663 body: &UpdateOrderClientExtensionsBody,
664 ) -> std::result::Result<
665 crate::ApiResponse<UpdateOrderClientExtensionsResponse>,
666 crate::OperationError<UpdateOrderClientExtensionsRejection>,
667 > {
668 let account_id_encoded = crate::client::segment(account_id.as_str());
669 let order_specifier_encoded = crate::client::segment(order_specifier.as_str());
670 let path = format!(
671 "/v3/accounts/{account_id_encoded}/orders/{order_specifier_encoded}/clientExtensions"
672 );
673 self.execute(
674 RequestParts::new(Method::PUT, &path)
675 .body(body)?
676 .mutation(crate::client::MutationAdmission::Ordinary(account_id)),
677 )
678 .await
679 }
680}