nautilus_data/option_chains/
atm_tracker.rs1use nautilus_model::{data::option_chain::OptionGreeks, types::Price};
22
23#[derive(Debug)]
28pub struct AtmTracker {
29 atm_price: Option<Price>,
30 forward_precision: u8,
32}
33
34impl AtmTracker {
35 pub fn new() -> Self {
37 Self {
38 atm_price: None,
39 forward_precision: 2,
40 }
41 }
42
43 pub fn set_forward_precision(&mut self, precision: u8) {
45 self.forward_precision = precision;
46 }
47
48 #[must_use]
50 pub fn atm_price(&self) -> Option<Price> {
51 self.atm_price
52 }
53
54 pub fn set_initial_price(&mut self, price: Price) {
59 self.atm_price = Some(price);
60 }
61
62 pub fn update_from_option_greeks(&mut self, greeks: &OptionGreeks) -> bool {
67 if let Some(fwd) = greeks.underlying_price {
68 self.atm_price = Some(Price::new(fwd, self.forward_precision));
69 return true;
70 }
71 false
72 }
73}
74
75impl Default for AtmTracker {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use nautilus_model::{
84 data::option_chain::OptionGreeks, identifiers::InstrumentId, types::Price,
85 };
86 use rstest::*;
87
88 use super::*;
89
90 #[rstest]
91 fn test_atm_tracker_initial_none() {
92 let tracker = AtmTracker::new();
93 assert!(tracker.atm_price().is_none());
94 }
95
96 #[rstest]
97 fn test_atm_tracker_update_from_option_greeks() {
98 let mut tracker = AtmTracker::new();
99 let greeks = OptionGreeks {
100 instrument_id: InstrumentId::from("BTC-20240101-50000-C.DERIBIT"),
101 underlying_price: Some(50500.0),
102 ..Default::default()
103 };
104 assert!(tracker.update_from_option_greeks(&greeks));
105 assert_eq!(tracker.atm_price().unwrap(), Price::from("50500.00"));
106 }
107
108 #[rstest]
109 fn test_atm_tracker_forward_ignores_none_underlying() {
110 let mut tracker = AtmTracker::new();
111 let greeks = OptionGreeks {
112 instrument_id: InstrumentId::from("BTC-20240101-50000-C.DERIBIT"),
113 underlying_price: None,
114 ..Default::default()
115 };
116 assert!(!tracker.update_from_option_greeks(&greeks));
117 assert!(tracker.atm_price().is_none());
118 }
119
120 #[rstest]
121 fn test_atm_tracker_set_initial_price() {
122 let mut tracker = AtmTracker::new();
123 tracker.set_initial_price(Price::from("50000.00"));
124 assert_eq!(tracker.atm_price().unwrap(), Price::from("50000.00"));
125 }
126
127 #[rstest]
128 fn test_atm_tracker_set_forward_precision() {
129 let mut tracker = AtmTracker::new();
130 tracker.set_forward_precision(4);
131 let greeks = OptionGreeks {
132 instrument_id: InstrumentId::from("BTC-20240101-50000-C.DERIBIT"),
133 underlying_price: Some(50500.1234),
134 ..Default::default()
135 };
136 assert!(tracker.update_from_option_greeks(&greeks));
137 assert_eq!(tracker.atm_price().unwrap(), Price::from("50500.1234"));
138 }
139}