Skip to main content

alpaca_data/options/
model.rs

1use rust_decimal::Decimal;
2use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
5pub struct Bar {
6    pub t: Option<String>,
7    #[serde(
8        default,
9        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
10    )]
11    pub o: Option<Decimal>,
12    #[serde(
13        default,
14        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
15    )]
16    pub h: Option<Decimal>,
17    #[serde(
18        default,
19        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
20    )]
21    pub l: Option<Decimal>,
22    #[serde(
23        default,
24        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
25    )]
26    pub c: Option<Decimal>,
27    pub v: Option<u64>,
28    pub n: Option<u64>,
29    #[serde(
30        default,
31        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
32    )]
33    pub vw: Option<Decimal>,
34}
35
36#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
37pub struct Quote {
38    pub t: Option<String>,
39    pub bx: Option<String>,
40    #[serde(
41        default,
42        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
43    )]
44    pub bp: Option<Decimal>,
45    pub bs: Option<u64>,
46    pub ax: Option<String>,
47    #[serde(
48        default,
49        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
50    )]
51    pub ap: Option<Decimal>,
52    #[serde(rename = "as")]
53    pub r#as: Option<u64>,
54    pub c: Option<String>,
55}
56
57#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
58pub struct Trade {
59    pub t: Option<String>,
60    pub x: Option<String>,
61    #[serde(
62        default,
63        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
64    )]
65    pub p: Option<Decimal>,
66    pub s: Option<u64>,
67    pub c: Option<String>,
68}
69
70#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
71pub struct Greeks {
72    #[serde(
73        default,
74        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
75    )]
76    pub delta: Option<f64>,
77    #[serde(
78        default,
79        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
80    )]
81    pub gamma: Option<f64>,
82    #[serde(
83        default,
84        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
85    )]
86    pub rho: Option<f64>,
87    #[serde(
88        default,
89        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
90    )]
91    pub theta: Option<f64>,
92    #[serde(
93        default,
94        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
95    )]
96    pub vega: Option<f64>,
97}
98
99#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
100pub struct Snapshot {
101    #[serde(rename = "latestTrade")]
102    pub latest_trade: Option<Trade>,
103    #[serde(rename = "latestQuote")]
104    pub latest_quote: Option<Quote>,
105    #[serde(rename = "minuteBar")]
106    pub minute_bar: Option<Bar>,
107    #[serde(rename = "dailyBar")]
108    pub daily_bar: Option<Bar>,
109    #[serde(rename = "prevDailyBar")]
110    pub prev_daily_bar: Option<Bar>,
111    pub greeks: Option<Greeks>,
112    #[serde(
113        rename = "impliedVolatility",
114        default,
115        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
116    )]
117    pub implied_volatility: Option<f64>,
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use serde_json::json;
124
125    fn assert_close(actual: Option<f64>, expected: f64) {
126        let actual = actual.expect("value should deserialize");
127        assert!(
128            (actual - expected).abs() <= 1e-12,
129            "actual={actual}, expected={expected}"
130        );
131    }
132
133    #[test]
134    fn option_snapshot_greeks_and_iv_deserialize_as_floats() {
135        let snapshot: Snapshot = serde_json::from_value(json!({
136            "greeks": {
137                "delta": "0.123456789123",
138                "gamma": 0.012345678912,
139                "rho": "0.034567891234",
140                "theta": -0.045678912345,
141                "vega": "0.156789123456"
142            },
143            "impliedVolatility": "0.267891234567"
144        }))
145        .expect("snapshot should deserialize");
146        let greeks = snapshot.greeks.expect("greeks should deserialize");
147
148        assert_close(greeks.delta, 0.123456789123);
149        assert_close(greeks.gamma, 0.012345678912);
150        assert_close(greeks.rho, 0.034567891234);
151        assert_close(greeks.theta, -0.045678912345);
152        assert_close(greeks.vega, 0.156789123456);
153        assert_close(snapshot.implied_volatility, 0.267891234567);
154    }
155}