Skip to main content

binance/spot/http/
api.rs

1use rust_decimal::Decimal;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    Timestamp,
6    spot::{
7        AccountType, ExchangeFilter, KlineInterval, OrderResponseType, OrderSide, OrderStatus,
8        OrderType, RateLimitInterval, RateLimiter, STPMode, SymbolStatus, TimeInForce,
9        WorkingFloor,
10    },
11};
12
13#[derive(Debug, PartialEq)]
14pub struct Response<T> {
15    pub result: T,
16    pub headers: Headers,
17}
18
19#[derive(Debug, PartialEq)]
20pub struct Headers {
21    pub retry_after: Option<Timestamp>,
22}
23
24#[derive(Debug, Deserialize, PartialEq)]
25pub struct TestConnectivity {}
26
27#[derive(Debug, Deserialize, PartialEq)]
28#[serde(rename_all = "camelCase")]
29pub struct ServerTime {
30    pub server_time: Timestamp,
31}
32
33#[derive(Debug, Default, Serialize, PartialEq)]
34#[serde(rename_all = "camelCase")]
35pub struct GetExchangeInfoParams {
36    /// Example: curl -X GET "https://api.binance.com/api/v3/exchangeInfo?symbol=BNBBTC"
37    symbol: Option<String>,
38    /// Examples: curl -X GET "https://api.binance.com/api/v3/exchangeInfo?symbols=%5B%22BNBBTC%22,%22BTCUSDT%22%5D"
39    /// or
40    /// curl -g -X GET 'https://api.binance.com/api/v3/exchangeInfo?symbols=["BTCUSDT","BNBBTC"]'
41    /// TODO: Check serialization.
42    symbols: Option<Vec<String>>,
43    /// Examples: curl -X GET "https://api.binance.com/api/v3/exchangeInfo?permissions=SPOT"
44    /// or
45    /// curl -X GET "https://api.binance.com/api/v3/exchangeInfo?permissions=%5B%22MARGIN%22%2C%22LEVERAGED%22%5D"
46    /// or
47    /// curl -g -X GET 'https://api.binance.com/api/v3/exchangeInfo?permissions=["MARGIN","LEVERAGED"]'
48    /// TODO: Check serialization.
49    permissions: Option<Vec<String>>,
50    /// Controls whether the content of the permissionSets field is populated or not. Defaults to true
51    show_permission_sets: Option<bool>,
52    /// Filters symbols that have this tradingStatus. Valid values: TRADING, HALT, BREAK
53    /// Cannot be used in combination with symbols or symbol.
54    symbol_status: Option<SymbolStatus>,
55}
56
57impl GetExchangeInfoParams {
58    pub fn new() -> Self {
59        Self::default()
60    }
61
62    pub fn symbol(mut self, symbol: impl Into<String>) -> Self {
63        self.symbol = Some(symbol.into());
64        self
65    }
66
67    pub fn symbols(mut self, symbols: Vec<String>) -> Self {
68        self.symbols = Some(symbols);
69        self
70    }
71
72    pub fn permissions(mut self, permissions: Vec<String>) -> Self {
73        self.permissions = Some(permissions);
74        self
75    }
76
77    pub fn show_permission_sets(mut self, value: bool) -> Self {
78        self.show_permission_sets = Some(value);
79        self
80    }
81
82    pub fn symbol_status(mut self, value: SymbolStatus) -> Self {
83        self.symbol_status = Some(value);
84        self
85    }
86}
87
88#[derive(Debug, Deserialize, PartialEq)]
89#[serde(rename_all = "camelCase")]
90pub struct ExchangeInfo {
91    pub timezone: String,
92    pub server_time: Timestamp,
93    pub rate_limits: Vec<RateLimit>,
94    pub exchange_filters: Vec<ExchangeFilter>,
95    pub symbols: Vec<SymbolInfo>,
96    /// Optional field. Present only when SOR is available.
97    /// LINK: https://github.com/binance/binance-spot-api-docs/blob/master/faqs/sor_faq.md
98    pub sors: Option<Vec<SOR>>,
99}
100
101#[derive(Debug, Deserialize, PartialEq)]
102#[serde(rename_all = "camelCase")]
103pub struct RateLimit {
104    pub rate_limit_type: RateLimiter,
105    pub interval: RateLimitInterval,
106    pub interval_num: u64,
107    pub limit: u64,
108}
109
110#[derive(Debug, Deserialize, PartialEq)]
111#[serde(rename_all = "camelCase")]
112pub struct SymbolInfo {
113    pub symbol: String,
114    pub status: SymbolStatus,
115    pub base_asset: String,
116    pub base_asset_precision: u8, // value range: [0:8]
117    pub quote_asset: String,
118    // INFO: 'quote_precision' will be removed in future api versions (v4+)
119    pub quote_asset_precision: u8,      // value range: [0:8]
120    pub base_commission_precision: u8,  // value range: [0:8]
121    pub quote_commission_precision: u8, // value range: [0:8]
122    pub order_types: Vec<OrderType>,
123    pub iceberg_allowed: bool,
124    pub oco_allowed: bool,
125    pub oto_allowed: bool,
126    pub quote_order_qty_market_allowed: bool,
127    pub allow_trailing_stop: bool,
128    pub cancel_replace_allowed: bool,
129    pub amend_allowed: bool,
130    pub is_spot_trading_allowed: bool,
131    pub is_margin_trading_allowed: bool,
132    pub filters: Vec<Filter>,
133    pub permissions: Vec<String>,
134    pub permission_sets: Vec<Vec<String>>,
135    pub default_self_trade_prevention_mode: STPMode,
136    pub allowed_self_trade_prevention_modes: Vec<STPMode>,
137}
138
139#[derive(Debug, Deserialize, PartialEq)]
140#[serde(rename_all = "camelCase")]
141pub struct Filter {
142    // TODO:
143}
144
145/// Smart Order Routing (SOR).
146#[derive(Debug, Deserialize, PartialEq)]
147#[serde(rename_all = "camelCase")]
148pub struct SOR {
149    pub base_asset: String,
150    pub symbols: Vec<String>,
151}
152
153#[derive(Debug, Serialize, PartialEq)]
154#[serde(rename_all = "camelCase")]
155pub struct GetOrderBookParams {
156    symbol: String,
157    /// Default: 100; Maximum: 5000.
158    /// If limit > 5000, only 5000 entries will be returned.
159    limit: Option<u64>,
160}
161
162impl GetOrderBookParams {
163    pub fn new(symbol: impl Into<String>) -> Self {
164        Self {
165            symbol: symbol.into(),
166            limit: None,
167        }
168    }
169
170    pub fn limit(mut self, limit: u64) -> Self {
171        self.limit = Some(limit);
172        self
173    }
174}
175
176#[derive(Debug, Deserialize, PartialEq)]
177#[serde(rename_all = "camelCase")]
178pub struct OrderBook {
179    pub last_update_id: i64,
180    pub bids: Vec<OrderLevel>,
181    pub asks: Vec<OrderLevel>,
182}
183
184#[derive(Debug, Deserialize, PartialEq)]
185pub struct OrderLevel(Decimal, Decimal);
186
187impl OrderLevel {
188    pub fn price(&self) -> Decimal {
189        self.0
190    }
191    pub fn qty(&self) -> Decimal {
192        self.0
193    }
194}
195
196#[derive(Debug, Serialize, PartialEq)]
197#[serde(rename_all = "camelCase")]
198pub struct GetRecentTradesParams {
199    symbol: String,
200    /// Default: 500; Maximum: 1000.
201    limit: Option<u64>,
202}
203
204impl GetRecentTradesParams {
205    pub fn new(symbol: impl Into<String>) -> Self {
206        Self {
207            symbol: symbol.into(),
208            limit: None,
209        }
210    }
211
212    pub fn limit(mut self, limit: u64) -> Self {
213        self.limit = Some(limit);
214        self
215    }
216}
217
218#[derive(Debug, Deserialize, PartialEq)]
219#[serde(rename_all = "camelCase")]
220pub struct RecentTrade {
221    pub id: i64,
222    pub price: Decimal,
223    pub qty: Decimal,
224    pub quote_qty: Decimal,
225    pub time: Timestamp,
226    pub is_buyer_maker: bool,
227    pub is_best_match: bool,
228}
229
230#[derive(Debug, Serialize, PartialEq)]
231#[serde(rename_all = "camelCase")]
232pub struct GetOlderTradesParams {
233    symbol: String,
234    /// Default: 500; Maximum: 1000.
235    limit: Option<u64>,
236    /// TradeId to fetch from. Default gets most recent trades.
237    from_id: Option<i64>,
238}
239
240impl GetOlderTradesParams {
241    pub fn new(symbol: impl Into<String>) -> Self {
242        Self {
243            symbol: symbol.into(),
244            limit: None,
245            from_id: None,
246        }
247    }
248
249    pub fn limit(mut self, limit: u64) -> Self {
250        self.limit = Some(limit);
251        self
252    }
253
254    pub fn from_id(mut self, from_id: i64) -> Self {
255        self.from_id = Some(from_id);
256        self
257    }
258}
259
260#[derive(Debug, Serialize, PartialEq)]
261#[serde(rename_all = "camelCase")]
262pub struct GetAggregateTradesParams {
263    symbol: String,
264    /// ID to get aggregate trades from INCLUSIVE.
265    from_id: Option<i64>,
266    /// Timestamp in ms to get aggregate trades from INCLUSIVE.
267    start_time: Option<Timestamp>,
268    /// Timestamp in ms to get aggregate trades until INCLUSIVE.
269    end_time: Option<Timestamp>,
270    /// Default: 500; Maximum: 1000.
271    limit: Option<u64>,
272}
273
274impl GetAggregateTradesParams {
275    pub fn new(symbol: impl Into<String>) -> Self {
276        Self {
277            symbol: symbol.into(),
278            from_id: None,
279            start_time: None,
280            end_time: None,
281            limit: None,
282        }
283    }
284
285    pub fn from_id(mut self, from_id: i64) -> Self {
286        self.from_id = Some(from_id);
287        self
288    }
289
290    pub fn start_time(mut self, start_time: Timestamp) -> Self {
291        self.start_time = Some(start_time);
292        self
293    }
294
295    pub fn end_time(mut self, end_time: Timestamp) -> Self {
296        self.end_time = Some(end_time);
297        self
298    }
299
300    pub fn limit(mut self, limit: u64) -> Self {
301        self.limit = Some(limit);
302        self
303    }
304}
305
306#[derive(Debug, Deserialize, PartialEq)]
307pub struct AggregateTrade {
308    /// Aggregate tradeId
309    #[serde(rename = "a")]
310    pub id: i64,
311    /// Price
312    #[serde(rename = "p")]
313    pub price: Decimal,
314    /// Quantity
315    #[serde(rename = "q")]
316    pub qty: Decimal,
317    /// First tradeId
318    #[serde(rename = "f")]
319    pub first_trade_id: i64,
320    /// Last tradeId
321    #[serde(rename = "l")]
322    pub last_trade_id: i64,
323    /// Timestamp
324    #[serde(rename = "T")]
325    pub time: Timestamp,
326    /// Was the buyer the maker?
327    #[serde(rename = "m")]
328    pub is_buyer_maker: bool,
329    /// Was the trade the best price match?
330    #[serde(rename = "M")]
331    pub is_best_match: bool,
332}
333
334#[derive(Debug, Serialize, PartialEq)]
335#[serde(rename_all = "camelCase")]
336pub struct GetKlineListParams {
337    symbol: String,
338    interval: KlineInterval,
339    start_time: Option<Timestamp>,
340    end_time: Option<Timestamp>,
341    time_zone: Option<String>,
342    /// Default: 500; Maximum: 1000.
343    limit: Option<u64>,
344}
345
346impl GetKlineListParams {
347    pub fn new(symbol: impl Into<String>, interval: KlineInterval) -> Self {
348        Self {
349            symbol: symbol.into(),
350            interval,
351            start_time: None,
352            end_time: None,
353            time_zone: None,
354            limit: None,
355        }
356    }
357
358    pub fn start_time(mut self, start_time: Timestamp) -> Self {
359        self.start_time = Some(start_time);
360        self
361    }
362
363    pub fn end_time(mut self, end_time: Timestamp) -> Self {
364        self.end_time = Some(end_time);
365        self
366    }
367
368    pub fn time_zone(mut self, time_zone: impl Into<String>) -> Self {
369        self.time_zone = Some(time_zone.into());
370        self
371    }
372
373    pub fn limit(mut self, limit: u64) -> Self {
374        self.limit = Some(limit);
375        self
376    }
377}
378
379#[derive(Debug, Deserialize, PartialEq)]
380pub struct Kline(
381    Timestamp, // Kline open time
382    Decimal,   // Open price
383    Decimal,   // High price
384    Decimal,   // Low price
385    Decimal,   // Close price
386    Decimal,   // Volume
387    Timestamp, // Kline Close time
388    Decimal,   // Quote asset volume
389    u64,       // Number of trades
390    Decimal,   // Taker buy base asset volume
391    Decimal,   // Taker buy quote asset volume
392    String,    // DEPRECATED: Unused field, ignore.
393);
394
395impl Kline {
396    /// Kline open time
397    pub fn time_open(&self) -> Timestamp {
398        self.0
399    }
400    /// Open price
401    pub fn open(&self) -> Decimal {
402        self.1
403    }
404    /// High price
405    pub fn high(&self) -> Decimal {
406        self.2
407    }
408    /// Low price
409    pub fn low(&self) -> Decimal {
410        self.3
411    }
412    /// Close price
413    pub fn close(&self) -> Decimal {
414        self.4
415    }
416    /// Volume
417    pub fn volume(&self) -> Decimal {
418        self.5
419    }
420    /// Kline Close time
421    pub fn time_close(&self) -> Timestamp {
422        self.6
423    }
424    /// Quote asset volume
425    pub fn quote_asset_volume(&self) -> Decimal {
426        self.7
427    }
428    /// Number of trades
429    pub fn id(&self) -> u64 {
430        self.8
431    }
432    /// Taker buy base asset volume
433    pub fn taker_buy_base_asset_volume(&self) -> Decimal {
434        self.9
435    }
436    /// Taker buy quote asset volume
437    pub fn taker_buy_quote_asset_volume(&self) -> Decimal {
438        self.10
439    }
440}
441
442#[derive(Debug, Serialize, PartialEq)]
443#[serde(rename_all = "camelCase")]
444pub struct GetCurrentAveragePriceParams {
445    symbol: String,
446}
447
448impl GetCurrentAveragePriceParams {
449    pub fn new(symbol: impl Into<String>) -> Self {
450        Self {
451            symbol: symbol.into(),
452        }
453    }
454}
455
456#[derive(Debug, Deserialize, PartialEq)]
457#[serde(rename_all = "camelCase")]
458pub struct CurrentAveragePrice {
459    /// Average price interval (in minutes)
460    pub mins: u64,
461    /// Average price
462    pub price: Decimal,
463    /// Last trade time
464    pub close_time: Timestamp,
465}
466
467/// Supported values: FULL or MINI.
468/// If none provided, the default is FULL
469#[derive(Debug, Serialize, PartialEq)]
470#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")]
471pub enum GetTickerPriceChangeStatisticsParams {
472    Mini(SymbolOrSymbols),
473    Full(SymbolOrSymbols),
474}
475
476#[derive(Debug, Default, Serialize, PartialEq)]
477pub struct SymbolOrSymbols {
478    /// Parameter symbol and symbols cannot be used in combination.
479    /// If neither parameter is sent, tickers for all symbols will be returned in an array.
480    symbol: Option<String>,
481    /// Examples of accepted format for the symbols parameter: ["BTCUSDT","BNBUSDT"]
482    /// TODO: check serialization
483    /// or
484    /// %5B%22BTCUSDT%22,%22BNBUSDT%22%5D
485    symbols: Option<Vec<String>>,
486}
487
488impl SymbolOrSymbols {
489    pub fn new() -> Self {
490        Self::default()
491    }
492
493    pub fn symbol(mut self, symbol: impl Into<String>) -> Self {
494        self.symbol = Some(symbol.into());
495        self
496    }
497
498    pub fn symbols(mut self, symbols: Vec<String>) -> Self {
499        self.symbols = Some(symbols);
500        self
501    }
502}
503
504#[derive(Debug, Deserialize, PartialEq)]
505#[serde(untagged)]
506pub enum TickerPriceChangeStatistic {
507    MiniElement(TickerPriceChangeStatisticMini),
508    MiniList(Vec<TickerPriceChangeStatisticMini>),
509    FullElement(TickerPriceChangeStatisticFull),
510    FullList(Vec<TickerPriceChangeStatisticFull>),
511}
512
513#[derive(Debug, Deserialize, PartialEq)]
514#[serde(rename_all = "camelCase")]
515pub struct TickerPriceChangeStatisticFull {
516    pub symbol: String,
517    pub price_change: Decimal,
518    pub price_change_percent: Decimal,
519    pub weighted_avg_price: Decimal,
520    pub prev_close_price: Decimal,
521    pub last_price: Decimal,
522    pub last_qty: Decimal,
523    pub bid_price: Decimal,
524    pub bid_qty: Decimal,
525    pub ask_price: Decimal,
526    pub ask_qty: Decimal,
527    pub open_price: Decimal,
528    pub high_price: Decimal,
529    pub low_price: Decimal,
530    pub volume: Decimal,
531    pub quote_volume: Decimal,
532    pub open_time: Timestamp,
533    pub close_time: Timestamp,
534    /// First traded
535    pub first_id: i64,
536    /// Last traded
537    pub last_id: i64,
538    /// Trade count
539    pub count: u64,
540}
541
542#[derive(Debug, Deserialize, PartialEq)]
543#[serde(rename_all = "camelCase")]
544pub struct TickerPriceChangeStatisticMini {
545    /// Symbol Name
546    pub symbol: String,
547    /// Opening price of the Interval
548    pub open_price: Decimal,
549    /// Highest price in the interval
550    pub high_price: Decimal,
551    /// Lowest  price in the interval
552    pub low_price: Decimal,
553    /// Closing price of the interval
554    pub last_price: Decimal,
555    /// Total trade volume (in base asset)
556    pub volume: Decimal,
557    /// Total trade volume (in quote asset)
558    pub quote_volume: Decimal,
559    /// Start of the ticker interval
560    pub open_time: Timestamp,
561    /// End of the ticker interval
562    pub close_time: Timestamp,
563    /// First tradeId considered
564    pub first_id: i64,
565    /// Last tradeId considered
566    pub last_id: i64,
567    /// Total trade count
568    pub count: u64,
569}
570
571#[derive(Debug, Serialize, PartialEq)]
572#[serde(rename_all = "camelCase")]
573pub struct NewOrderRequest {
574    symbol: String,
575    side: OrderSide,
576    #[serde(rename = "type")]
577    order_type: OrderType,
578    time_in_force: Option<TimeInForce>,
579    quantity: Option<Decimal>,
580    quote_order_qty: Option<Decimal>,
581    price: Option<Decimal>,
582    /// A unique id among open orders. Automatically generated if not sent.
583    /// Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected.
584    new_client_order_id: Option<String>,
585    strategy_id: Option<i64>,
586    /// The value cannot be less than 1000000.
587    strategy_type: Option<i64>,
588    /// Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders.
589    stop_price: Option<Decimal>,
590    /// See Trailing Stop order FAQ.
591    trailing_delta: Option<i64>,
592    /// Used with LIMIT, STOP_LOSS_LIMIT, and TAKE_PROFIT_LIMIT to create an iceberg order.
593    iceberg_qty: Option<Decimal>,
594    /// Set the response JSON. ACK, RESULT, or FULL; MARKET and LIMIT order types default to FULL, all other orders default to ACK.
595    /// Mandatory - because there is a problem with deserialization of untagged enum Order
596    new_order_resp_type: OrderResponseType,
597    /// The allowed enums is dependent on what is configured on the symbol. The possible supported values are: STP Modes.
598    self_trade_prevention_mode: Option<STPMode>,
599    /// The value cannot be greater than 60000
600    recv_window: Option<i64>,
601    /// Only for test endpoint to place a new order.
602    compute_commission_rates: Option<bool>,
603}
604
605impl NewOrderRequest {
606    pub fn new(
607        symbol: impl Into<String>,
608        side: OrderSide,
609        order_type: OrderType,
610        new_order_resp_type: OrderResponseType,
611    ) -> Self {
612        Self {
613            symbol: symbol.into(),
614            side,
615            order_type,
616            new_order_resp_type,
617            time_in_force: None,
618            quantity: None,
619            quote_order_qty: None,
620            price: None,
621            new_client_order_id: None,
622            strategy_id: None,
623            strategy_type: None,
624            stop_price: None,
625            trailing_delta: None,
626            iceberg_qty: None,
627            self_trade_prevention_mode: None,
628            recv_window: None,
629            compute_commission_rates: None,
630        }
631    }
632
633    pub fn time_in_force(mut self, value: TimeInForce) -> Self {
634        self.time_in_force = Some(value);
635        self
636    }
637
638    pub fn quantity(mut self, value: Decimal) -> Self {
639        self.quantity = Some(value);
640        self
641    }
642
643    pub fn quote_order_qty(mut self, value: Decimal) -> Self {
644        self.quote_order_qty = Some(value);
645        self
646    }
647
648    pub fn price(mut self, value: Decimal) -> Self {
649        self.price = Some(value);
650        self
651    }
652
653    pub fn new_client_order_id(mut self, value: impl Into<String>) -> Self {
654        self.new_client_order_id = Some(value.into());
655        self
656    }
657
658    pub fn strategy_id(mut self, value: i64) -> Self {
659        self.strategy_id = Some(value);
660        self
661    }
662
663    pub fn strategy_type(mut self, value: i64) -> Self {
664        self.strategy_type = Some(value);
665        self
666    }
667
668    pub fn stop_price(mut self, value: Decimal) -> Self {
669        self.stop_price = Some(value);
670        self
671    }
672
673    pub fn trailing_delta(mut self, value: i64) -> Self {
674        self.trailing_delta = Some(value);
675        self
676    }
677
678    pub fn iceberg_qty(mut self, value: Decimal) -> Self {
679        self.iceberg_qty = Some(value);
680        self
681    }
682
683    pub fn self_trade_prevention_mode(mut self, value: STPMode) -> Self {
684        self.self_trade_prevention_mode = Some(value);
685        self
686    }
687
688    pub fn recv_window(mut self, value: i64) -> Self {
689        self.recv_window = Some(value);
690        self
691    }
692
693    pub fn compute_commission_rates(mut self, value: bool) -> Self {
694        self.compute_commission_rates = Some(value);
695        self
696    }
697
698    pub fn is_valid(&self) -> bool {
699        match self.order_type {
700            OrderType::Limit => {
701                self.time_in_force.is_some() && self.quantity.is_some() && self.price.is_some()
702            }
703            OrderType::Market => {
704                // MARKET orders using the quantity field specifies the amount of the base asset the user wants to buy or sell at the market price.
705                // E.g. MARKET order on BTCUSDT will specify how much BTC the user is buying or selling.
706
707                // MARKET orders using quoteOrderQty specifies the amount the user wants to spend (when buying) or receive (when selling) the quote asset; the correct quantity will be determined based on the market liquidity and quoteOrderQty.
708                // E.g. Using the symbol BTCUSDT:
709                // BUY side, the order will buy as many BTC as quoteOrderQty USDT can.
710                // SELL side, the order will sell as much BTC needed to receive quoteOrderQty USDT.
711                self.quantity.is_some() || self.quote_order_qty.is_some()
712            }
713            OrderType::StopLoss => {
714                // This will execute a MARKET order when the conditions are met. (e.g. stopPrice is met or trailingDelta is activated)
715                self.quantity.is_some()
716                    && (self.stop_price.is_some() || self.trailing_delta.is_some())
717            }
718            OrderType::StopLossLimit => {
719                self.time_in_force.is_some()
720                    && self.quantity.is_some()
721                    && self.price.is_some()
722                    && (self.stop_price.is_some() || self.trailing_delta.is_some())
723            }
724            OrderType::TakeProfit => {
725                // This will execute a MARKET order when the conditions are met. (e.g. stopPrice is met or trailingDelta is activated)
726                self.quantity.is_some()
727                    && (self.stop_price.is_some() || self.trailing_delta.is_some())
728            }
729            OrderType::TakeProfitLimit => {
730                self.time_in_force.is_some()
731                    && self.quantity.is_some()
732                    && self.price.is_some()
733                    && (self.stop_price.is_some() || self.trailing_delta.is_some())
734            }
735            OrderType::LimitMaker => {
736                // This is a LIMIT order that will be rejected if the order immediately matches and trades as a taker.
737                // This is also known as a POST-ONLY order.
738                self.quantity.is_some() && self.price.is_some()
739            }
740        }
741    }
742}
743
744#[derive(Debug, Deserialize, PartialEq)]
745#[serde(untagged)]
746pub enum NewOrderResponse {
747    Full(NewOrderResponseFull),
748    Result(NewOrderResponseResult),
749    Ack(NewOrderResponseAck),
750}
751
752#[derive(Debug, Deserialize, PartialEq)]
753#[serde(rename_all = "camelCase")]
754pub struct NewOrderResponseAck {
755    pub symbol: String,
756    pub order_id: i64,
757    /// Unless it's part of an order list, value will be -1
758    pub order_list_id: i64,
759    pub client_order_id: String,
760    pub transact_time: Timestamp,
761    /// Quantity for the iceberg order
762    /// Appears only if the parameter icebergQty was sent in the request.
763    pub iceberg_qty: Option<Decimal>,
764    /// When used in combination with symbol, can be used to query a prevented match.
765    /// Appears only if the order expired due to STP.
766    pub prevented_match_id: Option<i64>,
767    /// Order quantity that expired due to STP
768    /// Appears only if the order expired due to STP.
769    pub prevented_quantity: Option<Decimal>,
770    /// Price when the algorithmic order will be triggered
771    /// Appears for STOP_LOSS. TAKE_PROFIT, STOP_LOSS_LIMIT and TAKE_PROFIT_LIMIT orders.
772    pub stop_price: Option<Decimal>,
773    /// Can be used to label an order that's part of an order strategy.
774    /// Appears if the parameter was populated in the request.
775    pub strategy_id: Option<i64>,
776    /// Can be used to label an order that is using an order strategy.
777    /// Appears if the parameter was populated in the request.
778    pub strategy_type: Option<i64>,
779    /// Delta price change required before order activation
780    /// Appears for Trailing Stop Orders.
781    pub trailing_delta: Option<i64>,
782    /// Time when the trailing order is now active and tracking price changes
783    /// Appears only for Trailing Stop Orders.
784    pub trailing_time: Option<i64>,
785    /// Field that determines whether order used SOR
786    /// Appears when placing orders using SOR
787    pub used_sor: Option<bool>,
788    /// Field that determines whether the order is being filled by the SOR or by the order book the order was submitted to.
789    /// Appears when placing orders using SOR
790    pub working_floor: Option<WorkingFloor>,
791}
792
793#[derive(Debug, Deserialize, PartialEq)]
794#[serde(rename_all = "camelCase")]
795pub struct NewOrderResponseResult {
796    pub symbol: String,
797    pub order_id: i64,
798    /// Unless it's part of an order list, value will be -1
799    pub order_list_id: i64,
800    pub client_order_id: String,
801    pub transact_time: Timestamp,
802    pub price: Decimal,
803    pub orig_qty: Decimal,
804    pub executed_qty: Decimal,
805    pub orig_quote_order_qty: Decimal,
806    pub cummulative_quote_qty: Decimal,
807    pub status: OrderStatus,
808    pub time_in_force: TimeInForce,
809    #[serde(rename = "type")]
810    pub order_type: OrderType,
811    pub side: OrderSide,
812    pub working_time: Timestamp,
813    pub self_trade_prevention_mode: STPMode,
814    /// Quantity for the iceberg order
815    /// Appears only if the parameter icebergQty was sent in the request.
816    pub iceberg_qty: Option<Decimal>,
817    /// When used in combination with symbol, can be used to query a prevented match.
818    /// Appears only if the order expired due to STP.
819    pub prevented_match_id: Option<i64>,
820    /// Order quantity that expired due to STP
821    /// Appears only if the order expired due to STP.
822    pub prevented_quantity: Option<Decimal>,
823    /// Price when the algorithmic order will be triggered
824    /// Appears for STOP_LOSS. TAKE_PROFIT, STOP_LOSS_LIMIT and TAKE_PROFIT_LIMIT orders.
825    pub stop_price: Option<Decimal>,
826    /// Can be used to label an order that's part of an order strategy.
827    /// Appears if the parameter was populated in the request.
828    pub strategy_id: Option<i64>,
829    /// Can be used to label an order that is using an order strategy.
830    /// Appears if the parameter was populated in the request.
831    pub strategy_type: Option<i64>,
832    /// Delta price change required before order activation
833    /// Appears for Trailing Stop Orders.
834    pub trailing_delta: Option<i64>,
835    /// Time when the trailing order is now active and tracking price changes
836    /// Appears only for Trailing Stop Orders.
837    pub trailing_time: Option<i64>,
838    /// Field that determines whether order used SOR
839    /// Appears when placing orders using SOR
840    pub used_sor: Option<bool>,
841    /// Field that determines whether the order is being filled by the SOR or by the order book the order was submitted to.
842    /// Appears when placing orders using SOR
843    pub working_floor: Option<WorkingFloor>,
844}
845
846#[derive(Debug, Deserialize, PartialEq)]
847#[serde(rename_all = "camelCase")]
848pub struct NewOrderResponseFull {
849    pub symbol: String,
850    pub order_id: i64,
851    /// Unless it's part of an order list, value will be -1
852    pub order_list_id: i64,
853    pub client_order_id: String,
854    pub transact_time: Timestamp,
855    pub price: Decimal,
856    pub orig_qty: Decimal,
857    pub executed_qty: Decimal,
858    pub orig_quote_order_qty: Decimal,
859    pub cummulative_quote_qty: Decimal,
860    pub status: OrderStatus,
861    pub time_in_force: TimeInForce,
862    #[serde(rename = "type")]
863    pub order_type: OrderType,
864    pub side: OrderSide,
865    pub working_time: Timestamp,
866    pub self_trade_prevention_mode: STPMode,
867    pub fills: Vec<OrderFill>,
868    /// Quantity for the iceberg order
869    /// Appears only if the parameter icebergQty was sent in the request.
870    pub iceberg_qty: Option<Decimal>,
871    /// When used in combination with symbol, can be used to query a prevented match.
872    /// Appears only if the order expired due to STP.
873    pub prevented_match_id: Option<i64>,
874    /// Order quantity that expired due to STP
875    /// Appears only if the order expired due to STP.
876    pub prevented_quantity: Option<Decimal>,
877    /// Price when the algorithmic order will be triggered
878    /// Appears for STOP_LOSS. TAKE_PROFIT, STOP_LOSS_LIMIT and TAKE_PROFIT_LIMIT orders.
879    pub stop_price: Option<Decimal>,
880    /// Can be used to label an order that's part of an order strategy.
881    /// Appears if the parameter was populated in the request.
882    pub strategy_id: Option<i64>,
883    /// Can be used to label an order that is using an order strategy.
884    /// Appears if the parameter was populated in the request.
885    pub strategy_type: Option<i64>,
886    /// Delta price change required before order activation
887    /// Appears for Trailing Stop Orders.
888    pub trailing_delta: Option<i64>,
889    /// Time when the trailing order is now active and tracking price changes
890    /// Appears only for Trailing Stop Orders.
891    pub trailing_time: Option<i64>,
892    /// Field that determines whether order used SOR
893    /// Appears when placing orders using SOR
894    pub used_sor: Option<bool>,
895    /// Field that determines whether the order is being filled by the SOR or by the order book the order was submitted to.
896    /// Appears when placing orders using SOR
897    pub working_floor: Option<WorkingFloor>,
898}
899
900#[derive(Debug, Deserialize, PartialEq)]
901#[serde(rename_all = "camelCase")]
902pub struct OrderFill {
903    pub price: Decimal,
904    pub qty: Decimal,
905    pub commission: Decimal,
906    pub commission_asset: String,
907    pub trade_id: i64,
908}
909
910#[derive(Debug, Deserialize, PartialEq)]
911#[serde(untagged)]
912pub enum TestCommissionRates {
913    Full(TestCommissionRatesFull),
914    Empty(TestCommissionRatesEmpty),
915}
916
917#[derive(Debug, Deserialize, PartialEq)]
918pub struct TestCommissionRatesEmpty {}
919
920#[derive(Debug, Deserialize, PartialEq)]
921#[serde(rename_all = "camelCase")]
922pub struct TestCommissionRatesFull {
923    /// Standard commission rates on trades from the order.
924    pub standard_commission_for_order: CommissionForOrder,
925    /// Tax commission rates for trades from the order.
926    pub tax_commission_for_order: CommissionForOrder,
927    /// Discount on standard commissions when paying in BNB.
928    pub discount: Discount,
929}
930#[derive(Debug, Deserialize, PartialEq)]
931#[serde(rename_all = "camelCase")]
932pub struct CommissionForOrder {
933    pub maker: Decimal,
934    pub taker: Decimal,
935}
936
937#[derive(Debug, Deserialize, PartialEq)]
938#[serde(rename_all = "camelCase")]
939pub struct Discount {
940    pub enabled_for_account: bool,
941    pub enabled_for_symbol: bool,
942    pub discount_asset: String,
943    /// Standard commission is reduced by this rate when paying commission in BNB.
944    pub discount: Decimal,
945}
946
947#[derive(Debug, Default, Serialize, PartialEq)]
948#[serde(rename_all = "camelCase")]
949pub struct GetAccountInformationParams {
950    /// When set to true, emits only the non-zero balances of an account.
951    /// Default value: false
952    omit_zero_balances: Option<bool>,
953    /// The value cannot be greater than 60000
954    recv_window: Option<i64>,
955}
956
957impl GetAccountInformationParams {
958    pub fn new() -> Self {
959        Self::default()
960    }
961
962    pub fn omit_zero_balances(mut self, value: bool) -> Self {
963        self.omit_zero_balances = Some(value);
964        self
965    }
966
967    pub fn recv_window(mut self, value: i64) -> Self {
968        self.recv_window = Some(value);
969        self
970    }
971}
972
973#[derive(Debug, Deserialize, PartialEq)]
974#[serde(rename_all = "camelCase")]
975pub struct AccountInformation {
976    pub maker_commission: f64,
977    pub taker_commission: f64,
978    pub buyer_commission: f64,
979    pub seller_commission: f64,
980    pub commission_rates: CommissionRates,
981    pub can_trade: bool,
982    pub can_withdraw: bool,
983    pub can_deposit: bool,
984    pub brokered: bool,
985    pub require_self_trade_prevention: bool,
986    pub prevent_sor: bool,
987    pub update_time: Timestamp,
988    pub account_type: AccountType,
989    pub balances: Vec<Balance>,
990    pub permissions: Option<Vec<String>>,
991    pub permission_sets: Option<Vec<Vec<String>>>,
992    pub uid: i64,
993}
994
995#[derive(Debug, Deserialize, PartialEq)]
996#[serde(rename_all = "camelCase")]
997pub struct CommissionRates {
998    pub maker: Decimal,
999    pub taker: Decimal,
1000    pub buyer: Decimal,
1001    pub seller: Decimal,
1002}
1003
1004#[derive(Debug, Deserialize, PartialEq)]
1005#[serde(rename_all = "camelCase")]
1006pub struct Balance {
1007    pub asset: String,
1008    pub free: Decimal,
1009    pub locked: Decimal,
1010}
1011
1012#[derive(Debug, Serialize, PartialEq)]
1013#[serde(rename_all = "camelCase")]
1014pub struct QueryOrderParams {
1015    symbol: String,
1016    order_id: Option<i64>,
1017    orig_client_order_id: Option<String>,
1018    /// The value cannot be greater than 60000
1019    recv_window: Option<i64>,
1020}
1021
1022impl QueryOrderParams {
1023    pub fn new(symbol: impl Into<String>) -> Self {
1024        Self {
1025            symbol: symbol.into(),
1026            order_id: None,
1027            orig_client_order_id: None,
1028            recv_window: None,
1029        }
1030    }
1031
1032    pub fn order_id(mut self, value: i64) -> Self {
1033        self.order_id = Some(value);
1034        self
1035    }
1036
1037    pub fn orig_client_order_id(mut self, value: impl Into<String>) -> Self {
1038        self.orig_client_order_id = Some(value.into());
1039        self
1040    }
1041
1042    pub fn recv_window(mut self, value: i64) -> Self {
1043        self.recv_window = Some(value);
1044        self
1045    }
1046}
1047
1048#[derive(Debug, Deserialize, PartialEq)]
1049#[serde(rename_all = "camelCase")]
1050pub struct Order {
1051    pub symbol: String,
1052    pub order_id: i64,
1053    /// This field will always have a value of -1 if not an order list.
1054    pub order_list_id: i64,
1055    pub client_order_id: String,
1056    pub price: Decimal,
1057    pub orig_qty: Decimal,
1058    pub executed_qty: Decimal,
1059    pub cummulative_quote_qty: Decimal,
1060    pub status: OrderStatus,
1061    pub time_in_force: TimeInForce,
1062    #[serde(rename = "type")]
1063    pub order_type: OrderType,
1064    pub side: OrderSide,
1065    /// Price when the algorithmic order will be triggered
1066    /// Appears for STOP_LOSS. TAKE_PROFIT, STOP_LOSS_LIMIT and TAKE_PROFIT_LIMIT orders.
1067    pub stop_price: Option<Decimal>,
1068    /// Quantity for the iceberg order
1069    /// Appears only if the parameter icebergQty was sent in the request.
1070    pub iceberg_qty: Option<Decimal>,
1071    pub time: Timestamp,
1072    pub update_time: Timestamp,
1073    pub is_working: bool,
1074    pub working_time: Timestamp,
1075    pub orig_quote_order_qty: Decimal,
1076    pub self_trade_prevention_mode: STPMode,
1077}
1078
1079#[cfg(test)]
1080mod tests {
1081    use rust_decimal::dec;
1082
1083    use crate::serde::deserialize_json;
1084
1085    use super::*;
1086
1087    #[test]
1088    fn deserialize_response_server_time() {
1089        let json = r#"{
1090            "serverTime": 1499827319559
1091        }"#;
1092        let expected = ServerTime {
1093            server_time: 1499827319559,
1094        };
1095
1096        let current = deserialize_json(json).unwrap();
1097
1098        assert_eq!(expected, current);
1099    }
1100
1101    #[test]
1102    fn deserialize_response_exchange_info() {
1103        let json = r#"{
1104            "timezone": "UTC",
1105            "serverTime": 1565246363776,
1106            "rateLimits": [],
1107            "exchangeFilters": [],
1108            "symbols": [
1109                {
1110                    "symbol": "ETHBTC",
1111                    "status": "TRADING",
1112                    "baseAsset": "ETH",
1113                    "baseAssetPrecision": 8,
1114                    "quoteAsset": "BTC",
1115                    "quotePrecision": 8,
1116                    "quoteAssetPrecision": 8,
1117                    "baseCommissionPrecision": 8,
1118                    "quoteCommissionPrecision": 8,
1119                    "orderTypes": [
1120                        "LIMIT",
1121                        "LIMIT_MAKER",
1122                        "MARKET",
1123                        "STOP_LOSS",
1124                        "STOP_LOSS_LIMIT",
1125                        "TAKE_PROFIT",
1126                        "TAKE_PROFIT_LIMIT"
1127                    ],
1128                    "icebergAllowed": true,
1129                    "ocoAllowed": true,
1130                    "otoAllowed": true,
1131                    "quoteOrderQtyMarketAllowed": true,
1132                    "allowTrailingStop": false,
1133                    "cancelReplaceAllowed":false,
1134                    "amendAllowed":false,
1135                    "isSpotTradingAllowed": true,
1136                    "isMarginTradingAllowed": true,
1137                    "filters": [],
1138                    "permissions": [],
1139                    "permissionSets": [
1140                        [
1141                            "SPOT",
1142                            "MARGIN"
1143                        ]
1144                    ],
1145                    "defaultSelfTradePreventionMode": "NONE",
1146                    "allowedSelfTradePreventionModes": [
1147                        "NONE"
1148                    ]
1149                }
1150            ],
1151            "sors": [
1152                {
1153                    "baseAsset": "BTC",
1154                    "symbols": [
1155                        "BTCUSDT",
1156                        "BTCUSDC"
1157                    ]
1158                }
1159            ]
1160        }"#;
1161        let expected = ExchangeInfo {
1162            timezone: String::from("UTC"),
1163            server_time: 1565246363776,
1164            rate_limits: vec![],
1165            exchange_filters: vec![],
1166            symbols: vec![SymbolInfo {
1167                symbol: String::from("ETHBTC"),
1168                status: SymbolStatus::Trading,
1169                base_asset: String::from("ETH"),
1170                base_asset_precision: 8,
1171                quote_asset: String::from("BTC"),
1172                quote_asset_precision: 8,
1173                base_commission_precision: 8,
1174                quote_commission_precision: 8,
1175                order_types: vec![
1176                    OrderType::Limit,
1177                    OrderType::LimitMaker,
1178                    OrderType::Market,
1179                    OrderType::StopLoss,
1180                    OrderType::StopLossLimit,
1181                    OrderType::TakeProfit,
1182                    OrderType::TakeProfitLimit,
1183                ],
1184                iceberg_allowed: true,
1185                oco_allowed: true,
1186                oto_allowed: true,
1187                quote_order_qty_market_allowed: true,
1188                allow_trailing_stop: false,
1189                cancel_replace_allowed: false,
1190                amend_allowed: false,
1191                is_spot_trading_allowed: true,
1192                is_margin_trading_allowed: true,
1193                filters: vec![],
1194                permissions: vec![],
1195                permission_sets: vec![vec![String::from("SPOT"), String::from("MARGIN")]],
1196                default_self_trade_prevention_mode: STPMode::None,
1197                allowed_self_trade_prevention_modes: vec![STPMode::None],
1198            }],
1199            sors: Some(vec![SOR {
1200                base_asset: String::from("BTC"),
1201                symbols: vec![String::from("BTCUSDT"), String::from("BTCUSDC")],
1202            }]),
1203        };
1204
1205        let current = deserialize_json(json).unwrap();
1206
1207        assert_eq!(expected, current);
1208    }
1209
1210    #[test]
1211    fn deserialize_response_order_book() {
1212        let json = r#"{
1213            "lastUpdateId": 1027024,
1214            "bids": [
1215                [
1216                "4.00000000",
1217                "431.00000000"
1218                ]
1219            ],
1220            "asks": [
1221                [
1222                "4.00000200",
1223                "12.00000000"
1224                ]
1225            ]
1226        }"#;
1227        let expected = OrderBook {
1228            last_update_id: 1027024,
1229            bids: vec![OrderLevel(dec!(4.00000000), dec!(431.00000000))],
1230            asks: vec![OrderLevel(dec!(4.00000200), dec!(12.00000000))],
1231        };
1232
1233        let current = deserialize_json(json).unwrap();
1234
1235        assert_eq!(expected, current);
1236    }
1237
1238    #[test]
1239    fn deserialize_response_order_ack() {
1240        let json = r#"{
1241            "symbol": "BTCUSDT",
1242            "orderId": 28,
1243            "orderListId": -1,
1244            "clientOrderId": "6gCrw2kRUAF9CvJDGP16IP",
1245            "transactTime": 1507725176595
1246        }"#;
1247        let response = NewOrderResponseAck {
1248            symbol: String::from("BTCUSDT"),
1249            order_id: 28,
1250            order_list_id: -1,
1251            client_order_id: String::from("6gCrw2kRUAF9CvJDGP16IP"),
1252            transact_time: 1507725176595,
1253            iceberg_qty: None,
1254            prevented_match_id: None,
1255            prevented_quantity: None,
1256            stop_price: None,
1257            strategy_id: None,
1258            strategy_type: None,
1259            trailing_delta: None,
1260            trailing_time: None,
1261            used_sor: None,
1262            working_floor: None,
1263        };
1264        let expected = NewOrderResponse::Ack(response);
1265
1266        let current = deserialize_json(json).unwrap();
1267
1268        assert_eq!(expected, current);
1269    }
1270
1271    #[test]
1272    fn deserialize_response_order_result() {
1273        let json = r#"{
1274            "symbol": "BTCUSDT",
1275            "orderId": 28,
1276            "orderListId": -1,
1277            "clientOrderId": "6gCrw2kRUAF9CvJDGP16IP",
1278            "transactTime": 1507725176595,
1279            "price": "0.00000000",
1280            "origQty": "10.00000000",
1281            "executedQty": "10.00000000",
1282            "origQuoteOrderQty": "0.000000",
1283            "cummulativeQuoteQty": "10.00000000",
1284            "status": "FILLED",
1285            "timeInForce": "GTC",
1286            "type": "MARKET",
1287            "side": "SELL",
1288            "workingTime": 1507725176595,
1289            "selfTradePreventionMode": "NONE"
1290        }"#;
1291        let response = NewOrderResponseResult {
1292            symbol: String::from("BTCUSDT"),
1293            order_id: 28,
1294            order_list_id: -1,
1295            client_order_id: String::from("6gCrw2kRUAF9CvJDGP16IP"),
1296            transact_time: 1507725176595,
1297            price: dec!(0.00000000),
1298            orig_qty: dec!(10.00000000),
1299            executed_qty: dec!(10.00000000),
1300            orig_quote_order_qty: dec!(0.00000000),
1301            cummulative_quote_qty: dec!(10.00000000),
1302            status: OrderStatus::Filled,
1303            time_in_force: TimeInForce::GTC,
1304            order_type: OrderType::Market,
1305            side: OrderSide::SELL,
1306            working_time: 1507725176595,
1307            self_trade_prevention_mode: STPMode::None,
1308            iceberg_qty: None,
1309            prevented_match_id: None,
1310            prevented_quantity: None,
1311            stop_price: None,
1312            strategy_id: None,
1313            strategy_type: None,
1314            trailing_delta: None,
1315            trailing_time: None,
1316            used_sor: None,
1317            working_floor: None,
1318        };
1319        let expected = NewOrderResponse::Result(response);
1320
1321        let current = deserialize_json(json).unwrap();
1322
1323        assert_eq!(expected, current);
1324    }
1325
1326    #[test]
1327    fn deserialize_response_order_full() {
1328        let json = r#"{
1329            "symbol": "BTCUSDT",
1330            "orderId": 28,
1331            "orderListId": -1,
1332            "clientOrderId": "6gCrw2kRUAF9CvJDGP16IP",
1333            "transactTime": 1507725176595,
1334            "price": "0.00000000",
1335            "origQty": "10.00000000",
1336            "executedQty": "10.00000000",
1337            "origQuoteOrderQty": "0.000000",
1338            "cummulativeQuoteQty": "10.00000000",
1339            "status": "FILLED",
1340            "timeInForce": "GTC",
1341            "type": "MARKET",
1342            "side": "SELL",
1343            "workingTime": 1507725176595,
1344            "selfTradePreventionMode": "NONE",
1345            "fills": [
1346                {
1347                    "price": "4000.00000000",
1348                    "qty": "1.00000000",
1349                    "commission": "4.00000000",
1350                    "commissionAsset": "USDT",
1351                    "tradeId": 56
1352                },
1353                {
1354                    "price": "3999.00000000",
1355                    "qty": "5.00000000",
1356                    "commission": "19.99500000",
1357                    "commissionAsset": "USDT",
1358                    "tradeId": 57
1359                },
1360                {
1361                    "price": "3998.00000000",
1362                    "qty": "2.00000000",
1363                    "commission": "7.99600000",
1364                    "commissionAsset": "USDT",
1365                    "tradeId": 58
1366                },
1367                {
1368                    "price": "3997.00000000",
1369                    "qty": "1.00000000",
1370                    "commission": "3.99700000",
1371                    "commissionAsset": "USDT",
1372                    "tradeId": 59
1373                },
1374                {
1375                    "price": "3995.00000000",
1376                    "qty": "1.00000000",
1377                    "commission": "3.99500000",
1378                    "commissionAsset": "USDT",
1379                    "tradeId": 60
1380                }
1381            ]
1382        }"#;
1383        let response = NewOrderResponseFull {
1384            symbol: String::from("BTCUSDT"),
1385            order_id: 28,
1386            order_list_id: -1,
1387            client_order_id: String::from("6gCrw2kRUAF9CvJDGP16IP"),
1388            transact_time: 1507725176595,
1389            price: dec!(0.00000000),
1390            orig_qty: dec!(10.00000000),
1391            executed_qty: dec!(10.00000000),
1392            orig_quote_order_qty: dec!(0.00000000),
1393            cummulative_quote_qty: dec!(10.00000000),
1394            status: OrderStatus::Filled,
1395            time_in_force: TimeInForce::GTC,
1396            order_type: OrderType::Market,
1397            side: OrderSide::SELL,
1398            working_time: 1507725176595,
1399            self_trade_prevention_mode: STPMode::None,
1400            fills: vec![
1401                OrderFill {
1402                    price: dec!(4000.00000000),
1403                    qty: dec!(1.00000000),
1404                    commission: dec!(4.00000000),
1405                    commission_asset: String::from("USDT"),
1406                    trade_id: 56,
1407                },
1408                OrderFill {
1409                    price: dec!(3999.00000000),
1410                    qty: dec!(5.00000000),
1411                    commission: dec!(19.99500000),
1412                    commission_asset: String::from("USDT"),
1413                    trade_id: 57,
1414                },
1415                OrderFill {
1416                    price: dec!(3998.00000000),
1417                    qty: dec!(2.00000000),
1418                    commission: dec!(7.99600000),
1419                    commission_asset: String::from("USDT"),
1420                    trade_id: 58,
1421                },
1422                OrderFill {
1423                    price: dec!(3997.00000000),
1424                    qty: dec!(1.00000000),
1425                    commission: dec!(3.99700000),
1426                    commission_asset: String::from("USDT"),
1427                    trade_id: 59,
1428                },
1429                OrderFill {
1430                    price: dec!(3995.00000000),
1431                    qty: dec!(1.00000000),
1432                    commission: dec!(3.99500000),
1433                    commission_asset: String::from("USDT"),
1434                    trade_id: 60,
1435                },
1436            ],
1437            iceberg_qty: None,
1438            prevented_match_id: None,
1439            prevented_quantity: None,
1440            stop_price: None,
1441            strategy_id: None,
1442            strategy_type: None,
1443            trailing_delta: None,
1444            trailing_time: None,
1445            used_sor: None,
1446            working_floor: None,
1447        };
1448        let expected = NewOrderResponse::Full(response);
1449
1450        let current = deserialize_json(json).unwrap();
1451
1452        assert_eq!(expected, current);
1453    }
1454
1455    #[test]
1456    fn deserialize_response_test_order_commission_rates_empty() {
1457        let json = r#"{}"#;
1458        let expected = TestCommissionRates::Empty(TestCommissionRatesEmpty {});
1459
1460        let current = deserialize_json(json).unwrap();
1461
1462        assert_eq!(expected, current);
1463    }
1464
1465    #[test]
1466    fn deserialize_response_test_order_commission_rates_full() {
1467        let json = r#"{
1468            "standardCommissionForOrder": {
1469                "maker": "0.00000112",
1470                "taker": "0.00000114"
1471            },
1472            "taxCommissionForOrder": {
1473                "maker": "0.00000112",
1474                "taker": "0.00000114"
1475            },
1476            "discount": {
1477                "enabledForAccount": true,
1478                "enabledForSymbol": true,
1479                "discountAsset": "BNB",
1480                "discount": "0.25000000"
1481            }
1482        }"#;
1483        let rates = TestCommissionRatesFull {
1484            standard_commission_for_order: CommissionForOrder {
1485                maker: dec!(0.00000112),
1486                taker: dec!(0.00000114),
1487            },
1488            tax_commission_for_order: CommissionForOrder {
1489                maker: dec!(0.00000112),
1490                taker: dec!(0.00000114),
1491            },
1492            discount: Discount {
1493                enabled_for_account: true,
1494                enabled_for_symbol: true,
1495                discount_asset: String::from("BNB"),
1496                discount: dec!(0.25000000),
1497            },
1498        };
1499        let expected = TestCommissionRates::Full(rates);
1500
1501        let current = deserialize_json(json).unwrap();
1502
1503        assert_eq!(expected, current);
1504    }
1505
1506    #[test]
1507    fn deserialize_response_account_information() {
1508        let json = r#"{
1509            "makerCommission": 15,
1510            "takerCommission": 15,
1511            "buyerCommission": 0,
1512            "sellerCommission": 0,
1513            "commissionRates": {
1514                "maker": "0.00150000",
1515                "taker": "0.00150000",
1516                "buyer": "0.00000000",
1517                "seller": "0.00000000"
1518            },
1519            "canTrade": true,
1520            "canWithdraw": true,
1521            "canDeposit": true,
1522            "brokered": false,
1523            "requireSelfTradePrevention": false,
1524            "preventSor": false,
1525            "updateTime": 123456789,
1526            "accountType": "SPOT",
1527            "balances": [
1528                {
1529                "asset": "BTC",
1530                "free": "4723846.89208129",
1531                "locked": "0.00000000"
1532                },
1533                {
1534                "asset": "LTC",
1535                "free": "4763368.68006011",
1536                "locked": "0.00000000"
1537                }
1538            ],
1539            "permissions": [
1540                "SPOT"
1541            ],
1542            "uid": 354937868
1543        }"#;
1544        let expected = AccountInformation {
1545            maker_commission: 15.0,
1546            taker_commission: 15.0,
1547            buyer_commission: 0.0,
1548            seller_commission: 0.0,
1549            commission_rates: CommissionRates {
1550                maker: dec!(0.00150000),
1551                taker: dec!(0.00150000),
1552                buyer: dec!(0.00000000),
1553                seller: dec!(0.00000000),
1554            },
1555            can_trade: true,
1556            can_withdraw: true,
1557            can_deposit: true,
1558            brokered: false,
1559            require_self_trade_prevention: false,
1560            prevent_sor: false,
1561            update_time: 123456789,
1562            account_type: AccountType::Spot,
1563            balances: vec![
1564                Balance {
1565                    asset: String::from("BTC"),
1566                    free: dec!(4723846.89208129),
1567                    locked: dec!(0.00000000),
1568                },
1569                Balance {
1570                    asset: String::from("LTC"),
1571                    free: dec!(4763368.68006011),
1572                    locked: dec!(0.00000000),
1573                },
1574            ],
1575            permissions: Some(vec![String::from("SPOT")]),
1576            permission_sets: None,
1577            uid: 354937868,
1578        };
1579
1580        let current = deserialize_json(json).unwrap();
1581
1582        assert_eq!(expected, current);
1583    }
1584
1585    #[test]
1586    fn deserialize_response_query_order() {
1587        let json = r#"{
1588            "symbol": "LTCBTC",
1589            "orderId": 1,
1590            "orderListId": -1,
1591            "clientOrderId": "myOrder1",
1592            "price": "0.1",
1593            "origQty": "1.0",
1594            "executedQty": "0.0",
1595            "cummulativeQuoteQty": "0.0",
1596            "status": "NEW",
1597            "timeInForce": "GTC",
1598            "type": "LIMIT",
1599            "side": "BUY",
1600            "stopPrice": "0.0",
1601            "icebergQty": "0.0",
1602            "time": 1499827319559,
1603            "updateTime": 1499827319559,
1604            "isWorking": true,
1605            "workingTime":1499827319559,
1606            "origQuoteOrderQty": "0.000000",
1607            "selfTradePreventionMode": "NONE"
1608        }"#;
1609        let expected = Order {
1610            symbol: String::from("LTCBTC"),
1611            order_id: 1,
1612            order_list_id: -1,
1613            client_order_id: String::from("myOrder1"),
1614            price: dec!(0.1),
1615            orig_qty: dec!(1.0),
1616            executed_qty: dec!(0.0),
1617            cummulative_quote_qty: dec!(0.0),
1618            status: OrderStatus::New,
1619            time_in_force: TimeInForce::GTC,
1620            order_type: OrderType::Limit,
1621            side: OrderSide::BUY,
1622            stop_price: Some(dec!(0.0)),
1623            iceberg_qty: Some(dec!(0.0)),
1624            time: 1499827319559,
1625            update_time: 1499827319559,
1626            is_working: true,
1627            working_time: 1499827319559,
1628            orig_quote_order_qty: dec!(0.000000),
1629            self_trade_prevention_mode: STPMode::None,
1630        };
1631
1632        let current = deserialize_json(json).unwrap();
1633
1634        assert_eq!(expected, current);
1635    }
1636}