binance/
account.rs

1use error_chain::bail;
2
3use crate::util::{build_signed_request, is_start_time_valid};
4use crate::model::{
5    AccountInformation, Balance, Empty, Order, OrderCanceled, TradeHistory, Transaction,
6};
7use crate::client::Client;
8use crate::errors::Result;
9use std::collections::BTreeMap;
10use std::fmt::Display;
11use crate::api::API;
12use crate::api::Spot;
13
14#[derive(Clone)]
15pub struct Account {
16    pub client: Client,
17    pub recv_window: u64,
18}
19
20struct OrderRequest {
21    pub symbol: String,
22    pub qty: f64,
23    pub price: f64,
24    pub stop_price: Option<f64>,
25    pub order_side: OrderSide,
26    pub order_type: OrderType,
27    pub time_in_force: TimeInForce,
28    pub new_client_order_id: Option<String>,
29}
30
31struct OrderQuoteQuantityRequest {
32    pub symbol: String,
33    pub quote_order_qty: f64,
34    pub price: f64,
35    pub order_side: OrderSide,
36    pub order_type: OrderType,
37    pub time_in_force: TimeInForce,
38    pub new_client_order_id: Option<String>,
39}
40
41pub enum OrderType {
42    Limit,
43    Market,
44    StopLossLimit,
45}
46
47impl OrderType {
48    pub fn from_int(value: i32) -> Option<Self> {
49        match value {
50            1 => Some(OrderType::Limit),
51            2 => Some(OrderType::Market),
52            3 => Some(OrderType::StopLossLimit),
53            _ => None,
54        }
55    }
56}
57
58impl Display for OrderType {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        match self {
61            Self::Limit => write!(f, "LIMIT"),
62            Self::Market => write!(f, "MARKET"),
63            Self::StopLossLimit => write!(f, "STOP_LOSS_LIMIT"),
64        }
65    }
66}
67
68pub enum OrderSide {
69    Buy,
70    Sell,
71}
72
73impl OrderSide {
74    pub fn from_int(value: i32) -> Option<Self> {
75        match value {
76            1 => Some(OrderSide::Buy),
77            2 => Some(OrderSide::Sell),
78            _ => None,
79        }
80    }
81}
82
83impl Display for OrderSide {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        match self {
86            Self::Buy => write!(f, "BUY"),
87            Self::Sell => write!(f, "SELL"),
88        }
89    }
90}
91
92#[allow(clippy::all)]
93pub enum TimeInForce {
94    GTC,
95    IOC,
96    FOK,
97}
98
99impl TimeInForce {
100    pub fn from_int(value: i32) -> Option<Self> {
101        match value {
102            1 => Some(TimeInForce::GTC),
103            2 => Some(TimeInForce::IOC),
104            3 => Some(TimeInForce::FOK),
105            _ => None,
106        }
107    }
108}
109
110impl Display for TimeInForce {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        match self {
113            Self::GTC => write!(f, "GTC"),
114            Self::IOC => write!(f, "IOC"),
115            Self::FOK => write!(f, "FOK"),
116        }
117    }
118}
119
120impl Account {
121    // Account Information
122    pub fn get_account(&self) -> Result<AccountInformation> {
123        let request = build_signed_request(BTreeMap::new(), self.recv_window)?;
124        self.client
125            .get_signed(API::Spot(Spot::Account), Some(request))
126    }
127
128    // Balance for a single Asset
129    pub fn get_balance<S>(&self, asset: S) -> Result<Balance>
130    where
131        S: Into<String>,
132    {
133        match self.get_account() {
134            Ok(account) => {
135                let cmp_asset = asset.into();
136                for balance in account.balances {
137                    if balance.asset == cmp_asset {
138                        return Ok(balance);
139                    }
140                }
141                bail!("Asset not found");
142            }
143            Err(e) => Err(e),
144        }
145    }
146
147    // Current open orders for ONE symbol
148    pub fn get_open_orders<S>(&self, symbol: S) -> Result<Vec<Order>>
149    where
150        S: Into<String>,
151    {
152        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
153        parameters.insert("symbol".into(), symbol.into());
154
155        let request = build_signed_request(parameters, self.recv_window)?;
156        self.client
157            .get_signed(API::Spot(Spot::OpenOrders), Some(request))
158    }
159
160    // All current open orders
161    pub fn get_all_open_orders(&self) -> Result<Vec<Order>> {
162        let parameters: BTreeMap<String, String> = BTreeMap::new();
163
164        let request = build_signed_request(parameters, self.recv_window)?;
165        self.client
166            .get_signed(API::Spot(Spot::OpenOrders), Some(request))
167    }
168
169    // Cancel all open orders for a single symbol
170    pub fn cancel_all_open_orders<S>(&self, symbol: S) -> Result<Vec<OrderCanceled>>
171    where
172        S: Into<String>,
173    {
174        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
175        parameters.insert("symbol".into(), symbol.into());
176        let request = build_signed_request(parameters, self.recv_window)?;
177        self.client
178            .delete_signed(API::Spot(Spot::OpenOrders), Some(request))
179    }
180
181    // Check an order's status
182    pub fn order_status<S>(&self, symbol: S, order_id: u64) -> Result<Order>
183    where
184        S: Into<String>,
185    {
186        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
187        parameters.insert("symbol".into(), symbol.into());
188        parameters.insert("orderId".into(), order_id.to_string());
189
190        let request = build_signed_request(parameters, self.recv_window)?;
191        self.client
192            .get_signed(API::Spot(Spot::Order), Some(request))
193    }
194
195    /// Place a test status order
196    ///
197    /// This order is sandboxed: it is validated, but not sent to the matching engine.
198    pub fn test_order_status<S>(&self, symbol: S, order_id: u64) -> Result<()>
199    where
200        S: Into<String>,
201    {
202        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
203        parameters.insert("symbol".into(), symbol.into());
204        parameters.insert("orderId".into(), order_id.to_string());
205
206        let request = build_signed_request(parameters, self.recv_window)?;
207        self.client
208            .get_signed::<Empty>(API::Spot(Spot::OrderTest), Some(request))
209            .map(|_| ())
210    }
211
212    // Place a LIMIT order - BUY
213    pub fn limit_buy<S, F>(&self, symbol: S, qty: F, price: f64) -> Result<Transaction>
214    where
215        S: Into<String>,
216        F: Into<f64>,
217    {
218        let buy = OrderRequest {
219            symbol: symbol.into(),
220            qty: qty.into(),
221            price,
222            stop_price: None,
223            order_side: OrderSide::Buy,
224            order_type: OrderType::Limit,
225            time_in_force: TimeInForce::GTC,
226            new_client_order_id: None,
227        };
228        let order = self.build_order(buy);
229        let request = build_signed_request(order, self.recv_window)?;
230        self.client.post_signed(API::Spot(Spot::Order), request)
231    }
232
233    /// Place a test limit order - BUY
234    ///
235    /// This order is sandboxed: it is validated, but not sent to the matching engine.
236    pub fn test_limit_buy<S, F>(&self, symbol: S, qty: F, price: f64) -> Result<()>
237    where
238        S: Into<String>,
239        F: Into<f64>,
240    {
241        let buy = OrderRequest {
242            symbol: symbol.into(),
243            qty: qty.into(),
244            price,
245            stop_price: None,
246            order_side: OrderSide::Buy,
247            order_type: OrderType::Limit,
248            time_in_force: TimeInForce::GTC,
249            new_client_order_id: None,
250        };
251        let order = self.build_order(buy);
252        let request = build_signed_request(order, self.recv_window)?;
253        self.client
254            .post_signed::<Empty>(API::Spot(Spot::OrderTest), request)
255            .map(|_| ())
256    }
257
258    // Place a LIMIT order - SELL
259    pub fn limit_sell<S, F>(&self, symbol: S, qty: F, price: f64) -> Result<Transaction>
260    where
261        S: Into<String>,
262        F: Into<f64>,
263    {
264        let sell = OrderRequest {
265            symbol: symbol.into(),
266            qty: qty.into(),
267            price,
268            stop_price: None,
269            order_side: OrderSide::Sell,
270            order_type: OrderType::Limit,
271            time_in_force: TimeInForce::GTC,
272            new_client_order_id: None,
273        };
274        let order = self.build_order(sell);
275        let request = build_signed_request(order, self.recv_window)?;
276        self.client.post_signed(API::Spot(Spot::Order), request)
277    }
278
279    /// Place a test LIMIT order - SELL
280    ///
281    /// This order is sandboxed: it is validated, but not sent to the matching engine.
282    pub fn test_limit_sell<S, F>(&self, symbol: S, qty: F, price: f64) -> Result<()>
283    where
284        S: Into<String>,
285        F: Into<f64>,
286    {
287        let sell = OrderRequest {
288            symbol: symbol.into(),
289            qty: qty.into(),
290            price,
291            stop_price: None,
292            order_side: OrderSide::Sell,
293            order_type: OrderType::Limit,
294            time_in_force: TimeInForce::GTC,
295            new_client_order_id: None,
296        };
297        let order = self.build_order(sell);
298        let request = build_signed_request(order, self.recv_window)?;
299        self.client
300            .post_signed::<Empty>(API::Spot(Spot::OrderTest), request)
301            .map(|_| ())
302    }
303
304    // Place a MARKET order - BUY
305    pub fn market_buy<S, F>(&self, symbol: S, qty: F) -> Result<Transaction>
306    where
307        S: Into<String>,
308        F: Into<f64>,
309    {
310        let buy = OrderRequest {
311            symbol: symbol.into(),
312            qty: qty.into(),
313            price: 0.0,
314            stop_price: None,
315            order_side: OrderSide::Buy,
316            order_type: OrderType::Market,
317            time_in_force: TimeInForce::GTC,
318            new_client_order_id: None,
319        };
320        let order = self.build_order(buy);
321        let request = build_signed_request(order, self.recv_window)?;
322        self.client.post_signed(API::Spot(Spot::Order), request)
323    }
324
325    /// Place a test MARKET order - BUY
326    ///
327    /// This order is sandboxed: it is validated, but not sent to the matching engine.
328    pub fn test_market_buy<S, F>(&self, symbol: S, qty: F) -> Result<()>
329    where
330        S: Into<String>,
331        F: Into<f64>,
332    {
333        let buy = OrderRequest {
334            symbol: symbol.into(),
335            qty: qty.into(),
336            price: 0.0,
337            stop_price: None,
338            order_side: OrderSide::Buy,
339            order_type: OrderType::Market,
340            time_in_force: TimeInForce::GTC,
341            new_client_order_id: None,
342        };
343        let order = self.build_order(buy);
344        let request = build_signed_request(order, self.recv_window)?;
345        self.client
346            .post_signed::<Empty>(API::Spot(Spot::OrderTest), request)
347            .map(|_| ())
348    }
349
350    // Place a MARKET order with quote quantity - BUY
351    pub fn market_buy_using_quote_quantity<S, F>(
352        &self, symbol: S, quote_order_qty: F,
353    ) -> Result<Transaction>
354    where
355        S: Into<String>,
356        F: Into<f64>,
357    {
358        let buy = OrderQuoteQuantityRequest {
359            symbol: symbol.into(),
360            quote_order_qty: quote_order_qty.into(),
361            price: 0.0,
362            order_side: OrderSide::Buy,
363            order_type: OrderType::Market,
364            time_in_force: TimeInForce::GTC,
365            new_client_order_id: None,
366        };
367        let order = self.build_quote_quantity_order(buy);
368        let request = build_signed_request(order, self.recv_window)?;
369        self.client.post_signed(API::Spot(Spot::Order), request)
370    }
371
372    /// Place a test MARKET order with quote quantity - BUY
373    ///
374    /// This order is sandboxed: it is validated, but not sent to the matching engine.
375    pub fn test_market_buy_using_quote_quantity<S, F>(
376        &self, symbol: S, quote_order_qty: F,
377    ) -> Result<()>
378    where
379        S: Into<String>,
380        F: Into<f64>,
381    {
382        let buy = OrderQuoteQuantityRequest {
383            symbol: symbol.into(),
384            quote_order_qty: quote_order_qty.into(),
385            price: 0.0,
386            order_side: OrderSide::Buy,
387            order_type: OrderType::Market,
388            time_in_force: TimeInForce::GTC,
389            new_client_order_id: None,
390        };
391        let order = self.build_quote_quantity_order(buy);
392        let request = build_signed_request(order, self.recv_window)?;
393        self.client
394            .post_signed::<Empty>(API::Spot(Spot::OrderTest), request)
395            .map(|_| ())
396    }
397
398    // Place a MARKET order - SELL
399    pub fn market_sell<S, F>(&self, symbol: S, qty: F) -> Result<Transaction>
400    where
401        S: Into<String>,
402        F: Into<f64>,
403    {
404        let sell = OrderRequest {
405            symbol: symbol.into(),
406            qty: qty.into(),
407            price: 0.0,
408            stop_price: None,
409            order_side: OrderSide::Sell,
410            order_type: OrderType::Market,
411            time_in_force: TimeInForce::GTC,
412            new_client_order_id: None,
413        };
414        let order = self.build_order(sell);
415        let request = build_signed_request(order, self.recv_window)?;
416        self.client.post_signed(API::Spot(Spot::Order), request)
417    }
418
419    /// Place a test MARKET order - SELL
420    ///
421    /// This order is sandboxed: it is validated, but not sent to the matching engine.
422    pub fn test_market_sell<S, F>(&self, symbol: S, qty: F) -> Result<()>
423    where
424        S: Into<String>,
425        F: Into<f64>,
426    {
427        let sell = OrderRequest {
428            symbol: symbol.into(),
429            qty: qty.into(),
430            price: 0.0,
431            stop_price: None,
432            order_side: OrderSide::Sell,
433            order_type: OrderType::Market,
434            time_in_force: TimeInForce::GTC,
435            new_client_order_id: None,
436        };
437        let order = self.build_order(sell);
438        let request = build_signed_request(order, self.recv_window)?;
439        self.client
440            .post_signed::<Empty>(API::Spot(Spot::OrderTest), request)
441            .map(|_| ())
442    }
443
444    // Place a MARKET order with quote quantity - SELL
445    pub fn market_sell_using_quote_quantity<S, F>(
446        &self, symbol: S, quote_order_qty: F,
447    ) -> Result<Transaction>
448    where
449        S: Into<String>,
450        F: Into<f64>,
451    {
452        let sell = OrderQuoteQuantityRequest {
453            symbol: symbol.into(),
454            quote_order_qty: quote_order_qty.into(),
455            price: 0.0,
456            order_side: OrderSide::Sell,
457            order_type: OrderType::Market,
458            time_in_force: TimeInForce::GTC,
459            new_client_order_id: None,
460        };
461        let order = self.build_quote_quantity_order(sell);
462        let request = build_signed_request(order, self.recv_window)?;
463        self.client.post_signed(API::Spot(Spot::Order), request)
464    }
465
466    /// Place a test MARKET order with quote quantity - SELL
467    ///
468    /// This order is sandboxed: it is validated, but not sent to the matching engine.
469    pub fn test_market_sell_using_quote_quantity<S, F>(
470        &self, symbol: S, quote_order_qty: F,
471    ) -> Result<()>
472    where
473        S: Into<String>,
474        F: Into<f64>,
475    {
476        let sell = OrderQuoteQuantityRequest {
477            symbol: symbol.into(),
478            quote_order_qty: quote_order_qty.into(),
479            price: 0.0,
480            order_side: OrderSide::Sell,
481            order_type: OrderType::Market,
482            time_in_force: TimeInForce::GTC,
483            new_client_order_id: None,
484        };
485        let order = self.build_quote_quantity_order(sell);
486        let request = build_signed_request(order, self.recv_window)?;
487        self.client
488            .post_signed::<Empty>(API::Spot(Spot::OrderTest), request)
489            .map(|_| ())
490    }
491
492    /// Create a stop limit buy order for the given symbol, price and stop price.
493    /// Returning a `Transaction` value with the same parameters sent on the order.
494    ///
495    ///```no_run
496    /// use binance::api::Binance;
497    /// use binance::account::*;
498    ///
499    /// fn main() {
500    ///     let api_key = Some("api_key".into());
501    ///     let secret_key = Some("secret_key".into());
502    ///     let account: Account = Binance::new(api_key, secret_key);
503    ///     let result = account.stop_limit_buy_order("LTCBTC", 1, 0.1, 0.09, TimeInForce::GTC);
504    /// }
505    /// ```
506    pub fn stop_limit_buy_order<S, F>(
507        &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce,
508    ) -> Result<Transaction>
509    where
510        S: Into<String>,
511        F: Into<f64>,
512    {
513        let sell = OrderRequest {
514            symbol: symbol.into(),
515            qty: qty.into(),
516            price,
517            stop_price: Some(stop_price),
518            order_side: OrderSide::Buy,
519            order_type: OrderType::StopLossLimit,
520            time_in_force,
521            new_client_order_id: None,
522        };
523        let order = self.build_order(sell);
524        let request = build_signed_request(order, self.recv_window)?;
525        self.client.post_signed(API::Spot(Spot::Order), request)
526    }
527
528    /// Create a stop limit buy test order for the given symbol, price and stop price.
529    /// Returning a `Transaction` value with the same parameters sent on the order.
530    ///
531    /// This order is sandboxed: it is validated, but not sent to the matching engine.
532    ///
533    ///```no_run
534    /// use binance::api::Binance;
535    /// use binance::account::*;
536    ///
537    /// fn main() {
538    ///     let api_key = Some("api_key".into());
539    ///     let secret_key = Some("secret_key".into());
540    ///     let account: Account = Binance::new(api_key, secret_key);
541    ///     let result = account.test_stop_limit_buy_order("LTCBTC", 1, 0.1, 0.09, TimeInForce::GTC);
542    /// }
543    /// ```
544    pub fn test_stop_limit_buy_order<S, F>(
545        &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce,
546    ) -> Result<()>
547    where
548        S: Into<String>,
549        F: Into<f64>,
550    {
551        let sell = OrderRequest {
552            symbol: symbol.into(),
553            qty: qty.into(),
554            price,
555            stop_price: Some(stop_price),
556            order_side: OrderSide::Buy,
557            order_type: OrderType::StopLossLimit,
558            time_in_force,
559            new_client_order_id: None,
560        };
561        let order = self.build_order(sell);
562        let request = build_signed_request(order, self.recv_window)?;
563        self.client
564            .post_signed::<Empty>(API::Spot(Spot::OrderTest), request)
565            .map(|_| ())
566    }
567
568    /// Create a stop limit sell order for the given symbol, price and stop price.
569    /// Returning a `Transaction` value with the same parameters sent on the order.
570    ///
571    ///```no_run
572    /// use binance::api::Binance;
573    /// use binance::account::*;
574    ///
575    /// fn main() {
576    ///     let api_key = Some("api_key".into());
577    ///     let secret_key = Some("secret_key".into());
578    ///     let account: Account = Binance::new(api_key, secret_key);
579    ///     let result = account.stop_limit_sell_order("LTCBTC", 1, 0.1, 0.09, TimeInForce::GTC);
580    /// }
581    /// ```
582    pub fn stop_limit_sell_order<S, F>(
583        &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce,
584    ) -> Result<Transaction>
585    where
586        S: Into<String>,
587        F: Into<f64>,
588    {
589        let sell = OrderRequest {
590            symbol: symbol.into(),
591            qty: qty.into(),
592            price,
593            stop_price: Some(stop_price),
594            order_side: OrderSide::Sell,
595            order_type: OrderType::StopLossLimit,
596            time_in_force,
597            new_client_order_id: None,
598        };
599        let order = self.build_order(sell);
600        let request = build_signed_request(order, self.recv_window)?;
601        self.client.post_signed(API::Spot(Spot::Order), request)
602    }
603
604    /// Create a stop limit sell order for the given symbol, price and stop price.
605    /// Returning a `Transaction` value with the same parameters sent on the order.
606    ///
607    /// This order is sandboxed: it is validated, but not sent to the matching engine.
608    ///
609    ///```no_run
610    /// use binance::api::Binance;
611    /// use binance::account::*;
612    ///
613    /// fn main() {
614    ///     let api_key = Some("api_key".into());
615    ///     let secret_key = Some("secret_key".into());
616    ///     let account: Account = Binance::new(api_key, secret_key);
617    ///     let result = account.test_stop_limit_sell_order("LTCBTC", 1, 0.1, 0.09, TimeInForce::GTC);
618    /// }
619    /// ```
620    pub fn test_stop_limit_sell_order<S, F>(
621        &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce,
622    ) -> Result<()>
623    where
624        S: Into<String>,
625        F: Into<f64>,
626    {
627        let sell = OrderRequest {
628            symbol: symbol.into(),
629            qty: qty.into(),
630            price,
631            stop_price: Some(stop_price),
632            order_side: OrderSide::Sell,
633            order_type: OrderType::StopLossLimit,
634            time_in_force,
635            new_client_order_id: None,
636        };
637        let order = self.build_order(sell);
638        let request = build_signed_request(order, self.recv_window)?;
639        self.client
640            .post_signed::<Empty>(API::Spot(Spot::OrderTest), request)
641            .map(|_| ())
642    }
643
644    /// Place a custom order
645    #[allow(clippy::too_many_arguments)]
646    pub fn custom_order<S, F>(
647        &self, symbol: S, qty: F, price: f64, stop_price: Option<f64>, order_side: OrderSide,
648        order_type: OrderType, time_in_force: TimeInForce, new_client_order_id: Option<String>,
649    ) -> Result<Transaction>
650    where
651        S: Into<String>,
652        F: Into<f64>,
653    {
654        let sell = OrderRequest {
655            symbol: symbol.into(),
656            qty: qty.into(),
657            price,
658            stop_price,
659            order_side,
660            order_type,
661            time_in_force,
662            new_client_order_id,
663        };
664        let order = self.build_order(sell);
665        let request = build_signed_request(order, self.recv_window)?;
666        self.client.post_signed(API::Spot(Spot::Order), request)
667    }
668
669    /// Place a test custom order
670    ///
671    /// This order is sandboxed: it is validated, but not sent to the matching engine.
672    #[allow(clippy::too_many_arguments)]
673    pub fn test_custom_order<S, F>(
674        &self, symbol: S, qty: F, price: f64, stop_price: Option<f64>, order_side: OrderSide,
675        order_type: OrderType, time_in_force: TimeInForce, new_client_order_id: Option<String>,
676    ) -> Result<()>
677    where
678        S: Into<String>,
679        F: Into<f64>,
680    {
681        let sell = OrderRequest {
682            symbol: symbol.into(),
683            qty: qty.into(),
684            price,
685            stop_price,
686            order_side,
687            order_type,
688            time_in_force,
689            new_client_order_id,
690        };
691        let order = self.build_order(sell);
692        let request = build_signed_request(order, self.recv_window)?;
693        self.client
694            .post_signed::<Empty>(API::Spot(Spot::OrderTest), request)
695            .map(|_| ())
696    }
697
698    // Check an order's status
699    pub fn cancel_order<S>(&self, symbol: S, order_id: u64) -> Result<OrderCanceled>
700    where
701        S: Into<String>,
702    {
703        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
704        parameters.insert("symbol".into(), symbol.into());
705        parameters.insert("orderId".into(), order_id.to_string());
706
707        let request = build_signed_request(parameters, self.recv_window)?;
708        self.client
709            .delete_signed(API::Spot(Spot::Order), Some(request))
710    }
711
712    pub fn cancel_order_with_client_id<S>(
713        &self, symbol: S, orig_client_order_id: String,
714    ) -> Result<OrderCanceled>
715    where
716        S: Into<String>,
717    {
718        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
719        parameters.insert("symbol".into(), symbol.into());
720        parameters.insert("origClientOrderId".into(), orig_client_order_id);
721
722        let request = build_signed_request(parameters, self.recv_window)?;
723        self.client
724            .delete_signed(API::Spot(Spot::Order), Some(request))
725    }
726
727    pub fn cancel_order_with_client_id_rs<S>() {}
728    /// Place a test cancel order
729    ///
730    /// This order is sandboxed: it is validated, but not sent to the matching engine.
731    pub fn test_cancel_order<S>(&self, symbol: S, order_id: u64) -> Result<()>
732    where
733        S: Into<String>,
734    {
735        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
736        parameters.insert("symbol".into(), symbol.into());
737        parameters.insert("orderId".into(), order_id.to_string());
738        let request = build_signed_request(parameters, self.recv_window)?;
739        self.client
740            .delete_signed::<Empty>(API::Spot(Spot::OrderTest), Some(request))
741            .map(|_| ())
742    }
743
744    // Trade history
745    pub fn trade_history<S>(&self, symbol: S) -> Result<Vec<TradeHistory>>
746    where
747        S: Into<String>,
748    {
749        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
750        parameters.insert("symbol".into(), symbol.into());
751
752        let request = build_signed_request(parameters, self.recv_window)?;
753        self.client
754            .get_signed(API::Spot(Spot::MyTrades), Some(request))
755    }
756
757    // Trade history starting from selected date
758    pub fn trade_history_from<S>(&self, symbol: S, start_time: u64) -> Result<Vec<TradeHistory>>
759    where
760        S: Into<String>,
761    {
762        if !is_start_time_valid(&start_time) {
763            return bail!("Start time should be less than the current time");
764        }
765
766        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
767        parameters.insert("symbol".into(), symbol.into());
768        parameters.insert("startTime".into(), start_time.to_string());
769        let request = build_signed_request(parameters, self.recv_window)?;
770        self.client
771            .get_signed(API::Spot(Spot::MyTrades), Some(request))
772    }
773
774    // Trade history starting from selected time to some time
775    pub fn trade_history_from_to<S>(
776        &self, symbol: S, start_time: u64, end_time: u64,
777    ) -> Result<Vec<TradeHistory>>
778    where
779        S: Into<String>,
780    {
781        if end_time <= start_time {
782            return bail!("End time should be greater than start time");
783        }
784        if !is_start_time_valid(&start_time) {
785            return bail!("Start time should be less than the current time");
786        }
787        self.get_trades(symbol, start_time, end_time)
788    }
789
790    fn get_trades<S>(&self, symbol: S, start_time: u64, end_time: u64) -> Result<Vec<TradeHistory>>
791    where
792        S: Into<String>,
793    {
794        let mut trades = match self.trade_history_from(symbol, start_time) {
795            Ok(trades) => trades,
796            Err(e) => return Err(e),
797        };
798        trades.retain(|trade| trade.time <= end_time);
799        Ok(trades)
800    }
801
802    fn build_order(&self, order: OrderRequest) -> BTreeMap<String, String> {
803        let mut order_parameters: BTreeMap<String, String> = BTreeMap::new();
804
805        order_parameters.insert("symbol".into(), order.symbol);
806        order_parameters.insert("side".into(), order.order_side.to_string());
807        order_parameters.insert("type".into(), order.order_type.to_string());
808        order_parameters.insert("quantity".into(), order.qty.to_string());
809
810        if let Some(stop_price) = order.stop_price {
811            order_parameters.insert("stopPrice".into(), stop_price.to_string());
812        }
813
814        if order.price != 0.0 {
815            order_parameters.insert("price".into(), order.price.to_string());
816            order_parameters.insert("timeInForce".into(), order.time_in_force.to_string());
817        }
818
819        if let Some(client_order_id) = order.new_client_order_id {
820            order_parameters.insert("newClientOrderId".into(), client_order_id);
821        }
822
823        order_parameters
824    }
825
826    fn build_quote_quantity_order(
827        &self, order: OrderQuoteQuantityRequest,
828    ) -> BTreeMap<String, String> {
829        let mut order_parameters: BTreeMap<String, String> = BTreeMap::new();
830
831        order_parameters.insert("symbol".into(), order.symbol);
832        order_parameters.insert("side".into(), order.order_side.to_string());
833        order_parameters.insert("type".into(), order.order_type.to_string());
834        order_parameters.insert("quoteOrderQty".into(), order.quote_order_qty.to_string());
835
836        if order.price != 0.0 {
837            order_parameters.insert("price".into(), order.price.to_string());
838            order_parameters.insert("timeInForce".into(), order.time_in_force.to_string());
839        }
840
841        if let Some(client_order_id) = order.new_client_order_id {
842            order_parameters.insert("newClientOrderId".into(), client_order_id);
843        }
844
845        order_parameters
846    }
847}