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}