1use crate::model::instrument::Instrument;
7use crate::model::ticker::TickerData;
8use crate::model::{BasicGreeks, BasicOptionData, OptionType, Spread};
9use chrono::{DateTime, TimeZone, Utc};
10use pretty_simple_display::{DebugPretty, DisplaySimple};
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use serde_with::skip_serializing_none;
14
15#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
17pub struct DeliveryPriceData {
18 pub date: String,
20 pub delivery_price: f64,
22}
23
24#[skip_serializing_none]
26#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
27pub struct Greeks {
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub delta: Option<f64>,
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub gamma: Option<f64>,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub vega: Option<f64>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub theta: Option<f64>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub rho: Option<f64>,
43}
44
45#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
47pub struct OptionInstrument {
48 pub instrument: Instrument,
50 pub ticker: TickerData,
52}
53
54#[skip_serializing_none]
61#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
62pub struct OptionInstrumentPair {
63 pub call: Option<OptionInstrument>,
65 pub put: Option<OptionInstrument>,
67}
68
69impl OptionInstrumentPair {
70 pub fn expiration(&self) -> Option<DateTime<Utc>> {
77 let expiration_timestamp = self.instrument()?.expiration_timestamp;
78
79 if let Some(expiration_timestamp) = expiration_timestamp {
80 Utc.timestamp_millis_opt(expiration_timestamp).single()
81 } else {
82 None
83 }
84 }
85 pub fn instrument(&self) -> Option<Instrument> {
92 self.call
93 .as_ref()
94 .map(|i| i.instrument.clone())
95 .or_else(|| self.put.as_ref().map(|i| i.instrument.clone()))
96 }
97 pub fn ticker(&self) -> Option<TickerData> {
104 self.call
105 .as_ref()
106 .map(|i| i.ticker.clone())
107 .or_else(|| self.put.as_ref().map(|i| i.ticker.clone()))
108 }
109 pub fn volume(&self) -> f64 {
115 let mut volume: f64 = 0.0;
116 if let Some(call) = &self.call {
117 volume += call.ticker.stats.volume
118 }
119 if let Some(put) = &self.put {
120 volume += put.ticker.stats.volume
121 }
122 volume
123 }
124 pub fn open_interest(&self) -> f64 {
130 let mut open_interest: f64 = 0.0;
131 if let Some(call) = &self.call {
132 open_interest += call.ticker.open_interest.unwrap_or(0.0)
133 }
134 if let Some(put) = &self.put {
135 open_interest += put.ticker.open_interest.unwrap_or(0.0)
136 }
137 open_interest
138 }
139
140 pub fn interest_rate(&self) -> f64 {
146 let mut interest_rate: f64 = 0.0;
147 if let Some(call) = &self.call {
148 interest_rate += call.ticker.interest_rate.unwrap_or(0.0)
149 }
150 if let Some(put) = &self.put {
151 interest_rate += put.ticker.interest_rate.unwrap_or(0.0)
152 }
153 interest_rate
154 }
155
156 pub fn value(&self) -> Option<Value> {
163 serde_json::to_value(self).ok()
164 }
165
166 pub fn call_spread(&self) -> Spread {
173 if let Some(call) = &self.call {
174 let bid = call.ticker.best_bid_price;
175 let ask = call.ticker.best_ask_price;
176 let mid = match (bid, ask) {
177 (Some(b), Some(a)) => Some((b + a) / 2.0),
178 (Some(b), None) => Some(b),
179 (None, Some(a)) => Some(a),
180 (None, None) => None,
181 };
182 Spread { bid, ask, mid }
183 } else {
184 Spread {
185 bid: None,
186 ask: None,
187 mid: None,
188 }
189 }
190 }
191
192 pub fn put_spread(&self) -> Spread {
199 if let Some(put) = &self.put {
200 let bid = put.ticker.best_bid_price;
201 let ask = put.ticker.best_ask_price;
202 let mid = match (bid, ask) {
203 (Some(b), Some(a)) => Some((b + a) / 2.0),
204 (Some(b), None) => Some(b),
205 (None, Some(a)) => Some(a),
206 (None, None) => None,
207 };
208 Spread { bid, ask, mid }
209 } else {
210 Spread {
211 bid: None,
212 ask: None,
213 mid: None,
214 }
215 }
216 }
217
218 pub fn iv(&self) -> (Option<f64>, Option<f64>) {
225 let call_iv = self.call.as_ref().and_then(|c| c.ticker.mark_iv);
226 let put_iv = self.put.as_ref().and_then(|p| p.ticker.mark_iv);
227 (call_iv, put_iv)
228 }
229
230 pub fn greeks(&self) -> BasicGreeks {
237 let delta_call = self
238 .call
239 .as_ref()
240 .and_then(|c| c.ticker.greeks.as_ref().and_then(|g| g.delta));
241 let delta_put = self
242 .put
243 .as_ref()
244 .and_then(|p| p.ticker.greeks.as_ref().and_then(|g| g.delta));
245 let gamma = self
246 .call
247 .as_ref()
248 .and_then(|c| c.ticker.greeks.as_ref().and_then(|g| g.gamma))
249 .or_else(|| {
250 self.put
251 .as_ref()
252 .and_then(|p| p.ticker.greeks.as_ref().and_then(|g| g.gamma))
253 });
254 BasicGreeks {
255 delta_call,
256 delta_put,
257 gamma,
258 }
259 }
260
261 pub fn data(&self) -> BasicOptionData {
269 let strike_price: f64 = match self.instrument() {
270 Some(i) => i.strike.unwrap_or(0.0),
271 None => 0.0,
272 };
273 let call_spread = self.call_spread();
274 let call_bid: Option<f64> = call_spread.bid;
275 let call_ask: Option<f64> = call_spread.ask;
276 let put_spread = self.put_spread();
277 let put_bid: Option<f64> = put_spread.bid;
278 let put_ask: Option<f64> = put_spread.ask;
279 let implied_volatility = self.iv();
280 let greeks = self.greeks();
281 let delta_call: Option<f64> = greeks.delta_call;
282 let delta_put: Option<f64> = greeks.delta_put;
283 let gamma: Option<f64> = greeks.gamma;
284 let volume = self.volume();
285 let open_interest: f64 = self.open_interest();
286 let expiration_date: Option<DateTime<Utc>> = self.expiration();
287 let underlying_price: Option<f64> = self.ticker().and_then(|t| t.underlying_price);
288 let risk_free_rate: f64 = self.interest_rate();
289 let extra_fields: Option<Value> = self.value();
290 BasicOptionData {
291 strike_price,
292 call_bid,
293 call_ask,
294 put_bid,
295 put_ask,
296 implied_volatility,
297 delta_call,
298 delta_put,
299 gamma,
300 volume,
301 open_interest,
302 expiration_date,
303 underlying_price,
304 risk_free_rate,
305 extra_fields,
306 }
307 }
308}
309
310#[skip_serializing_none]
312#[derive(DebugPretty, DisplaySimple, Clone, Serialize)]
313pub struct ParsedOptionWithTicker {
314 pub instrument_name: String,
316 pub strike: f64,
318 pub option_type: OptionType,
320 pub expiry: String,
322 pub ticker: TickerData,
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize, Default)]
328#[serde(rename_all = "lowercase")]
329pub enum SortDirection {
330 #[default]
332 Asc,
333 Desc,
335 Default,
337}
338
339impl std::fmt::Display for SortDirection {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 match self {
342 SortDirection::Asc => write!(f, "asc"),
343 SortDirection::Desc => write!(f, "desc"),
344 SortDirection::Default => write!(f, "default"),
345 }
346 }
347}