rustrade_data/subscription/greeks.rs
1//! Option Greeks subscription type.
2//!
3//! This module defines the [`OptionGreeks`] data type for option analytics
4//! (delta, gamma, theta, vega, implied volatility).
5//!
6//! Unlike other subscription types in this module, Greeks are typically
7//! computed by the exchange/broker from live market data rather than being
8//! raw market data themselves.
9
10use serde::{Deserialize, Serialize};
11
12/// Option Greeks values.
13///
14/// All fields are optional because the data source may not return all Greeks
15/// depending on the contract type, market state, or subscription level.
16///
17/// # Fields
18///
19/// - `delta`: Rate of change of option price with respect to underlying price
20/// - `gamma`: Rate of change of delta with respect to underlying price
21/// - `theta`: Rate of change of option price with respect to time (per day)
22/// - `vega`: Rate of change of option price with respect to volatility
23/// - `implied_volatility`: Market-implied volatility
24/// - `theoretical_price`: Model price computed by the exchange/broker
25/// - `underlying_price`: Current underlying price used in computation
26///
27/// # Note on Precision
28///
29/// Values are stored as `f64` rather than `Decimal` because Greeks are
30/// analytics (not monetary values) and the precision loss is acceptable.
31#[non_exhaustive]
32#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
33pub struct OptionGreeks {
34 pub delta: Option<f64>,
35 pub gamma: Option<f64>,
36 pub theta: Option<f64>,
37 pub vega: Option<f64>,
38 pub implied_volatility: Option<f64>,
39 pub theoretical_price: Option<f64>,
40 pub underlying_price: Option<f64>,
41}
42
43impl OptionGreeks {
44 /// Returns true if at least one first-order Greek (delta, gamma, theta,
45 /// vega, or implied volatility) is present.
46 ///
47 /// Does NOT consider `theoretical_price` or `underlying_price` — a tick
48 /// containing only those fields is treated as having no Greek data.
49 pub fn has_any_greek(&self) -> bool {
50 self.delta.is_some()
51 || self.gamma.is_some()
52 || self.theta.is_some()
53 || self.vega.is_some()
54 || self.implied_volatility.is_some()
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn option_greeks_default_has_no_values() {
64 let greeks = OptionGreeks::default();
65 assert!(!greeks.has_any_greek());
66 }
67
68 #[test]
69 fn option_greeks_partial_has_any_greek() {
70 let greeks = OptionGreeks {
71 delta: Some(0.55),
72 ..Default::default()
73 };
74 assert!(greeks.has_any_greek());
75 }
76
77 #[test]
78 fn option_greeks_full_has_any_greek() {
79 let greeks = OptionGreeks {
80 delta: Some(0.55),
81 gamma: Some(0.02),
82 theta: Some(-0.05),
83 vega: Some(0.15),
84 implied_volatility: Some(0.25),
85 theoretical_price: Some(5.50),
86 underlying_price: Some(150.0),
87 };
88 assert!(greeks.has_any_greek());
89 }
90
91 #[test]
92 fn option_greeks_price_only_has_no_greek() {
93 let greeks = OptionGreeks {
94 theoretical_price: Some(5.50),
95 underlying_price: Some(150.0),
96 ..Default::default()
97 };
98 assert!(!greeks.has_any_greek());
99 }
100}