1use pretty_simple_display::{DebugPretty, DisplaySimple};
2use serde::{Deserialize, Serialize};
3
4#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
6pub struct Greeks {
7 #[serde(skip_serializing_if = "Option::is_none")]
9 pub delta: Option<f64>,
10 #[serde(skip_serializing_if = "Option::is_none")]
12 pub gamma: Option<f64>,
13 #[serde(skip_serializing_if = "Option::is_none")]
15 pub vega: Option<f64>,
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub theta: Option<f64>,
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub rho: Option<f64>,
22}
23
24#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
26pub struct TickerStats {
27 pub volume: f64,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub volume_usd: Option<f64>,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub price_change: Option<f64>,
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub high: Option<f64>,
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub low: Option<f64>,
41}
42
43#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
45pub struct TickerData {
46 pub instrument_name: String,
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub last_price: Option<f64>,
51 pub mark_price: 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 pub best_bid_amount: f64,
61 pub best_ask_amount: f64,
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub volume: Option<f64>,
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub volume_usd: Option<f64>,
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub open_interest: Option<f64>,
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub high: Option<f64>,
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub low: Option<f64>,
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub price_change: Option<f64>,
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub price_change_percentage: Option<f64>,
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub bid_iv: Option<f64>,
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub ask_iv: Option<f64>,
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub mark_iv: Option<f64>,
93 pub timestamp: u64,
95 pub state: String,
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub settlement_price: Option<f64>,
100 pub stats: TickerStats,
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub greeks: Option<Greeks>,
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub index_price: Option<f64>,
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub min_price: Option<f64>,
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub max_price: Option<f64>,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub interest_rate: Option<f64>,
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub underlying_price: Option<f64>,
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub underlying_index: Option<String>,
123 #[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 let ticker_data: TickerData =
175 serde_json::from_str(json_str).expect("Failed to deserialize ticker data");
176
177 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 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 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 let serialized =
224 serde_json::to_string(&ticker_data).expect("Failed to serialize ticker data");
225
226 let _: TickerData = serde_json::from_str(&serialized)
228 .expect("Failed to deserialize serialized ticker data");
229 }
230}