Skip to main content

bybit/http/
market.rs

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)]
15pub struct GetKLinesParams {
16    pub category: Category,
17    pub symbol: String,
18    pub interval: Interval,
19    pub start: Option<Timestamp>,
20    pub end: Option<Timestamp>,
21    pub limit: Option<u64>,
22}
23
24impl GetKLinesParams {
25    pub fn new(category: Category, symbol: impl Into<String>, interval: Interval) -> Self {
26        Self {
27            category,
28            symbol: symbol.into(),
29            interval,
30            start: None,
31            end: None,
32            limit: None,
33        }
34    }
35
36    pub fn with_start(mut self, v: Timestamp) -> Self {
37        self.start = Some(v);
38        self
39    }
40
41    pub fn with_end(mut self, v: Timestamp) -> Self {
42        self.end = Some(v);
43        self
44    }
45
46    pub fn with_limit(mut self, v: u64) -> Self {
47        self.limit = Some(v);
48        self
49    }
50}
51
52#[derive(Debug, Deserialize, PartialEq)]
53#[serde(tag = "category")]
54pub enum KLine {
55    #[serde(rename = "inverse")]
56    Inverse { symbol: String, list: Vec<KLineRow> },
57    #[serde(rename = "linear")]
58    Linear { symbol: String, list: Vec<KLineRow> },
59    #[serde(rename = "option")]
60    Option { symbol: String, list: Vec<KLineRow> },
61    #[serde(rename = "spot")]
62    Spot { symbol: String, list: Vec<KLineRow> },
63}
64
65#[derive(Debug, Deserialize, PartialEq)]
66pub struct KLineRow {
67    /// Start time of the candle (ms)
68    #[serde(rename = "startTime", deserialize_with = "number")]
69    pub start_time: Timestamp,
70    /// Open price
71    #[serde(rename = "openPrice")]
72    pub open_price: Decimal,
73    /// Highest price
74    #[serde(rename = "highPrice")]
75    pub high_price: Decimal,
76    /// Lowest price
77    #[serde(rename = "lowPrice")]
78    pub low_price: Decimal,
79    /// Close price. Is the last traded price when the candle is not closed
80    #[serde(rename = "closePrice")]
81    pub close_price: Decimal,
82    /// Trade volume. Unit of contract: pieces of contract. Unit of spot: quantity of coins
83    #[serde(rename = "volume")]
84    pub volume: Decimal,
85    /// Turnover. Unit of figure: quantity of quota coin
86    #[serde(rename = "turnover")]
87    pub turnover: Decimal,
88}
89
90#[derive(Debug, Serialize, Clone)]
91pub struct GetTickersParams {
92    pub category: Category,
93    pub symbol: Option<String>,
94    pub base_coin: Option<String>,
95    pub exp_date: Option<String>,
96}
97
98impl GetTickersParams {
99    pub fn new(category: Category) -> Self {
100        Self {
101            category,
102            symbol: None,
103            base_coin: None,
104            exp_date: None,
105        }
106    }
107
108    pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
109        self.symbol = Some(v.into());
110        self
111    }
112
113    pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
114        self.base_coin = Some(v.into());
115        self
116    }
117
118    pub fn with_exp_date(mut self, v: impl Into<String>) -> Self {
119        self.exp_date = Some(v.into());
120        self
121    }
122}
123
124#[derive(Debug, Deserialize, PartialEq)]
125#[serde(tag = "category")]
126pub enum Ticker {
127    #[serde(rename = "inverse")]
128    Inverse { list: Vec<LinearInverseTicker> },
129    #[serde(rename = "linear")]
130    Linear { list: Vec<LinearInverseTicker> },
131    #[serde(rename = "option")]
132    Option { list: Vec<OptionTicker> },
133    #[serde(rename = "spot")]
134    Spot { list: Vec<SpotTicker> },
135}
136
137#[derive(Debug, Deserialize, PartialEq)]
138#[serde(rename_all = "camelCase")]
139pub struct LinearInverseTicker {
140    /// Symbol name
141    pub symbol: String,
142    /// Last price
143    pub last_price: Decimal,
144    /// Mark price
145    pub mark_price: Decimal,
146    /// Index price
147    pub index_price: Decimal,
148    /// Market price 24 hours ago
149    pub prev_price24h: Decimal,
150    /// Percentage change of market price in the last 24 hours
151    pub price24h_pcnt: Decimal,
152    /// The highest price in the last 24 hours
153    pub high_price24h: Decimal,
154    /// The lowest price in the last 24 hours
155    pub low_price24h: Decimal,
156    /// Market price an hour ago
157    pub prev_price1h: Decimal,
158    /// Open interest size
159    pub open_interest: Decimal,
160    /// Open interest value
161    pub open_interest_value: Decimal,
162    /// Turnover for 24h
163    pub turnover24h: Decimal,
164    /// Volume for 24h
165    pub volume24h: Decimal,
166    /// Funding rate
167    #[serde(default, deserialize_with = "option_decimal")]
168    pub funding_rate: Option<Decimal>,
169    /// Next funding timestamp (ms)
170    #[serde(deserialize_with = "number")]
171    pub next_funding_time: Timestamp,
172    /// Predicated delivery price. It has value when 30 min before delivery
173    #[serde(default, deserialize_with = "option_decimal")]
174    pub predicted_delivery_price: Option<Decimal>,
175    /// Basis rate. Unique field for inverse futures & USDC futures
176    #[serde(default, deserialize_with = "option_decimal")]
177    pub basis_rate: Option<Decimal>,
178    /// Basis. Unique field for inverse futures & USDC futures
179    #[serde(default, deserialize_with = "option_decimal")]
180    pub basis: Option<Decimal>,
181    /// Delivery fee rate. Unique field for inverse futures & USDC futures
182    #[serde(default, deserialize_with = "option_decimal")]
183    pub delivery_fee_rate: Option<Decimal>,
184    /// Delivery date time (UTC+0). Unique field for inverse futures & USDC futures
185    #[serde(deserialize_with = "option_number")]
186    pub delivery_time: Option<Timestamp>,
187    /// Best bid price
188    pub bid1_price: Decimal,
189    /// Best bid size
190    pub bid1_size: Decimal,
191    /// Best ask price
192    pub ask1_price: Decimal,
193    /// Best ask size
194    pub ask1_size: Decimal,
195    /// Estimated pre-market contract open price. The value is meaningless when entering continuous trading phase.
196    #[serde(default, deserialize_with = "option_decimal")]
197    pub pre_open_price: Option<Decimal>,
198    /// Estimated pre-market contract open qty. The value is meaningless when entering continuous trading phase.
199    #[serde(default, deserialize_with = "option_decimal")]
200    pub pre_qty: Option<Decimal>,
201    /// Enum: NotStarted, Finished, CallAuction, CallAuctionNoCancel, CrossMatching, ContinuousTrading.
202    #[serde(default, deserialize_with = "empty_string_as_none")]
203    pub cur_pre_listing_phase: Option<CurAuctionPhase>,
204}
205
206#[derive(Debug, Deserialize, PartialEq)]
207#[serde(rename_all = "camelCase")]
208pub struct OptionTicker {
209    /// Symbol name
210    pub symbol: String,
211    /// Best bid price
212    pub bid1_price: Decimal,
213    /// Best bid size
214    pub bid1_size: Decimal,
215    /// Best bid iv
216    pub bid1_iv: Decimal,
217    /// Best ask price
218    pub ask1_price: Decimal,
219    /// Best ask size
220    pub ask1_size: Decimal,
221    /// Best ask iv
222    pub ask1_iv: Decimal,
223    /// Last price
224    pub last_price: Decimal,
225    /// The highest price in the last 24 hours
226    pub high_price24h: Decimal,
227    /// The lowest price in the last 24 hours
228    pub low_price24h: Decimal,
229    /// Mark price
230    pub mark_price: Decimal,
231    /// Index price
232    pub index_price: Decimal,
233    /// Mark price iv
234    pub mark_iv: Decimal,
235    /// Underlying price
236    pub underlying_price: Decimal,
237    /// Open interest size
238    pub open_interest: Decimal,
239    /// Turnover for 24h
240    pub turnover24h: Decimal,
241    /// Volume for 24h
242    pub volume24h: Decimal,
243    /// Total volume
244    pub total_volume: Decimal,
245    /// Total turnover
246    pub total_turnover: Decimal,
247    /// Delta
248    pub delta: Decimal,
249    /// Gamma
250    pub gamma: Decimal,
251    /// Vega
252    pub vega: Decimal,
253    /// Theta
254    pub theta: Decimal,
255    /// Predicated delivery price. It has value when 30 min before delivery
256    pub predicted_delivery_price: Decimal,
257    /// The change in the last 24 hours
258    pub change24h: Decimal,
259}
260
261#[derive(Debug, Deserialize, PartialEq)]
262#[serde(rename_all = "camelCase")]
263pub struct SpotTicker {
264    /// Symbol name
265    pub symbol: String,
266    /// Best bid price
267    pub bid1_price: Decimal,
268    /// Best bid size
269    pub bid1_size: Decimal,
270    /// Best ask price
271    pub ask1_price: Decimal,
272    /// Best ask size
273    pub ask1_size: Decimal,
274    /// Last price
275    pub last_price: Decimal,
276    /// Market price 24 hours ago
277    pub prev_price24h: Decimal,
278    /// Percentage change of market price in the last 24 hours
279    pub price24h_pcnt: Decimal,
280    /// The highest price in the last 24 hours
281    pub high_price24h: Decimal,
282    /// The lowest price in the last 24 hours
283    pub low_price24h: Decimal,
284    /// Turnover for 24h
285    pub turnover24h: Decimal,
286    /// Volume for 24h
287    pub volume24h: Decimal,
288    /// USD index price
289    /// - used to calculate USD value of the assets in Unified account
290    /// - non-collateral margin coin returns ""
291    /// - Only those trading pairs like "XXX/USDT" or "XXX/USDC" have the value
292    #[serde(default, deserialize_with = "option_decimal")]
293    pub usd_index_price: Option<Decimal>,
294}
295
296#[derive(Debug, Serialize, Clone)]
297pub struct GetOrderbookParams {
298    pub category: Category,
299    pub symbol: String,
300    /// Limit size for each bid and ask
301    /// - spot: [1, 1000]. Default: 1.
302    /// - linear&inverse: [1, 1000]. Default: 25.
303    /// - option: [1, 25]. Default: 1.
304    pub limit: Option<u64>,
305}
306
307impl GetOrderbookParams {
308    pub fn new(category: Category, symbol: impl Into<String>) -> Self {
309        Self {
310            category,
311            symbol: symbol.into(),
312            limit: None,
313        }
314    }
315
316    pub fn with_limit(mut self, v: u64) -> Self {
317        self.limit = Some(v);
318        self
319    }
320}
321
322#[derive(Debug, Deserialize, PartialEq)]
323pub struct Orderbook {
324    /// Symbol name
325    #[serde(rename = "s")]
326    pub symbol: String,
327    /// Bid, buy side. Sorted by price in descending order
328    #[serde(rename = "b")]
329    pub bids: Vec<OrderbookLevel>,
330    /// Ask, sell side. Sorted by price in ascending order
331    #[serde(rename = "a")]
332    pub asks: Vec<OrderbookLevel>,
333    /// The timestamp (ms) that the system generates the data
334    pub ts: Timestamp,
335    /// Update ID, is a sequence. Occasionally, you'll receive "u"=1, which is a snapshot
336    /// data due to the restart of the service. So please overwrite your local orderbook
337    #[serde(rename = "u")]
338    pub update_id: i64,
339    /// Cross sequence. You can use this field to compare different levels orderbook data,
340    /// and for the smaller seq, then it means the data is generated earlier
341    pub seq: i64,
342    /// Cross timestamp (ms). Spot only
343    #[serde(default)]
344    pub cts: Option<Timestamp>,
345}
346
347#[derive(Debug, Deserialize, PartialEq)]
348pub struct OrderbookLevel {
349    /// Price
350    pub price: Decimal,
351    /// Size
352    pub size: Decimal,
353}
354
355#[derive(Debug, Serialize, Clone)]
356#[serde(rename_all = "camelCase")]
357pub struct GetTradesParams {
358    pub category: Category,
359    /// required for spot/linear/inverse
360    /// optional for option
361    pub symbol: Option<String>,
362    /// Apply to option only
363    /// If the field is not passed, return BTC data by default
364    pub base_coin: Option<String>,
365    /// Option type. Apply to option only
366    pub option_type: Option<OptionType>,
367    /// spot: `[1, 60]`, default: 60
368    /// others: `[1, 1000]`, default: 500
369    pub limit: Option<u64>,
370}
371
372impl GetTradesParams {
373    pub fn new(category: Category) -> Self {
374        Self {
375            category,
376            symbol: None,
377            base_coin: None,
378            option_type: None,
379            limit: None,
380        }
381    }
382
383    pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
384        self.symbol = Some(v.into());
385        self
386    }
387
388    pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
389        self.base_coin = Some(v.into());
390        self
391    }
392
393    pub fn with_option_type(mut self, v: OptionType) -> Self {
394        self.option_type = Some(v);
395        self
396    }
397
398    pub fn with_limit(mut self, v: u64) -> Self {
399        self.limit = Some(v);
400        self
401    }
402}
403
404#[derive(Debug, Deserialize, PartialEq)]
405#[serde(tag = "category")]
406pub enum Trade {
407    #[serde(rename = "inverse")]
408    Inverse { list: Vec<InverseLinearSpotTrade> },
409    #[serde(rename = "linear")]
410    Linear { list: Vec<InverseLinearSpotTrade> },
411    #[serde(rename = "option")]
412    Option { list: Vec<OptionTrade> },
413    #[serde(rename = "spot")]
414    Spot { list: Vec<InverseLinearSpotTrade> },
415}
416
417#[derive(Debug, Deserialize, PartialEq)]
418#[serde(rename_all = "camelCase")]
419pub struct InverseLinearSpotTrade {
420    /// Execution ID
421    pub exec_id: String,
422    /// Symbol name
423    pub symbol: String,
424    /// Trade price
425    pub price: Decimal,
426    /// Trade size
427    pub size: Decimal,
428    /// Side of taker Buy, Sell
429    pub side: Side,
430    /// Trade time (ms)
431    #[serde(deserialize_with = "number")]
432    pub time: Timestamp,
433    /// boolean Whether the trade is block trade
434    pub is_block_trade: bool,
435    /// Whether the trade is RPI trade
436    #[serde(rename = "isRPITrade")]
437    pub is_rpi_trade: bool,
438}
439
440#[derive(Debug, Deserialize, PartialEq)]
441#[serde(rename_all = "camelCase")]
442pub struct OptionTrade {
443    /// Execution ID
444    pub exec_id: String,
445    /// Symbol name
446    pub symbol: String,
447    /// Trade price
448    pub price: Decimal,
449    /// Trade size
450    pub size: Decimal,
451    /// Side of taker Buy, Sell
452    pub side: Side,
453    /// Trade time (ms)
454    #[serde(deserialize_with = "number")]
455    pub time: Timestamp,
456    /// boolean Whether the trade is block trade
457    pub is_block_trade: bool,
458    /// Whether the trade is RPI trade
459    #[serde(rename = "isRPITrade")]
460    pub is_rpi_trade: bool,
461    /// Mark price
462    #[serde(rename = "mP")]
463    pub mark_price: Decimal,
464    /// Index price
465    #[serde(rename = "iP")]
466    pub index_price: Decimal,
467    /// Mark iv
468    #[serde(rename = "mIv")]
469    pub mark_iv: Decimal,
470    /// iv
471    #[serde(rename = "iv")]
472    pub iv: Decimal,
473}
474
475#[derive(Debug, Deserialize, PartialEq)]
476#[serde(rename_all = "camelCase")]
477pub struct ServerTime {
478    /// Bybit server timestamp (sec)
479    #[serde(deserialize_with = "number")]
480    pub time_second: u64,
481    /// Bybit server timestamp (nano)
482    #[serde(deserialize_with = "number")]
483    pub time_nano: u64,
484}
485
486#[derive(Debug, Serialize, Clone)]
487pub struct GetInstrumentsInfoParams {
488    pub category: Category,
489    pub symbol: Option<String>,
490    pub status: Option<Status>,
491    pub base_coin: Option<String>,
492    pub limit: Option<i64>,
493    pub cursor: Option<String>,
494}
495
496impl GetInstrumentsInfoParams {
497    pub fn new(category: Category) -> Self {
498        Self {
499            category,
500            symbol: None,
501            status: None,
502            base_coin: None,
503            limit: None,
504            cursor: None,
505        }
506    }
507
508    pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
509        self.symbol = Some(v.into());
510        self
511    }
512
513    pub fn with_status(mut self, v: Status) -> Self {
514        self.status = Some(v);
515        self
516    }
517
518    pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
519        self.base_coin = Some(v.into());
520        self
521    }
522
523    pub fn with_limit(mut self, v: i64) -> Self {
524        self.limit = Some(v);
525        self
526    }
527
528    pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
529        self.cursor = Some(v.into());
530        self
531    }
532}
533
534#[derive(Debug, Deserialize, PartialEq)]
535#[serde(tag = "category")]
536pub enum InstrumentsInfo {
537    #[serde(rename = "inverse", rename_all = "camelCase")]
538    Inverse {
539        next_page_cursor: String,
540        list: Vec<InverseLinearInstrumentsInfo>,
541    },
542    #[serde(rename = "linear", rename_all = "camelCase")]
543    Linear {
544        next_page_cursor: String,
545        list: Vec<InverseLinearInstrumentsInfo>,
546    },
547    #[serde(rename = "option", rename_all = "camelCase")]
548    Option {
549        next_page_cursor: String,
550        list: Vec<OptionInstrumentsInfo>,
551    },
552    #[serde(rename = "spot", rename_all = "camelCase")]
553    Spot {
554        #[serde(default, deserialize_with = "empty_string_as_none")]
555        next_page_cursor: Option<String>,
556        list: Vec<SpotInstrumentsInfo>,
557    },
558}
559
560#[derive(Debug, Deserialize, PartialEq)]
561#[serde(rename_all = "camelCase")]
562pub struct InverseLinearInstrumentsInfo {
563    pub symbol: String,
564    pub contract_type: ContractType,
565    pub status: Status,
566    pub base_coin: String,
567    pub quote_coin: String,
568    #[serde(deserialize_with = "number")]
569    pub launch_time: Timestamp,
570    #[serde(deserialize_with = "number")]
571    pub delivery_time: Timestamp,
572    #[serde(deserialize_with = "option_decimal")]
573    pub delivery_fee_rate: Option<Decimal>,
574    #[serde(deserialize_with = "number")]
575    pub price_scale: i64,
576    pub leverage_filter: LeverageFilter,
577    pub price_filter: PriceFilter,
578    pub lot_size_filter: LotSizeFilter,
579    pub unified_margin_trade: bool,
580    pub funding_interval: i64,
581    pub settle_coin: String,
582    pub copy_trading: CopyTrading,
583    pub upper_funding_rate: Decimal,
584    pub lower_funding_rate: Decimal,
585    pub risk_parameters: RiskParameters,
586    pub is_pre_listing: bool,
587    pub pre_listing_info: Option<PreListingInfo>,
588}
589
590#[derive(Debug, Deserialize, PartialEq)]
591#[serde(rename_all = "camelCase")]
592pub struct OptionInstrumentsInfo {
593    pub symbol: String,
594    pub contract_type: ContractType,
595    pub status: Status,
596    pub base_coin: String,
597    pub quote_coin: String,
598    #[serde(deserialize_with = "number")]
599    pub launch_time: i64,
600    #[serde(deserialize_with = "number")]
601    pub delivery_time: i64,
602    #[serde(deserialize_with = "option_decimal")]
603    pub delivery_fee_rate: Option<Decimal>,
604    #[serde(deserialize_with = "number")]
605    pub price_scale: i64,
606    pub leverage_filter: LeverageFilter,
607    pub price_filter: PriceFilter,
608    pub lot_size_filter: LotSizeFilter,
609    pub unified_margin_trade: bool,
610    pub funding_interval: i64,
611    pub settle_coin: String,
612    pub copy_trading: CopyTrading,
613    pub upper_funding_rate: Decimal,
614    pub lower_funding_rate: Decimal,
615    pub risk_parameters: RiskParameters,
616    pub is_pre_listing: bool,
617    pub pre_listing_info: Option<PreListingInfo>,
618}
619
620#[derive(Debug, Deserialize, PartialEq)]
621#[serde(rename_all = "camelCase")]
622pub struct SpotInstrumentsInfo {
623    /// Symbol name
624    pub symbol: String,
625    /// Base coin
626    pub base_coin: String,
627    /// Quote coin
628    pub quote_coin: String,
629    /// Whether or not this is an innovation zone token. 0: false, 1: true
630    #[serde(deserialize_with = "string_to_bool")]
631    pub innovation: bool,
632    /// Instrument status
633    pub status: Status,
634    /// Margin trade symbol or not
635    /// This is to identify if the symbol support margin trading under different account modes
636    /// You may find some symbols not supporting margin buy or margin sell, so you need to go to Collateral Info (UTA) to check if that coin is borrowable
637    pub margin_trading: String,
638    /// Whether or not it has an special treatment label. 0: false, 1: true
639    #[serde(deserialize_with = "string_to_bool")]
640    pub st_tag: bool,
641    /// Size attributes
642    pub lot_size_filter: SpotLotSizeFilter,
643    /// Price attributes
644    pub price_filter: SpotPriceFilter,
645    /// Risk parameters for limit order price, refer to announcement
646    pub risk_parameters: RiskParameters,
647}
648
649#[derive(Debug, Deserialize, PartialEq)]
650#[serde(rename_all = "camelCase")]
651pub struct LeverageFilter {
652    pub min_leverage: Decimal,
653    pub max_leverage: Decimal,
654    pub leverage_step: Decimal,
655}
656
657#[derive(Debug, Deserialize, PartialEq)]
658#[serde(rename_all = "camelCase")]
659pub struct PriceFilter {
660    pub min_price: Decimal,
661    pub max_price: Decimal,
662    pub tick_size: Decimal,
663}
664
665#[derive(Debug, Deserialize, PartialEq)]
666#[serde(rename_all = "camelCase")]
667pub struct SpotPriceFilter {
668    /// The step to increase/reduce order price
669    pub tick_size: Decimal,
670}
671
672#[derive(Debug, Deserialize, PartialEq)]
673#[serde(rename_all = "camelCase")]
674pub struct LotSizeFilter {
675    pub min_notional_value: Decimal,
676    pub max_order_qty: Decimal,
677    pub max_mkt_order_qty: Decimal,
678    pub min_order_qty: Decimal,
679    pub qty_step: Decimal,
680    pub post_only_max_order_qty: Decimal,
681}
682
683#[derive(Debug, Deserialize, PartialEq)]
684#[serde(rename_all = "camelCase")]
685pub struct SpotLotSizeFilter {
686    /// The precision of base coin
687    pub base_precision: Decimal,
688    /// The precision of quote coin
689    pub quote_precision: Decimal,
690    /// Minimum order quantity
691    pub min_order_qty: Decimal,
692    /// Maximum order quantity
693    pub max_order_qty: Decimal,
694    /// Minimum order amount
695    pub min_order_amt: Decimal,
696    /// Maximum order amount
697    pub max_order_amt: Decimal,
698}
699
700#[derive(Debug, Deserialize, PartialEq)]
701#[serde(rename_all = "camelCase")]
702pub struct RiskParameters {
703    pub price_limit_ratio_x: Decimal,
704    pub price_limit_ratio_y: Decimal,
705}
706
707#[derive(Debug, Deserialize, PartialEq)]
708#[serde(rename_all = "camelCase")]
709pub struct PreListingInfo {
710    pub cur_auction_phase: CurAuctionPhase,
711    pub phases: Vec<Phase>,
712    pub auction_fee_info: AuctionFeeInfo,
713}
714
715#[derive(Debug, Deserialize, PartialEq)]
716#[serde(rename_all = "camelCase")]
717pub struct Phase {
718    pub phase: CurAuctionPhase,
719    #[serde(deserialize_with = "option_number")]
720    pub start_time: Option<Timestamp>,
721    #[serde(deserialize_with = "option_number")]
722    pub end_time: Option<Timestamp>,
723}
724
725#[derive(Debug, Deserialize, PartialEq)]
726#[serde(rename_all = "camelCase")]
727pub struct AuctionFeeInfo {
728    pub auction_fee_rate: Decimal,
729    pub taker_fee_rate: Decimal,
730    pub maker_fee_rate: Decimal,
731}
732
733// --- Funding Rate History ---
734
735/// Query params for [`Client::get_funding_rate_history`](crate::http::Client::get_funding_rate_history).
736///
737/// Covers: linear / inverse
738#[derive(Debug, Serialize, Clone)]
739#[serde(rename_all = "camelCase")]
740pub struct GetFundingRateHistoryParams {
741    pub category: Category,
742    pub symbol: String,
743    pub start_time: Option<Timestamp>,
744    pub end_time: Option<Timestamp>,
745    /// Max 200. Default 200.
746    pub limit: Option<u64>,
747}
748
749impl GetFundingRateHistoryParams {
750    pub fn new(category: Category, symbol: impl Into<String>) -> Self {
751        Self {
752            category,
753            symbol: symbol.into(),
754            start_time: None,
755            end_time: None,
756            limit: None,
757        }
758    }
759
760    pub fn with_start_time(mut self, v: Timestamp) -> Self {
761        self.start_time = Some(v);
762        self
763    }
764
765    pub fn with_end_time(mut self, v: Timestamp) -> Self {
766        self.end_time = Some(v);
767        self
768    }
769
770    pub fn with_limit(mut self, v: u64) -> Self {
771        self.limit = Some(v);
772        self
773    }
774}
775
776/// Response for [`Client::get_funding_rate_history`](crate::http::Client::get_funding_rate_history).
777#[derive(Debug, Deserialize, PartialEq)]
778#[serde(tag = "category")]
779pub enum FundingRateHistory {
780    #[serde(rename = "linear")]
781    Linear { list: Vec<FundingRateEntry> },
782    #[serde(rename = "inverse")]
783    Inverse { list: Vec<FundingRateEntry> },
784}
785
786#[derive(Debug, Deserialize, PartialEq)]
787#[serde(rename_all = "camelCase")]
788pub struct FundingRateEntry {
789    pub symbol: String,
790    pub funding_rate: Decimal,
791    #[serde(deserialize_with = "number")]
792    pub funding_rate_timestamp: Timestamp,
793}
794
795// --- Open Interest ---
796
797/// Query params for [`Client::get_open_interest`](crate::http::Client::get_open_interest).
798///
799/// Covers: linear / inverse
800#[derive(Debug, Serialize, Clone)]
801#[serde(rename_all = "camelCase")]
802pub struct GetOpenInterestParams {
803    pub category: Category,
804    pub symbol: String,
805    pub interval_time: IntervalTime,
806    pub start_time: Option<Timestamp>,
807    pub end_time: Option<Timestamp>,
808    /// Max 200. Default 50.
809    pub limit: Option<u64>,
810    pub cursor: Option<String>,
811}
812
813impl GetOpenInterestParams {
814    pub fn new(category: Category, symbol: impl Into<String>, interval_time: IntervalTime) -> Self {
815        Self {
816            category,
817            symbol: symbol.into(),
818            interval_time,
819            start_time: None,
820            end_time: None,
821            limit: None,
822            cursor: None,
823        }
824    }
825
826    pub fn with_start_time(mut self, v: Timestamp) -> Self {
827        self.start_time = Some(v);
828        self
829    }
830
831    pub fn with_end_time(mut self, v: Timestamp) -> Self {
832        self.end_time = Some(v);
833        self
834    }
835
836    pub fn with_limit(mut self, v: u64) -> Self {
837        self.limit = Some(v);
838        self
839    }
840
841    pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
842        self.cursor = Some(v.into());
843        self
844    }
845}
846
847/// Response for [`Client::get_open_interest`](crate::http::Client::get_open_interest).
848#[derive(Debug, Deserialize, PartialEq)]
849#[serde(rename_all = "camelCase")]
850pub struct OpenInterest {
851    pub symbol: String,
852    pub category: String,
853    pub list: Vec<OpenInterestEntry>,
854    #[serde(default, deserialize_with = "empty_string_as_none")]
855    pub next_page_cursor: Option<String>,
856}
857
858#[derive(Debug, Deserialize, PartialEq)]
859#[serde(rename_all = "camelCase")]
860pub struct OpenInterestEntry {
861    pub open_interest: Decimal,
862    #[serde(deserialize_with = "number")]
863    pub timestamp: Timestamp,
864}
865
866// --- Historical Volatility ---
867
868/// Query params for [`Client::get_historical_volatility`](crate::http::Client::get_historical_volatility).
869///
870/// Covers: option only
871#[derive(Debug, Serialize, Clone)]
872#[serde(rename_all = "camelCase")]
873pub struct GetHistoricalVolatilityParams {
874    pub category: Category,
875    /// Default: BTC.
876    pub base_coin: Option<String>,
877    /// Accepted values: 7, 14, 21, 30, 60, 90, 180, 270.
878    pub period: Option<u16>,
879    pub start_time: Option<Timestamp>,
880    pub end_time: Option<Timestamp>,
881}
882
883impl GetHistoricalVolatilityParams {
884    pub fn new(category: Category) -> Self {
885        Self {
886            category,
887            base_coin: None,
888            period: None,
889            start_time: None,
890            end_time: None,
891        }
892    }
893
894    pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
895        self.base_coin = Some(v.into());
896        self
897    }
898
899    pub fn with_period(mut self, v: u16) -> Self {
900        self.period = Some(v);
901        self
902    }
903
904    pub fn with_start_time(mut self, v: Timestamp) -> Self {
905        self.start_time = Some(v);
906        self
907    }
908
909    pub fn with_end_time(mut self, v: Timestamp) -> Self {
910        self.end_time = Some(v);
911        self
912    }
913}
914
915/// One data point from
916/// [`Client::get_historical_volatility`](crate::http::Client::get_historical_volatility).
917///
918/// The endpoint returns `result` as a top-level JSON array, so the full
919/// response type is `Response<Vec<HistoricalVolatilityEntry>>`.
920#[derive(Debug, Deserialize, PartialEq)]
921pub struct HistoricalVolatilityEntry {
922    pub period: u16,
923    pub value: Decimal,
924    #[serde(deserialize_with = "number")]
925    pub time: Timestamp,
926}
927
928// --- Insurance ---
929
930/// Query params for [`Client::get_insurance`](crate::http::Client::get_insurance).
931#[derive(Debug, Serialize, Clone, Default)]
932pub struct GetInsuranceParams {
933    pub coin: Option<String>,
934}
935
936impl GetInsuranceParams {
937    pub fn new() -> Self {
938        Self { coin: None }
939    }
940
941    pub fn with_coin(mut self, v: impl Into<String>) -> Self {
942        self.coin = Some(v.into());
943        self
944    }
945}
946
947/// Response for [`Client::get_insurance`](crate::http::Client::get_insurance).
948#[derive(Debug, Deserialize, PartialEq)]
949#[serde(rename_all = "camelCase")]
950pub struct Insurance {
951    #[serde(deserialize_with = "number")]
952    pub updated_time: Timestamp,
953    pub list: Vec<InsuranceEntry>,
954}
955
956#[derive(Debug, Deserialize, PartialEq)]
957#[serde(rename_all = "camelCase")]
958pub struct InsuranceEntry {
959    pub coin: String,
960    pub balance: Decimal,
961    pub value: Decimal,
962}
963
964// --- Risk Limit ---
965
966/// Query params for [`Client::get_risk_limit`](crate::http::Client::get_risk_limit).
967///
968/// Covers: linear / inverse
969#[derive(Debug, Serialize, Clone)]
970pub struct GetRiskLimitParams {
971    pub category: Category,
972    pub symbol: Option<String>,
973    pub cursor: Option<String>,
974}
975
976impl GetRiskLimitParams {
977    pub fn new(category: Category) -> Self {
978        Self {
979            category,
980            symbol: None,
981            cursor: None,
982        }
983    }
984
985    pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
986        self.symbol = Some(v.into());
987        self
988    }
989
990    pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
991        self.cursor = Some(v.into());
992        self
993    }
994}
995
996/// Response for [`Client::get_risk_limit`](crate::http::Client::get_risk_limit).
997#[derive(Debug, Deserialize, PartialEq)]
998#[serde(rename_all = "camelCase")]
999pub struct RiskLimit {
1000    pub category: String,
1001    pub list: Vec<RiskLimitEntry>,
1002}
1003
1004#[derive(Debug, Deserialize, PartialEq)]
1005#[serde(rename_all = "camelCase")]
1006pub struct RiskLimitEntry {
1007    pub id: u32,
1008    pub symbol: String,
1009    pub risk_limit_value: Decimal,
1010    pub maintenance_margin: Decimal,
1011    pub initial_margin: Decimal,
1012    #[serde(deserialize_with = "int_to_bool")]
1013    pub is_lowest_risk: bool,
1014    pub max_leverage: Decimal,
1015}
1016
1017// --- Delivery Price ---
1018
1019/// Query params for [`Client::get_delivery_price`](crate::http::Client::get_delivery_price).
1020///
1021/// Covers: linear / inverse / option
1022#[derive(Debug, Serialize, Clone)]
1023#[serde(rename_all = "camelCase")]
1024pub struct GetDeliveryPriceParams {
1025    pub category: Category,
1026    pub symbol: Option<String>,
1027    pub base_coin: Option<String>,
1028    /// Max 200. Default 50.
1029    pub limit: Option<u64>,
1030    pub cursor: Option<String>,
1031}
1032
1033impl GetDeliveryPriceParams {
1034    pub fn new(category: Category) -> Self {
1035        Self {
1036            category,
1037            symbol: None,
1038            base_coin: None,
1039            limit: None,
1040            cursor: None,
1041        }
1042    }
1043
1044    pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
1045        self.symbol = Some(v.into());
1046        self
1047    }
1048
1049    pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
1050        self.base_coin = Some(v.into());
1051        self
1052    }
1053
1054    pub fn with_limit(mut self, v: u64) -> Self {
1055        self.limit = Some(v);
1056        self
1057    }
1058
1059    pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
1060        self.cursor = Some(v.into());
1061        self
1062    }
1063}
1064
1065/// Response for [`Client::get_delivery_price`](crate::http::Client::get_delivery_price).
1066#[derive(Debug, Deserialize, PartialEq)]
1067#[serde(rename_all = "camelCase")]
1068pub struct DeliveryPrice {
1069    pub category: String,
1070    #[serde(default, deserialize_with = "empty_string_as_none")]
1071    pub next_page_cursor: Option<String>,
1072    pub list: Vec<DeliveryPriceEntry>,
1073}
1074
1075#[derive(Debug, Deserialize, PartialEq)]
1076#[serde(rename_all = "camelCase")]
1077pub struct DeliveryPriceEntry {
1078    pub symbol: String,
1079    pub delivery_price: Decimal,
1080    #[serde(deserialize_with = "number")]
1081    pub delivery_time: Timestamp,
1082}
1083
1084#[cfg(test)]
1085mod tests {
1086    use super::*;
1087    use crate::{Category, serde::serialize_query};
1088
1089    #[test]
1090    fn trades_params_serialize_option_type_as_call_or_put() {
1091        let params = GetTradesParams {
1092            category: Category::Option,
1093            symbol: None,
1094            base_coin: Some(String::from("BTC")),
1095            option_type: Some(OptionType::Put),
1096            limit: None,
1097        };
1098
1099        let query = serialize_query(&params).unwrap();
1100
1101        assert_eq!(query, "category=option&baseCoin=BTC&optionType=Put");
1102    }
1103}