use pretty_simple_display::{DebugPretty, DisplaySimple};
use serde::{Deserialize, Serialize};
#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
pub struct Greeks {
#[serde(skip_serializing_if = "Option::is_none")]
pub delta: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gamma: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vega: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theta: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rho: Option<f64>,
}
#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
pub struct TickerStats {
pub volume: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub volume_usd: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price_change: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub high: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub low: Option<f64>,
}
#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
pub struct TickerData {
pub instrument_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_price: Option<f64>,
pub mark_price: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub best_bid_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub best_ask_price: Option<f64>,
pub best_bid_amount: f64,
pub best_ask_amount: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub volume: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub volume_usd: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub open_interest: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub high: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub low: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price_change: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price_change_percentage: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bid_iv: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ask_iv: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mark_iv: Option<f64>,
pub timestamp: u64,
pub state: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub settlement_price: Option<f64>,
pub stats: TickerStats,
#[serde(skip_serializing_if = "Option::is_none")]
pub greeks: Option<Greeks>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub interest_rate: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub underlying_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub underlying_index: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_delivery_price: Option<f64>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
#[test]
fn test_ticker_data_serialization() {
let json_str = r#"{
"timestamp": 1757433676689,
"state": "open",
"stats": {
"high": 0.0002,
"low": 0.0001,
"price_change": 100.0,
"volume": 70.7,
"volume_usd": 974.07
},
"greeks": {
"delta": -0.01874,
"gamma": 2.0e-5,
"vega": 2.16672,
"theta": -15.99927,
"rho": -0.03815
},
"index_price": 110881.2,
"instrument_name": "BTC-10SEP25-106000-P",
"last_price": 0.0002,
"settlement_price": 2.533e-4,
"min_price": 0.0001,
"max_price": 0.0175,
"open_interest": 18.6,
"mark_price": 0.0001,
"best_bid_price": 0.0001,
"best_ask_price": 0.0002,
"interest_rate": 0.0,
"mark_iv": 49.22,
"bid_iv": 46.67,
"ask_iv": 51.77,
"underlying_price": 110714.7602,
"underlying_index": "SYN.BTC-10SEP25",
"estimated_delivery_price": 110881.2,
"best_ask_amount": 4.1,
"best_bid_amount": 2.2
}"#;
let ticker_data: TickerData =
serde_json::from_str(json_str).expect("Failed to deserialize ticker data");
assert_eq!(ticker_data.instrument_name, "BTC-10SEP25-106000-P");
assert_eq!(ticker_data.timestamp, 1757433676689);
assert_eq!(ticker_data.state, "open");
assert_eq!(ticker_data.last_price, Some(0.0002));
assert_eq!(ticker_data.mark_price, 0.0001);
assert_eq!(ticker_data.best_bid_price, Some(0.0001));
assert_eq!(ticker_data.best_ask_price, Some(0.0002));
assert_eq!(ticker_data.best_bid_amount, 2.2);
assert_eq!(ticker_data.best_ask_amount, 4.1);
assert_eq!(ticker_data.open_interest, Some(18.6));
assert_eq!(ticker_data.settlement_price, Some(2.533e-4));
assert_eq!(ticker_data.min_price, Some(0.0001));
assert_eq!(ticker_data.max_price, Some(0.0175));
assert_eq!(ticker_data.interest_rate, Some(0.0));
assert_eq!(ticker_data.mark_iv, Some(49.22));
assert_eq!(ticker_data.bid_iv, Some(46.67));
assert_eq!(ticker_data.ask_iv, Some(51.77));
assert_eq!(ticker_data.underlying_price, Some(110714.7602));
assert_eq!(
ticker_data.underlying_index,
Some("SYN.BTC-10SEP25".to_string())
);
assert_eq!(ticker_data.estimated_delivery_price, Some(110881.2));
assert_eq!(ticker_data.index_price, Some(110881.2));
let stats = ticker_data.stats.clone();
assert_eq!(stats.high, Some(0.0002));
assert_eq!(stats.low, Some(0.0001));
assert_eq!(stats.price_change, Some(100.0));
assert_eq!(stats.volume, 70.7);
assert_eq!(stats.volume_usd, Some(974.07));
let greeks = ticker_data
.greeks
.as_ref()
.expect("Greeks should be present");
assert_eq!(greeks.delta, Some(-0.01874));
assert_eq!(greeks.gamma, Some(2.0e-5));
assert_eq!(greeks.vega, Some(2.16672));
assert_eq!(greeks.theta, Some(-15.99927));
assert_eq!(greeks.rho, Some(-0.03815));
let serialized =
serde_json::to_string(&ticker_data).expect("Failed to serialize ticker data");
let _: TickerData = serde_json::from_str(&serialized)
.expect("Failed to deserialize serialized ticker data");
}
}