1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Serialize, Deserialize)]
5pub struct Greeks {
6 #[serde(skip_serializing_if = "Option::is_none")]
8 pub delta: Option<f64>,
9 #[serde(skip_serializing_if = "Option::is_none")]
11 pub gamma: Option<f64>,
12 #[serde(skip_serializing_if = "Option::is_none")]
14 pub vega: Option<f64>,
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub theta: Option<f64>,
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub rho: Option<f64>,
21}
22
23#[derive(Clone, Serialize, Deserialize)]
25pub struct TickerStats {
26 pub volume: f64,
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub volume_usd: Option<f64>,
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub price_change: Option<f64>,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub high: Option<f64>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub low: Option<f64>,
40}
41
42#[derive(Clone, Serialize, Deserialize)]
44pub struct TickerData {
45 pub instrument_name: String,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub last_price: Option<f64>,
50 pub mark_price: f64,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub best_bid_price: Option<f64>,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub best_ask_price: Option<f64>,
58 pub best_bid_amount: f64,
60 pub best_ask_amount: f64,
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub volume: Option<f64>,
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub volume_usd: Option<f64>,
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub open_interest: Option<f64>,
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub high: Option<f64>,
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub low: Option<f64>,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub price_change: Option<f64>,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub price_change_percentage: Option<f64>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub bid_iv: Option<f64>,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub ask_iv: Option<f64>,
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub mark_iv: Option<f64>,
92 pub timestamp: u64,
94 pub state: String,
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub settlement_price: Option<f64>,
99 pub stats: TickerStats,
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub greeks: Option<Greeks>,
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub index_price: Option<f64>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub min_price: Option<f64>,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub max_price: Option<f64>,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub interest_rate: Option<f64>,
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub underlying_price: Option<f64>,
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub underlying_index: Option<String>,
122 #[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 let ticker_data: TickerData =
183 serde_json::from_str(json_str).expect("Failed to deserialize ticker data");
184
185 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 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 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 let serialized =
232 serde_json::to_string(&ticker_data).expect("Failed to serialize ticker data");
233
234 let _: TickerData = serde_json::from_str(&serialized)
236 .expect("Failed to deserialize serialized ticker data");
237 }
238}