Skip to main content

binance_rs_plus/
model.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{from_value, Value};
3use crate::errors::{Error, Result};
4
5#[derive(Deserialize, Clone)]
6pub struct Empty {}
7
8#[derive(Serialize, Deserialize, Clone)]
9#[serde(rename_all = "camelCase")]
10pub struct ServerTime {
11    pub server_time: u64,
12}
13
14#[derive(Debug, Serialize, Deserialize, Clone)]
15#[serde(rename_all = "camelCase")]
16pub struct ExchangeInformation {
17    pub timezone: String,
18    pub server_time: u64,
19    pub rate_limits: Vec<RateLimit>,
20    pub symbols: Vec<Symbol>,
21}
22
23#[derive(Debug, Serialize, Deserialize, Clone)]
24#[serde(rename_all = "camelCase")]
25pub struct RateLimit {
26    pub rate_limit_type: String,
27    pub interval: String,
28    pub interval_num: u16,
29    pub limit: u64,
30}
31
32#[derive(Debug, Serialize, Deserialize, Clone)]
33#[serde(rename_all = "camelCase")]
34pub struct Symbol {
35    pub symbol: String,
36    pub status: String,
37    pub base_asset: String,
38    pub base_asset_precision: u64,
39    pub quote_asset: String,
40    pub quote_precision: u64,
41    pub order_types: Vec<String>,
42    pub iceberg_allowed: bool,
43    pub is_spot_trading_allowed: bool,
44    pub is_margin_trading_allowed: bool,
45    pub filters: Vec<Filters>,
46}
47
48#[derive(Debug, Serialize, Deserialize, Clone)]
49#[serde(tag = "filterType")]
50pub enum Filters {
51    #[serde(rename = "PRICE_FILTER")]
52    #[serde(rename_all = "camelCase")]
53    PriceFilter {
54        min_price: String,
55        max_price: String,
56        tick_size: String,
57    },
58    #[serde(rename = "PERCENT_PRICE")]
59    #[serde(rename_all = "camelCase")]
60    PercentPrice {
61        multiplier_up: String,
62        multiplier_down: String,
63        avg_price_mins: Option<f64>,
64    },
65    #[serde(rename = "PERCENT_PRICE_BY_SIDE")]
66    #[serde(rename_all = "camelCase")]
67    PercentPriceBySide {
68        bid_multiplier_up: String,
69        bid_multiplier_down: String,
70        ask_multiplier_up: String,
71        ask_multiplier_down: String,
72        avg_price_mins: Option<f64>,
73    },
74    #[serde(rename = "LOT_SIZE")]
75    #[serde(rename_all = "camelCase")]
76    LotSize {
77        min_qty: String,
78        max_qty: String,
79        step_size: String,
80    },
81    #[serde(rename = "MIN_NOTIONAL")]
82    #[serde(rename_all = "camelCase")]
83    MinNotional {
84        notional: Option<String>,
85        min_notional: Option<String>,
86        apply_to_market: Option<bool>,
87        avg_price_mins: Option<f64>,
88    },
89    #[serde(rename = "NOTIONAL")]
90    #[serde(rename_all = "camelCase")]
91    Notional {
92        notional: Option<String>,
93        min_notional: Option<String>,
94        apply_to_market: Option<bool>,
95        avg_price_mins: Option<f64>,
96    },
97    #[serde(rename = "ICEBERG_PARTS")]
98    #[serde(rename_all = "camelCase")]
99    IcebergParts { limit: Option<u16> },
100    #[serde(rename = "MAX_NUM_ORDERS")]
101    #[serde(rename_all = "camelCase")]
102    MaxNumOrders { max_num_orders: Option<u16> },
103    #[serde(rename = "MAX_NUM_ALGO_ORDERS")]
104    #[serde(rename_all = "camelCase")]
105    MaxNumAlgoOrders { max_num_algo_orders: Option<u16> },
106    #[serde(rename = "MAX_NUM_ICEBERG_ORDERS")]
107    #[serde(rename_all = "camelCase")]
108    MaxNumIcebergOrders { max_num_iceberg_orders: u16 },
109    #[serde(rename = "MAX_POSITION")]
110    #[serde(rename_all = "camelCase")]
111    MaxPosition { max_position: String },
112    #[serde(rename = "MARKET_LOT_SIZE")]
113    #[serde(rename_all = "camelCase")]
114    MarketLotSize {
115        min_qty: String,
116        max_qty: String,
117        step_size: String,
118    },
119    #[serde(rename = "TRAILING_DELTA")]
120    #[serde(rename_all = "camelCase")]
121    TrailingData {
122        min_trailing_above_delta: Option<u16>,
123        max_trailing_above_delta: Option<u16>,
124        min_trailing_below_delta: Option<u16>,
125        max_trailing_below_delta: Option<u16>,
126    },
127}
128
129#[derive(Debug, Serialize, Deserialize, Clone)]
130#[serde(rename_all = "camelCase")]
131pub struct AccountInformation {
132    pub maker_commission: f32,
133    pub taker_commission: f32,
134    pub buyer_commission: f32,
135    pub seller_commission: f32,
136    pub can_trade: bool,
137    pub can_withdraw: bool,
138    pub can_deposit: bool,
139    pub balances: Vec<Balance>,
140}
141
142#[derive(Debug, Serialize, Deserialize, Clone)]
143#[serde(rename_all = "camelCase")]
144pub struct Balance {
145    pub asset: String,
146    pub free: String,
147    pub locked: String,
148}
149
150#[derive(Debug, Serialize, Deserialize, Clone)]
151#[serde(rename_all = "camelCase")]
152pub struct Order {
153    pub symbol: String,
154    pub order_id: u64,
155    pub order_list_id: i64,
156    pub client_order_id: String,
157    #[serde(with = "string_or_float")]
158    pub price: f64,
159    pub orig_qty: String,
160    pub executed_qty: String,
161    pub cummulative_quote_qty: String,
162    pub status: String,
163    pub time_in_force: String,
164    #[serde(rename = "type")]
165    pub type_name: String,
166    pub side: String,
167    #[serde(with = "string_or_float")]
168    pub stop_price: f64,
169    pub iceberg_qty: String,
170    pub time: u64,
171    pub update_time: u64,
172    pub is_working: bool,
173    pub orig_quote_order_qty: String,
174}
175
176#[derive(Debug, Serialize, Deserialize, Clone)]
177#[serde(rename_all = "camelCase")]
178pub struct OrderCanceled {
179    pub symbol: String,
180    pub orig_client_order_id: Option<String>,
181    pub order_id: Option<u64>,
182    pub client_order_id: Option<String>,
183}
184#[derive(Debug, Serialize, Deserialize, Clone)]
185#[serde(rename_all = "camelCase")]
186pub enum SpotFuturesTransferType {
187    SpotToUsdtFutures = 1,
188    UsdtFuturesToSpot = 2,
189    SpotToCoinFutures = 3,
190    CoinFuturesToSpot = 4,
191}
192
193#[derive(Debug, Serialize, Deserialize, Clone)]
194#[serde(rename_all = "camelCase")]
195pub struct TransactionId {
196    pub tran_id: u64,
197}
198
199#[derive(Debug, Serialize, Deserialize, Clone)]
200#[serde(rename_all = "camelCase")]
201pub struct Transaction {
202    pub symbol: String,
203    pub order_id: u64,
204    pub order_list_id: Option<i64>,
205    pub client_order_id: String,
206    pub transact_time: u64,
207    #[serde(with = "string_or_float")]
208    pub price: f64,
209    #[serde(with = "string_or_float")]
210    pub orig_qty: f64,
211    #[serde(with = "string_or_float")]
212    pub executed_qty: f64,
213    #[serde(with = "string_or_float")]
214    pub cummulative_quote_qty: f64,
215    #[serde(with = "string_or_float", default = "default_stop_price")]
216    pub stop_price: f64,
217    pub status: String,
218    pub time_in_force: String,
219    #[serde(rename = "type")]
220    pub type_name: String,
221    pub side: String,
222    pub fills: Option<Vec<FillInfo>>,
223}
224
225fn default_stop_price() -> f64 {
226    0.0
227}
228
229#[derive(Debug, Serialize, Deserialize, Clone)]
230#[serde(rename_all = "camelCase")]
231pub struct FillInfo {
232    #[serde(with = "string_or_float")]
233    pub price: f64,
234    #[serde(with = "string_or_float")]
235    pub qty: f64,
236    #[serde(with = "string_or_float")]
237    pub commission: f64,
238    pub commission_asset: String,
239    pub trade_id: Option<u64>,
240}
241/// Response to a test order (endpoint /api/v3/order/test).
242///
243/// Currently, the API responds {} on a successfull test transaction,
244/// hence this struct has no fields.
245#[derive(Debug, Serialize, Deserialize, Clone)]
246#[serde(rename_all = "camelCase")]
247pub struct TestResponse {}
248
249#[derive(Debug, Serialize, Deserialize, Clone)]
250#[serde(rename_all = "camelCase")]
251pub struct OrderBook {
252    pub last_update_id: u64,
253    pub bids: Vec<Bids>,
254    pub asks: Vec<Asks>,
255}
256
257#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
258pub struct Bids {
259    #[serde(with = "string_or_float")]
260    pub price: f64,
261    #[serde(with = "string_or_float")]
262    pub qty: f64,
263}
264
265impl Bids {
266    pub fn new(price: f64, qty: f64) -> Bids {
267        Bids { price, qty }
268    }
269}
270
271#[derive(Debug, Serialize, Deserialize, Clone)]
272pub struct Asks {
273    #[serde(with = "string_or_float")]
274    pub price: f64,
275    #[serde(with = "string_or_float")]
276    pub qty: f64,
277}
278
279#[derive(Debug, Serialize, Deserialize, Clone)]
280#[serde(rename_all = "camelCase")]
281pub struct UserDataStream {
282    pub listen_key: String,
283}
284
285#[derive(Debug, Serialize, Deserialize, Clone)]
286pub struct Success {}
287
288#[derive(Debug, Serialize, Deserialize, Clone)]
289#[serde(rename_all = "camelCase")]
290#[serde(untagged)]
291pub enum Prices {
292    AllPrices(Vec<SymbolPrice>),
293}
294
295#[derive(Debug, Serialize, Deserialize, Clone)]
296pub struct SymbolPrice {
297    pub symbol: String,
298    #[serde(with = "string_or_float")]
299    pub price: f64,
300}
301
302#[derive(Debug, Serialize, Deserialize, Clone)]
303pub struct AveragePrice {
304    pub mins: u64,
305    #[serde(with = "string_or_float")]
306    pub price: f64,
307}
308
309#[derive(Debug, Serialize, Deserialize, Clone)]
310#[serde(rename_all = "camelCase")]
311#[serde(untagged)]
312pub enum BookTickers {
313    AllBookTickers(Vec<Tickers>),
314}
315
316#[derive(Debug, Serialize, Deserialize, Clone)]
317pub enum KlineSummaries {
318    AllKlineSummaries(Vec<KlineSummary>),
319}
320
321#[derive(Debug, Serialize, Deserialize, Clone)]
322#[serde(rename_all = "camelCase")]
323pub struct Tickers {
324    pub symbol: String,
325    #[serde(with = "string_or_float")]
326    pub bid_price: f64,
327    #[serde(with = "string_or_float")]
328    pub bid_qty: f64,
329    #[serde(with = "string_or_float")]
330    pub ask_price: f64,
331    #[serde(with = "string_or_float")]
332    pub ask_qty: f64,
333}
334
335#[derive(Debug, Serialize, Deserialize, Clone)]
336#[serde(rename_all = "camelCase")]
337pub struct TradeHistory {
338    pub id: u64,
339    #[serde(with = "string_or_float")]
340    pub price: f64,
341    #[serde(with = "string_or_float")]
342    pub qty: f64,
343    pub commission: String,
344    pub commission_asset: String,
345    pub time: u64,
346    pub is_buyer: bool,
347    pub is_maker: bool,
348    pub is_best_match: bool,
349}
350
351#[derive(Debug, Serialize, Deserialize, Clone)]
352#[serde(rename_all = "camelCase")]
353pub struct PriceStats {
354    pub symbol: String,
355    pub price_change: String,
356    pub price_change_percent: String,
357    pub weighted_avg_price: String,
358    #[serde(with = "string_or_float")]
359    pub prev_close_price: f64,
360    #[serde(with = "string_or_float")]
361    pub last_price: f64,
362    #[serde(with = "string_or_float")]
363    pub bid_price: f64,
364    #[serde(with = "string_or_float")]
365    pub ask_price: f64,
366    #[serde(with = "string_or_float")]
367    pub open_price: f64,
368    #[serde(with = "string_or_float")]
369    pub high_price: f64,
370    #[serde(with = "string_or_float")]
371    pub low_price: f64,
372    #[serde(with = "string_or_float")]
373    pub volume: f64,
374    pub open_time: u64,
375    pub close_time: u64,
376    pub first_id: i64,
377    pub last_id: i64,
378    pub count: u64,
379}
380
381#[derive(Debug, Serialize, Deserialize, Clone)]
382pub struct AggTrade {
383    #[serde(rename = "T")]
384    pub time: u64,
385    #[serde(rename = "a")]
386    pub agg_id: u64,
387    #[serde(rename = "f")]
388    pub first_id: u64,
389    #[serde(rename = "l")]
390    pub last_id: u64,
391    #[serde(rename = "m")]
392    pub maker: bool,
393    #[serde(rename = "M")]
394    pub best_match: bool,
395    #[serde(rename = "p", with = "string_or_float")]
396    pub price: f64,
397    #[serde(rename = "q", with = "string_or_float")]
398    pub qty: f64,
399}
400
401#[derive(Debug, Serialize, Deserialize, Clone)]
402#[serde(rename_all = "camelCase")]
403pub struct UserDataStreamExpiredEvent {
404    #[serde(rename = "e")]
405    pub event_type: String,
406
407    #[serde(rename = "E")]
408    pub event_time: u64,
409}
410
411#[derive(Debug, Serialize, Deserialize, Clone)]
412#[serde(rename_all = "camelCase")]
413pub struct AccountUpdateEvent {
414    #[serde(rename = "e")]
415    pub event_type: String,
416
417    #[serde(rename = "E")]
418    pub event_time: u64,
419
420    #[serde(rename = "a")]
421    pub data: AccountUpdateDataEvent,
422}
423
424#[derive(Debug, Serialize, Deserialize, Clone)]
425#[serde(rename_all = "camelCase")]
426pub struct AccountUpdateDataEvent {
427    #[serde(rename = "m")]
428    pub reason: String,
429
430    #[serde(rename = "B")]
431    pub balances: Vec<EventBalance>,
432
433    #[serde(rename = "P")]
434    pub positions: Vec<EventPosition>,
435}
436
437#[derive(Debug, Serialize, Deserialize, Clone)]
438#[serde(rename_all = "camelCase")]
439pub struct EventBalance {
440    #[serde(rename = "a")]
441    pub asset: String,
442    #[serde(rename = "wb")]
443    pub wallet_balance: String,
444    #[serde(rename = "cw")]
445    pub cross_wallet_balance: String,
446    #[serde(rename = "bc")]
447    pub balance_change: String, // Balance Change except PnL and Commission
448}
449
450#[derive(Debug, Serialize, Deserialize, Clone)]
451#[serde(rename_all = "camelCase")]
452pub struct EventPosition {
453    #[serde(rename = "s")]
454    pub symbol: String,
455    #[serde(rename = "pa")]
456    pub position_amount: String,
457    #[serde(rename = "ep")]
458    pub entry_price: String,
459    #[serde(rename = "cr")]
460    pub accumulated_realized: String, // (Pre-fee) Accumulated Realized
461    #[serde(rename = "up")]
462    pub unrealized_pnl: String,
463    #[serde(rename = "mt")]
464    pub margin_type: String,
465    #[serde(rename = "iw")]
466    pub isolated_wallet: String,
467    #[serde(rename = "ps")]
468    pub position_side: String,
469}
470
471#[derive(Debug, Serialize, Deserialize, Clone)]
472#[serde(rename_all = "camelCase")]
473pub struct BalanceUpdateEvent {
474    #[serde(rename = "B")]
475    pub balance: Vec<EventBalance>,
476
477    #[serde(rename = "e")]
478    pub event_type: String,
479
480    #[serde(rename = "E")]
481    pub event_time: u64,
482
483    #[serde(rename = "u")]
484    pub last_account_update_time: u64,
485}
486
487#[derive(Debug, Serialize, Deserialize, Clone)]
488#[serde(rename_all = "camelCase")]
489pub struct OrderTradeEvent {
490    #[serde(rename = "e")]
491    pub event_type: String,
492
493    #[serde(rename = "E")]
494    pub event_time: u64,
495
496    #[serde(rename = "s")]
497    pub symbol: String,
498
499    #[serde(rename = "c")]
500    pub new_client_order_id: String,
501
502    #[serde(rename = "S")]
503    pub side: String,
504
505    #[serde(rename = "o")]
506    pub order_type: String,
507
508    #[serde(rename = "f")]
509    pub time_in_force: String,
510
511    #[serde(rename = "q")]
512    pub qty: String,
513
514    #[serde(rename = "p")]
515    pub price: String,
516
517    #[serde(skip, rename = "P")]
518    pub p_ignore: String,
519
520    #[serde(skip, rename = "F")]
521    pub f_ignore: String,
522
523    #[serde(skip)]
524    pub g: i32,
525
526    #[serde(skip, rename = "C")]
527    pub c_ignore: Option<String>,
528
529    #[serde(rename = "x")]
530    pub execution_type: String,
531
532    #[serde(rename = "X")]
533    pub order_status: String,
534
535    #[serde(rename = "r")]
536    pub order_reject_reason: String,
537
538    #[serde(rename = "i")]
539    pub order_id: u64,
540
541    #[serde(rename = "l")]
542    pub qty_last_filled_trade: String,
543
544    #[serde(rename = "z")]
545    pub accumulated_qty_filled_trades: String,
546
547    #[serde(rename = "L")]
548    pub price_last_filled_trade: String,
549
550    #[serde(rename = "n")]
551    pub commission: String,
552
553    #[serde(skip, rename = "N")]
554    pub asset_commisioned: Option<String>,
555
556    #[serde(rename = "T")]
557    pub trade_order_time: u64,
558
559    #[serde(rename = "t")]
560    pub trade_id: i64,
561
562    #[serde(skip, rename = "I")]
563    pub i_ignore: u64,
564
565    #[serde(skip)]
566    pub w: bool,
567
568    #[serde(rename = "m")]
569    pub is_buyer_maker: bool,
570
571    #[serde(skip, rename = "M")]
572    pub m_ignore: bool,
573}
574
575/// The Aggregate Trade Streams push trade information that is aggregated for a single taker order.
576///
577/// Stream Name: \<symbol\>@aggTrade
578///
579/// Update Speed: Real-time
580///
581/// <https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-streams.md#aggregate-trade-streams>
582#[derive(Debug, Serialize, Deserialize, Clone)]
583#[serde(rename_all = "camelCase")]
584pub struct AggrTradesEvent {
585    #[serde(rename = "e")]
586    pub event_type: String,
587
588    #[serde(rename = "E")]
589    pub event_time: u64,
590
591    #[serde(rename = "s")]
592    pub symbol: String,
593
594    #[serde(rename = "a")]
595    pub aggregated_trade_id: u64,
596
597    #[serde(rename = "p")]
598    pub price: String,
599
600    #[serde(rename = "q")]
601    pub qty: String,
602
603    #[serde(rename = "f")]
604    pub first_break_trade_id: u64,
605
606    #[serde(rename = "l")]
607    pub last_break_trade_id: u64,
608
609    #[serde(rename = "T")]
610    pub trade_order_time: u64,
611
612    #[serde(rename = "m")]
613    pub is_buyer_maker: bool,
614
615    #[serde(skip, rename = "M")]
616    pub m_ignore: bool,
617}
618
619/// The Trade Streams push raw trade information; each trade has a unique buyer and seller.
620///
621/// Stream Name: \<symbol\>@trade
622///
623/// Update Speed: Real-time
624///
625/// <https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-streams.md#trade-streams>
626#[derive(Debug, Serialize, Deserialize, Clone)]
627#[serde(rename_all = "camelCase")]
628pub struct TradeEvent {
629    #[serde(rename = "e")]
630    pub event_type: String,
631
632    #[serde(rename = "E")]
633    pub event_time: u64,
634
635    #[serde(rename = "s")]
636    pub symbol: String,
637
638    #[serde(rename = "t")]
639    pub trade_id: u64,
640
641    #[serde(rename = "p")]
642    pub price: String,
643
644    #[serde(rename = "q")]
645    pub qty: String,
646
647    #[serde(rename = "b")]
648    pub buyer_order_id: u64,
649
650    #[serde(rename = "a")]
651    pub seller_order_id: u64,
652
653    #[serde(rename = "T")]
654    pub trade_order_time: u64,
655
656    #[serde(rename = "m")]
657    pub is_buyer_maker: bool,
658
659    #[serde(skip, rename = "M")]
660    pub m_ignore: bool,
661}
662
663#[derive(Debug, Serialize, Deserialize, Clone)]
664#[serde(rename_all = "camelCase")]
665pub struct IndexPriceEvent {
666    #[serde(rename = "e")]
667    pub event_type: String,
668
669    #[serde(rename = "E")]
670    pub event_time: u64,
671
672    #[serde(rename = "i")]
673    pub pair: String,
674
675    #[serde(rename = "p")]
676    pub price: String,
677}
678// https://binance-docs.github.io/apidocs/futures/en/#mark-price-stream
679// https://binance-docs.github.io/apidocs/delivery/en/#mark-price-stream
680#[derive(Debug, Serialize, Deserialize, Clone)]
681#[serde(rename_all = "camelCase")]
682pub struct MarkPriceEvent {
683    #[serde(rename = "E")]
684    pub event_time: u64,
685
686    #[serde(rename = "P")]
687    pub estimate_settle_price: String,
688
689    #[serde(rename = "T")]
690    pub next_funding_time: u64,
691
692    #[serde(rename = "e")]
693    pub event_type: String,
694
695    #[serde(rename = "i")]
696    pub index_price: Option<String>,
697
698    #[serde(rename = "p")]
699    pub mark_price: String,
700
701    #[serde(rename = "r")]
702    pub funding_rate: String,
703
704    #[serde(rename = "s")]
705    pub symbol: String,
706}
707
708// Object({"E": Number(1626118018407), "e": String("forceOrder"), "o": Object({"S": String("SELL"), "T": Number(1626118018404), "X": String("FILLED"), "ap": String("33028.07"), "f": String("IOC"), "l": String("0.010"), "o": String("LIMIT"), "p": String("32896.00"), "q": String("0.010"), "s": String("BTCUSDT"), "z": String("0.010")})})
709// https://binance-docs.github.io/apidocs/futures/en/#liquidation-order-streams
710
711#[derive(Debug, Serialize, Deserialize, Clone)]
712#[serde(rename_all = "camelCase")]
713pub struct LiquidationEvent {
714    #[serde(rename = "e")]
715    pub event_type: String,
716
717    #[serde(rename = "E")]
718    pub event_time: u64,
719
720    #[serde(rename = "o")]
721    pub liquidation_order: LiquidationOrder,
722}
723
724#[derive(Debug, Serialize, Deserialize, Clone)]
725#[serde(rename_all = "camelCase")]
726pub struct LiquidationOrder {
727    #[serde(rename = "s")]
728    pub symbol: String,
729
730    #[serde(rename = "S")]
731    pub side: String,
732
733    #[serde(rename = "o")]
734    pub order_type: String,
735
736    #[serde(rename = "f")]
737    pub time_in_force: String,
738
739    #[serde(rename = "q")]
740    pub original_quantity: String,
741
742    #[serde(rename = "p")]
743    pub price: String,
744
745    #[serde(rename = "ap")]
746    pub average_price: String,
747
748    #[serde(rename = "X")]
749    pub order_status: String,
750
751    #[serde(rename = "l")]
752    pub order_last_filled_quantity: String,
753
754    #[serde(rename = "z")]
755    pub order_filled_accumulated_quantity: String,
756
757    #[serde(rename = "T")]
758    pub order_trade_time: u64,
759}
760
761#[derive(Debug, Serialize, Deserialize, Clone)]
762#[serde(rename_all = "camelCase")]
763pub struct BookTickerEvent {
764    #[serde(rename = "u")]
765    pub update_id: u64,
766
767    #[serde(rename = "s")]
768    pub symbol: String,
769
770    #[serde(rename = "b")]
771    pub best_bid: String,
772
773    #[serde(rename = "B")]
774    pub best_bid_qty: String,
775
776    #[serde(rename = "a")]
777    pub best_ask: String,
778
779    #[serde(rename = "A")]
780    pub best_ask_qty: String,
781}
782
783#[derive(Debug, Serialize, Deserialize, Clone)]
784#[serde(rename_all = "camelCase")]
785pub struct DayTickerEvent {
786    #[serde(rename = "e")]
787    pub event_type: String,
788
789    #[serde(rename = "E")]
790    pub event_time: u64,
791
792    #[serde(rename = "s")]
793    pub symbol: String,
794
795    #[serde(rename = "p")]
796    pub price_change: String,
797
798    #[serde(rename = "P")]
799    pub price_change_percent: String,
800
801    #[serde(rename = "w")]
802    pub average_price: String,
803
804    #[serde(rename = "x")]
805    pub prev_close: String,
806
807    #[serde(rename = "c")]
808    pub current_close: String,
809
810    #[serde(rename = "Q")]
811    pub current_close_qty: String,
812
813    #[serde(rename = "b")]
814    pub best_bid: String,
815
816    #[serde(rename = "B")]
817    pub best_bid_qty: String,
818
819    #[serde(rename = "a")]
820    pub best_ask: String,
821
822    #[serde(rename = "A")]
823    pub best_ask_qty: String,
824
825    #[serde(rename = "o")]
826    pub open: String,
827
828    #[serde(rename = "h")]
829    pub high: String,
830
831    #[serde(rename = "l")]
832    pub low: String,
833
834    #[serde(rename = "v")]
835    pub volume: String,
836
837    #[serde(rename = "q")]
838    pub quote_volume: String,
839
840    #[serde(rename = "O")]
841    pub open_time: u64,
842
843    #[serde(rename = "C")]
844    pub close_time: u64,
845
846    #[serde(rename = "F")]
847    pub first_trade_id: i64,
848
849    #[serde(rename = "L")]
850    pub last_trade_id: i64,
851
852    #[serde(rename = "n")]
853    pub num_trades: u64,
854}
855
856#[derive(Debug, Serialize, Deserialize, Clone)]
857#[serde(rename_all = "camelCase")]
858pub struct WindowTickerEvent {
859    #[serde(rename = "e")]
860    pub event_type: String,
861
862    #[serde(rename = "E")]
863    pub event_time: u64,
864
865    #[serde(rename = "s")]
866    pub symbol: String,
867
868    #[serde(rename = "p")]
869    pub price_change: String,
870
871    #[serde(rename = "P")]
872    pub price_change_percent: String,
873
874    #[serde(rename = "o")]
875    pub open: String,
876
877    #[serde(rename = "h")]
878    pub high: String,
879
880    #[serde(rename = "l")]
881    pub low: String,
882
883    #[serde(rename = "c")]
884    pub current_close: String,
885
886    #[serde(rename = "w")]
887    pub average_price: String,
888
889    #[serde(rename = "v")]
890    pub volume: String,
891
892    #[serde(rename = "q")]
893    pub quote_volume: String,
894
895    #[serde(rename = "O")]
896    pub open_time: u64,
897
898    #[serde(rename = "C")]
899    pub close_time: u64,
900
901    #[serde(rename = "F")]
902    pub first_trade_id: i64,
903
904    #[serde(rename = "L")]
905    pub last_trade_id: i64,
906
907    #[serde(rename = "n")]
908    pub num_trades: u64,
909}
910
911#[derive(Debug, Serialize, Deserialize, Clone)]
912#[serde(rename_all = "camelCase")]
913pub struct MiniTickerEvent {
914    #[serde(rename = "e")]
915    pub event_type: String,
916
917    #[serde(rename = "E")]
918    pub event_time: u64,
919
920    #[serde(rename = "s")]
921    pub symbol: String,
922
923    #[serde(rename = "c")]
924    pub close: String,
925
926    #[serde(rename = "o")]
927    pub open: String,
928
929    #[serde(rename = "h")]
930    pub high: String,
931
932    #[serde(rename = "l")]
933    pub low: String,
934
935    #[serde(rename = "v")]
936    pub volume: String,
937
938    #[serde(rename = "q")]
939    pub quote_volume: String,
940}
941
942#[derive(Debug, Serialize, Deserialize, Clone)]
943#[serde(rename_all = "camelCase")]
944pub struct KlineEvent {
945    #[serde(rename = "e")]
946    pub event_type: String,
947
948    #[serde(rename = "E")]
949    pub event_time: u64,
950
951    #[serde(rename = "s")]
952    pub symbol: String,
953
954    #[serde(rename = "k")]
955    pub kline: Kline,
956}
957
958// https://binance-docs.github.io/apidocs/futures/en/#continuous-contract-kline-candlestick-streams
959
960#[derive(Debug, Serialize, Deserialize, Clone)]
961#[serde(rename_all = "camelCase")]
962pub struct ContinuousKlineEvent {
963    #[serde(rename = "e")]
964    pub event_type: String,
965
966    #[serde(rename = "E")]
967    pub event_time: u64,
968
969    #[serde(rename = "ps")]
970    pub pair: String,
971
972    #[serde(rename = "ct")]
973    pub contract_type: String,
974
975    #[serde(rename = "k")]
976    pub kline: ContinuousKline,
977}
978
979// https://binance-docs.github.io/apidocs/delivery/en/#index-kline-candlestick-streams
980
981#[derive(Debug, Serialize, Deserialize, Clone)]
982#[serde(rename_all = "camelCase")]
983pub struct IndexKlineEvent {
984    #[serde(rename = "e")]
985    pub event_type: String,
986
987    #[serde(rename = "E")]
988    pub event_time: u64,
989
990    #[serde(rename = "ps")]
991    pub pair: String,
992
993    #[serde(rename = "k")]
994    pub kline: IndexKline,
995}
996
997#[derive(Debug, Serialize, Deserialize, Clone)]
998pub struct KlineSummary {
999    pub open_time: i64,
1000
1001    pub open: String,
1002
1003    pub high: String,
1004
1005    pub low: String,
1006
1007    pub close: String,
1008
1009    pub volume: String,
1010
1011    pub close_time: i64,
1012
1013    pub quote_asset_volume: String,
1014
1015    pub number_of_trades: i64,
1016
1017    pub taker_buy_base_asset_volume: String,
1018
1019    pub taker_buy_quote_asset_volume: String,
1020}
1021
1022fn get_value(row: &[Value], index: usize, name: &'static str) -> Result<Value> {
1023    Ok(row
1024        .get(index)
1025        .ok_or_else(|| Error::KlineValueMissingError { index, name })?
1026        .clone())
1027}
1028
1029impl TryFrom<&Vec<Value>> for KlineSummary {
1030    type Error = Error;
1031
1032    fn try_from(row: &Vec<Value>) -> Result<Self> {
1033        Ok(Self {
1034            open_time: from_value(get_value(row, 0, "open_time")?)?,
1035            open: from_value(get_value(row, 1, "open")?)?,
1036            high: from_value(get_value(row, 2, "high")?)?,
1037            low: from_value(get_value(row, 3, "low")?)?,
1038            close: from_value(get_value(row, 4, "close")?)?,
1039            volume: from_value(get_value(row, 5, "volume")?)?,
1040            close_time: from_value(get_value(row, 6, "close_time")?)?,
1041            quote_asset_volume: from_value(get_value(row, 7, "quote_asset_volume")?)?,
1042            number_of_trades: from_value(get_value(row, 8, "number_of_trades")?)?,
1043            taker_buy_base_asset_volume: from_value(get_value(
1044                row,
1045                9,
1046                "taker_buy_base_asset_volume",
1047            )?)?,
1048            taker_buy_quote_asset_volume: from_value(get_value(
1049                row,
1050                10,
1051                "taker_buy_quote_asset_volume",
1052            )?)?,
1053        })
1054    }
1055}
1056
1057#[derive(Debug, Serialize, Deserialize, Clone)]
1058#[serde(rename_all = "camelCase")]
1059pub struct Kline {
1060    #[serde(rename = "t")]
1061    pub open_time: i64,
1062
1063    #[serde(rename = "T")]
1064    pub close_time: i64,
1065
1066    #[serde(rename = "s")]
1067    pub symbol: String,
1068
1069    #[serde(rename = "i")]
1070    pub interval: String,
1071
1072    #[serde(rename = "f")]
1073    pub first_trade_id: i64,
1074
1075    #[serde(rename = "L")]
1076    pub last_trade_id: i64,
1077
1078    #[serde(rename = "o")]
1079    pub open: String,
1080
1081    #[serde(rename = "c")]
1082    pub close: String,
1083
1084    #[serde(rename = "h")]
1085    pub high: String,
1086
1087    #[serde(rename = "l")]
1088    pub low: String,
1089
1090    #[serde(rename = "v")]
1091    pub volume: String,
1092
1093    #[serde(rename = "n")]
1094    pub number_of_trades: i64,
1095
1096    #[serde(rename = "x")]
1097    pub is_final_bar: bool,
1098
1099    #[serde(rename = "q")]
1100    pub quote_asset_volume: String,
1101
1102    #[serde(rename = "V")]
1103    pub taker_buy_base_asset_volume: String,
1104
1105    #[serde(rename = "Q")]
1106    pub taker_buy_quote_asset_volume: String,
1107
1108    #[serde(skip, rename = "B")]
1109    pub ignore_me: String,
1110}
1111
1112#[derive(Debug, Serialize, Deserialize, Clone)]
1113#[serde(rename_all = "camelCase")]
1114pub struct ContinuousKline {
1115    #[serde(rename = "t")]
1116    pub start_time: i64,
1117
1118    #[serde(rename = "T")]
1119    pub end_time: i64,
1120
1121    #[serde(rename = "i")]
1122    pub interval: String,
1123
1124    #[serde(rename = "f")]
1125    pub first_trade_id: i64,
1126
1127    #[serde(rename = "L")]
1128    pub last_trade_id: i64,
1129
1130    #[serde(rename = "o")]
1131    pub open: String,
1132
1133    #[serde(rename = "c")]
1134    pub close: String,
1135
1136    #[serde(rename = "h")]
1137    pub high: String,
1138
1139    #[serde(rename = "l")]
1140    pub low: String,
1141
1142    #[serde(rename = "v")]
1143    pub volume: String,
1144
1145    #[serde(rename = "n")]
1146    pub number_of_trades: i64,
1147
1148    #[serde(rename = "x")]
1149    pub is_final_bar: bool,
1150
1151    #[serde(rename = "q")]
1152    pub quote_volume: String,
1153
1154    #[serde(rename = "V")]
1155    pub active_buy_volume: String,
1156
1157    #[serde(rename = "Q")]
1158    pub active_volume_buy_quote: String,
1159
1160    #[serde(skip, rename = "B")]
1161    pub ignore_me: String,
1162}
1163
1164// https://binance-docs.github.io/apidocs/delivery/en/#index-kline-candlestick-streams
1165#[derive(Debug, Serialize, Deserialize, Clone)]
1166#[serde(rename_all = "camelCase")]
1167pub struct IndexKline {
1168    #[serde(rename = "t")]
1169    pub start_time: i64,
1170
1171    #[serde(rename = "T")]
1172    pub end_time: i64,
1173
1174    #[serde(skip, rename = "s")]
1175    pub ignore_me: String,
1176
1177    #[serde(rename = "i")]
1178    pub interval: String,
1179
1180    #[serde(rename = "f")]
1181    pub first_trade_id: i64,
1182
1183    #[serde(rename = "L")]
1184    pub last_trade_id: i64,
1185
1186    #[serde(rename = "o")]
1187    pub open: String,
1188
1189    #[serde(rename = "c")]
1190    pub close: String,
1191
1192    #[serde(rename = "h")]
1193    pub high: String,
1194
1195    #[serde(rename = "l")]
1196    pub low: String,
1197
1198    #[serde(rename = "v")]
1199    pub volume: String,
1200
1201    #[serde(rename = "n")]
1202    pub number_of_trades: i64,
1203
1204    #[serde(rename = "x")]
1205    pub is_final_bar: bool,
1206
1207    #[serde(skip, rename = "q")]
1208    pub ignore_me2: String,
1209
1210    #[serde(skip, rename = "V")]
1211    pub ignore_me3: String,
1212
1213    #[serde(skip, rename = "Q")]
1214    pub ignore_me4: String,
1215
1216    #[serde(skip, rename = "B")]
1217    pub ignore_me5: String,
1218}
1219
1220#[derive(Debug, Serialize, Deserialize, Clone)]
1221#[serde(rename_all = "camelCase")]
1222pub struct DepthOrderBookEvent {
1223    #[serde(rename = "e")]
1224    pub event_type: String,
1225
1226    #[serde(rename = "E")]
1227    pub event_time: u64,
1228
1229    #[serde(rename = "s")]
1230    pub symbol: String,
1231
1232    #[serde(rename = "U")]
1233    pub first_update_id: u64,
1234
1235    #[serde(rename = "u")]
1236    pub final_update_id: u64,
1237
1238    #[serde(rename = "pu")]
1239    #[serde(default)]
1240    pub previous_final_update_id: Option<u64>,
1241
1242    #[serde(rename = "b")]
1243    pub bids: Vec<Bids>,
1244
1245    #[serde(rename = "a")]
1246    pub asks: Vec<Asks>,
1247}
1248
1249/// Response to the Savings API get all coins request
1250#[derive(Debug, Serialize, Deserialize, Clone)]
1251#[serde(rename_all = "camelCase")]
1252pub struct CoinInfo {
1253    pub coin: String,
1254    pub deposit_all_enable: bool,
1255    #[serde(with = "string_or_float")]
1256    pub free: f64,
1257    #[serde(with = "string_or_float")]
1258    pub freeze: f64,
1259    #[serde(with = "string_or_float")]
1260    pub ipoable: f64,
1261    #[serde(with = "string_or_float")]
1262    pub ipoing: f64,
1263    pub is_legal_money: bool,
1264    #[serde(with = "string_or_float")]
1265    pub locked: f64,
1266    pub name: String,
1267    pub network_list: Vec<Network>,
1268    #[serde(with = "string_or_float")]
1269    pub storage: f64,
1270    pub trading: bool,
1271    pub withdraw_all_enable: bool,
1272    #[serde(with = "string_or_float")]
1273    pub withdrawing: f64,
1274}
1275
1276/// Part of the Savings API get all coins response
1277#[derive(Debug, Serialize, Deserialize, Clone)]
1278#[serde(rename_all = "camelCase")]
1279pub struct Network {
1280    pub address_regex: String,
1281    pub coin: String,
1282    /// shown only when "depositEnable" is false.
1283    pub deposit_desc: Option<String>,
1284    pub deposit_enable: bool,
1285    pub is_default: bool,
1286    pub memo_regex: String,
1287    /// min number for balance confirmation
1288    pub min_confirm: u32,
1289    pub name: String,
1290    pub network: String,
1291    pub reset_address_status: bool,
1292    pub special_tips: Option<String>,
1293    /// confirmation number for balance unlock
1294    pub un_lock_confirm: u32,
1295    /// shown only when "withdrawEnable" is false.
1296    pub withdraw_desc: Option<String>,
1297    pub withdraw_enable: bool,
1298    #[serde(with = "string_or_float")]
1299    pub withdraw_fee: f64,
1300    #[serde(with = "string_or_float")]
1301    pub withdraw_min: f64,
1302    // pub insert_time: Option<u64>, //commented out for now, because they are not inside the actual response (only the api doc example)
1303    // pub update_time: Option<u64>,
1304    pub withdraw_integer_multiple: Option<String>,
1305}
1306
1307#[derive(Debug, Serialize, Deserialize, Clone)]
1308#[serde(rename_all = "camelCase")]
1309pub struct AssetDetail {
1310    #[serde(with = "string_or_float")]
1311    pub min_withdraw_amount: f64,
1312    /// false if ALL of networks' are false
1313    pub deposit_status: bool,
1314    #[serde(with = "string_or_float")]
1315    pub withdraw_fee: f64,
1316    /// false if ALL of networks' are false
1317    pub withdraw_status: bool,
1318    /// reason
1319    pub deposit_tip: Option<String>,
1320}
1321
1322#[derive(Debug, Serialize, Deserialize, Clone)]
1323pub struct DepositAddress {
1324    pub address: String,
1325    pub coin: String,
1326    pub tag: String,
1327    pub url: String,
1328}
1329
1330pub(crate) mod string_or_float {
1331    use std::fmt;
1332
1333    use serde::{de, Serializer, Deserialize, Deserializer};
1334
1335    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
1336    where
1337        T: fmt::Display,
1338        S: Serializer,
1339    {
1340        serializer.collect_str(value)
1341    }
1342
1343    pub fn deserialize<'de, D>(deserializer: D) -> Result<f64, D::Error>
1344    where
1345        D: Deserializer<'de>,
1346    {
1347        #[derive(Deserialize)]
1348        #[serde(untagged)]
1349        enum StringOrFloat {
1350            String(String),
1351            Float(f64),
1352        }
1353
1354        match StringOrFloat::deserialize(deserializer)? {
1355            StringOrFloat::String(s) => {
1356                if s == "INF" {
1357                    Ok(f64::INFINITY)
1358                } else {
1359                    s.parse().map_err(de::Error::custom)
1360                }
1361            }
1362            StringOrFloat::Float(i) => Ok(i),
1363        }
1364    }
1365}
1366
1367pub(crate) mod string_or_float_opt {
1368    use std::fmt;
1369
1370    use serde::{Serializer, Deserialize, Deserializer};
1371
1372    pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
1373    where
1374        T: fmt::Display,
1375        S: Serializer,
1376    {
1377        match value {
1378            Some(v) => crate::model::string_or_float::serialize(v, serializer),
1379            None => serializer.serialize_none(),
1380        }
1381    }
1382
1383    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
1384    where
1385        D: Deserializer<'de>,
1386    {
1387        #[derive(Deserialize)]
1388        #[serde(untagged)]
1389        enum StringOrFloat {
1390            String(()),
1391            Float(()),
1392        }
1393
1394        Ok(Some(crate::model::string_or_float::deserialize(
1395            deserializer,
1396        )?))
1397    }
1398}
1399
1400pub(crate) mod string_or_bool {
1401    use std::fmt;
1402
1403    use serde::{de, Serializer, Deserialize, Deserializer};
1404
1405    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
1406    where
1407        T: fmt::Display,
1408        S: Serializer,
1409    {
1410        serializer.collect_str(value)
1411    }
1412
1413    pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
1414    where
1415        D: Deserializer<'de>,
1416    {
1417        #[derive(Deserialize)]
1418        #[serde(untagged)]
1419        enum StringOrFloat {
1420            String(String),
1421            Bool(bool),
1422        }
1423
1424        match StringOrFloat::deserialize(deserializer)? {
1425            StringOrFloat::String(s) => s.parse().map_err(de::Error::custom),
1426            StringOrFloat::Bool(i) => Ok(i),
1427        }
1428    }
1429}
1430
1431#[test]
1432fn test_account_update_event() {
1433    let json = r#"
1434    {
1435  "e": "ACCOUNT_UPDATE",
1436  "E": 1564745798939,
1437  "T": 1564745798938,
1438  "a": {
1439    "m": "ORDER",
1440    "B": [
1441      {
1442        "a": "USDT",
1443        "wb": "122624.12345678",
1444        "cw": "100.12345678",
1445        "bc": "50.12345678"
1446      },
1447      {
1448        "a": "BUSD",
1449        "wb": "1.00000000",
1450        "cw": "0.00000000",
1451        "bc": "-49.12345678"
1452      }
1453    ],
1454    "P": [
1455      {
1456        "s": "BTCUSDT",
1457        "pa": "0",
1458        "ep": "0.00000",
1459        "cr": "200",
1460        "up": "0",
1461        "mt": "isolated",
1462        "iw": "0.00000000",
1463        "ps": "BOTH"
1464      },
1465      {
1466        "s": "BTCUSDT",
1467        "pa": "20",
1468        "ep": "6563.66500",
1469        "cr": "0",
1470        "up": "2850.21200",
1471        "mt": "isolated",
1472        "iw": "13200.70726908",
1473        "ps": "LONG"
1474      },
1475      {
1476        "s": "BTCUSDT",
1477        "pa": "-10",
1478        "ep": "6563.86000",
1479        "cr": "-45.04000000",
1480        "up": "-1423.15600",
1481        "mt": "isolated",
1482        "iw": "6570.42511771",
1483        "ps": "SHORT"
1484      }
1485    ]
1486  }
1487}
1488    "#;
1489
1490    let res = r#"AccountUpdateEvent { event_type: "ACCOUNT_UPDATE", event_time: 1564745798939, data: AccountUpdateDataEvent { reason: "ORDER", balances: [EventBalance { asset: "USDT", wallet_balance: "122624.12345678", cross_wallet_balance: "100.12345678", balance_change: "50.12345678" }, EventBalance { asset: "BUSD", wallet_balance: "1.00000000", cross_wallet_balance: "0.00000000", balance_change: "-49.12345678" }], positions: [EventPosition { symbol: "BTCUSDT", position_amount: "0", entry_price: "0.00000", accumulated_realized: "200", unrealized_pnl: "0", margin_type: "isolated", isolated_wallet: "0.00000000", position_side: "BOTH" }, EventPosition { symbol: "BTCUSDT", position_amount: "20", entry_price: "6563.66500", accumulated_realized: "0", unrealized_pnl: "2850.21200", margin_type: "isolated", isolated_wallet: "13200.70726908", position_side: "LONG" }, EventPosition { symbol: "BTCUSDT", position_amount: "-10", entry_price: "6563.86000", accumulated_realized: "-45.04000000", unrealized_pnl: "-1423.15600", margin_type: "isolated", isolated_wallet: "6570.42511771", position_side: "SHORT" }] } }"#;
1491    let v: AccountUpdateEvent = serde_json::from_str(json).unwrap();
1492    assert_eq!(format!("{:?}", v), res);
1493    //let event =  from_value::<AccountUpdateEvent>(json).unwrap();
1494}