Skip to main content

binance/
account.rs

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