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 #[serde(skip_serializing_if = "Option::is_none")]
52 pub mark_price: Option<f64>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub best_bid_price: Option<f64>,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub best_ask_price: Option<f64>,
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub best_bid_amount: Option<f64>,
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub best_ask_amount: Option<f64>,
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub volume: Option<f64>,
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub volume_usd: Option<f64>,
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub open_interest: Option<f64>,
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub high: Option<f64>,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub low: Option<f64>,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub price_change: Option<f64>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub price_change_percentage: Option<f64>,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub bid_iv: Option<f64>,
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub ask_iv: Option<f64>,
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub mark_iv: Option<f64>,
95 pub timestamp: u64,
97 pub state: String,
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub settlement_price: Option<f64>,
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub stats: Option<TickerStats>,
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub greeks: Option<Greeks>,
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub index_price: Option<f64>,
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub min_price: Option<f64>,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub max_price: Option<f64>,
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub interest_rate: Option<f64>,
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub underlying_price: Option<f64>,
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub underlying_index: Option<String>,
126 #[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 let ticker_data: TickerData =
187 serde_json::from_str(json_str).expect("Failed to deserialize ticker data");
188
189 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 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 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 let serialized =
236 serde_json::to_string(&ticker_data).expect("Failed to serialize ticker data");
237
238 let _: TickerData = serde_json::from_str(&serialized)
240 .expect("Failed to deserialize serialized ticker data");
241 }
242}