deribit_base/model/
ticker.rs

1use serde::{Deserialize, Serialize};
2
3/// Greeks sub-structure for options
4#[derive(Clone, Serialize, Deserialize)]
5pub struct Greeks {
6    /// Delta value
7    #[serde(skip_serializing_if = "Option::is_none")]
8    pub delta: Option<f64>,
9    /// Gamma value
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub gamma: Option<f64>,
12    /// Vega value
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub vega: Option<f64>,
15    /// Theta value
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub theta: Option<f64>,
18    /// Rho value
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub rho: Option<f64>,
21}
22
23/// Ticker stats sub-structure
24#[derive(Clone, Serialize, Deserialize)]
25pub struct TickerStats {
26    /// Trading volume
27    pub volume: f64,
28    /// Trading volume in USD
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub volume_usd: Option<f64>,
31    /// Price change from previous period
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub price_change: Option<f64>,
34    /// Highest price in the period
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub high: Option<f64>,
37    /// Lowest price in the period
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub low: Option<f64>,
40}
41
42/// Ticker data structure with corrected field types
43#[derive(Clone, Serialize, Deserialize)]
44pub struct TickerData {
45    /// Name of the instrument
46    pub instrument_name: String,
47    /// Last traded price
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub last_price: Option<f64>,
50    /// Current mark price
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub mark_price: Option<f64>,
53    /// Best bid price available
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub best_bid_price: Option<f64>,
56    /// Best ask price available
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub best_ask_price: Option<f64>,
59    /// Amount available at best bid price
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub best_bid_amount: Option<f64>,
62    /// Amount available at best ask price
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub best_ask_amount: Option<f64>,
65    /// Trading volume in base currency
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub volume: Option<f64>,
68    /// Trading volume in USD
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub volume_usd: Option<f64>,
71    /// Open interest for the instrument
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub open_interest: Option<f64>,
74    /// Highest price in 24h period
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub high: Option<f64>,
77    /// Lowest price in 24h period
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub low: Option<f64>,
80    /// Absolute price change in 24h
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub price_change: Option<f64>,
83    /// Percentage price change in 24h
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub price_change_percentage: Option<f64>,
86    /// Implied volatility at best bid
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub bid_iv: Option<f64>,
89    /// Implied volatility at best ask
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub ask_iv: Option<f64>,
92    /// Mark implied volatility
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub mark_iv: Option<f64>,
95    /// Timestamp of the ticker data
96    pub timestamp: u64,
97    /// Current state of the instrument
98    pub state: String,
99    /// Settlement price (for expired instruments)
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub settlement_price: Option<f64>,
102    /// Additional ticker statistics
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub stats: Option<TickerStats>,
105    /// Greeks for options (delta, gamma, vega, theta, rho)
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub greeks: Option<Greeks>,
108    /// Index price
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub index_price: Option<f64>,
111    /// Minimum price
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub min_price: Option<f64>,
114    /// Maximum price
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub max_price: Option<f64>,
117    /// Interest rate
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub interest_rate: Option<f64>,
120    /// Underlying price
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub underlying_price: Option<f64>,
123    /// Underlying index
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub underlying_index: Option<String>,
126    /// Estimated delivery price
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub estimated_delivery_price: Option<f64>,
129}
130
131crate::impl_json_display!(TickerData);
132crate::impl_json_debug_pretty!(TickerData);
133
134crate::impl_json_display!(TickerStats);
135crate::impl_json_debug_pretty!(TickerStats);
136
137crate::impl_json_display!(Greeks);
138crate::impl_json_debug_pretty!(Greeks);
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    use serde_json;
144
145    #[test]
146    fn test_ticker_data_serialization() {
147        let json_str = r#"{
148            "timestamp": 1757433676689,
149            "state": "open",
150            "stats": {
151                "high": 0.0002,
152                "low": 0.0001,
153                "price_change": 100.0,
154                "volume": 70.7,
155                "volume_usd": 974.07
156            },
157            "greeks": {
158                "delta": -0.01874,
159                "gamma": 2.0e-5,
160                "vega": 2.16672,
161                "theta": -15.99927,
162                "rho": -0.03815
163            },
164            "index_price": 110881.2,
165            "instrument_name": "BTC-10SEP25-106000-P",
166            "last_price": 0.0002,
167            "settlement_price": 2.533e-4,
168            "min_price": 0.0001,
169            "max_price": 0.0175,
170            "open_interest": 18.6,
171            "mark_price": 0.0001,
172            "best_bid_price": 0.0001,
173            "best_ask_price": 0.0002,
174            "interest_rate": 0.0,
175            "mark_iv": 49.22,
176            "bid_iv": 46.67,
177            "ask_iv": 51.77,
178            "underlying_price": 110714.7602,
179            "underlying_index": "SYN.BTC-10SEP25",
180            "estimated_delivery_price": 110881.2,
181            "best_ask_amount": 4.1,
182            "best_bid_amount": 2.2
183        }"#;
184
185        // Test deserialization
186        let ticker_data: TickerData =
187            serde_json::from_str(json_str).expect("Failed to deserialize ticker data");
188
189        // Verify some key fields
190        assert_eq!(ticker_data.instrument_name, "BTC-10SEP25-106000-P");
191        assert_eq!(ticker_data.timestamp, 1757433676689);
192        assert_eq!(ticker_data.state, "open");
193        assert_eq!(ticker_data.last_price, Some(0.0002));
194        assert_eq!(ticker_data.mark_price, Some(0.0001));
195        assert_eq!(ticker_data.best_bid_price, Some(0.0001));
196        assert_eq!(ticker_data.best_ask_price, Some(0.0002));
197        assert_eq!(ticker_data.best_bid_amount, Some(2.2));
198        assert_eq!(ticker_data.best_ask_amount, Some(4.1));
199        assert_eq!(ticker_data.open_interest, Some(18.6));
200        assert_eq!(ticker_data.settlement_price, Some(2.533e-4));
201        assert_eq!(ticker_data.min_price, Some(0.0001));
202        assert_eq!(ticker_data.max_price, Some(0.0175));
203        assert_eq!(ticker_data.interest_rate, Some(0.0));
204        assert_eq!(ticker_data.mark_iv, Some(49.22));
205        assert_eq!(ticker_data.bid_iv, Some(46.67));
206        assert_eq!(ticker_data.ask_iv, Some(51.77));
207        assert_eq!(ticker_data.underlying_price, Some(110714.7602));
208        assert_eq!(
209            ticker_data.underlying_index,
210            Some("SYN.BTC-10SEP25".to_string())
211        );
212        assert_eq!(ticker_data.estimated_delivery_price, Some(110881.2));
213        assert_eq!(ticker_data.index_price, Some(110881.2));
214
215        // Verify stats
216        let stats = ticker_data.stats.as_ref().expect("Stats should be present");
217        assert_eq!(stats.high, Some(0.0002));
218        assert_eq!(stats.low, Some(0.0001));
219        assert_eq!(stats.price_change, Some(100.0));
220        assert_eq!(stats.volume, 70.7);
221        assert_eq!(stats.volume_usd, Some(974.07));
222
223        // Verify greeks
224        let greeks = ticker_data
225            .greeks
226            .as_ref()
227            .expect("Greeks should be present");
228        assert_eq!(greeks.delta, Some(-0.01874));
229        assert_eq!(greeks.gamma, Some(2.0e-5));
230        assert_eq!(greeks.vega, Some(2.16672));
231        assert_eq!(greeks.theta, Some(-15.99927));
232        assert_eq!(greeks.rho, Some(-0.03815));
233
234        // Test serialization back to JSON
235        let serialized =
236            serde_json::to_string(&ticker_data).expect("Failed to serialize ticker data");
237
238        // Verify we can deserialize it again
239        let _: TickerData = serde_json::from_str(&serialized)
240            .expect("Failed to deserialize serialized ticker data");
241    }
242}