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