deribit_base/model/
ticker.rs

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