bybit/models/
linear_ticker_data.rs

1use crate::prelude::*;
2
3/// Structure for linear perpetual futures ticker data.
4///
5/// Contains ticker metrics specific to linear perpetual futures, such as funding rates and open interest. Bots use this for real-time market analysis and risk management in USDT-margined contracts.
6
7#[derive(Deserialize, Serialize, Clone)]
8#[serde(rename_all = "camelCase")]
9#[serde(deny_unknown_fields)]
10pub struct LinearTickerData {
11    /// The trading pair symbol (e.g., "BTCUSDT").
12    ///
13    /// The ONLY required field for the ticker data. Bots use this to identify the market they are trading in.
14    ///
15    /// Identifies the perpetual futures contract for the ticker data. Bots use this to verify the correct market.
16    pub symbol: String,
17
18    /// The tick direction of the last price change.
19    ///
20    /// Indicates whether the last price change was an uptick or downtick (e.g., "PlusTick"). Bots use this to analyze short-term price momentum.
21    pub tick_direction: Option<String>,
22
23    /// The 24-hour price change percentage.
24    ///
25    /// The percentage change in price over the last 24 hours. Bots use this to assess market trends and volatility.
26    #[serde(default, with = "string_to_float_optional")]
27    pub price_24h_pcnt: Option<f64>,
28
29    /// The last traded price.
30    ///
31    /// The most recent price at which the contract was traded. Bots use this for real-time price tracking and technical analysis.
32    #[serde(default, with = "string_to_float_optional")]
33    pub last_price: Option<f64>,
34
35    /// The price 24 hours ago.
36    ///
37    /// The price of the contract 24 hours prior. Bots use this to calculate price changes and validate `price_24h_pcnt`.
38    #[serde(default, with = "string_to_float_optional")]
39    pub prev_price_24h: Option<f64>,
40
41    /// The highest price in the last 24 hours.
42    ///
43    /// The peak price reached in the last 24 hours. Bots use this to identify resistance levels and assess volatility.
44    #[serde(default, with = "string_to_float_optional")]
45    pub high_price_24h: Option<f64>,
46
47    /// The lowest price in the last 24 hours.
48    ///
49    /// The lowest price reached in the last 24 hours. Bots use this to identify support levels and assess volatility.
50    #[serde(default, with = "string_to_float_optional")]
51    pub low_price_24h: Option<f64>,
52
53    /// The price 1 hour ago.
54    ///
55    /// The price of the contract 1 hour prior. Bots use this to calculate short-term price changes and momentum.
56    #[serde(default, with = "string_to_float_optional")]
57    pub prev_price_1h: Option<f64>,
58
59    /// The open interest value in settlement currency.
60    ///
61    /// The monetary value of open interest (`open_interest` * `mark_price`). Bots use this to assess market exposure and leverage levels.
62    #[serde(default, with = "string_to_float_optional")]
63    pub open_interest_value: Option<f64>,
64
65    /// The 24-hour trading turnover.
66    ///
67    /// The total trading value in the last 24 hours, in settlement currency. Bots use this to assess market activity and liquidity.
68    #[serde(default, with = "string_to_float_optional")]
69    pub turnover_24h: Option<f64>,
70
71    /// The 24-hour trading volume.
72    ///
73    /// The total quantity of contracts traded in the last 24 hours. Bots use this to analyze market activity and trading intensity.
74    #[serde(default, with = "string_to_float_optional")]
75    pub volume_24h: Option<f64>,
76
77    /// The best bid price.
78    ///
79    /// The highest price at which someone is willing to buy. Bots use this to assess buy-side liquidity and calculate spreads.
80    #[serde(default, rename = "bid1Price", with = "string_to_float_optional")]
81    pub bid_price: Option<f64>,
82
83    /// The best bid size.
84    ///
85    /// The quantity available at the best bid price. Bots use this to evaluate buy-side liquidity and potential slippage.
86    #[serde(default, rename = "bid1Size", with = "string_to_float_optional")]
87    pub bid_size: Option<f64>,
88
89    /// The best ask price.
90    ///
91    /// The lowest price at which someone is willing to sell. Bots use this to assess sell-side liquidity and calculate spreads.
92    #[serde(default, rename = "ask1Price", with = "string_to_float_optional")]
93    pub ask_price: Option<f64>,
94
95    /// The best ask size.
96    ///
97    /// The quantity available at the best ask price. Bots use this to evaluate sell-side liquidity and potential slippage.
98    #[serde(default, rename = "ask1Size", with = "string_to_float_optional")]
99    pub ask_size: Option<f64>,
100
101    /// The pre-open price, set by Bybit for a trading pair before it enters
102    /// regular trading, often during phases like subscription, announcement,
103    /// or pre-trading for new pairs. This price guides early trading or
104    /// subscription activities.
105    #[serde(default, with = "string_to_float_optional")]
106    pub pre_open_price: Option<f64>,
107
108    /// The pre-open quantity, represents the indicative or reference price for
109    /// a trading pair during a pre-listing or pre-trading phase. This price is
110    /// typically set by the exchange to guide trading activity when the pair is
111    /// not yet fully open for unrestricted trading (e.g., during a subscription
112    /// or announcement phase for new pairs).
113    #[serde(default, with = "string_to_float_optional")]
114    pub pre_qty: Option<f64>,
115
116    /// Indicates the current stage of a trading pair in Bybit’s pre-listing
117    /// process. Pre-listing phases are used for new or upcoming trading pairs
118    /// that are not yet fully available for trading but may be in a preparatory
119    ///  or promotional stage.
120    pub cur_pre_listing_phase: Option<String>,
121
122    /// The current funding rate.
123    ///
124    /// The funding rate applied to positions, as a decimal (e.g., 0.0001 for 0.01%). Bots use this to calculate funding costs or profits for long/short positions.
125    #[serde(default, with = "string_to_float_optional")]
126    pub funding_rate: Option<f64>,
127
128    /// The timestamp of the next funding event in milliseconds.
129    ///
130    /// Indicates when the next funding rate payment will occur. Bots use this to schedule funding fee calculations and position adjustments.
131    #[serde(default, with = "string_to_u64_optional")]
132    pub next_funding_time: Option<u64>,
133
134    /// The current index price.
135    ///
136    /// The index price, based on external spot markets, used for reference in perpetual futures. Bots use this to compare with mark price for funding rate calculations.
137    #[serde(default, with = "string_to_float_optional")]
138    pub index_price: Option<f64>,
139
140    /// The current mark price.
141    ///
142    /// The mark price used for P&L calculations in perpetual futures. Bots use this to calculate unrealized P&L and assess position health.
143    #[serde(default, with = "string_to_float_optional")]
144    pub mark_price: Option<f64>,
145
146    /// The open interest in contracts.
147    ///
148    /// The total number of open contracts in the market. Bots use this to gauge market participation and liquidity.
149    #[serde(default, with = "string_to_float_optional")]
150    pub open_interest: Option<f64>,
151}
152
153impl std::fmt::Debug for LinearTickerData {
154    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        let mut debug_struct = fmt.debug_struct("LinearTickerData");
156
157        debug_struct.field("symbol", &self.symbol);
158
159        if let Some(index_price) = &self.index_price {
160            debug_struct.field("index_price", index_price);
161        }
162        if let Some(tick_direction) = &self.tick_direction {
163            debug_struct.field("tick_direction", tick_direction);
164        }
165        if let Some(price_24h_pcnt) = &self.price_24h_pcnt {
166            debug_struct.field("price_24h_pcnt", price_24h_pcnt);
167        }
168        if let Some(last_price) = &self.last_price {
169            debug_struct.field("last_price", last_price);
170        }
171        if let Some(prev_price_24h) = &self.prev_price_24h {
172            debug_struct.field("prev_price_24h", prev_price_24h);
173        }
174        if let Some(high_price_24h) = &self.high_price_24h {
175            debug_struct.field("high_price_24h", high_price_24h);
176        }
177        if let Some(low_price_24h) = &self.low_price_24h {
178            debug_struct.field("low_price_24h", low_price_24h);
179        }
180        if let Some(prev_price_1h) = &self.prev_price_1h {
181            debug_struct.field("prev_price_1h", prev_price_1h);
182        }
183        if let Some(mark_price) = &self.mark_price {
184            debug_struct.field("mark_price", mark_price);
185        }
186        if let Some(open_interest) = &self.open_interest {
187            debug_struct.field("open_interest", open_interest);
188        }
189        if let Some(open_interest_value) = &self.open_interest_value {
190            debug_struct.field("open_interest_value", open_interest_value);
191        }
192        if let Some(turnover_24h) = &self.turnover_24h {
193            debug_struct.field("turnover_24h", turnover_24h);
194        }
195        if let Some(volume_24h) = &self.volume_24h {
196            debug_struct.field("volume_24h", volume_24h);
197        }
198        if let Some(next_funding_time) = &self.next_funding_time {
199            debug_struct.field("next_funding_time", next_funding_time);
200        }
201        if let Some(funding_rate) = &self.funding_rate {
202            debug_struct.field("funding_rate", funding_rate);
203        }
204        if let Some(bid_price) = &self.bid_price {
205            debug_struct.field("bid_price", bid_price);
206        }
207        if let Some(bid_size) = &self.bid_size {
208            debug_struct.field("bid_size", bid_size);
209        }
210        if let Some(ask_price) = &self.ask_price {
211            debug_struct.field("ask_price", ask_price);
212        }
213        if let Some(ask_size) = &self.ask_size {
214            debug_struct.field("ask_size", ask_size);
215        }
216        if let Some(pre_qty) = &self.pre_qty {
217            debug_struct.field("pre_qty", pre_qty);
218        }
219        if let Some(cur_pre_listing_phase) = &self.cur_pre_listing_phase {
220            debug_struct.field("cur_pre_listing_phase", cur_pre_listing_phase);
221        }
222        if let Some(pre_open_price) = &self.pre_open_price {
223            debug_struct.field("pre_open_price", pre_open_price);
224        }
225
226        debug_struct.finish()
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use itertools::Itertools;
233
234    use crate::fixture;
235
236    use super::*;
237
238    #[test]
239    fn deserialize() {
240        let json = fixture!("ws_linear_ticker");
241        let values = serde_json::from_str::<Vec<WsTicker>>(json)
242            .unwrap()
243            .into_iter()
244            .map(|t| t.data.try_unwrap_linear().unwrap())
245            .collect_vec();
246        assert_eq!(values.len(), 102);
247    }
248}