1use rust_decimal::{Decimal, serde::str_option::deserialize as option_decimal};
2use serde::{Deserialize, Serialize};
3use serde_aux::prelude::{
4 deserialize_number_from_string as number,
5 deserialize_option_number_from_string as option_number,
6};
7
8use crate::{
9 ContractType, CopyTrading, CurAuctionPhase, OptionType, Side, Status, Timestamp,
10 enums::{Category, Interval, IntervalTime},
11 serde::{empty_string_as_none, int_to_bool, string_to_bool},
12};
13
14#[derive(Debug, Serialize, Clone)]
16pub struct GetKLinesParams {
17 pub category: Category,
19 pub symbol: String,
21 pub interval: Interval,
23 pub start: Option<Timestamp>,
25 pub end: Option<Timestamp>,
27 pub limit: Option<u64>,
29}
30
31impl GetKLinesParams {
32 pub fn new(category: Category, symbol: impl Into<String>, interval: Interval) -> Self {
34 Self {
35 category,
36 symbol: symbol.into(),
37 interval,
38 start: None,
39 end: None,
40 limit: None,
41 }
42 }
43
44 pub fn with_start(mut self, v: Timestamp) -> Self {
46 self.start = Some(v);
47 self
48 }
49
50 pub fn with_end(mut self, v: Timestamp) -> Self {
52 self.end = Some(v);
53 self
54 }
55
56 pub fn with_limit(mut self, v: u64) -> Self {
58 self.limit = Some(v);
59 self
60 }
61}
62
63#[derive(Debug, Deserialize, PartialEq)]
65#[serde(tag = "category")]
66pub enum KLine {
67 #[serde(rename = "inverse")]
69 Inverse {
70 symbol: String,
72 list: Vec<KLineRow>,
74 },
75 #[serde(rename = "linear")]
77 Linear {
78 symbol: String,
80 list: Vec<KLineRow>,
82 },
83 #[serde(rename = "option")]
85 Option {
86 symbol: String,
88 list: Vec<KLineRow>,
90 },
91 #[serde(rename = "spot")]
93 Spot {
94 symbol: String,
96 list: Vec<KLineRow>,
98 },
99}
100
101#[derive(Debug, Deserialize, PartialEq)]
104pub struct PriceKLine {
105 pub category: Category,
107 pub symbol: String,
109 pub list: Vec<PriceKLineRow>,
111}
112
113#[derive(Debug, Deserialize, PartialEq)]
115pub struct PriceKLineRow {
116 #[serde(deserialize_with = "number")]
118 pub start_time: Timestamp,
119 pub open_price: Decimal,
121 pub high_price: Decimal,
123 pub low_price: Decimal,
125 pub close_price: Decimal,
127}
128
129#[derive(Debug, Deserialize, PartialEq)]
131pub struct KLineRow {
132 #[serde(rename = "startTime", deserialize_with = "number")]
134 pub start_time: Timestamp,
135 #[serde(rename = "openPrice")]
137 pub open_price: Decimal,
138 #[serde(rename = "highPrice")]
140 pub high_price: Decimal,
141 #[serde(rename = "lowPrice")]
143 pub low_price: Decimal,
144 #[serde(rename = "closePrice")]
146 pub close_price: Decimal,
147 #[serde(rename = "volume")]
149 pub volume: Decimal,
150 #[serde(rename = "turnover")]
152 pub turnover: Decimal,
153}
154
155#[derive(Debug, Serialize, Clone)]
157#[serde(rename_all = "camelCase")]
158pub struct GetTickersParams {
159 pub category: Category,
161 pub symbol: Option<String>,
163 pub base_coin: Option<String>,
165 pub exp_date: Option<String>,
167}
168
169impl GetTickersParams {
170 pub fn new(category: Category) -> Self {
172 Self {
173 category,
174 symbol: None,
175 base_coin: None,
176 exp_date: None,
177 }
178 }
179
180 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
182 self.symbol = Some(v.into());
183 self
184 }
185
186 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
188 self.base_coin = Some(v.into());
189 self
190 }
191
192 pub fn with_exp_date(mut self, v: impl Into<String>) -> Self {
194 self.exp_date = Some(v.into());
195 self
196 }
197}
198
199#[derive(Debug, Deserialize, PartialEq)]
201#[serde(tag = "category")]
202pub enum Ticker {
203 #[serde(rename = "inverse")]
205 Inverse {
206 list: Vec<LinearInverseTicker>,
208 },
209 #[serde(rename = "linear")]
211 Linear {
212 list: Vec<LinearInverseTicker>,
214 },
215 #[serde(rename = "option")]
217 Option {
218 list: Vec<OptionTicker>,
220 },
221 #[serde(rename = "spot")]
223 Spot {
224 list: Vec<SpotTicker>,
226 },
227}
228
229#[derive(Debug, Deserialize, PartialEq)]
231#[serde(rename_all = "camelCase")]
232pub struct LinearInverseTicker {
233 pub symbol: String,
235 pub last_price: Decimal,
237 pub mark_price: Decimal,
239 pub index_price: Decimal,
241 pub prev_price24h: Decimal,
243 pub price24h_pcnt: Decimal,
245 pub high_price24h: Decimal,
247 pub low_price24h: Decimal,
249 pub prev_price1h: Decimal,
251 pub open_interest: Decimal,
253 pub open_interest_value: Decimal,
255 pub turnover24h: Decimal,
257 pub volume24h: Decimal,
259 #[serde(default, deserialize_with = "option_decimal")]
261 pub funding_rate: Option<Decimal>,
262 #[serde(deserialize_with = "number")]
264 pub next_funding_time: Timestamp,
265 #[serde(default, deserialize_with = "option_decimal")]
267 pub predicted_delivery_price: Option<Decimal>,
268 #[serde(default, deserialize_with = "option_decimal")]
270 pub basis_rate: Option<Decimal>,
271 #[serde(default, deserialize_with = "option_decimal")]
273 pub basis: Option<Decimal>,
274 #[serde(default, deserialize_with = "option_decimal")]
276 pub delivery_fee_rate: Option<Decimal>,
277 #[serde(deserialize_with = "option_number")]
279 pub delivery_time: Option<Timestamp>,
280 pub bid1_price: Decimal,
282 pub bid1_size: Decimal,
284 pub ask1_price: Decimal,
286 pub ask1_size: Decimal,
288 #[serde(default, deserialize_with = "option_decimal")]
290 pub pre_open_price: Option<Decimal>,
291 #[serde(default, deserialize_with = "option_decimal")]
293 pub pre_qty: Option<Decimal>,
294 #[serde(default, deserialize_with = "empty_string_as_none")]
296 pub cur_pre_listing_phase: Option<CurAuctionPhase>,
297}
298
299#[derive(Debug, Deserialize, PartialEq)]
301#[serde(rename_all = "camelCase")]
302pub struct OptionTicker {
303 pub symbol: String,
305 pub bid1_price: Decimal,
307 pub bid1_size: Decimal,
309 pub bid1_iv: Decimal,
311 pub ask1_price: Decimal,
313 pub ask1_size: Decimal,
315 pub ask1_iv: Decimal,
317 pub last_price: Decimal,
319 pub high_price24h: Decimal,
321 pub low_price24h: Decimal,
323 pub mark_price: Decimal,
325 pub index_price: Decimal,
327 pub mark_iv: Decimal,
329 pub underlying_price: Decimal,
331 pub open_interest: Decimal,
333 pub turnover24h: Decimal,
335 pub volume24h: Decimal,
337 pub total_volume: Decimal,
339 pub total_turnover: Decimal,
341 pub delta: Decimal,
343 pub gamma: Decimal,
345 pub vega: Decimal,
347 pub theta: Decimal,
349 pub predicted_delivery_price: Decimal,
351 pub change24h: Decimal,
353}
354
355#[derive(Debug, Deserialize, PartialEq)]
357#[serde(rename_all = "camelCase")]
358pub struct SpotTicker {
359 pub symbol: String,
361 pub bid1_price: Decimal,
363 pub bid1_size: Decimal,
365 pub ask1_price: Decimal,
367 pub ask1_size: Decimal,
369 pub last_price: Decimal,
371 pub prev_price24h: Decimal,
373 pub price24h_pcnt: Decimal,
375 pub high_price24h: Decimal,
377 pub low_price24h: Decimal,
379 pub turnover24h: Decimal,
381 pub volume24h: Decimal,
383 #[serde(default, deserialize_with = "option_decimal")]
388 pub usd_index_price: Option<Decimal>,
389}
390
391#[derive(Debug, Serialize, Clone)]
393pub struct GetOrderbookParams {
394 pub category: Category,
396 pub symbol: String,
398 pub limit: Option<u64>,
403}
404
405impl GetOrderbookParams {
406 pub fn new(category: Category, symbol: impl Into<String>) -> Self {
408 Self {
409 category,
410 symbol: symbol.into(),
411 limit: None,
412 }
413 }
414
415 pub fn with_limit(mut self, v: u64) -> Self {
417 self.limit = Some(v);
418 self
419 }
420}
421
422#[derive(Debug, Deserialize, PartialEq)]
424pub struct Orderbook {
425 #[serde(rename = "s")]
427 pub symbol: String,
428 #[serde(rename = "b")]
430 pub bids: Vec<OrderbookLevel>,
431 #[serde(rename = "a")]
433 pub asks: Vec<OrderbookLevel>,
434 pub ts: Timestamp,
436 #[serde(rename = "u")]
439 pub update_id: i64,
440 pub seq: i64,
443 #[serde(default)]
445 pub cts: Option<Timestamp>,
446}
447
448#[derive(Debug, Deserialize, PartialEq)]
450pub struct OrderbookLevel {
451 pub price: Decimal,
453 pub size: Decimal,
455}
456
457#[derive(Debug, Serialize, Clone)]
459#[serde(rename_all = "camelCase")]
460pub struct GetTradesParams {
461 pub category: Category,
463 pub symbol: Option<String>,
466 pub base_coin: Option<String>,
469 pub option_type: Option<OptionType>,
471 pub limit: Option<u64>,
474}
475
476impl GetTradesParams {
477 pub fn new(category: Category) -> Self {
479 Self {
480 category,
481 symbol: None,
482 base_coin: None,
483 option_type: None,
484 limit: None,
485 }
486 }
487
488 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
490 self.symbol = Some(v.into());
491 self
492 }
493
494 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
496 self.base_coin = Some(v.into());
497 self
498 }
499
500 pub fn with_option_type(mut self, v: OptionType) -> Self {
502 self.option_type = Some(v);
503 self
504 }
505
506 pub fn with_limit(mut self, v: u64) -> Self {
508 self.limit = Some(v);
509 self
510 }
511}
512
513#[derive(Debug, Deserialize, PartialEq)]
515#[serde(tag = "category")]
516pub enum Trade {
517 #[serde(rename = "inverse")]
519 Inverse {
520 list: Vec<InverseLinearSpotTrade>,
522 },
523 #[serde(rename = "linear")]
525 Linear {
526 list: Vec<InverseLinearSpotTrade>,
528 },
529 #[serde(rename = "option")]
531 Option {
532 list: Vec<OptionTrade>,
534 },
535 #[serde(rename = "spot")]
537 Spot {
538 list: Vec<InverseLinearSpotTrade>,
540 },
541}
542
543#[derive(Debug, Deserialize, PartialEq)]
545#[serde(rename_all = "camelCase")]
546pub struct InverseLinearSpotTrade {
547 pub exec_id: String,
549 pub symbol: String,
551 pub price: Decimal,
553 pub size: Decimal,
555 pub side: Side,
557 #[serde(deserialize_with = "number")]
559 pub time: Timestamp,
560 pub is_block_trade: bool,
562 #[serde(rename = "isRPITrade")]
564 pub is_rpi_trade: bool,
565}
566
567#[derive(Debug, Deserialize, PartialEq)]
569#[serde(rename_all = "camelCase")]
570pub struct OptionTrade {
571 pub exec_id: String,
573 pub symbol: String,
575 pub price: Decimal,
577 pub size: Decimal,
579 pub side: Side,
581 #[serde(deserialize_with = "number")]
583 pub time: Timestamp,
584 pub is_block_trade: bool,
586 #[serde(rename = "isRPITrade", default)]
588 pub is_rpi_trade: bool,
589 #[serde(rename = "mP")]
591 pub mark_price: Decimal,
592 #[serde(rename = "iP")]
594 pub index_price: Decimal,
595 #[serde(rename = "mIv")]
597 pub mark_iv: Decimal,
598 #[serde(rename = "iv")]
600 pub iv: Decimal,
601}
602
603#[derive(Debug, Deserialize, PartialEq)]
605#[serde(rename_all = "camelCase")]
606pub struct ServerTime {
607 #[serde(deserialize_with = "number")]
609 pub time_second: u64,
610 #[serde(deserialize_with = "number")]
612 pub time_nano: u64,
613}
614
615#[derive(Debug, Serialize, Clone)]
617#[serde(rename_all = "camelCase")]
618pub struct GetInstrumentsInfoParams {
619 pub category: Category,
621 pub symbol: Option<String>,
623 pub status: Option<Status>,
625 pub base_coin: Option<String>,
627 pub limit: Option<i64>,
629 pub cursor: Option<String>,
631}
632
633impl GetInstrumentsInfoParams {
634 pub fn new(category: Category) -> Self {
636 Self {
637 category,
638 symbol: None,
639 status: None,
640 base_coin: None,
641 limit: None,
642 cursor: None,
643 }
644 }
645
646 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
648 self.symbol = Some(v.into());
649 self
650 }
651
652 pub fn with_status(mut self, v: Status) -> Self {
654 self.status = Some(v);
655 self
656 }
657
658 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
660 self.base_coin = Some(v.into());
661 self
662 }
663
664 pub fn with_limit(mut self, v: i64) -> Self {
666 self.limit = Some(v);
667 self
668 }
669
670 pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
672 self.cursor = Some(v.into());
673 self
674 }
675}
676
677#[derive(Debug, Deserialize, PartialEq)]
679#[serde(tag = "category")]
680pub enum InstrumentsInfo {
681 #[serde(rename = "inverse", rename_all = "camelCase")]
683 Inverse {
684 next_page_cursor: String,
686 list: Vec<InverseLinearInstrumentsInfo>,
688 },
689 #[serde(rename = "linear", rename_all = "camelCase")]
691 Linear {
692 next_page_cursor: String,
694 list: Vec<InverseLinearInstrumentsInfo>,
696 },
697 #[serde(rename = "option", rename_all = "camelCase")]
699 Option {
700 next_page_cursor: String,
702 list: Vec<OptionInstrumentsInfo>,
704 },
705 #[serde(rename = "spot", rename_all = "camelCase")]
707 Spot {
708 #[serde(default, deserialize_with = "empty_string_as_none")]
710 next_page_cursor: Option<String>,
711 list: Vec<SpotInstrumentsInfo>,
713 },
714}
715
716#[derive(Debug, Deserialize, PartialEq)]
718#[serde(rename_all = "camelCase")]
719pub struct InverseLinearInstrumentsInfo {
720 pub symbol: String,
722 pub contract_type: ContractType,
724 pub status: Status,
726 pub base_coin: String,
728 pub quote_coin: String,
730 #[serde(deserialize_with = "number")]
732 pub launch_time: Timestamp,
733 #[serde(deserialize_with = "number")]
735 pub delivery_time: Timestamp,
736 #[serde(deserialize_with = "option_decimal")]
738 pub delivery_fee_rate: Option<Decimal>,
739 #[serde(deserialize_with = "number")]
741 pub price_scale: i64,
742 pub leverage_filter: LeverageFilter,
744 pub price_filter: PriceFilter,
746 pub lot_size_filter: LotSizeFilter,
748 pub unified_margin_trade: bool,
750 pub funding_interval: i64,
752 pub settle_coin: String,
754 pub copy_trading: CopyTrading,
756 pub upper_funding_rate: Decimal,
758 pub lower_funding_rate: Decimal,
760 pub risk_parameters: RiskParameters,
762 pub is_pre_listing: bool,
764 pub pre_listing_info: Option<PreListingInfo>,
766}
767
768#[derive(Debug, Deserialize, PartialEq)]
770#[serde(rename_all = "camelCase")]
771pub struct OptionInstrumentsInfo {
772 pub symbol: String,
774 #[serde(default)]
776 pub display_name: Option<String>,
777 pub status: Status,
779 pub base_coin: String,
781 pub quote_coin: String,
783 pub settle_coin: String,
785 pub options_type: OptionType,
787 #[serde(deserialize_with = "number")]
789 pub launch_time: Timestamp,
790 #[serde(deserialize_with = "number")]
792 pub delivery_time: Timestamp,
793 #[serde(default, deserialize_with = "option_decimal")]
795 pub delivery_fee_rate: Option<Decimal>,
796 pub price_filter: PriceFilter,
798 pub lot_size_filter: OptionLotSizeFilter,
800}
801
802#[derive(Debug, Deserialize, PartialEq)]
804#[serde(rename_all = "camelCase")]
805pub struct OptionLotSizeFilter {
806 pub max_order_qty: Decimal,
808 pub min_order_qty: Decimal,
810 pub qty_step: Decimal,
812}
813
814#[derive(Debug, Deserialize, PartialEq)]
816#[serde(rename_all = "camelCase")]
817pub struct SpotInstrumentsInfo {
818 pub symbol: String,
820 pub base_coin: String,
822 pub quote_coin: String,
824 #[serde(deserialize_with = "string_to_bool")]
826 pub innovation: bool,
827 pub status: Status,
829 pub margin_trading: String,
833 #[serde(deserialize_with = "string_to_bool")]
835 pub st_tag: bool,
836 pub lot_size_filter: SpotLotSizeFilter,
838 pub price_filter: SpotPriceFilter,
840 pub risk_parameters: RiskParameters,
842}
843
844#[derive(Debug, Deserialize, PartialEq)]
846#[serde(rename_all = "camelCase")]
847pub struct LeverageFilter {
848 pub min_leverage: Decimal,
850 pub max_leverage: Decimal,
852 pub leverage_step: Decimal,
854}
855
856#[derive(Debug, Deserialize, PartialEq)]
858#[serde(rename_all = "camelCase")]
859pub struct PriceFilter {
860 pub min_price: Decimal,
862 pub max_price: Decimal,
864 pub tick_size: Decimal,
866}
867
868#[derive(Debug, Deserialize, PartialEq)]
870#[serde(rename_all = "camelCase")]
871pub struct SpotPriceFilter {
872 pub tick_size: Decimal,
874}
875
876#[derive(Debug, Deserialize, PartialEq)]
878#[serde(rename_all = "camelCase")]
879pub struct LotSizeFilter {
880 pub min_notional_value: Decimal,
882 pub max_order_qty: Decimal,
884 pub max_mkt_order_qty: Decimal,
886 pub min_order_qty: Decimal,
888 pub qty_step: Decimal,
890 pub post_only_max_order_qty: Decimal,
892}
893
894#[derive(Debug, Deserialize, PartialEq)]
896#[serde(rename_all = "camelCase")]
897pub struct SpotLotSizeFilter {
898 pub base_precision: Decimal,
900 pub quote_precision: Decimal,
902 pub min_order_qty: Decimal,
904 pub max_order_qty: Decimal,
906 pub min_order_amt: Decimal,
908 pub max_order_amt: Decimal,
910}
911
912#[derive(Debug, Deserialize, PartialEq)]
914#[serde(rename_all = "camelCase")]
915pub struct RiskParameters {
916 pub price_limit_ratio_x: Decimal,
918 pub price_limit_ratio_y: Decimal,
920}
921
922#[derive(Debug, Deserialize, PartialEq)]
924#[serde(rename_all = "camelCase")]
925pub struct PreListingInfo {
926 pub cur_auction_phase: CurAuctionPhase,
928 pub phases: Vec<Phase>,
930 pub auction_fee_info: AuctionFeeInfo,
932}
933
934#[derive(Debug, Deserialize, PartialEq)]
936#[serde(rename_all = "camelCase")]
937pub struct Phase {
938 pub phase: CurAuctionPhase,
940 #[serde(deserialize_with = "option_number")]
942 pub start_time: Option<Timestamp>,
943 #[serde(deserialize_with = "option_number")]
945 pub end_time: Option<Timestamp>,
946}
947
948#[derive(Debug, Deserialize, PartialEq)]
950#[serde(rename_all = "camelCase")]
951pub struct AuctionFeeInfo {
952 pub auction_fee_rate: Decimal,
954 pub taker_fee_rate: Decimal,
956 pub maker_fee_rate: Decimal,
958}
959
960#[derive(Debug, Serialize, Clone)]
966#[serde(rename_all = "camelCase")]
967pub struct GetFundingRateHistoryParams {
968 pub category: Category,
970 pub symbol: String,
972 pub start_time: Option<Timestamp>,
974 pub end_time: Option<Timestamp>,
976 pub limit: Option<u64>,
978}
979
980impl GetFundingRateHistoryParams {
981 pub fn new(category: Category, symbol: impl Into<String>) -> Self {
983 Self {
984 category,
985 symbol: symbol.into(),
986 start_time: None,
987 end_time: None,
988 limit: None,
989 }
990 }
991
992 pub fn with_start_time(mut self, v: Timestamp) -> Self {
994 self.start_time = Some(v);
995 self
996 }
997
998 pub fn with_end_time(mut self, v: Timestamp) -> Self {
1000 self.end_time = Some(v);
1001 self
1002 }
1003
1004 pub fn with_limit(mut self, v: u64) -> Self {
1006 self.limit = Some(v);
1007 self
1008 }
1009}
1010
1011#[derive(Debug, Deserialize, PartialEq)]
1013#[serde(tag = "category")]
1014pub enum FundingRateHistory {
1015 #[serde(rename = "linear")]
1017 Linear {
1018 list: Vec<FundingRateEntry>,
1020 },
1021 #[serde(rename = "inverse")]
1023 Inverse {
1024 list: Vec<FundingRateEntry>,
1026 },
1027}
1028
1029#[derive(Debug, Deserialize, PartialEq)]
1031#[serde(rename_all = "camelCase")]
1032pub struct FundingRateEntry {
1033 pub symbol: String,
1035 pub funding_rate: Decimal,
1037 #[serde(deserialize_with = "number")]
1039 pub funding_rate_timestamp: Timestamp,
1040}
1041
1042#[derive(Debug, Serialize, Clone)]
1048#[serde(rename_all = "camelCase")]
1049pub struct GetOpenInterestParams {
1050 pub category: Category,
1052 pub symbol: String,
1054 pub interval_time: IntervalTime,
1056 pub start_time: Option<Timestamp>,
1058 pub end_time: Option<Timestamp>,
1060 pub limit: Option<u64>,
1062 pub cursor: Option<String>,
1064}
1065
1066impl GetOpenInterestParams {
1067 pub fn new(category: Category, symbol: impl Into<String>, interval_time: IntervalTime) -> Self {
1069 Self {
1070 category,
1071 symbol: symbol.into(),
1072 interval_time,
1073 start_time: None,
1074 end_time: None,
1075 limit: None,
1076 cursor: None,
1077 }
1078 }
1079
1080 pub fn with_start_time(mut self, v: Timestamp) -> Self {
1082 self.start_time = Some(v);
1083 self
1084 }
1085
1086 pub fn with_end_time(mut self, v: Timestamp) -> Self {
1088 self.end_time = Some(v);
1089 self
1090 }
1091
1092 pub fn with_limit(mut self, v: u64) -> Self {
1094 self.limit = Some(v);
1095 self
1096 }
1097
1098 pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
1100 self.cursor = Some(v.into());
1101 self
1102 }
1103}
1104
1105#[derive(Debug, Deserialize, PartialEq)]
1107#[serde(rename_all = "camelCase")]
1108pub struct OpenInterest {
1109 pub symbol: String,
1111 pub category: String,
1113 pub list: Vec<OpenInterestEntry>,
1115 #[serde(default, deserialize_with = "empty_string_as_none")]
1117 pub next_page_cursor: Option<String>,
1118}
1119
1120#[derive(Debug, Deserialize, PartialEq)]
1122#[serde(rename_all = "camelCase")]
1123pub struct OpenInterestEntry {
1124 pub open_interest: Decimal,
1126 #[serde(deserialize_with = "number")]
1128 pub timestamp: Timestamp,
1129}
1130
1131#[derive(Debug, Serialize, Clone)]
1137#[serde(rename_all = "camelCase")]
1138pub struct GetHistoricalVolatilityParams {
1139 pub category: Category,
1141 pub base_coin: Option<String>,
1143 pub period: Option<u16>,
1145 pub start_time: Option<Timestamp>,
1147 pub end_time: Option<Timestamp>,
1149}
1150
1151impl GetHistoricalVolatilityParams {
1152 pub fn new(category: Category) -> Self {
1154 Self {
1155 category,
1156 base_coin: None,
1157 period: None,
1158 start_time: None,
1159 end_time: None,
1160 }
1161 }
1162
1163 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
1165 self.base_coin = Some(v.into());
1166 self
1167 }
1168
1169 pub fn with_period(mut self, v: u16) -> Self {
1171 self.period = Some(v);
1172 self
1173 }
1174
1175 pub fn with_start_time(mut self, v: Timestamp) -> Self {
1177 self.start_time = Some(v);
1178 self
1179 }
1180
1181 pub fn with_end_time(mut self, v: Timestamp) -> Self {
1183 self.end_time = Some(v);
1184 self
1185 }
1186}
1187
1188#[derive(Debug, Deserialize, PartialEq)]
1194pub struct HistoricalVolatilityEntry {
1195 pub period: u16,
1197 pub value: Decimal,
1199 #[serde(deserialize_with = "number")]
1201 pub time: Timestamp,
1202}
1203
1204#[derive(Debug, Serialize, Clone, Default)]
1208pub struct GetInsuranceParams {
1209 pub coin: Option<String>,
1211}
1212
1213impl GetInsuranceParams {
1214 pub fn new() -> Self {
1216 Self { coin: None }
1217 }
1218
1219 pub fn with_coin(mut self, v: impl Into<String>) -> Self {
1221 self.coin = Some(v.into());
1222 self
1223 }
1224}
1225
1226#[derive(Debug, Deserialize, PartialEq)]
1228#[serde(rename_all = "camelCase")]
1229pub struct Insurance {
1230 #[serde(deserialize_with = "number")]
1232 pub updated_time: Timestamp,
1233 pub list: Vec<InsuranceEntry>,
1235}
1236
1237#[derive(Debug, Deserialize, PartialEq)]
1239#[serde(rename_all = "camelCase")]
1240pub struct InsuranceEntry {
1241 pub coin: String,
1243 pub balance: Decimal,
1245 pub value: Decimal,
1247}
1248
1249#[derive(Debug, Serialize, Clone)]
1255pub struct GetRiskLimitParams {
1256 pub category: Category,
1258 pub symbol: Option<String>,
1260 pub cursor: Option<String>,
1262}
1263
1264impl GetRiskLimitParams {
1265 pub fn new(category: Category) -> Self {
1267 Self {
1268 category,
1269 symbol: None,
1270 cursor: None,
1271 }
1272 }
1273
1274 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
1276 self.symbol = Some(v.into());
1277 self
1278 }
1279
1280 pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
1282 self.cursor = Some(v.into());
1283 self
1284 }
1285}
1286
1287#[derive(Debug, Deserialize, PartialEq)]
1289#[serde(rename_all = "camelCase")]
1290pub struct RiskLimit {
1291 pub category: String,
1293 pub list: Vec<RiskLimitEntry>,
1295}
1296
1297#[derive(Debug, Deserialize, PartialEq)]
1299#[serde(rename_all = "camelCase")]
1300pub struct RiskLimitEntry {
1301 pub id: u32,
1303 pub symbol: String,
1305 pub risk_limit_value: Decimal,
1307 pub maintenance_margin: Decimal,
1309 pub initial_margin: Decimal,
1311 #[serde(deserialize_with = "int_to_bool")]
1313 pub is_lowest_risk: bool,
1314 pub max_leverage: Decimal,
1316}
1317
1318#[derive(Debug, Serialize, Clone)]
1324#[serde(rename_all = "camelCase")]
1325pub struct GetDeliveryPriceParams {
1326 pub category: Category,
1328 pub symbol: Option<String>,
1330 pub base_coin: Option<String>,
1332 pub limit: Option<u64>,
1334 pub cursor: Option<String>,
1336}
1337
1338impl GetDeliveryPriceParams {
1339 pub fn new(category: Category) -> Self {
1341 Self {
1342 category,
1343 symbol: None,
1344 base_coin: None,
1345 limit: None,
1346 cursor: None,
1347 }
1348 }
1349
1350 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
1352 self.symbol = Some(v.into());
1353 self
1354 }
1355
1356 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
1358 self.base_coin = Some(v.into());
1359 self
1360 }
1361
1362 pub fn with_limit(mut self, v: u64) -> Self {
1364 self.limit = Some(v);
1365 self
1366 }
1367
1368 pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
1370 self.cursor = Some(v.into());
1371 self
1372 }
1373}
1374
1375#[derive(Debug, Deserialize, PartialEq)]
1377#[serde(rename_all = "camelCase")]
1378pub struct DeliveryPrice {
1379 pub category: String,
1381 #[serde(default, deserialize_with = "empty_string_as_none")]
1383 pub next_page_cursor: Option<String>,
1384 pub list: Vec<DeliveryPriceEntry>,
1386}
1387
1388#[derive(Debug, Deserialize, PartialEq)]
1390#[serde(rename_all = "camelCase")]
1391pub struct DeliveryPriceEntry {
1392 pub symbol: String,
1394 pub delivery_price: Decimal,
1396 #[serde(deserialize_with = "number")]
1398 pub delivery_time: Timestamp,
1399}
1400
1401#[cfg(test)]
1402mod tests {
1403 use super::*;
1404 use crate::{Category, serde::serialize_query};
1405
1406 #[test]
1407 fn trades_params_serialize_option_type_as_call_or_put() {
1408 let params = GetTradesParams {
1409 category: Category::Option,
1410 symbol: None,
1411 base_coin: Some(String::from("BTC")),
1412 option_type: Some(OptionType::Put),
1413 limit: None,
1414 };
1415
1416 let query = serialize_query(¶ms).unwrap();
1417
1418 assert_eq!(query, "category=option&baseCoin=BTC&optionType=Put");
1419 }
1420
1421 #[test]
1422 fn base_coin_and_exp_date_are_sent_in_camel_case() {
1423 let tickers = GetTickersParams::new(Category::Option)
1424 .with_base_coin("ETH")
1425 .with_exp_date("25DEC26");
1426 let instruments = GetInstrumentsInfoParams::new(Category::Option).with_base_coin("ETH");
1427
1428 assert_eq!(
1429 serialize_query(&tickers).unwrap(),
1430 "category=option&baseCoin=ETH&expDate=25DEC26"
1431 );
1432 assert_eq!(
1433 serialize_query(&instruments).unwrap(),
1434 "category=option&baseCoin=ETH"
1435 );
1436 }
1437
1438 #[test]
1439 fn option_instruments_info_parses() {
1440 let json = r#"{
1441 "category": "option",
1442 "nextPageCursor": "",
1443 "list": [{
1444 "symbolId": 379919,
1445 "symbol": "ETH-25JUN27-7000-P-USDT",
1446 "status": "Trading",
1447 "baseCoin": "ETH",
1448 "quoteCoin": "USDT",
1449 "settleCoin": "USDT",
1450 "optionsType": "Put",
1451 "launchTime": "1787350800000",
1452 "deliveryTime": "1813910400000",
1453 "deliveryFeeRate": "0.00015",
1454 "priceFilter": {"minPrice": "0.1", "maxPrice": "41000", "tickSize": "0.1"},
1455 "lotSizeFilter": {"maxOrderQty": "5000", "minOrderQty": "0.1", "qtyStep": "0.1"},
1456 "displayName": "ETHUSDT-25JUN27-7000-P"
1457 }]
1458 }"#;
1459
1460 let info: InstrumentsInfo = serde_json::from_str(json).unwrap();
1461
1462 let InstrumentsInfo::Option { list, .. } = info else {
1463 panic!("not an option list");
1464 };
1465 assert_eq!(list[0].options_type, OptionType::Put);
1466 assert_eq!(list[0].lot_size_filter.qty_step, rust_decimal::dec!(0.1));
1467 assert_eq!(list[0].delivery_time, 1813910400000);
1468 }
1469}