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