Skip to main content

binance_api_client/api/
account.rs

1//! Account and trading API endpoints.
2//!
3//! This module provides authenticated endpoints for account information,
4//! order management, and trading.
5
6use serde::Serialize;
7
8use crate::client::Client;
9use reqwest::StatusCode;
10
11use crate::Result;
12use crate::error::{BinanceApiError, Error};
13use crate::models::{
14    AccountCommission, AccountInfo, Allocation, AmendOrderResponse, CancelOrderResponse,
15    CancelReplaceErrorResponse, CancelReplaceResponse, OcoOrder, Order, OrderAmendment, OrderFull,
16    PreventedMatch, SorOrderTestResponse, UnfilledOrderCount, UserTrade,
17};
18use crate::types::{
19    CancelReplaceMode, CancelRestrictions, OrderRateLimitExceededMode, OrderResponseType,
20    OrderSide, OrderType, TimeInForce,
21};
22
23// API endpoints.
24const API_V3_ACCOUNT: &str = "/api/v3/account";
25const API_V3_MY_TRADES: &str = "/api/v3/myTrades";
26const API_V3_ORDER: &str = "/api/v3/order";
27const API_V3_ORDER_TEST: &str = "/api/v3/order/test";
28const API_V3_OPEN_ORDERS: &str = "/api/v3/openOrders";
29const API_V3_ALL_ORDERS: &str = "/api/v3/allOrders";
30const API_V3_ORDER_OCO: &str = "/api/v3/order/oco";
31const API_V3_ORDER_LIST_OTO: &str = "/api/v3/orderList/oto";
32const API_V3_ORDER_LIST_OTOCO: &str = "/api/v3/orderList/otoco";
33const API_V3_ORDER_LIST_OPO: &str = "/api/v3/orderList/opo";
34const API_V3_ORDER_LIST_OPOCO: &str = "/api/v3/orderList/opoco";
35const API_V3_ORDER_LIST: &str = "/api/v3/orderList";
36const API_V3_ALL_ORDER_LIST: &str = "/api/v3/allOrderList";
37const API_V3_OPEN_ORDER_LIST: &str = "/api/v3/openOrderList";
38const API_V3_MY_PREVENTED_MATCHES: &str = "/api/v3/myPreventedMatches";
39const API_V3_MY_ALLOCATIONS: &str = "/api/v3/myAllocations";
40const API_V3_ACCOUNT_COMMISSION: &str = "/api/v3/account/commission";
41const API_V3_ORDER_CANCEL_REPLACE: &str = "/api/v3/order/cancelReplace";
42const API_V3_SOR_ORDER: &str = "/api/v3/sor/order";
43const API_V3_SOR_ORDER_TEST: &str = "/api/v3/sor/order/test";
44const API_V3_RATE_LIMIT_ORDER: &str = "/api/v3/rateLimit/order";
45const API_V3_ORDER_AMEND: &str = "/api/v3/order/amend/keepPriority";
46const API_V3_ORDER_AMENDMENTS: &str = "/api/v3/order/amendments";
47
48/// Account and trading API client.
49///
50/// Provides authenticated endpoints for account information and trading.
51/// All methods require authentication.
52#[derive(Clone)]
53pub struct Account {
54    client: Client,
55}
56
57impl Account {
58    /// Create a new Account API client.
59    pub(crate) fn new(client: Client) -> Self {
60        Self { client }
61    }
62
63    // Account Endpoints.
64
65    /// Get current account information including balances.
66    ///
67    /// # Example
68    ///
69    /// ```rust,ignore
70    /// let client = Binance::new("api_key", "secret_key")?;
71    /// let account = client.account().get_account().await?;
72    ///
73    /// for balance in account.balances {
74    ///     if balance.free > 0.0 || balance.locked > 0.0 {
75    ///         println!("{}: free={}, locked={}", balance.asset, balance.free, balance.locked);
76    ///     }
77    /// }
78    /// ```
79    pub async fn get_account(&self) -> Result<AccountInfo> {
80        self.client.get_signed(API_V3_ACCOUNT, &[]).await
81    }
82
83    /// Get account trade history for a symbol.
84    ///
85    /// # Arguments
86    ///
87    /// * `symbol` - Trading pair symbol
88    /// * `from_id` - Trade ID to fetch from
89    /// * `start_time` - Start time in milliseconds
90    /// * `end_time` - End time in milliseconds
91    /// * `limit` - Max number of trades (default 500, max 1000)
92    ///
93    /// # Example
94    ///
95    /// ```rust,ignore
96    /// let client = Binance::new("api_key", "secret_key")?;
97    /// let trades = client.account().my_trades("BTCUSDT", None, None, None, Some(10)).await?;
98    /// ```
99    pub async fn my_trades(
100        &self,
101        symbol: &str,
102        from_id: Option<u64>,
103        start_time: Option<u64>,
104        end_time: Option<u64>,
105        limit: Option<u32>,
106    ) -> Result<Vec<UserTrade>> {
107        let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
108
109        if let Some(id) = from_id {
110            params.push(("fromId", id.to_string()));
111        }
112        if let Some(start) = start_time {
113            params.push(("startTime", start.to_string()));
114        }
115        if let Some(end) = end_time {
116            params.push(("endTime", end.to_string()));
117        }
118        if let Some(l) = limit {
119            params.push(("limit", l.to_string()));
120        }
121
122        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
123        self.client.get_signed(API_V3_MY_TRADES, &params_ref).await
124    }
125
126    /// Get orders that were expired due to self-trade prevention.
127    ///
128    /// # Arguments
129    ///
130    /// * `symbol` - Trading pair symbol
131    /// * `prevented_match_id` - Prevented match ID
132    /// * `order_id` - Order ID
133    /// * `from_prevented_match_id` - Start from prevented match ID
134    /// * `limit` - Max number of entries (default 500, max 1000)
135    pub async fn my_prevented_matches(
136        &self,
137        symbol: &str,
138        prevented_match_id: Option<u64>,
139        order_id: Option<u64>,
140        from_prevented_match_id: Option<u64>,
141        limit: Option<u32>,
142    ) -> Result<Vec<PreventedMatch>> {
143        let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
144
145        if let Some(id) = prevented_match_id {
146            params.push(("preventedMatchId", id.to_string()));
147        }
148        if let Some(id) = order_id {
149            params.push(("orderId", id.to_string()));
150        }
151        if let Some(id) = from_prevented_match_id {
152            params.push(("fromPreventedMatchId", id.to_string()));
153        }
154        if let Some(l) = limit {
155            params.push(("limit", l.to_string()));
156        }
157
158        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
159        self.client
160            .get_signed(API_V3_MY_PREVENTED_MATCHES, &params_ref)
161            .await
162    }
163
164    /// Get SOR allocations for a symbol.
165    ///
166    /// # Arguments
167    ///
168    /// * `symbol` - Trading pair symbol
169    /// * `start_time` - Start time in milliseconds
170    /// * `end_time` - End time in milliseconds
171    /// * `from_allocation_id` - Allocation ID to fetch from
172    /// * `limit` - Max number of entries (default 500, max 1000)
173    /// * `order_id` - Optional order ID to filter
174    pub async fn my_allocations(
175        &self,
176        symbol: &str,
177        start_time: Option<u64>,
178        end_time: Option<u64>,
179        from_allocation_id: Option<u64>,
180        limit: Option<u32>,
181        order_id: Option<u64>,
182    ) -> Result<Vec<Allocation>> {
183        let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
184
185        if let Some(start) = start_time {
186            params.push(("startTime", start.to_string()));
187        }
188        if let Some(end) = end_time {
189            params.push(("endTime", end.to_string()));
190        }
191        if let Some(id) = from_allocation_id {
192            params.push(("fromAllocationId", id.to_string()));
193        }
194        if let Some(l) = limit {
195            params.push(("limit", l.to_string()));
196        }
197        if let Some(id) = order_id {
198            params.push(("orderId", id.to_string()));
199        }
200
201        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
202        self.client
203            .get_signed(API_V3_MY_ALLOCATIONS, &params_ref)
204            .await
205    }
206
207    /// Get commission rates for a symbol.
208    ///
209    /// # Arguments
210    ///
211    /// * `symbol` - Trading pair symbol
212    pub async fn commission_rates(&self, symbol: &str) -> Result<AccountCommission> {
213        let params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
214        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
215        self.client
216            .get_signed(API_V3_ACCOUNT_COMMISSION, &params_ref)
217            .await
218    }
219
220    /// Query unfilled order count for all rate limit intervals.
221    ///
222    /// Returns the current count of unfilled orders for each rate limit interval
223    /// (e.g., per second, per day). This is useful for monitoring order placement
224    /// rate limits.
225    ///
226    /// # Example
227    ///
228    /// ```rust,ignore
229    /// let client = Binance::new("api_key", "secret_key")?;
230    /// let counts = client.account().unfilled_order_count().await?;
231    /// for count in counts {
232    ///     println!("{}: {}/{} orders", count.interval, count.count, count.limit);
233    /// }
234    /// ```
235    pub async fn unfilled_order_count(&self) -> Result<Vec<UnfilledOrderCount>> {
236        self.client.get_signed(API_V3_RATE_LIMIT_ORDER, &[]).await
237    }
238
239    /// Query amendment history for a specific order.
240    ///
241    /// Returns all amendments made to a single order.
242    ///
243    /// # Arguments
244    ///
245    /// * `symbol` - Trading pair symbol
246    /// * `order_id` - Order ID to query amendments for
247    /// * `from_execution_id` - Optional execution ID to start from
248    /// * `limit` - Max number of entries (default 500, max 1000)
249    ///
250    /// # Example
251    ///
252    /// ```rust,ignore
253    /// let client = Binance::new("api_key", "secret_key")?;
254    /// let amendments = client.account().order_amendments("BTCUSDT", 12345, None, None).await?;
255    /// for amendment in amendments {
256    ///     println!("Qty changed from {} to {}", amendment.orig_qty, amendment.new_qty);
257    /// }
258    /// ```
259    pub async fn order_amendments(
260        &self,
261        symbol: &str,
262        order_id: u64,
263        from_execution_id: Option<u64>,
264        limit: Option<u32>,
265    ) -> Result<Vec<OrderAmendment>> {
266        let mut params: Vec<(&str, String)> = vec![
267            ("symbol", symbol.to_string()),
268            ("orderId", order_id.to_string()),
269        ];
270
271        if let Some(from_id) = from_execution_id {
272            params.push(("fromExecutionId", from_id.to_string()));
273        }
274        if let Some(l) = limit {
275            params.push(("limit", l.to_string()));
276        }
277
278        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
279        self.client
280            .get_signed(API_V3_ORDER_AMENDMENTS, &params_ref)
281            .await
282    }
283
284    // Order Endpoints.
285
286    /// Create a new order.
287    ///
288    /// Use `OrderBuilder` to construct orders with the desired parameters.
289    ///
290    /// # Example
291    ///
292    /// ```rust,ignore
293    /// use binance_api_client::{OrderBuilder, OrderSide, OrderType, TimeInForce};
294    ///
295    /// let client = Binance::new("api_key", "secret_key")?;
296    ///
297    /// // Limit buy order
298    /// let order = OrderBuilder::new("BTCUSDT", OrderSide::Buy, OrderType::Limit)
299    ///     .quantity("0.001")
300    ///     .price("50000.00")
301    ///     .time_in_force(TimeInForce::GTC)
302    ///     .build();
303    ///
304    /// let response = client.account().create_order(&order).await?;
305    /// ```
306    pub async fn create_order(&self, order: &NewOrder) -> Result<OrderFull> {
307        let params = order.to_params();
308        let params_ref: Vec<(&str, &str)> = params
309            .iter()
310            .map(|(k, v)| (k.as_str(), v.as_str()))
311            .collect();
312        self.client.post_signed(API_V3_ORDER, &params_ref).await
313    }
314
315    /// Test a new order without executing it.
316    ///
317    /// Validates order parameters but doesn't place the order.
318    ///
319    /// # Example
320    ///
321    /// ```rust,ignore
322    /// let order = OrderBuilder::new("BTCUSDT", OrderSide::Buy, OrderType::Market)
323    ///     .quantity("0.001")
324    ///     .build();
325    ///
326    /// client.account().test_order(&order).await?;
327    /// println!("Order parameters are valid");
328    /// ```
329    pub async fn test_order(&self, order: &NewOrder) -> Result<()> {
330        let params = order.to_params();
331        let params_ref: Vec<(&str, &str)> = params
332            .iter()
333            .map(|(k, v)| (k.as_str(), v.as_str()))
334            .collect();
335        let _: serde_json::Value = self
336            .client
337            .post_signed(API_V3_ORDER_TEST, &params_ref)
338            .await?;
339        Ok(())
340    }
341
342    /// Amend an order's quantity while keeping queue priority.
343    ///
344    /// This endpoint allows reducing the quantity of an existing open order
345    /// without losing its place in the order queue. The new quantity must be
346    /// greater than 0 and less than the current order quantity.
347    ///
348    /// # Arguments
349    ///
350    /// * `symbol` - Trading pair symbol
351    /// * `order_id` - Order ID to amend (either order_id or orig_client_order_id required)
352    /// * `orig_client_order_id` - Client order ID to amend
353    /// * `new_qty` - New quantity (must be less than current quantity)
354    /// * `new_client_order_id` - Optional new client order ID after amendment
355    ///
356    /// # Example
357    ///
358    /// ```rust,ignore
359    /// let client = Binance::new("api_key", "secret_key")?;
360    ///
361    /// // Reduce an order's quantity from 10 to 5
362    /// let response = client.account().amend_order_keep_priority(
363    ///     "BTCUSDT",
364    ///     Some(12345),
365    ///     None,
366    ///     "5.0",
367    ///     None,
368    /// ).await?;
369    ///
370    /// println!("Amended order ID: {}", response.amended_order.order_id);
371    /// ```
372    pub async fn amend_order_keep_priority(
373        &self,
374        symbol: &str,
375        order_id: Option<u64>,
376        orig_client_order_id: Option<&str>,
377        new_qty: &str,
378        new_client_order_id: Option<&str>,
379    ) -> Result<AmendOrderResponse> {
380        let mut params: Vec<(&str, String)> = vec![
381            ("symbol", symbol.to_string()),
382            ("newQty", new_qty.to_string()),
383        ];
384
385        if let Some(id) = order_id {
386            params.push(("orderId", id.to_string()));
387        }
388        if let Some(cid) = orig_client_order_id {
389            params.push(("origClientOrderId", cid.to_string()));
390        }
391        if let Some(new_cid) = new_client_order_id {
392            params.push(("newClientOrderId", new_cid.to_string()));
393        }
394
395        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
396        self.client
397            .put_signed(API_V3_ORDER_AMEND, &params_ref)
398            .await
399    }
400
401    /// Cancel an existing order and place a new order.
402    ///
403    /// # Example
404    ///
405    /// ```rust,ignore
406    /// use binance_api_client::api::account::CancelReplaceOrderBuilder;
407    /// use binance_api_client::{CancelReplaceMode, OrderSide, OrderType, TimeInForce};
408    ///
409    /// let request = CancelReplaceOrderBuilder::new("BTCUSDT", OrderSide::Buy, OrderType::Limit, CancelReplaceMode::StopOnFailure)
410    ///     .cancel_order_id(12345)
411    ///     .price("25000.00")
412    ///     .quantity("0.01")
413    ///     .time_in_force(TimeInForce::GTC)
414    ///     .build();
415    ///
416    /// let response = client.account().cancel_replace_order(&request).await?;
417    /// println!("Cancel result: {:?}", response.cancel_result);
418    /// ```
419    pub async fn cancel_replace_order(
420        &self,
421        request: &CancelReplaceOrder,
422    ) -> Result<CancelReplaceResponse> {
423        let params = request.to_params();
424        let params_ref: Vec<(&str, &str)> = params
425            .iter()
426            .map(|(k, v)| (k.as_str(), v.as_str()))
427            .collect();
428        let response = self
429            .client
430            .post_signed_raw(API_V3_ORDER_CANCEL_REPLACE, &params_ref)
431            .await?;
432
433        match response.status() {
434            StatusCode::OK => Ok(response.json().await?),
435            StatusCode::BAD_REQUEST | StatusCode::CONFLICT => {
436                let error: CancelReplaceErrorResponse = response.json().await?;
437                Err(Error::from_cancel_replace_error(error))
438            }
439            StatusCode::UNAUTHORIZED => Err(Error::Api {
440                code: 401,
441                message: "Unauthorized".to_string(),
442            }),
443            StatusCode::FORBIDDEN | StatusCode::TOO_MANY_REQUESTS => {
444                let error: BinanceApiError = response.json().await?;
445                Err(Error::from_binance_error(error))
446            }
447            StatusCode::INTERNAL_SERVER_ERROR => Err(Error::Api {
448                code: 500,
449                message: "Internal server error".to_string(),
450            }),
451            StatusCode::SERVICE_UNAVAILABLE => Err(Error::Api {
452                code: 503,
453                message: "Service unavailable".to_string(),
454            }),
455            status => Err(Error::Api {
456                code: status.as_u16() as i32,
457                message: format!("Unexpected status code: {}", status),
458            }),
459        }
460    }
461
462    /// Place an order using smart order routing (SOR).
463    pub async fn create_sor_order(&self, order: &NewOrder) -> Result<OrderFull> {
464        let params = order.to_params();
465        let params_ref: Vec<(&str, &str)> = params
466            .iter()
467            .map(|(k, v)| (k.as_str(), v.as_str()))
468            .collect();
469        self.client.post_signed(API_V3_SOR_ORDER, &params_ref).await
470    }
471
472    /// Test a new SOR order without executing it.
473    pub async fn test_sor_order(
474        &self,
475        order: &NewOrder,
476        compute_commission_rates: bool,
477    ) -> Result<SorOrderTestResponse> {
478        let mut params = order.to_params();
479        if compute_commission_rates {
480            params.push((
481                "computeCommissionRates".to_string(),
482                compute_commission_rates.to_string(),
483            ));
484        }
485        let params_ref: Vec<(&str, &str)> = params
486            .iter()
487            .map(|(k, v)| (k.as_str(), v.as_str()))
488            .collect();
489        self.client
490            .post_signed(API_V3_SOR_ORDER_TEST, &params_ref)
491            .await
492    }
493
494    /// Query an order's status.
495    ///
496    /// # Arguments
497    ///
498    /// * `symbol` - Trading pair symbol
499    /// * `order_id` - Order ID to query (either order_id or client_order_id required)
500    /// * `client_order_id` - Client order ID to query
501    ///
502    /// # Example
503    ///
504    /// ```rust,ignore
505    /// let client = Binance::new("api_key", "secret_key")?;
506    /// let order = client.account().get_order("BTCUSDT", Some(12345), None).await?;
507    /// println!("Order status: {:?}", order.status);
508    /// ```
509    pub async fn get_order(
510        &self,
511        symbol: &str,
512        order_id: Option<u64>,
513        client_order_id: Option<&str>,
514    ) -> Result<Order> {
515        let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
516
517        if let Some(id) = order_id {
518            params.push(("orderId", id.to_string()));
519        }
520        if let Some(cid) = client_order_id {
521            params.push(("origClientOrderId", cid.to_string()));
522        }
523
524        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
525        self.client.get_signed(API_V3_ORDER, &params_ref).await
526    }
527
528    /// Cancel an order.
529    ///
530    /// # Arguments
531    ///
532    /// * `symbol` - Trading pair symbol
533    /// * `order_id` - Order ID to cancel (either order_id or client_order_id required)
534    /// * `client_order_id` - Client order ID to cancel
535    ///
536    /// # Example
537    ///
538    /// ```rust,ignore
539    /// let client = Binance::new("api_key", "secret_key")?;
540    /// let result = client.account().cancel_order("BTCUSDT", Some(12345), None).await?;
541    /// println!("Canceled order: {}", result.order_id);
542    /// ```
543    pub async fn cancel_order(
544        &self,
545        symbol: &str,
546        order_id: Option<u64>,
547        client_order_id: Option<&str>,
548    ) -> Result<CancelOrderResponse> {
549        let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
550
551        if let Some(id) = order_id {
552            params.push(("orderId", id.to_string()));
553        }
554        if let Some(cid) = client_order_id {
555            params.push(("origClientOrderId", cid.to_string()));
556        }
557
558        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
559        self.client.delete_signed(API_V3_ORDER, &params_ref).await
560    }
561
562    /// Get all open orders for a symbol, or all symbols if none specified.
563    ///
564    /// # Arguments
565    ///
566    /// * `symbol` - Optional trading pair symbol
567    ///
568    /// # Example
569    ///
570    /// ```rust,ignore
571    /// let client = Binance::new("api_key", "secret_key")?;
572    ///
573    /// // Get open orders for a specific symbol
574    /// let orders = client.account().open_orders(Some("BTCUSDT")).await?;
575    ///
576    /// // Get all open orders
577    /// let all_orders = client.account().open_orders(None).await?;
578    /// ```
579    pub async fn open_orders(&self, symbol: Option<&str>) -> Result<Vec<Order>> {
580        let params: Vec<(&str, String)> = match symbol {
581            Some(s) => vec![("symbol", s.to_string())],
582            None => vec![],
583        };
584
585        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
586        self.client
587            .get_signed(API_V3_OPEN_ORDERS, &params_ref)
588            .await
589    }
590
591    /// Cancel all open orders for a symbol.
592    ///
593    /// # Arguments
594    ///
595    /// * `symbol` - Trading pair symbol
596    ///
597    /// # Example
598    ///
599    /// ```rust,ignore
600    /// let client = Binance::new("api_key", "secret_key")?;
601    /// let canceled = client.account().cancel_all_orders("BTCUSDT").await?;
602    /// println!("Canceled {} orders", canceled.len());
603    /// ```
604    pub async fn cancel_all_orders(&self, symbol: &str) -> Result<Vec<CancelOrderResponse>> {
605        let params = [("symbol", symbol)];
606        self.client.delete_signed(API_V3_OPEN_ORDERS, &params).await
607    }
608
609    /// Get all orders for a symbol (active, canceled, or filled).
610    ///
611    /// # Arguments
612    ///
613    /// * `symbol` - Trading pair symbol
614    /// * `order_id` - If set, get orders >= this order ID
615    /// * `start_time` - Start time in milliseconds
616    /// * `end_time` - End time in milliseconds
617    /// * `limit` - Max number of orders (default 500, max 1000)
618    ///
619    /// # Example
620    ///
621    /// ```rust,ignore
622    /// let client = Binance::new("api_key", "secret_key")?;
623    /// let orders = client.account().all_orders("BTCUSDT", None, None, None, Some(10)).await?;
624    /// ```
625    pub async fn all_orders(
626        &self,
627        symbol: &str,
628        order_id: Option<u64>,
629        start_time: Option<u64>,
630        end_time: Option<u64>,
631        limit: Option<u32>,
632    ) -> Result<Vec<Order>> {
633        let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
634
635        if let Some(id) = order_id {
636            params.push(("orderId", id.to_string()));
637        }
638        if let Some(start) = start_time {
639            params.push(("startTime", start.to_string()));
640        }
641        if let Some(end) = end_time {
642            params.push(("endTime", end.to_string()));
643        }
644        if let Some(l) = limit {
645            params.push(("limit", l.to_string()));
646        }
647
648        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
649        self.client.get_signed(API_V3_ALL_ORDERS, &params_ref).await
650    }
651
652    // OCO Order Endpoints.
653
654    /// Create a new OCO (One-Cancels-Other) order.
655    ///
656    /// An OCO order combines a limit order with a stop-limit order.
657    ///
658    /// # Example
659    ///
660    /// ```rust,ignore
661    /// let oco = OcoOrderBuilder::new("BTCUSDT", OrderSide::Sell, "1.0", "55000.00", "48000.00")
662    ///     .stop_limit_price("47900.00")
663    ///     .build();
664    ///
665    /// let result = client.account().create_oco(&oco).await?;
666    /// ```
667    pub async fn create_oco(&self, order: &NewOcoOrder) -> Result<OcoOrder> {
668        let params = order.to_params();
669        let params_ref: Vec<(&str, &str)> = params
670            .iter()
671            .map(|(k, v)| (k.as_str(), v.as_str()))
672            .collect();
673        self.client.post_signed(API_V3_ORDER_OCO, &params_ref).await
674    }
675
676    /// Create a new OTO (One-Triggers-the-Other) order list.
677    pub async fn create_oto(&self, order: &NewOtoOrder) -> Result<OcoOrder> {
678        let params = order.to_params();
679        let params_ref: Vec<(&str, &str)> = params
680            .iter()
681            .map(|(k, v)| (k.as_str(), v.as_str()))
682            .collect();
683        self.client
684            .post_signed(API_V3_ORDER_LIST_OTO, &params_ref)
685            .await
686    }
687
688    /// Create a new OTOCO (One-Triggers-One-Cancels-the-Other) order list.
689    pub async fn create_otoco(&self, order: &NewOtocoOrder) -> Result<OcoOrder> {
690        let params = order.to_params();
691        let params_ref: Vec<(&str, &str)> = params
692            .iter()
693            .map(|(k, v)| (k.as_str(), v.as_str()))
694            .collect();
695        self.client
696            .post_signed(API_V3_ORDER_LIST_OTOCO, &params_ref)
697            .await
698    }
699
700    /// Create a new OPO (One-Places-the-Other) order list.
701    pub async fn create_opo(&self, order: &NewOpoOrder) -> Result<OcoOrder> {
702        let params = order.to_params();
703        let params_ref: Vec<(&str, &str)> = params
704            .iter()
705            .map(|(k, v)| (k.as_str(), v.as_str()))
706            .collect();
707        self.client
708            .post_signed(API_V3_ORDER_LIST_OPO, &params_ref)
709            .await
710    }
711
712    /// Create a new OPOCO (One-Places-One-Cancels-the-Other) order list.
713    pub async fn create_opoco(&self, order: &NewOpocoOrder) -> Result<OcoOrder> {
714        let params = order.to_params();
715        let params_ref: Vec<(&str, &str)> = params
716            .iter()
717            .map(|(k, v)| (k.as_str(), v.as_str()))
718            .collect();
719        self.client
720            .post_signed(API_V3_ORDER_LIST_OPOCO, &params_ref)
721            .await
722    }
723
724    /// Query an order list by ID or client order list ID.
725    ///
726    /// This applies to all order list types (OCO/OTO/OTOCO/OPO/OPOCO).
727    pub async fn get_order_list(
728        &self,
729        order_list_id: Option<u64>,
730        client_order_list_id: Option<&str>,
731    ) -> Result<OcoOrder> {
732        self.get_oco(order_list_id, client_order_list_id).await
733    }
734
735    /// Cancel an order list by symbol and list identifiers.
736    ///
737    /// This applies to all order list types (OCO/OTO/OTOCO/OPO/OPOCO).
738    pub async fn cancel_order_list(
739        &self,
740        symbol: &str,
741        order_list_id: Option<u64>,
742        client_order_list_id: Option<&str>,
743    ) -> Result<OcoOrder> {
744        self.cancel_oco(symbol, order_list_id, client_order_list_id)
745            .await
746    }
747
748    /// Get all order lists.
749    ///
750    /// This applies to all order list types (OCO/OTO/OTOCO/OPO/OPOCO).
751    pub async fn all_order_lists(
752        &self,
753        from_id: Option<u64>,
754        start_time: Option<u64>,
755        end_time: Option<u64>,
756        limit: Option<u32>,
757    ) -> Result<Vec<OcoOrder>> {
758        self.all_oco(from_id, start_time, end_time, limit).await
759    }
760
761    /// Get all open order lists.
762    ///
763    /// This applies to all order list types (OCO/OTO/OTOCO/OPO/OPOCO).
764    pub async fn open_order_lists(&self) -> Result<Vec<OcoOrder>> {
765        self.open_oco().await
766    }
767
768    /// Query an OCO order's status.
769    ///
770    /// # Arguments
771    ///
772    /// * `order_list_id` - OCO order list ID
773    ///
774    /// # Example
775    ///
776    /// ```rust,ignore
777    /// let oco = client.account().get_oco(Some(12345), None).await?;
778    /// ```
779    pub async fn get_oco(
780        &self,
781        order_list_id: Option<u64>,
782        client_order_list_id: Option<&str>,
783    ) -> Result<OcoOrder> {
784        let mut params: Vec<(&str, String)> = vec![];
785
786        if let Some(id) = order_list_id {
787            params.push(("orderListId", id.to_string()));
788        }
789        if let Some(cid) = client_order_list_id {
790            params.push(("origClientOrderId", cid.to_string()));
791        }
792
793        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
794        self.client.get_signed(API_V3_ORDER_LIST, &params_ref).await
795    }
796
797    /// Cancel an OCO order.
798    ///
799    /// # Arguments
800    ///
801    /// * `symbol` - Trading pair symbol
802    /// * `order_list_id` - OCO order list ID
803    ///
804    /// # Example
805    ///
806    /// ```rust,ignore
807    /// let result = client.account().cancel_oco("BTCUSDT", Some(12345), None).await?;
808    /// ```
809    pub async fn cancel_oco(
810        &self,
811        symbol: &str,
812        order_list_id: Option<u64>,
813        client_order_list_id: Option<&str>,
814    ) -> Result<OcoOrder> {
815        let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
816
817        if let Some(id) = order_list_id {
818            params.push(("orderListId", id.to_string()));
819        }
820        if let Some(cid) = client_order_list_id {
821            params.push(("listClientOrderId", cid.to_string()));
822        }
823
824        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
825        self.client
826            .delete_signed(API_V3_ORDER_LIST, &params_ref)
827            .await
828    }
829
830    /// Get all OCO orders.
831    ///
832    /// # Arguments
833    ///
834    /// * `from_id` - If set, get orders >= this order list ID
835    /// * `start_time` - Start time in milliseconds
836    /// * `end_time` - End time in milliseconds
837    /// * `limit` - Max number of orders (default 500, max 1000)
838    pub async fn all_oco(
839        &self,
840        from_id: Option<u64>,
841        start_time: Option<u64>,
842        end_time: Option<u64>,
843        limit: Option<u32>,
844    ) -> Result<Vec<OcoOrder>> {
845        let mut params: Vec<(&str, String)> = vec![];
846
847        if let Some(id) = from_id {
848            params.push(("fromId", id.to_string()));
849        }
850        if let Some(start) = start_time {
851            params.push(("startTime", start.to_string()));
852        }
853        if let Some(end) = end_time {
854            params.push(("endTime", end.to_string()));
855        }
856        if let Some(l) = limit {
857            params.push(("limit", l.to_string()));
858        }
859
860        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
861        self.client
862            .get_signed(API_V3_ALL_ORDER_LIST, &params_ref)
863            .await
864    }
865
866    /// Get all open OCO orders.
867    pub async fn open_oco(&self) -> Result<Vec<OcoOrder>> {
868        self.client.get_signed(API_V3_OPEN_ORDER_LIST, &[]).await
869    }
870
871    // Convenience Methods.
872
873    /// Place a limit buy order.
874    ///
875    /// # Arguments
876    ///
877    /// * `symbol` - Trading pair symbol
878    /// * `quantity` - Order quantity
879    /// * `price` - Limit price
880    ///
881    /// # Example
882    ///
883    /// ```rust,ignore
884    /// let client = Binance::new("api_key", "secret_key")?;
885    /// let order = client.account().limit_buy("BTCUSDT", "0.001", "50000.00").await?;
886    /// ```
887    pub async fn limit_buy(&self, symbol: &str, quantity: &str, price: &str) -> Result<OrderFull> {
888        let order = OrderBuilder::new(symbol, OrderSide::Buy, OrderType::Limit)
889            .quantity(quantity)
890            .price(price)
891            .time_in_force(TimeInForce::GTC)
892            .build();
893        self.create_order(&order).await
894    }
895
896    /// Place a limit sell order.
897    ///
898    /// # Arguments
899    ///
900    /// * `symbol` - Trading pair symbol
901    /// * `quantity` - Order quantity
902    /// * `price` - Limit price
903    ///
904    /// # Example
905    ///
906    /// ```rust,ignore
907    /// let client = Binance::new("api_key", "secret_key")?;
908    /// let order = client.account().limit_sell("BTCUSDT", "0.001", "55000.00").await?;
909    /// ```
910    pub async fn limit_sell(&self, symbol: &str, quantity: &str, price: &str) -> Result<OrderFull> {
911        let order = OrderBuilder::new(symbol, OrderSide::Sell, OrderType::Limit)
912            .quantity(quantity)
913            .price(price)
914            .time_in_force(TimeInForce::GTC)
915            .build();
916        self.create_order(&order).await
917    }
918
919    /// Place a market buy order.
920    ///
921    /// # Arguments
922    ///
923    /// * `symbol` - Trading pair symbol
924    /// * `quantity` - Order quantity
925    ///
926    /// # Example
927    ///
928    /// ```rust,ignore
929    /// let client = Binance::new("api_key", "secret_key")?;
930    /// let order = client.account().market_buy("BTCUSDT", "0.001").await?;
931    /// ```
932    pub async fn market_buy(&self, symbol: &str, quantity: &str) -> Result<OrderFull> {
933        let order = OrderBuilder::new(symbol, OrderSide::Buy, OrderType::Market)
934            .quantity(quantity)
935            .build();
936        self.create_order(&order).await
937    }
938
939    /// Place a market sell order.
940    ///
941    /// # Arguments
942    ///
943    /// * `symbol` - Trading pair symbol
944    /// * `quantity` - Order quantity
945    ///
946    /// # Example
947    ///
948    /// ```rust,ignore
949    /// let client = Binance::new("api_key", "secret_key")?;
950    /// let order = client.account().market_sell("BTCUSDT", "0.001").await?;
951    /// ```
952    pub async fn market_sell(&self, symbol: &str, quantity: &str) -> Result<OrderFull> {
953        let order = OrderBuilder::new(symbol, OrderSide::Sell, OrderType::Market)
954            .quantity(quantity)
955            .build();
956        self.create_order(&order).await
957    }
958
959    /// Place a market buy order using quote asset quantity.
960    ///
961    /// This allows you to specify how much of the quote asset (e.g., USDT) to spend.
962    ///
963    /// # Arguments
964    ///
965    /// * `symbol` - Trading pair symbol
966    /// * `quote_quantity` - Amount of quote asset to spend
967    ///
968    /// # Example
969    ///
970    /// ```rust,ignore
971    /// let client = Binance::new("api_key", "secret_key")?;
972    /// // Spend 100 USDT to buy BTC
973    /// let order = client.account().market_buy_quote("BTCUSDT", "100.00").await?;
974    /// ```
975    pub async fn market_buy_quote(&self, symbol: &str, quote_quantity: &str) -> Result<OrderFull> {
976        let order = OrderBuilder::new(symbol, OrderSide::Buy, OrderType::Market)
977            .quote_quantity(quote_quantity)
978            .build();
979        self.create_order(&order).await
980    }
981}
982
983/// Builder for creating new orders.
984///
985/// # Example
986///
987/// ```rust
988/// use binance_api_client::api::account::OrderBuilder;
989/// use binance_api_client::{OrderSide, OrderType, TimeInForce};
990///
991/// let order = OrderBuilder::new("BTCUSDT", OrderSide::Buy, OrderType::Limit)
992///     .quantity("0.001")
993///     .price("50000.00")
994///     .time_in_force(TimeInForce::GTC)
995///     .build();
996/// ```
997#[derive(Debug, Clone)]
998pub struct OrderBuilder {
999    symbol: String,
1000    side: OrderSide,
1001    order_type: OrderType,
1002    quantity: Option<String>,
1003    quote_quantity: Option<String>,
1004    price: Option<String>,
1005    stop_price: Option<String>,
1006    time_in_force: Option<TimeInForce>,
1007    client_order_id: Option<String>,
1008    iceberg_qty: Option<String>,
1009    response_type: Option<OrderResponseType>,
1010}
1011
1012/// Builder for cancel-replace orders.
1013#[derive(Debug, Clone)]
1014pub struct CancelReplaceOrderBuilder {
1015    symbol: String,
1016    side: OrderSide,
1017    order_type: OrderType,
1018    cancel_replace_mode: CancelReplaceMode,
1019    time_in_force: Option<TimeInForce>,
1020    quantity: Option<String>,
1021    quote_quantity: Option<String>,
1022    price: Option<String>,
1023    cancel_new_client_order_id: Option<String>,
1024    cancel_orig_client_order_id: Option<String>,
1025    cancel_order_id: Option<u64>,
1026    new_client_order_id: Option<String>,
1027    strategy_id: Option<u64>,
1028    strategy_type: Option<i32>,
1029    stop_price: Option<String>,
1030    trailing_delta: Option<u64>,
1031    iceberg_qty: Option<String>,
1032    response_type: Option<OrderResponseType>,
1033    self_trade_prevention_mode: Option<String>,
1034    cancel_restrictions: Option<CancelRestrictions>,
1035    order_rate_limit_exceeded_mode: Option<OrderRateLimitExceededMode>,
1036    peg_price_type: Option<String>,
1037    peg_offset_value: Option<i32>,
1038    peg_offset_type: Option<String>,
1039}
1040
1041impl CancelReplaceOrderBuilder {
1042    /// Create a new cancel-replace order builder.
1043    pub fn new(
1044        symbol: &str,
1045        side: OrderSide,
1046        order_type: OrderType,
1047        cancel_replace_mode: CancelReplaceMode,
1048    ) -> Self {
1049        Self {
1050            symbol: symbol.to_string(),
1051            side,
1052            order_type,
1053            cancel_replace_mode,
1054            time_in_force: None,
1055            quantity: None,
1056            quote_quantity: None,
1057            price: None,
1058            cancel_new_client_order_id: None,
1059            cancel_orig_client_order_id: None,
1060            cancel_order_id: None,
1061            new_client_order_id: None,
1062            strategy_id: None,
1063            strategy_type: None,
1064            stop_price: None,
1065            trailing_delta: None,
1066            iceberg_qty: None,
1067            response_type: None,
1068            self_trade_prevention_mode: None,
1069            cancel_restrictions: None,
1070            order_rate_limit_exceeded_mode: None,
1071            peg_price_type: None,
1072            peg_offset_value: None,
1073            peg_offset_type: None,
1074        }
1075    }
1076
1077    /// Set the order quantity.
1078    pub fn quantity(mut self, quantity: &str) -> Self {
1079        self.quantity = Some(quantity.to_string());
1080        self
1081    }
1082
1083    /// Set the quote order quantity.
1084    pub fn quote_quantity(mut self, quantity: &str) -> Self {
1085        self.quote_quantity = Some(quantity.to_string());
1086        self
1087    }
1088
1089    /// Set the order price.
1090    pub fn price(mut self, price: &str) -> Self {
1091        self.price = Some(price.to_string());
1092        self
1093    }
1094
1095    /// Set the stop price.
1096    pub fn stop_price(mut self, price: &str) -> Self {
1097        self.stop_price = Some(price.to_string());
1098        self
1099    }
1100
1101    /// Set the time in force.
1102    pub fn time_in_force(mut self, tif: TimeInForce) -> Self {
1103        self.time_in_force = Some(tif);
1104        self
1105    }
1106
1107    /// Set a new client order ID for the cancel.
1108    pub fn cancel_new_client_order_id(mut self, id: &str) -> Self {
1109        self.cancel_new_client_order_id = Some(id.to_string());
1110        self
1111    }
1112
1113    /// Set the original client order ID to cancel.
1114    pub fn cancel_orig_client_order_id(mut self, id: &str) -> Self {
1115        self.cancel_orig_client_order_id = Some(id.to_string());
1116        self
1117    }
1118
1119    /// Set the order ID to cancel.
1120    pub fn cancel_order_id(mut self, id: u64) -> Self {
1121        self.cancel_order_id = Some(id);
1122        self
1123    }
1124
1125    /// Set the new client order ID.
1126    pub fn new_client_order_id(mut self, id: &str) -> Self {
1127        self.new_client_order_id = Some(id.to_string());
1128        self
1129    }
1130
1131    /// Set the strategy ID.
1132    pub fn strategy_id(mut self, id: u64) -> Self {
1133        self.strategy_id = Some(id);
1134        self
1135    }
1136
1137    /// Set the strategy type.
1138    pub fn strategy_type(mut self, strategy_type: i32) -> Self {
1139        self.strategy_type = Some(strategy_type);
1140        self
1141    }
1142
1143    /// Set the trailing delta.
1144    pub fn trailing_delta(mut self, delta: u64) -> Self {
1145        self.trailing_delta = Some(delta);
1146        self
1147    }
1148
1149    /// Set the iceberg quantity.
1150    pub fn iceberg_qty(mut self, qty: &str) -> Self {
1151        self.iceberg_qty = Some(qty.to_string());
1152        self
1153    }
1154
1155    /// Set the response type.
1156    pub fn response_type(mut self, resp_type: OrderResponseType) -> Self {
1157        self.response_type = Some(resp_type);
1158        self
1159    }
1160
1161    /// Set self-trade prevention mode.
1162    pub fn self_trade_prevention_mode(mut self, mode: &str) -> Self {
1163        self.self_trade_prevention_mode = Some(mode.to_string());
1164        self
1165    }
1166
1167    /// Set cancel restrictions.
1168    pub fn cancel_restrictions(mut self, restrictions: CancelRestrictions) -> Self {
1169        self.cancel_restrictions = Some(restrictions);
1170        self
1171    }
1172
1173    /// Set order rate limit exceeded mode.
1174    pub fn order_rate_limit_exceeded_mode(mut self, mode: OrderRateLimitExceededMode) -> Self {
1175        self.order_rate_limit_exceeded_mode = Some(mode);
1176        self
1177    }
1178
1179    /// Set pegged price type.
1180    pub fn peg_price_type(mut self, peg_price_type: &str) -> Self {
1181        self.peg_price_type = Some(peg_price_type.to_string());
1182        self
1183    }
1184
1185    /// Set pegged offset value.
1186    pub fn peg_offset_value(mut self, peg_offset_value: i32) -> Self {
1187        self.peg_offset_value = Some(peg_offset_value);
1188        self
1189    }
1190
1191    /// Set pegged offset type.
1192    pub fn peg_offset_type(mut self, peg_offset_type: &str) -> Self {
1193        self.peg_offset_type = Some(peg_offset_type.to_string());
1194        self
1195    }
1196
1197    /// Build the cancel-replace order request.
1198    pub fn build(self) -> CancelReplaceOrder {
1199        CancelReplaceOrder {
1200            symbol: self.symbol,
1201            side: self.side,
1202            order_type: self.order_type,
1203            cancel_replace_mode: self.cancel_replace_mode,
1204            time_in_force: self.time_in_force,
1205            quantity: self.quantity,
1206            quote_quantity: self.quote_quantity,
1207            price: self.price,
1208            cancel_new_client_order_id: self.cancel_new_client_order_id,
1209            cancel_orig_client_order_id: self.cancel_orig_client_order_id,
1210            cancel_order_id: self.cancel_order_id,
1211            new_client_order_id: self.new_client_order_id,
1212            strategy_id: self.strategy_id,
1213            strategy_type: self.strategy_type,
1214            stop_price: self.stop_price,
1215            trailing_delta: self.trailing_delta,
1216            iceberg_qty: self.iceberg_qty,
1217            response_type: self.response_type,
1218            self_trade_prevention_mode: self.self_trade_prevention_mode,
1219            cancel_restrictions: self.cancel_restrictions,
1220            order_rate_limit_exceeded_mode: self.order_rate_limit_exceeded_mode,
1221            peg_price_type: self.peg_price_type,
1222            peg_offset_value: self.peg_offset_value,
1223            peg_offset_type: self.peg_offset_type,
1224        }
1225    }
1226}
1227
1228/// Cancel-replace order request parameters.
1229#[derive(Debug, Clone)]
1230pub struct CancelReplaceOrder {
1231    symbol: String,
1232    side: OrderSide,
1233    order_type: OrderType,
1234    cancel_replace_mode: CancelReplaceMode,
1235    time_in_force: Option<TimeInForce>,
1236    quantity: Option<String>,
1237    quote_quantity: Option<String>,
1238    price: Option<String>,
1239    cancel_new_client_order_id: Option<String>,
1240    cancel_orig_client_order_id: Option<String>,
1241    cancel_order_id: Option<u64>,
1242    new_client_order_id: Option<String>,
1243    strategy_id: Option<u64>,
1244    strategy_type: Option<i32>,
1245    stop_price: Option<String>,
1246    trailing_delta: Option<u64>,
1247    iceberg_qty: Option<String>,
1248    response_type: Option<OrderResponseType>,
1249    self_trade_prevention_mode: Option<String>,
1250    cancel_restrictions: Option<CancelRestrictions>,
1251    order_rate_limit_exceeded_mode: Option<OrderRateLimitExceededMode>,
1252    peg_price_type: Option<String>,
1253    peg_offset_value: Option<i32>,
1254    peg_offset_type: Option<String>,
1255}
1256
1257impl CancelReplaceOrder {
1258    fn to_params(&self) -> Vec<(String, String)> {
1259        let mut params = vec![
1260            ("symbol".to_string(), self.symbol.clone()),
1261            (
1262                "side".to_string(),
1263                format!("{:?}", self.side).to_uppercase(),
1264            ),
1265            (
1266                "type".to_string(),
1267                format!("{:?}", self.order_type).to_uppercase(),
1268            ),
1269            (
1270                "cancelReplaceMode".to_string(),
1271                self.cancel_replace_mode.to_string(),
1272            ),
1273        ];
1274
1275        if let Some(ref tif) = self.time_in_force {
1276            params.push(("timeInForce".to_string(), format!("{:?}", tif)));
1277        }
1278        if let Some(ref qty) = self.quantity {
1279            params.push(("quantity".to_string(), qty.clone()));
1280        }
1281        if let Some(ref qty) = self.quote_quantity {
1282            params.push(("quoteOrderQty".to_string(), qty.clone()));
1283        }
1284        if let Some(ref price) = self.price {
1285            params.push(("price".to_string(), price.clone()));
1286        }
1287        if let Some(ref id) = self.cancel_new_client_order_id {
1288            params.push(("cancelNewClientOrderId".to_string(), id.clone()));
1289        }
1290        if let Some(ref id) = self.cancel_orig_client_order_id {
1291            params.push(("cancelOrigClientOrderId".to_string(), id.clone()));
1292        }
1293        if let Some(id) = self.cancel_order_id {
1294            params.push(("cancelOrderId".to_string(), id.to_string()));
1295        }
1296        if let Some(ref id) = self.new_client_order_id {
1297            params.push(("newClientOrderId".to_string(), id.clone()));
1298        }
1299        if let Some(id) = self.strategy_id {
1300            params.push(("strategyId".to_string(), id.to_string()));
1301        }
1302        if let Some(id) = self.strategy_type {
1303            params.push(("strategyType".to_string(), id.to_string()));
1304        }
1305        if let Some(ref stop) = self.stop_price {
1306            params.push(("stopPrice".to_string(), stop.clone()));
1307        }
1308        if let Some(delta) = self.trailing_delta {
1309            params.push(("trailingDelta".to_string(), delta.to_string()));
1310        }
1311        if let Some(ref ice) = self.iceberg_qty {
1312            params.push(("icebergQty".to_string(), ice.clone()));
1313        }
1314        if let Some(ref resp) = self.response_type {
1315            params.push((
1316                "newOrderRespType".to_string(),
1317                format!("{:?}", resp).to_uppercase(),
1318            ));
1319        }
1320        if let Some(ref mode) = self.self_trade_prevention_mode {
1321            params.push(("selfTradePreventionMode".to_string(), mode.clone()));
1322        }
1323        if let Some(restrictions) = self.cancel_restrictions {
1324            params.push(("cancelRestrictions".to_string(), restrictions.to_string()));
1325        }
1326        if let Some(mode) = self.order_rate_limit_exceeded_mode {
1327            params.push(("orderRateLimitExceededMode".to_string(), mode.to_string()));
1328        }
1329        if let Some(ref peg) = self.peg_price_type {
1330            params.push(("pegPriceType".to_string(), peg.clone()));
1331        }
1332        if let Some(value) = self.peg_offset_value {
1333            params.push(("pegOffsetValue".to_string(), value.to_string()));
1334        }
1335        if let Some(ref peg) = self.peg_offset_type {
1336            params.push(("pegOffsetType".to_string(), peg.clone()));
1337        }
1338
1339        params
1340    }
1341}
1342
1343impl OrderBuilder {
1344    /// Create a new order builder.
1345    pub fn new(symbol: &str, side: OrderSide, order_type: OrderType) -> Self {
1346        Self {
1347            symbol: symbol.to_string(),
1348            side,
1349            order_type,
1350            quantity: None,
1351            quote_quantity: None,
1352            price: None,
1353            stop_price: None,
1354            time_in_force: None,
1355            client_order_id: None,
1356            iceberg_qty: None,
1357            response_type: None,
1358        }
1359    }
1360
1361    /// Set the order quantity.
1362    pub fn quantity(mut self, quantity: &str) -> Self {
1363        self.quantity = Some(quantity.to_string());
1364        self
1365    }
1366
1367    /// Set the quote order quantity (for market orders).
1368    pub fn quote_quantity(mut self, quantity: &str) -> Self {
1369        self.quote_quantity = Some(quantity.to_string());
1370        self
1371    }
1372
1373    /// Set the order price (required for limit orders).
1374    pub fn price(mut self, price: &str) -> Self {
1375        self.price = Some(price.to_string());
1376        self
1377    }
1378
1379    /// Set the stop price (for stop orders).
1380    pub fn stop_price(mut self, price: &str) -> Self {
1381        self.stop_price = Some(price.to_string());
1382        self
1383    }
1384
1385    /// Set the time in force.
1386    pub fn time_in_force(mut self, tif: TimeInForce) -> Self {
1387        self.time_in_force = Some(tif);
1388        self
1389    }
1390
1391    /// Set a custom client order ID.
1392    pub fn client_order_id(mut self, id: &str) -> Self {
1393        self.client_order_id = Some(id.to_string());
1394        self
1395    }
1396
1397    /// Set the iceberg quantity.
1398    pub fn iceberg_qty(mut self, qty: &str) -> Self {
1399        self.iceberg_qty = Some(qty.to_string());
1400        self
1401    }
1402
1403    /// Set the response type.
1404    pub fn response_type(mut self, resp_type: OrderResponseType) -> Self {
1405        self.response_type = Some(resp_type);
1406        self
1407    }
1408
1409    /// Build the order.
1410    pub fn build(self) -> NewOrder {
1411        NewOrder {
1412            symbol: self.symbol,
1413            side: self.side,
1414            order_type: self.order_type,
1415            quantity: self.quantity,
1416            quote_quantity: self.quote_quantity,
1417            price: self.price,
1418            stop_price: self.stop_price,
1419            time_in_force: self.time_in_force,
1420            client_order_id: self.client_order_id,
1421            iceberg_qty: self.iceberg_qty,
1422            response_type: self.response_type,
1423        }
1424    }
1425}
1426
1427/// New order parameters.
1428#[derive(Debug, Clone, Serialize)]
1429#[serde(rename_all = "camelCase")]
1430pub struct NewOrder {
1431    symbol: String,
1432    side: OrderSide,
1433    #[serde(rename = "type")]
1434    order_type: OrderType,
1435    #[serde(skip_serializing_if = "Option::is_none")]
1436    quantity: Option<String>,
1437    #[serde(skip_serializing_if = "Option::is_none", rename = "quoteOrderQty")]
1438    quote_quantity: Option<String>,
1439    #[serde(skip_serializing_if = "Option::is_none")]
1440    price: Option<String>,
1441    #[serde(skip_serializing_if = "Option::is_none")]
1442    stop_price: Option<String>,
1443    #[serde(skip_serializing_if = "Option::is_none")]
1444    time_in_force: Option<TimeInForce>,
1445    #[serde(skip_serializing_if = "Option::is_none", rename = "newClientOrderId")]
1446    client_order_id: Option<String>,
1447    #[serde(skip_serializing_if = "Option::is_none")]
1448    iceberg_qty: Option<String>,
1449    #[serde(skip_serializing_if = "Option::is_none", rename = "newOrderRespType")]
1450    response_type: Option<OrderResponseType>,
1451}
1452
1453impl NewOrder {
1454    fn to_params(&self) -> Vec<(String, String)> {
1455        let mut params = vec![
1456            ("symbol".to_string(), self.symbol.clone()),
1457            (
1458                "side".to_string(),
1459                format!("{:?}", self.side).to_uppercase(),
1460            ),
1461            (
1462                "type".to_string(),
1463                format!("{:?}", self.order_type).to_uppercase(),
1464            ),
1465        ];
1466
1467        if let Some(ref qty) = self.quantity {
1468            params.push(("quantity".to_string(), qty.clone()));
1469        }
1470        if let Some(ref qty) = self.quote_quantity {
1471            params.push(("quoteOrderQty".to_string(), qty.clone()));
1472        }
1473        if let Some(ref price) = self.price {
1474            params.push(("price".to_string(), price.clone()));
1475        }
1476        if let Some(ref stop) = self.stop_price {
1477            params.push(("stopPrice".to_string(), stop.clone()));
1478        }
1479        if let Some(ref tif) = self.time_in_force {
1480            params.push(("timeInForce".to_string(), format!("{:?}", tif)));
1481        }
1482        if let Some(ref cid) = self.client_order_id {
1483            params.push(("newClientOrderId".to_string(), cid.clone()));
1484        }
1485        if let Some(ref ice) = self.iceberg_qty {
1486            params.push(("icebergQty".to_string(), ice.clone()));
1487        }
1488        if let Some(ref resp) = self.response_type {
1489            params.push((
1490                "newOrderRespType".to_string(),
1491                format!("{:?}", resp).to_uppercase(),
1492            ));
1493        }
1494
1495        params
1496    }
1497}
1498
1499/// Builder for creating OCO orders.
1500#[derive(Debug, Clone)]
1501pub struct OcoOrderBuilder {
1502    symbol: String,
1503    side: OrderSide,
1504    quantity: String,
1505    price: String,
1506    stop_price: String,
1507    stop_limit_price: Option<String>,
1508    stop_limit_time_in_force: Option<TimeInForce>,
1509    list_client_order_id: Option<String>,
1510    limit_client_order_id: Option<String>,
1511    stop_client_order_id: Option<String>,
1512}
1513
1514impl OcoOrderBuilder {
1515    /// Create a new OCO order builder.
1516    ///
1517    /// # Arguments
1518    ///
1519    /// * `symbol` - Trading pair symbol
1520    /// * `side` - Order side (Buy or Sell)
1521    /// * `quantity` - Order quantity
1522    /// * `price` - Limit order price
1523    /// * `stop_price` - Stop order trigger price
1524    pub fn new(
1525        symbol: &str,
1526        side: OrderSide,
1527        quantity: &str,
1528        price: &str,
1529        stop_price: &str,
1530    ) -> Self {
1531        Self {
1532            symbol: symbol.to_string(),
1533            side,
1534            quantity: quantity.to_string(),
1535            price: price.to_string(),
1536            stop_price: stop_price.to_string(),
1537            stop_limit_price: None,
1538            stop_limit_time_in_force: None,
1539            list_client_order_id: None,
1540            limit_client_order_id: None,
1541            stop_client_order_id: None,
1542        }
1543    }
1544
1545    /// Set the stop limit price.
1546    pub fn stop_limit_price(mut self, price: &str) -> Self {
1547        self.stop_limit_price = Some(price.to_string());
1548        self
1549    }
1550
1551    /// Set the stop limit time in force.
1552    pub fn stop_limit_time_in_force(mut self, tif: TimeInForce) -> Self {
1553        self.stop_limit_time_in_force = Some(tif);
1554        self
1555    }
1556
1557    /// Set a custom list client order ID.
1558    pub fn list_client_order_id(mut self, id: &str) -> Self {
1559        self.list_client_order_id = Some(id.to_string());
1560        self
1561    }
1562
1563    /// Set a custom limit client order ID.
1564    pub fn limit_client_order_id(mut self, id: &str) -> Self {
1565        self.limit_client_order_id = Some(id.to_string());
1566        self
1567    }
1568
1569    /// Set a custom stop client order ID.
1570    pub fn stop_client_order_id(mut self, id: &str) -> Self {
1571        self.stop_client_order_id = Some(id.to_string());
1572        self
1573    }
1574
1575    /// Build the OCO order.
1576    pub fn build(self) -> NewOcoOrder {
1577        NewOcoOrder {
1578            symbol: self.symbol,
1579            side: self.side,
1580            quantity: self.quantity,
1581            price: self.price,
1582            stop_price: self.stop_price,
1583            stop_limit_price: self.stop_limit_price,
1584            stop_limit_time_in_force: self.stop_limit_time_in_force,
1585            list_client_order_id: self.list_client_order_id,
1586            limit_client_order_id: self.limit_client_order_id,
1587            stop_client_order_id: self.stop_client_order_id,
1588        }
1589    }
1590}
1591
1592/// New OCO order parameters.
1593#[derive(Debug, Clone)]
1594pub struct NewOcoOrder {
1595    symbol: String,
1596    side: OrderSide,
1597    quantity: String,
1598    price: String,
1599    stop_price: String,
1600    stop_limit_price: Option<String>,
1601    stop_limit_time_in_force: Option<TimeInForce>,
1602    list_client_order_id: Option<String>,
1603    limit_client_order_id: Option<String>,
1604    stop_client_order_id: Option<String>,
1605}
1606
1607impl NewOcoOrder {
1608    fn to_params(&self) -> Vec<(String, String)> {
1609        let mut params = vec![
1610            ("symbol".to_string(), self.symbol.clone()),
1611            (
1612                "side".to_string(),
1613                format!("{:?}", self.side).to_uppercase(),
1614            ),
1615            ("quantity".to_string(), self.quantity.clone()),
1616            ("price".to_string(), self.price.clone()),
1617            ("stopPrice".to_string(), self.stop_price.clone()),
1618        ];
1619
1620        if let Some(ref slp) = self.stop_limit_price {
1621            params.push(("stopLimitPrice".to_string(), slp.clone()));
1622        }
1623        if let Some(ref tif) = self.stop_limit_time_in_force {
1624            params.push(("stopLimitTimeInForce".to_string(), format!("{:?}", tif)));
1625        }
1626        if let Some(ref id) = self.list_client_order_id {
1627            params.push(("listClientOrderId".to_string(), id.clone()));
1628        }
1629        if let Some(ref id) = self.limit_client_order_id {
1630            params.push(("limitClientOrderId".to_string(), id.clone()));
1631        }
1632        if let Some(ref id) = self.stop_client_order_id {
1633            params.push(("stopClientOrderId".to_string(), id.clone()));
1634        }
1635
1636        params
1637    }
1638}
1639
1640/// Builder for creating OTO order lists.
1641#[derive(Debug, Clone)]
1642pub struct OtoOrderBuilder {
1643    symbol: String,
1644    working_type: OrderType,
1645    working_side: OrderSide,
1646    working_price: String,
1647    working_quantity: String,
1648    pending_type: OrderType,
1649    pending_side: OrderSide,
1650    pending_quantity: String,
1651    list_client_order_id: Option<String>,
1652    response_type: Option<OrderResponseType>,
1653    self_trade_prevention_mode: Option<String>,
1654    working_client_order_id: Option<String>,
1655    working_iceberg_qty: Option<String>,
1656    working_time_in_force: Option<TimeInForce>,
1657    working_strategy_id: Option<u64>,
1658    working_strategy_type: Option<i32>,
1659    working_peg_price_type: Option<String>,
1660    working_peg_offset_type: Option<String>,
1661    working_peg_offset_value: Option<i32>,
1662    pending_client_order_id: Option<String>,
1663    pending_price: Option<String>,
1664    pending_stop_price: Option<String>,
1665    pending_trailing_delta: Option<u64>,
1666    pending_iceberg_qty: Option<String>,
1667    pending_time_in_force: Option<TimeInForce>,
1668    pending_strategy_id: Option<u64>,
1669    pending_strategy_type: Option<i32>,
1670    pending_peg_price_type: Option<String>,
1671    pending_peg_offset_type: Option<String>,
1672    pending_peg_offset_value: Option<i32>,
1673}
1674
1675impl OtoOrderBuilder {
1676    /// Create a new OTO order list builder.
1677    #[allow(clippy::too_many_arguments)]
1678    pub fn new(
1679        symbol: &str,
1680        working_type: OrderType,
1681        working_side: OrderSide,
1682        working_price: &str,
1683        working_quantity: &str,
1684        pending_type: OrderType,
1685        pending_side: OrderSide,
1686        pending_quantity: &str,
1687    ) -> Self {
1688        Self {
1689            symbol: symbol.to_string(),
1690            working_type,
1691            working_side,
1692            working_price: working_price.to_string(),
1693            working_quantity: working_quantity.to_string(),
1694            pending_type,
1695            pending_side,
1696            pending_quantity: pending_quantity.to_string(),
1697            list_client_order_id: None,
1698            response_type: None,
1699            self_trade_prevention_mode: None,
1700            working_client_order_id: None,
1701            working_iceberg_qty: None,
1702            working_time_in_force: None,
1703            working_strategy_id: None,
1704            working_strategy_type: None,
1705            working_peg_price_type: None,
1706            working_peg_offset_type: None,
1707            working_peg_offset_value: None,
1708            pending_client_order_id: None,
1709            pending_price: None,
1710            pending_stop_price: None,
1711            pending_trailing_delta: None,
1712            pending_iceberg_qty: None,
1713            pending_time_in_force: None,
1714            pending_strategy_id: None,
1715            pending_strategy_type: None,
1716            pending_peg_price_type: None,
1717            pending_peg_offset_type: None,
1718            pending_peg_offset_value: None,
1719        }
1720    }
1721
1722    pub fn list_client_order_id(mut self, id: &str) -> Self {
1723        self.list_client_order_id = Some(id.to_string());
1724        self
1725    }
1726
1727    pub fn response_type(mut self, resp_type: OrderResponseType) -> Self {
1728        self.response_type = Some(resp_type);
1729        self
1730    }
1731
1732    pub fn self_trade_prevention_mode(mut self, mode: &str) -> Self {
1733        self.self_trade_prevention_mode = Some(mode.to_string());
1734        self
1735    }
1736
1737    pub fn working_client_order_id(mut self, id: &str) -> Self {
1738        self.working_client_order_id = Some(id.to_string());
1739        self
1740    }
1741
1742    pub fn working_iceberg_qty(mut self, qty: &str) -> Self {
1743        self.working_iceberg_qty = Some(qty.to_string());
1744        self
1745    }
1746
1747    pub fn working_time_in_force(mut self, tif: TimeInForce) -> Self {
1748        self.working_time_in_force = Some(tif);
1749        self
1750    }
1751
1752    pub fn working_strategy_id(mut self, id: u64) -> Self {
1753        self.working_strategy_id = Some(id);
1754        self
1755    }
1756
1757    pub fn working_strategy_type(mut self, strategy_type: i32) -> Self {
1758        self.working_strategy_type = Some(strategy_type);
1759        self
1760    }
1761
1762    pub fn working_peg_price_type(mut self, peg_price_type: &str) -> Self {
1763        self.working_peg_price_type = Some(peg_price_type.to_string());
1764        self
1765    }
1766
1767    pub fn working_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
1768        self.working_peg_offset_type = Some(peg_offset_type.to_string());
1769        self
1770    }
1771
1772    pub fn working_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
1773        self.working_peg_offset_value = Some(peg_offset_value);
1774        self
1775    }
1776
1777    pub fn pending_client_order_id(mut self, id: &str) -> Self {
1778        self.pending_client_order_id = Some(id.to_string());
1779        self
1780    }
1781
1782    pub fn pending_price(mut self, price: &str) -> Self {
1783        self.pending_price = Some(price.to_string());
1784        self
1785    }
1786
1787    pub fn pending_stop_price(mut self, price: &str) -> Self {
1788        self.pending_stop_price = Some(price.to_string());
1789        self
1790    }
1791
1792    pub fn pending_trailing_delta(mut self, delta: u64) -> Self {
1793        self.pending_trailing_delta = Some(delta);
1794        self
1795    }
1796
1797    pub fn pending_iceberg_qty(mut self, qty: &str) -> Self {
1798        self.pending_iceberg_qty = Some(qty.to_string());
1799        self
1800    }
1801
1802    pub fn pending_time_in_force(mut self, tif: TimeInForce) -> Self {
1803        self.pending_time_in_force = Some(tif);
1804        self
1805    }
1806
1807    pub fn pending_strategy_id(mut self, id: u64) -> Self {
1808        self.pending_strategy_id = Some(id);
1809        self
1810    }
1811
1812    pub fn pending_strategy_type(mut self, strategy_type: i32) -> Self {
1813        self.pending_strategy_type = Some(strategy_type);
1814        self
1815    }
1816
1817    pub fn pending_peg_price_type(mut self, peg_price_type: &str) -> Self {
1818        self.pending_peg_price_type = Some(peg_price_type.to_string());
1819        self
1820    }
1821
1822    pub fn pending_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
1823        self.pending_peg_offset_type = Some(peg_offset_type.to_string());
1824        self
1825    }
1826
1827    pub fn pending_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
1828        self.pending_peg_offset_value = Some(peg_offset_value);
1829        self
1830    }
1831
1832    pub fn build(self) -> NewOtoOrder {
1833        NewOtoOrder {
1834            symbol: self.symbol,
1835            working_type: self.working_type,
1836            working_side: self.working_side,
1837            working_price: self.working_price,
1838            working_quantity: self.working_quantity,
1839            pending_type: self.pending_type,
1840            pending_side: self.pending_side,
1841            pending_quantity: Some(self.pending_quantity),
1842            list_client_order_id: self.list_client_order_id,
1843            response_type: self.response_type,
1844            self_trade_prevention_mode: self.self_trade_prevention_mode,
1845            working_client_order_id: self.working_client_order_id,
1846            working_iceberg_qty: self.working_iceberg_qty,
1847            working_time_in_force: self.working_time_in_force,
1848            working_strategy_id: self.working_strategy_id,
1849            working_strategy_type: self.working_strategy_type,
1850            working_peg_price_type: self.working_peg_price_type,
1851            working_peg_offset_type: self.working_peg_offset_type,
1852            working_peg_offset_value: self.working_peg_offset_value,
1853            pending_client_order_id: self.pending_client_order_id,
1854            pending_price: self.pending_price,
1855            pending_stop_price: self.pending_stop_price,
1856            pending_trailing_delta: self.pending_trailing_delta,
1857            pending_iceberg_qty: self.pending_iceberg_qty,
1858            pending_time_in_force: self.pending_time_in_force,
1859            pending_strategy_id: self.pending_strategy_id,
1860            pending_strategy_type: self.pending_strategy_type,
1861            pending_peg_price_type: self.pending_peg_price_type,
1862            pending_peg_offset_type: self.pending_peg_offset_type,
1863            pending_peg_offset_value: self.pending_peg_offset_value,
1864        }
1865    }
1866}
1867
1868/// New OTO order list parameters.
1869#[derive(Debug, Clone)]
1870pub struct NewOtoOrder {
1871    symbol: String,
1872    working_type: OrderType,
1873    working_side: OrderSide,
1874    working_price: String,
1875    working_quantity: String,
1876    pending_type: OrderType,
1877    pending_side: OrderSide,
1878    pending_quantity: Option<String>,
1879    list_client_order_id: Option<String>,
1880    response_type: Option<OrderResponseType>,
1881    self_trade_prevention_mode: Option<String>,
1882    working_client_order_id: Option<String>,
1883    working_iceberg_qty: Option<String>,
1884    working_time_in_force: Option<TimeInForce>,
1885    working_strategy_id: Option<u64>,
1886    working_strategy_type: Option<i32>,
1887    working_peg_price_type: Option<String>,
1888    working_peg_offset_type: Option<String>,
1889    working_peg_offset_value: Option<i32>,
1890    pending_client_order_id: Option<String>,
1891    pending_price: Option<String>,
1892    pending_stop_price: Option<String>,
1893    pending_trailing_delta: Option<u64>,
1894    pending_iceberg_qty: Option<String>,
1895    pending_time_in_force: Option<TimeInForce>,
1896    pending_strategy_id: Option<u64>,
1897    pending_strategy_type: Option<i32>,
1898    pending_peg_price_type: Option<String>,
1899    pending_peg_offset_type: Option<String>,
1900    pending_peg_offset_value: Option<i32>,
1901}
1902
1903impl NewOtoOrder {
1904    fn to_params(&self) -> Vec<(String, String)> {
1905        let mut params = vec![
1906            ("symbol".to_string(), self.symbol.clone()),
1907            (
1908                "workingType".to_string(),
1909                format!("{:?}", self.working_type).to_uppercase(),
1910            ),
1911            (
1912                "workingSide".to_string(),
1913                format!("{:?}", self.working_side).to_uppercase(),
1914            ),
1915            ("workingPrice".to_string(), self.working_price.clone()),
1916            ("workingQuantity".to_string(), self.working_quantity.clone()),
1917            (
1918                "pendingType".to_string(),
1919                format!("{:?}", self.pending_type).to_uppercase(),
1920            ),
1921            (
1922                "pendingSide".to_string(),
1923                format!("{:?}", self.pending_side).to_uppercase(),
1924            ),
1925        ];
1926
1927        if let Some(ref qty) = self.pending_quantity {
1928            params.push(("pendingQuantity".to_string(), qty.clone()));
1929        }
1930        if let Some(ref id) = self.list_client_order_id {
1931            params.push(("listClientOrderId".to_string(), id.clone()));
1932        }
1933        if let Some(ref resp) = self.response_type {
1934            params.push((
1935                "newOrderRespType".to_string(),
1936                format!("{:?}", resp).to_uppercase(),
1937            ));
1938        }
1939        if let Some(ref mode) = self.self_trade_prevention_mode {
1940            params.push(("selfTradePreventionMode".to_string(), mode.clone()));
1941        }
1942        if let Some(ref id) = self.working_client_order_id {
1943            params.push(("workingClientOrderId".to_string(), id.clone()));
1944        }
1945        if let Some(ref qty) = self.working_iceberg_qty {
1946            params.push(("workingIcebergQty".to_string(), qty.clone()));
1947        }
1948        if let Some(ref tif) = self.working_time_in_force {
1949            params.push(("workingTimeInForce".to_string(), format!("{:?}", tif)));
1950        }
1951        if let Some(id) = self.working_strategy_id {
1952            params.push(("workingStrategyId".to_string(), id.to_string()));
1953        }
1954        if let Some(id) = self.working_strategy_type {
1955            params.push(("workingStrategyType".to_string(), id.to_string()));
1956        }
1957        if let Some(ref peg) = self.working_peg_price_type {
1958            params.push(("workingPegPriceType".to_string(), peg.clone()));
1959        }
1960        if let Some(ref peg) = self.working_peg_offset_type {
1961            params.push(("workingPegOffsetType".to_string(), peg.clone()));
1962        }
1963        if let Some(value) = self.working_peg_offset_value {
1964            params.push(("workingPegOffsetValue".to_string(), value.to_string()));
1965        }
1966        if let Some(ref id) = self.pending_client_order_id {
1967            params.push(("pendingClientOrderId".to_string(), id.clone()));
1968        }
1969        if let Some(ref price) = self.pending_price {
1970            params.push(("pendingPrice".to_string(), price.clone()));
1971        }
1972        if let Some(ref price) = self.pending_stop_price {
1973            params.push(("pendingStopPrice".to_string(), price.clone()));
1974        }
1975        if let Some(delta) = self.pending_trailing_delta {
1976            params.push(("pendingTrailingDelta".to_string(), delta.to_string()));
1977        }
1978        if let Some(ref qty) = self.pending_iceberg_qty {
1979            params.push(("pendingIcebergQty".to_string(), qty.clone()));
1980        }
1981        if let Some(ref tif) = self.pending_time_in_force {
1982            params.push(("pendingTimeInForce".to_string(), format!("{:?}", tif)));
1983        }
1984        if let Some(id) = self.pending_strategy_id {
1985            params.push(("pendingStrategyId".to_string(), id.to_string()));
1986        }
1987        if let Some(id) = self.pending_strategy_type {
1988            params.push(("pendingStrategyType".to_string(), id.to_string()));
1989        }
1990        if let Some(ref peg) = self.pending_peg_price_type {
1991            params.push(("pendingPegPriceType".to_string(), peg.clone()));
1992        }
1993        if let Some(ref peg) = self.pending_peg_offset_type {
1994            params.push(("pendingPegOffsetType".to_string(), peg.clone()));
1995        }
1996        if let Some(value) = self.pending_peg_offset_value {
1997            params.push(("pendingPegOffsetValue".to_string(), value.to_string()));
1998        }
1999
2000        params
2001    }
2002}
2003
2004/// Builder for creating OPO order lists.
2005#[derive(Debug, Clone)]
2006pub struct OpoOrderBuilder {
2007    inner: NewOtoOrder,
2008}
2009
2010impl OpoOrderBuilder {
2011    /// Create a new OPO order list builder.
2012    pub fn new(
2013        symbol: &str,
2014        working_type: OrderType,
2015        working_side: OrderSide,
2016        working_price: &str,
2017        working_quantity: &str,
2018        pending_type: OrderType,
2019        pending_side: OrderSide,
2020    ) -> Self {
2021        Self {
2022            inner: NewOtoOrder {
2023                symbol: symbol.to_string(),
2024                working_type,
2025                working_side,
2026                working_price: working_price.to_string(),
2027                working_quantity: working_quantity.to_string(),
2028                pending_type,
2029                pending_side,
2030                pending_quantity: None,
2031                list_client_order_id: None,
2032                response_type: None,
2033                self_trade_prevention_mode: None,
2034                working_client_order_id: None,
2035                working_iceberg_qty: None,
2036                working_time_in_force: None,
2037                working_strategy_id: None,
2038                working_strategy_type: None,
2039                working_peg_price_type: None,
2040                working_peg_offset_type: None,
2041                working_peg_offset_value: None,
2042                pending_client_order_id: None,
2043                pending_price: None,
2044                pending_stop_price: None,
2045                pending_trailing_delta: None,
2046                pending_iceberg_qty: None,
2047                pending_time_in_force: None,
2048                pending_strategy_id: None,
2049                pending_strategy_type: None,
2050                pending_peg_price_type: None,
2051                pending_peg_offset_type: None,
2052                pending_peg_offset_value: None,
2053            },
2054        }
2055    }
2056
2057    pub fn list_client_order_id(mut self, id: &str) -> Self {
2058        self.inner.list_client_order_id = Some(id.to_string());
2059        self
2060    }
2061
2062    pub fn response_type(mut self, resp_type: OrderResponseType) -> Self {
2063        self.inner.response_type = Some(resp_type);
2064        self
2065    }
2066
2067    pub fn self_trade_prevention_mode(mut self, mode: &str) -> Self {
2068        self.inner.self_trade_prevention_mode = Some(mode.to_string());
2069        self
2070    }
2071
2072    pub fn working_client_order_id(mut self, id: &str) -> Self {
2073        self.inner.working_client_order_id = Some(id.to_string());
2074        self
2075    }
2076
2077    pub fn working_iceberg_qty(mut self, qty: &str) -> Self {
2078        self.inner.working_iceberg_qty = Some(qty.to_string());
2079        self
2080    }
2081
2082    pub fn working_time_in_force(mut self, tif: TimeInForce) -> Self {
2083        self.inner.working_time_in_force = Some(tif);
2084        self
2085    }
2086
2087    pub fn working_strategy_id(mut self, id: u64) -> Self {
2088        self.inner.working_strategy_id = Some(id);
2089        self
2090    }
2091
2092    pub fn working_strategy_type(mut self, strategy_type: i32) -> Self {
2093        self.inner.working_strategy_type = Some(strategy_type);
2094        self
2095    }
2096
2097    pub fn working_peg_price_type(mut self, peg_price_type: &str) -> Self {
2098        self.inner.working_peg_price_type = Some(peg_price_type.to_string());
2099        self
2100    }
2101
2102    pub fn working_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
2103        self.inner.working_peg_offset_type = Some(peg_offset_type.to_string());
2104        self
2105    }
2106
2107    pub fn working_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
2108        self.inner.working_peg_offset_value = Some(peg_offset_value);
2109        self
2110    }
2111
2112    pub fn pending_client_order_id(mut self, id: &str) -> Self {
2113        self.inner.pending_client_order_id = Some(id.to_string());
2114        self
2115    }
2116
2117    pub fn pending_quantity(mut self, qty: &str) -> Self {
2118        self.inner.pending_quantity = Some(qty.to_string());
2119        self
2120    }
2121
2122    pub fn pending_price(mut self, price: &str) -> Self {
2123        self.inner.pending_price = Some(price.to_string());
2124        self
2125    }
2126
2127    pub fn pending_stop_price(mut self, price: &str) -> Self {
2128        self.inner.pending_stop_price = Some(price.to_string());
2129        self
2130    }
2131
2132    pub fn pending_trailing_delta(mut self, delta: u64) -> Self {
2133        self.inner.pending_trailing_delta = Some(delta);
2134        self
2135    }
2136
2137    pub fn pending_iceberg_qty(mut self, qty: &str) -> Self {
2138        self.inner.pending_iceberg_qty = Some(qty.to_string());
2139        self
2140    }
2141
2142    pub fn pending_time_in_force(mut self, tif: TimeInForce) -> Self {
2143        self.inner.pending_time_in_force = Some(tif);
2144        self
2145    }
2146
2147    pub fn pending_strategy_id(mut self, id: u64) -> Self {
2148        self.inner.pending_strategy_id = Some(id);
2149        self
2150    }
2151
2152    pub fn pending_strategy_type(mut self, strategy_type: i32) -> Self {
2153        self.inner.pending_strategy_type = Some(strategy_type);
2154        self
2155    }
2156
2157    pub fn pending_peg_price_type(mut self, peg_price_type: &str) -> Self {
2158        self.inner.pending_peg_price_type = Some(peg_price_type.to_string());
2159        self
2160    }
2161
2162    pub fn pending_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
2163        self.inner.pending_peg_offset_type = Some(peg_offset_type.to_string());
2164        self
2165    }
2166
2167    pub fn pending_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
2168        self.inner.pending_peg_offset_value = Some(peg_offset_value);
2169        self
2170    }
2171
2172    pub fn build(self) -> NewOpoOrder {
2173        NewOpoOrder { inner: self.inner }
2174    }
2175}
2176
2177/// New OPO order list parameters.
2178#[derive(Debug, Clone)]
2179pub struct NewOpoOrder {
2180    inner: NewOtoOrder,
2181}
2182
2183impl NewOpoOrder {
2184    fn to_params(&self) -> Vec<(String, String)> {
2185        self.inner.to_params()
2186    }
2187}
2188
2189/// Builder for creating OTOCO order lists.
2190#[derive(Debug, Clone)]
2191pub struct OtocoOrderBuilder {
2192    symbol: String,
2193    working_type: OrderType,
2194    working_side: OrderSide,
2195    working_price: String,
2196    working_quantity: String,
2197    pending_side: OrderSide,
2198    pending_quantity: String,
2199    pending_above_type: OrderType,
2200    list_client_order_id: Option<String>,
2201    response_type: Option<OrderResponseType>,
2202    self_trade_prevention_mode: Option<String>,
2203    working_client_order_id: Option<String>,
2204    working_iceberg_qty: Option<String>,
2205    working_time_in_force: Option<TimeInForce>,
2206    working_strategy_id: Option<u64>,
2207    working_strategy_type: Option<i32>,
2208    working_peg_price_type: Option<String>,
2209    working_peg_offset_type: Option<String>,
2210    working_peg_offset_value: Option<i32>,
2211    pending_above_client_order_id: Option<String>,
2212    pending_above_price: Option<String>,
2213    pending_above_stop_price: Option<String>,
2214    pending_above_trailing_delta: Option<u64>,
2215    pending_above_iceberg_qty: Option<String>,
2216    pending_above_time_in_force: Option<TimeInForce>,
2217    pending_above_strategy_id: Option<u64>,
2218    pending_above_strategy_type: Option<i32>,
2219    pending_above_peg_price_type: Option<String>,
2220    pending_above_peg_offset_type: Option<String>,
2221    pending_above_peg_offset_value: Option<i32>,
2222    pending_below_type: Option<OrderType>,
2223    pending_below_client_order_id: Option<String>,
2224    pending_below_price: Option<String>,
2225    pending_below_stop_price: Option<String>,
2226    pending_below_trailing_delta: Option<u64>,
2227    pending_below_iceberg_qty: Option<String>,
2228    pending_below_time_in_force: Option<TimeInForce>,
2229    pending_below_strategy_id: Option<u64>,
2230    pending_below_strategy_type: Option<i32>,
2231    pending_below_peg_price_type: Option<String>,
2232    pending_below_peg_offset_type: Option<String>,
2233    pending_below_peg_offset_value: Option<i32>,
2234}
2235
2236impl OtocoOrderBuilder {
2237    /// Create a new OTOCO order list builder.
2238    #[allow(clippy::too_many_arguments)]
2239    pub fn new(
2240        symbol: &str,
2241        working_type: OrderType,
2242        working_side: OrderSide,
2243        working_price: &str,
2244        working_quantity: &str,
2245        pending_side: OrderSide,
2246        pending_quantity: &str,
2247        pending_above_type: OrderType,
2248    ) -> Self {
2249        Self {
2250            symbol: symbol.to_string(),
2251            working_type,
2252            working_side,
2253            working_price: working_price.to_string(),
2254            working_quantity: working_quantity.to_string(),
2255            pending_side,
2256            pending_quantity: pending_quantity.to_string(),
2257            pending_above_type,
2258            list_client_order_id: None,
2259            response_type: None,
2260            self_trade_prevention_mode: None,
2261            working_client_order_id: None,
2262            working_iceberg_qty: None,
2263            working_time_in_force: None,
2264            working_strategy_id: None,
2265            working_strategy_type: None,
2266            working_peg_price_type: None,
2267            working_peg_offset_type: None,
2268            working_peg_offset_value: None,
2269            pending_above_client_order_id: None,
2270            pending_above_price: None,
2271            pending_above_stop_price: None,
2272            pending_above_trailing_delta: None,
2273            pending_above_iceberg_qty: None,
2274            pending_above_time_in_force: None,
2275            pending_above_strategy_id: None,
2276            pending_above_strategy_type: None,
2277            pending_above_peg_price_type: None,
2278            pending_above_peg_offset_type: None,
2279            pending_above_peg_offset_value: None,
2280            pending_below_type: None,
2281            pending_below_client_order_id: None,
2282            pending_below_price: None,
2283            pending_below_stop_price: None,
2284            pending_below_trailing_delta: None,
2285            pending_below_iceberg_qty: None,
2286            pending_below_time_in_force: None,
2287            pending_below_strategy_id: None,
2288            pending_below_strategy_type: None,
2289            pending_below_peg_price_type: None,
2290            pending_below_peg_offset_type: None,
2291            pending_below_peg_offset_value: None,
2292        }
2293    }
2294
2295    pub fn list_client_order_id(mut self, id: &str) -> Self {
2296        self.list_client_order_id = Some(id.to_string());
2297        self
2298    }
2299
2300    pub fn response_type(mut self, resp_type: OrderResponseType) -> Self {
2301        self.response_type = Some(resp_type);
2302        self
2303    }
2304
2305    pub fn self_trade_prevention_mode(mut self, mode: &str) -> Self {
2306        self.self_trade_prevention_mode = Some(mode.to_string());
2307        self
2308    }
2309
2310    pub fn working_client_order_id(mut self, id: &str) -> Self {
2311        self.working_client_order_id = Some(id.to_string());
2312        self
2313    }
2314
2315    pub fn working_iceberg_qty(mut self, qty: &str) -> Self {
2316        self.working_iceberg_qty = Some(qty.to_string());
2317        self
2318    }
2319
2320    pub fn working_time_in_force(mut self, tif: TimeInForce) -> Self {
2321        self.working_time_in_force = Some(tif);
2322        self
2323    }
2324
2325    pub fn working_strategy_id(mut self, id: u64) -> Self {
2326        self.working_strategy_id = Some(id);
2327        self
2328    }
2329
2330    pub fn working_strategy_type(mut self, strategy_type: i32) -> Self {
2331        self.working_strategy_type = Some(strategy_type);
2332        self
2333    }
2334
2335    pub fn working_peg_price_type(mut self, peg_price_type: &str) -> Self {
2336        self.working_peg_price_type = Some(peg_price_type.to_string());
2337        self
2338    }
2339
2340    pub fn working_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
2341        self.working_peg_offset_type = Some(peg_offset_type.to_string());
2342        self
2343    }
2344
2345    pub fn working_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
2346        self.working_peg_offset_value = Some(peg_offset_value);
2347        self
2348    }
2349
2350    pub fn pending_above_client_order_id(mut self, id: &str) -> Self {
2351        self.pending_above_client_order_id = Some(id.to_string());
2352        self
2353    }
2354
2355    pub fn pending_above_price(mut self, price: &str) -> Self {
2356        self.pending_above_price = Some(price.to_string());
2357        self
2358    }
2359
2360    pub fn pending_above_stop_price(mut self, price: &str) -> Self {
2361        self.pending_above_stop_price = Some(price.to_string());
2362        self
2363    }
2364
2365    pub fn pending_above_trailing_delta(mut self, delta: u64) -> Self {
2366        self.pending_above_trailing_delta = Some(delta);
2367        self
2368    }
2369
2370    pub fn pending_above_iceberg_qty(mut self, qty: &str) -> Self {
2371        self.pending_above_iceberg_qty = Some(qty.to_string());
2372        self
2373    }
2374
2375    pub fn pending_above_time_in_force(mut self, tif: TimeInForce) -> Self {
2376        self.pending_above_time_in_force = Some(tif);
2377        self
2378    }
2379
2380    pub fn pending_above_strategy_id(mut self, id: u64) -> Self {
2381        self.pending_above_strategy_id = Some(id);
2382        self
2383    }
2384
2385    pub fn pending_above_strategy_type(mut self, strategy_type: i32) -> Self {
2386        self.pending_above_strategy_type = Some(strategy_type);
2387        self
2388    }
2389
2390    pub fn pending_above_peg_price_type(mut self, peg_price_type: &str) -> Self {
2391        self.pending_above_peg_price_type = Some(peg_price_type.to_string());
2392        self
2393    }
2394
2395    pub fn pending_above_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
2396        self.pending_above_peg_offset_type = Some(peg_offset_type.to_string());
2397        self
2398    }
2399
2400    pub fn pending_above_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
2401        self.pending_above_peg_offset_value = Some(peg_offset_value);
2402        self
2403    }
2404
2405    pub fn pending_below_type(mut self, order_type: OrderType) -> Self {
2406        self.pending_below_type = Some(order_type);
2407        self
2408    }
2409
2410    pub fn pending_below_client_order_id(mut self, id: &str) -> Self {
2411        self.pending_below_client_order_id = Some(id.to_string());
2412        self
2413    }
2414
2415    pub fn pending_below_price(mut self, price: &str) -> Self {
2416        self.pending_below_price = Some(price.to_string());
2417        self
2418    }
2419
2420    pub fn pending_below_stop_price(mut self, price: &str) -> Self {
2421        self.pending_below_stop_price = Some(price.to_string());
2422        self
2423    }
2424
2425    pub fn pending_below_trailing_delta(mut self, delta: u64) -> Self {
2426        self.pending_below_trailing_delta = Some(delta);
2427        self
2428    }
2429
2430    pub fn pending_below_iceberg_qty(mut self, qty: &str) -> Self {
2431        self.pending_below_iceberg_qty = Some(qty.to_string());
2432        self
2433    }
2434
2435    pub fn pending_below_time_in_force(mut self, tif: TimeInForce) -> Self {
2436        self.pending_below_time_in_force = Some(tif);
2437        self
2438    }
2439
2440    pub fn pending_below_strategy_id(mut self, id: u64) -> Self {
2441        self.pending_below_strategy_id = Some(id);
2442        self
2443    }
2444
2445    pub fn pending_below_strategy_type(mut self, strategy_type: i32) -> Self {
2446        self.pending_below_strategy_type = Some(strategy_type);
2447        self
2448    }
2449
2450    pub fn pending_below_peg_price_type(mut self, peg_price_type: &str) -> Self {
2451        self.pending_below_peg_price_type = Some(peg_price_type.to_string());
2452        self
2453    }
2454
2455    pub fn pending_below_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
2456        self.pending_below_peg_offset_type = Some(peg_offset_type.to_string());
2457        self
2458    }
2459
2460    pub fn pending_below_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
2461        self.pending_below_peg_offset_value = Some(peg_offset_value);
2462        self
2463    }
2464
2465    pub fn build(self) -> NewOtocoOrder {
2466        NewOtocoOrder {
2467            symbol: self.symbol,
2468            working_type: self.working_type,
2469            working_side: self.working_side,
2470            working_price: self.working_price,
2471            working_quantity: self.working_quantity,
2472            pending_side: self.pending_side,
2473            pending_quantity: Some(self.pending_quantity),
2474            pending_above_type: self.pending_above_type,
2475            list_client_order_id: self.list_client_order_id,
2476            response_type: self.response_type,
2477            self_trade_prevention_mode: self.self_trade_prevention_mode,
2478            working_client_order_id: self.working_client_order_id,
2479            working_iceberg_qty: self.working_iceberg_qty,
2480            working_time_in_force: self.working_time_in_force,
2481            working_strategy_id: self.working_strategy_id,
2482            working_strategy_type: self.working_strategy_type,
2483            working_peg_price_type: self.working_peg_price_type,
2484            working_peg_offset_type: self.working_peg_offset_type,
2485            working_peg_offset_value: self.working_peg_offset_value,
2486            pending_above_client_order_id: self.pending_above_client_order_id,
2487            pending_above_price: self.pending_above_price,
2488            pending_above_stop_price: self.pending_above_stop_price,
2489            pending_above_trailing_delta: self.pending_above_trailing_delta,
2490            pending_above_iceberg_qty: self.pending_above_iceberg_qty,
2491            pending_above_time_in_force: self.pending_above_time_in_force,
2492            pending_above_strategy_id: self.pending_above_strategy_id,
2493            pending_above_strategy_type: self.pending_above_strategy_type,
2494            pending_above_peg_price_type: self.pending_above_peg_price_type,
2495            pending_above_peg_offset_type: self.pending_above_peg_offset_type,
2496            pending_above_peg_offset_value: self.pending_above_peg_offset_value,
2497            pending_below_type: self.pending_below_type,
2498            pending_below_client_order_id: self.pending_below_client_order_id,
2499            pending_below_price: self.pending_below_price,
2500            pending_below_stop_price: self.pending_below_stop_price,
2501            pending_below_trailing_delta: self.pending_below_trailing_delta,
2502            pending_below_iceberg_qty: self.pending_below_iceberg_qty,
2503            pending_below_time_in_force: self.pending_below_time_in_force,
2504            pending_below_strategy_id: self.pending_below_strategy_id,
2505            pending_below_strategy_type: self.pending_below_strategy_type,
2506            pending_below_peg_price_type: self.pending_below_peg_price_type,
2507            pending_below_peg_offset_type: self.pending_below_peg_offset_type,
2508            pending_below_peg_offset_value: self.pending_below_peg_offset_value,
2509        }
2510    }
2511}
2512
2513/// New OTOCO order list parameters.
2514#[derive(Debug, Clone)]
2515pub struct NewOtocoOrder {
2516    symbol: String,
2517    working_type: OrderType,
2518    working_side: OrderSide,
2519    working_price: String,
2520    working_quantity: String,
2521    pending_side: OrderSide,
2522    pending_quantity: Option<String>,
2523    pending_above_type: OrderType,
2524    list_client_order_id: Option<String>,
2525    response_type: Option<OrderResponseType>,
2526    self_trade_prevention_mode: Option<String>,
2527    working_client_order_id: Option<String>,
2528    working_iceberg_qty: Option<String>,
2529    working_time_in_force: Option<TimeInForce>,
2530    working_strategy_id: Option<u64>,
2531    working_strategy_type: Option<i32>,
2532    working_peg_price_type: Option<String>,
2533    working_peg_offset_type: Option<String>,
2534    working_peg_offset_value: Option<i32>,
2535    pending_above_client_order_id: Option<String>,
2536    pending_above_price: Option<String>,
2537    pending_above_stop_price: Option<String>,
2538    pending_above_trailing_delta: Option<u64>,
2539    pending_above_iceberg_qty: Option<String>,
2540    pending_above_time_in_force: Option<TimeInForce>,
2541    pending_above_strategy_id: Option<u64>,
2542    pending_above_strategy_type: Option<i32>,
2543    pending_above_peg_price_type: Option<String>,
2544    pending_above_peg_offset_type: Option<String>,
2545    pending_above_peg_offset_value: Option<i32>,
2546    pending_below_type: Option<OrderType>,
2547    pending_below_client_order_id: Option<String>,
2548    pending_below_price: Option<String>,
2549    pending_below_stop_price: Option<String>,
2550    pending_below_trailing_delta: Option<u64>,
2551    pending_below_iceberg_qty: Option<String>,
2552    pending_below_time_in_force: Option<TimeInForce>,
2553    pending_below_strategy_id: Option<u64>,
2554    pending_below_strategy_type: Option<i32>,
2555    pending_below_peg_price_type: Option<String>,
2556    pending_below_peg_offset_type: Option<String>,
2557    pending_below_peg_offset_value: Option<i32>,
2558}
2559
2560impl NewOtocoOrder {
2561    fn to_params(&self) -> Vec<(String, String)> {
2562        let mut params = vec![
2563            ("symbol".to_string(), self.symbol.clone()),
2564            (
2565                "workingType".to_string(),
2566                format!("{:?}", self.working_type).to_uppercase(),
2567            ),
2568            (
2569                "workingSide".to_string(),
2570                format!("{:?}", self.working_side).to_uppercase(),
2571            ),
2572            ("workingPrice".to_string(), self.working_price.clone()),
2573            ("workingQuantity".to_string(), self.working_quantity.clone()),
2574            (
2575                "pendingSide".to_string(),
2576                format!("{:?}", self.pending_side).to_uppercase(),
2577            ),
2578            (
2579                "pendingAboveType".to_string(),
2580                format!("{:?}", self.pending_above_type).to_uppercase(),
2581            ),
2582        ];
2583
2584        if let Some(ref qty) = self.pending_quantity {
2585            params.push(("pendingQuantity".to_string(), qty.clone()));
2586        }
2587        if let Some(ref id) = self.list_client_order_id {
2588            params.push(("listClientOrderId".to_string(), id.clone()));
2589        }
2590        if let Some(ref resp) = self.response_type {
2591            params.push((
2592                "newOrderRespType".to_string(),
2593                format!("{:?}", resp).to_uppercase(),
2594            ));
2595        }
2596        if let Some(ref mode) = self.self_trade_prevention_mode {
2597            params.push(("selfTradePreventionMode".to_string(), mode.clone()));
2598        }
2599        if let Some(ref id) = self.working_client_order_id {
2600            params.push(("workingClientOrderId".to_string(), id.clone()));
2601        }
2602        if let Some(ref qty) = self.working_iceberg_qty {
2603            params.push(("workingIcebergQty".to_string(), qty.clone()));
2604        }
2605        if let Some(ref tif) = self.working_time_in_force {
2606            params.push(("workingTimeInForce".to_string(), format!("{:?}", tif)));
2607        }
2608        if let Some(id) = self.working_strategy_id {
2609            params.push(("workingStrategyId".to_string(), id.to_string()));
2610        }
2611        if let Some(id) = self.working_strategy_type {
2612            params.push(("workingStrategyType".to_string(), id.to_string()));
2613        }
2614        if let Some(ref peg) = self.working_peg_price_type {
2615            params.push(("workingPegPriceType".to_string(), peg.clone()));
2616        }
2617        if let Some(ref peg) = self.working_peg_offset_type {
2618            params.push(("workingPegOffsetType".to_string(), peg.clone()));
2619        }
2620        if let Some(value) = self.working_peg_offset_value {
2621            params.push(("workingPegOffsetValue".to_string(), value.to_string()));
2622        }
2623        if let Some(ref id) = self.pending_above_client_order_id {
2624            params.push(("pendingAboveClientOrderId".to_string(), id.clone()));
2625        }
2626        if let Some(ref price) = self.pending_above_price {
2627            params.push(("pendingAbovePrice".to_string(), price.clone()));
2628        }
2629        if let Some(ref price) = self.pending_above_stop_price {
2630            params.push(("pendingAboveStopPrice".to_string(), price.clone()));
2631        }
2632        if let Some(delta) = self.pending_above_trailing_delta {
2633            params.push(("pendingAboveTrailingDelta".to_string(), delta.to_string()));
2634        }
2635        if let Some(ref qty) = self.pending_above_iceberg_qty {
2636            params.push(("pendingAboveIcebergQty".to_string(), qty.clone()));
2637        }
2638        if let Some(ref tif) = self.pending_above_time_in_force {
2639            params.push(("pendingAboveTimeInForce".to_string(), format!("{:?}", tif)));
2640        }
2641        if let Some(id) = self.pending_above_strategy_id {
2642            params.push(("pendingAboveStrategyId".to_string(), id.to_string()));
2643        }
2644        if let Some(id) = self.pending_above_strategy_type {
2645            params.push(("pendingAboveStrategyType".to_string(), id.to_string()));
2646        }
2647        if let Some(ref peg) = self.pending_above_peg_price_type {
2648            params.push(("pendingAbovePegPriceType".to_string(), peg.clone()));
2649        }
2650        if let Some(ref peg) = self.pending_above_peg_offset_type {
2651            params.push(("pendingAbovePegOffsetType".to_string(), peg.clone()));
2652        }
2653        if let Some(value) = self.pending_above_peg_offset_value {
2654            params.push(("pendingAbovePegOffsetValue".to_string(), value.to_string()));
2655        }
2656        if let Some(order_type) = self.pending_below_type {
2657            params.push((
2658                "pendingBelowType".to_string(),
2659                format!("{:?}", order_type).to_uppercase(),
2660            ));
2661        }
2662        if let Some(ref id) = self.pending_below_client_order_id {
2663            params.push(("pendingBelowClientOrderId".to_string(), id.clone()));
2664        }
2665        if let Some(ref price) = self.pending_below_price {
2666            params.push(("pendingBelowPrice".to_string(), price.clone()));
2667        }
2668        if let Some(ref price) = self.pending_below_stop_price {
2669            params.push(("pendingBelowStopPrice".to_string(), price.clone()));
2670        }
2671        if let Some(delta) = self.pending_below_trailing_delta {
2672            params.push(("pendingBelowTrailingDelta".to_string(), delta.to_string()));
2673        }
2674        if let Some(ref qty) = self.pending_below_iceberg_qty {
2675            params.push(("pendingBelowIcebergQty".to_string(), qty.clone()));
2676        }
2677        if let Some(ref tif) = self.pending_below_time_in_force {
2678            params.push(("pendingBelowTimeInForce".to_string(), format!("{:?}", tif)));
2679        }
2680        if let Some(id) = self.pending_below_strategy_id {
2681            params.push(("pendingBelowStrategyId".to_string(), id.to_string()));
2682        }
2683        if let Some(id) = self.pending_below_strategy_type {
2684            params.push(("pendingBelowStrategyType".to_string(), id.to_string()));
2685        }
2686        if let Some(ref peg) = self.pending_below_peg_price_type {
2687            params.push(("pendingBelowPegPriceType".to_string(), peg.clone()));
2688        }
2689        if let Some(ref peg) = self.pending_below_peg_offset_type {
2690            params.push(("pendingBelowPegOffsetType".to_string(), peg.clone()));
2691        }
2692        if let Some(value) = self.pending_below_peg_offset_value {
2693            params.push(("pendingBelowPegOffsetValue".to_string(), value.to_string()));
2694        }
2695
2696        params
2697    }
2698}
2699
2700/// Builder for creating OPOCO order lists.
2701#[derive(Debug, Clone)]
2702pub struct OpocoOrderBuilder {
2703    inner: NewOtocoOrder,
2704}
2705
2706impl OpocoOrderBuilder {
2707    /// Create a new OPOCO order list builder.
2708    pub fn new(
2709        symbol: &str,
2710        working_type: OrderType,
2711        working_side: OrderSide,
2712        working_price: &str,
2713        working_quantity: &str,
2714        pending_side: OrderSide,
2715        pending_above_type: OrderType,
2716    ) -> Self {
2717        Self {
2718            inner: NewOtocoOrder {
2719                symbol: symbol.to_string(),
2720                working_type,
2721                working_side,
2722                working_price: working_price.to_string(),
2723                working_quantity: working_quantity.to_string(),
2724                pending_side,
2725                pending_quantity: None,
2726                pending_above_type,
2727                list_client_order_id: None,
2728                response_type: None,
2729                self_trade_prevention_mode: None,
2730                working_client_order_id: None,
2731                working_iceberg_qty: None,
2732                working_time_in_force: None,
2733                working_strategy_id: None,
2734                working_strategy_type: None,
2735                working_peg_price_type: None,
2736                working_peg_offset_type: None,
2737                working_peg_offset_value: None,
2738                pending_above_client_order_id: None,
2739                pending_above_price: None,
2740                pending_above_stop_price: None,
2741                pending_above_trailing_delta: None,
2742                pending_above_iceberg_qty: None,
2743                pending_above_time_in_force: None,
2744                pending_above_strategy_id: None,
2745                pending_above_strategy_type: None,
2746                pending_above_peg_price_type: None,
2747                pending_above_peg_offset_type: None,
2748                pending_above_peg_offset_value: None,
2749                pending_below_type: None,
2750                pending_below_client_order_id: None,
2751                pending_below_price: None,
2752                pending_below_stop_price: None,
2753                pending_below_trailing_delta: None,
2754                pending_below_iceberg_qty: None,
2755                pending_below_time_in_force: None,
2756                pending_below_strategy_id: None,
2757                pending_below_strategy_type: None,
2758                pending_below_peg_price_type: None,
2759                pending_below_peg_offset_type: None,
2760                pending_below_peg_offset_value: None,
2761            },
2762        }
2763    }
2764
2765    pub fn list_client_order_id(mut self, id: &str) -> Self {
2766        self.inner.list_client_order_id = Some(id.to_string());
2767        self
2768    }
2769
2770    pub fn response_type(mut self, resp_type: OrderResponseType) -> Self {
2771        self.inner.response_type = Some(resp_type);
2772        self
2773    }
2774
2775    pub fn self_trade_prevention_mode(mut self, mode: &str) -> Self {
2776        self.inner.self_trade_prevention_mode = Some(mode.to_string());
2777        self
2778    }
2779
2780    pub fn working_client_order_id(mut self, id: &str) -> Self {
2781        self.inner.working_client_order_id = Some(id.to_string());
2782        self
2783    }
2784
2785    pub fn working_iceberg_qty(mut self, qty: &str) -> Self {
2786        self.inner.working_iceberg_qty = Some(qty.to_string());
2787        self
2788    }
2789
2790    pub fn working_time_in_force(mut self, tif: TimeInForce) -> Self {
2791        self.inner.working_time_in_force = Some(tif);
2792        self
2793    }
2794
2795    pub fn working_strategy_id(mut self, id: u64) -> Self {
2796        self.inner.working_strategy_id = Some(id);
2797        self
2798    }
2799
2800    pub fn working_strategy_type(mut self, strategy_type: i32) -> Self {
2801        self.inner.working_strategy_type = Some(strategy_type);
2802        self
2803    }
2804
2805    pub fn working_peg_price_type(mut self, peg_price_type: &str) -> Self {
2806        self.inner.working_peg_price_type = Some(peg_price_type.to_string());
2807        self
2808    }
2809
2810    pub fn working_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
2811        self.inner.working_peg_offset_type = Some(peg_offset_type.to_string());
2812        self
2813    }
2814
2815    pub fn working_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
2816        self.inner.working_peg_offset_value = Some(peg_offset_value);
2817        self
2818    }
2819
2820    pub fn pending_quantity(mut self, qty: &str) -> Self {
2821        self.inner.pending_quantity = Some(qty.to_string());
2822        self
2823    }
2824
2825    pub fn pending_above_client_order_id(mut self, id: &str) -> Self {
2826        self.inner.pending_above_client_order_id = Some(id.to_string());
2827        self
2828    }
2829
2830    pub fn pending_above_price(mut self, price: &str) -> Self {
2831        self.inner.pending_above_price = Some(price.to_string());
2832        self
2833    }
2834
2835    pub fn pending_above_stop_price(mut self, price: &str) -> Self {
2836        self.inner.pending_above_stop_price = Some(price.to_string());
2837        self
2838    }
2839
2840    pub fn pending_above_trailing_delta(mut self, delta: u64) -> Self {
2841        self.inner.pending_above_trailing_delta = Some(delta);
2842        self
2843    }
2844
2845    pub fn pending_above_iceberg_qty(mut self, qty: &str) -> Self {
2846        self.inner.pending_above_iceberg_qty = Some(qty.to_string());
2847        self
2848    }
2849
2850    pub fn pending_above_time_in_force(mut self, tif: TimeInForce) -> Self {
2851        self.inner.pending_above_time_in_force = Some(tif);
2852        self
2853    }
2854
2855    pub fn pending_above_strategy_id(mut self, id: u64) -> Self {
2856        self.inner.pending_above_strategy_id = Some(id);
2857        self
2858    }
2859
2860    pub fn pending_above_strategy_type(mut self, strategy_type: i32) -> Self {
2861        self.inner.pending_above_strategy_type = Some(strategy_type);
2862        self
2863    }
2864
2865    pub fn pending_above_peg_price_type(mut self, peg_price_type: &str) -> Self {
2866        self.inner.pending_above_peg_price_type = Some(peg_price_type.to_string());
2867        self
2868    }
2869
2870    pub fn pending_above_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
2871        self.inner.pending_above_peg_offset_type = Some(peg_offset_type.to_string());
2872        self
2873    }
2874
2875    pub fn pending_above_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
2876        self.inner.pending_above_peg_offset_value = Some(peg_offset_value);
2877        self
2878    }
2879
2880    pub fn pending_below_type(mut self, order_type: OrderType) -> Self {
2881        self.inner.pending_below_type = Some(order_type);
2882        self
2883    }
2884
2885    pub fn pending_below_client_order_id(mut self, id: &str) -> Self {
2886        self.inner.pending_below_client_order_id = Some(id.to_string());
2887        self
2888    }
2889
2890    pub fn pending_below_price(mut self, price: &str) -> Self {
2891        self.inner.pending_below_price = Some(price.to_string());
2892        self
2893    }
2894
2895    pub fn pending_below_stop_price(mut self, price: &str) -> Self {
2896        self.inner.pending_below_stop_price = Some(price.to_string());
2897        self
2898    }
2899
2900    pub fn pending_below_trailing_delta(mut self, delta: u64) -> Self {
2901        self.inner.pending_below_trailing_delta = Some(delta);
2902        self
2903    }
2904
2905    pub fn pending_below_iceberg_qty(mut self, qty: &str) -> Self {
2906        self.inner.pending_below_iceberg_qty = Some(qty.to_string());
2907        self
2908    }
2909
2910    pub fn pending_below_time_in_force(mut self, tif: TimeInForce) -> Self {
2911        self.inner.pending_below_time_in_force = Some(tif);
2912        self
2913    }
2914
2915    pub fn pending_below_strategy_id(mut self, id: u64) -> Self {
2916        self.inner.pending_below_strategy_id = Some(id);
2917        self
2918    }
2919
2920    pub fn pending_below_strategy_type(mut self, strategy_type: i32) -> Self {
2921        self.inner.pending_below_strategy_type = Some(strategy_type);
2922        self
2923    }
2924
2925    pub fn pending_below_peg_price_type(mut self, peg_price_type: &str) -> Self {
2926        self.inner.pending_below_peg_price_type = Some(peg_price_type.to_string());
2927        self
2928    }
2929
2930    pub fn pending_below_peg_offset_type(mut self, peg_offset_type: &str) -> Self {
2931        self.inner.pending_below_peg_offset_type = Some(peg_offset_type.to_string());
2932        self
2933    }
2934
2935    pub fn pending_below_peg_offset_value(mut self, peg_offset_value: i32) -> Self {
2936        self.inner.pending_below_peg_offset_value = Some(peg_offset_value);
2937        self
2938    }
2939
2940    pub fn build(self) -> NewOpocoOrder {
2941        NewOpocoOrder { inner: self.inner }
2942    }
2943}
2944
2945/// New OPOCO order list parameters.
2946#[derive(Debug, Clone)]
2947pub struct NewOpocoOrder {
2948    inner: NewOtocoOrder,
2949}
2950
2951impl NewOpocoOrder {
2952    fn to_params(&self) -> Vec<(String, String)> {
2953        self.inner.to_params()
2954    }
2955}
2956
2957#[cfg(test)]
2958mod tests {
2959    use super::*;
2960
2961    #[test]
2962    fn test_order_builder_limit() {
2963        let order = OrderBuilder::new("BTCUSDT", OrderSide::Buy, OrderType::Limit)
2964            .quantity("0.001")
2965            .price("50000.00")
2966            .time_in_force(TimeInForce::GTC)
2967            .build();
2968
2969        assert_eq!(order.symbol, "BTCUSDT");
2970        assert_eq!(order.side, OrderSide::Buy);
2971        assert_eq!(order.order_type, OrderType::Limit);
2972        assert_eq!(order.quantity, Some("0.001".to_string()));
2973        assert_eq!(order.price, Some("50000.00".to_string()));
2974        assert_eq!(order.time_in_force, Some(TimeInForce::GTC));
2975    }
2976
2977    #[test]
2978    fn test_order_builder_market() {
2979        let order = OrderBuilder::new("BTCUSDT", OrderSide::Sell, OrderType::Market)
2980            .quantity("1.0")
2981            .build();
2982
2983        assert_eq!(order.symbol, "BTCUSDT");
2984        assert_eq!(order.side, OrderSide::Sell);
2985        assert_eq!(order.order_type, OrderType::Market);
2986        assert_eq!(order.quantity, Some("1.0".to_string()));
2987        assert!(order.price.is_none());
2988    }
2989
2990    #[test]
2991    fn test_order_to_params() {
2992        let order = OrderBuilder::new("BTCUSDT", OrderSide::Buy, OrderType::Limit)
2993            .quantity("0.001")
2994            .price("50000.00")
2995            .time_in_force(TimeInForce::GTC)
2996            .build();
2997
2998        let params = order.to_params();
2999
3000        assert!(params.iter().any(|(k, v)| k == "symbol" && v == "BTCUSDT"));
3001        assert!(params.iter().any(|(k, v)| k == "side" && v == "BUY"));
3002        assert!(params.iter().any(|(k, v)| k == "type" && v == "LIMIT"));
3003        assert!(params.iter().any(|(k, v)| k == "quantity" && v == "0.001"));
3004        assert!(params.iter().any(|(k, v)| k == "price" && v == "50000.00"));
3005    }
3006
3007    #[test]
3008    fn test_oco_order_builder() {
3009        let order = OcoOrderBuilder::new("BTCUSDT", OrderSide::Sell, "1.0", "55000.00", "48000.00")
3010            .stop_limit_price("47900.00")
3011            .stop_limit_time_in_force(TimeInForce::GTC)
3012            .build();
3013
3014        assert_eq!(order.symbol, "BTCUSDT");
3015        assert_eq!(order.side, OrderSide::Sell);
3016        assert_eq!(order.quantity, "1.0");
3017        assert_eq!(order.price, "55000.00");
3018        assert_eq!(order.stop_price, "48000.00");
3019        assert_eq!(order.stop_limit_price, Some("47900.00".to_string()));
3020    }
3021}