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 symbol: Option<String>,
38 symbols: Option<Vec<String>>,
43 permissions: Option<Vec<String>>,
50 show_permission_sets: Option<bool>,
52 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 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, pub quote_asset: String,
118 pub quote_asset_precision: u8, pub base_commission_precision: u8, pub quote_commission_precision: u8, 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 }
144
145#[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 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 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 limit: Option<u64>,
236 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 from_id: Option<i64>,
266 start_time: Option<Timestamp>,
268 end_time: Option<Timestamp>,
270 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 #[serde(rename = "a")]
310 pub id: i64,
311 #[serde(rename = "p")]
313 pub price: Decimal,
314 #[serde(rename = "q")]
316 pub qty: Decimal,
317 #[serde(rename = "f")]
319 pub first_trade_id: i64,
320 #[serde(rename = "l")]
322 pub last_trade_id: i64,
323 #[serde(rename = "T")]
325 pub time: Timestamp,
326 #[serde(rename = "m")]
328 pub is_buyer_maker: bool,
329 #[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 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, Decimal, Decimal, Decimal, Decimal, Decimal, Timestamp, Decimal, u64, Decimal, Decimal, String, );
394
395impl Kline {
396 pub fn time_open(&self) -> Timestamp {
398 self.0
399 }
400 pub fn open(&self) -> Decimal {
402 self.1
403 }
404 pub fn high(&self) -> Decimal {
406 self.2
407 }
408 pub fn low(&self) -> Decimal {
410 self.3
411 }
412 pub fn close(&self) -> Decimal {
414 self.4
415 }
416 pub fn volume(&self) -> Decimal {
418 self.5
419 }
420 pub fn time_close(&self) -> Timestamp {
422 self.6
423 }
424 pub fn quote_asset_volume(&self) -> Decimal {
426 self.7
427 }
428 pub fn id(&self) -> u64 {
430 self.8
431 }
432 pub fn taker_buy_base_asset_volume(&self) -> Decimal {
434 self.9
435 }
436 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 pub mins: u64,
461 pub price: Decimal,
463 pub close_time: Timestamp,
465}
466
467#[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 symbol: Option<String>,
481 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 pub first_id: i64,
536 pub last_id: i64,
538 pub count: u64,
540}
541
542#[derive(Debug, Deserialize, PartialEq)]
543#[serde(rename_all = "camelCase")]
544pub struct TickerPriceChangeStatisticMini {
545 pub symbol: String,
547 pub open_price: Decimal,
549 pub high_price: Decimal,
551 pub low_price: Decimal,
553 pub last_price: Decimal,
555 pub volume: Decimal,
557 pub quote_volume: Decimal,
559 pub open_time: Timestamp,
561 pub close_time: Timestamp,
563 pub first_id: i64,
565 pub last_id: i64,
567 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 new_client_order_id: Option<String>,
585 strategy_id: Option<i64>,
586 strategy_type: Option<i64>,
588 stop_price: Option<Decimal>,
590 trailing_delta: Option<i64>,
592 iceberg_qty: Option<Decimal>,
594 new_order_resp_type: OrderResponseType,
597 self_trade_prevention_mode: Option<STPMode>,
599 recv_window: Option<i64>,
601 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 self.quantity.is_some() || self.quote_order_qty.is_some()
712 }
713 OrderType::StopLoss => {
714 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 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 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 pub order_list_id: i64,
759 pub client_order_id: String,
760 pub transact_time: Timestamp,
761 pub iceberg_qty: Option<Decimal>,
764 pub prevented_match_id: Option<i64>,
767 pub prevented_quantity: Option<Decimal>,
770 pub stop_price: Option<Decimal>,
773 pub strategy_id: Option<i64>,
776 pub strategy_type: Option<i64>,
779 pub trailing_delta: Option<i64>,
782 pub trailing_time: Option<i64>,
785 pub used_sor: Option<bool>,
788 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 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 pub iceberg_qty: Option<Decimal>,
817 pub prevented_match_id: Option<i64>,
820 pub prevented_quantity: Option<Decimal>,
823 pub stop_price: Option<Decimal>,
826 pub strategy_id: Option<i64>,
829 pub strategy_type: Option<i64>,
832 pub trailing_delta: Option<i64>,
835 pub trailing_time: Option<i64>,
838 pub used_sor: Option<bool>,
841 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 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 pub iceberg_qty: Option<Decimal>,
871 pub prevented_match_id: Option<i64>,
874 pub prevented_quantity: Option<Decimal>,
877 pub stop_price: Option<Decimal>,
880 pub strategy_id: Option<i64>,
883 pub strategy_type: Option<i64>,
886 pub trailing_delta: Option<i64>,
889 pub trailing_time: Option<i64>,
892 pub used_sor: Option<bool>,
895 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 pub standard_commission_for_order: CommissionForOrder,
925 pub tax_commission_for_order: CommissionForOrder,
927 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 pub discount: Decimal,
945}
946
947#[derive(Debug, Default, Serialize, PartialEq)]
948#[serde(rename_all = "camelCase")]
949pub struct GetAccountInformationParams {
950 omit_zero_balances: Option<bool>,
953 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 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 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 pub stop_price: Option<Decimal>,
1068 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}