use nautilus_model::{data::option_chain::OptionGreeks, types::Price};
#[derive(Debug)]
pub struct AtmTracker {
atm_price: Option<Price>,
forward_precision: u8,
}
impl AtmTracker {
pub fn new() -> Self {
Self {
atm_price: None,
forward_precision: 2,
}
}
pub fn set_forward_precision(&mut self, precision: u8) {
self.forward_precision = precision;
}
#[must_use]
pub fn atm_price(&self) -> Option<Price> {
self.atm_price
}
pub fn set_initial_price(&mut self, price: Price) {
self.atm_price = Some(price);
}
pub fn update_from_option_greeks(&mut self, greeks: &OptionGreeks) -> bool {
if let Some(fwd) = greeks.underlying_price {
self.atm_price = Some(Price::new(fwd, self.forward_precision));
return true;
}
false
}
}
impl Default for AtmTracker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use nautilus_model::{
data::option_chain::OptionGreeks, identifiers::InstrumentId, types::Price,
};
use rstest::*;
use super::*;
#[rstest]
fn test_atm_tracker_initial_none() {
let tracker = AtmTracker::new();
assert!(tracker.atm_price().is_none());
}
#[rstest]
fn test_atm_tracker_update_from_option_greeks() {
let mut tracker = AtmTracker::new();
let greeks = OptionGreeks {
instrument_id: InstrumentId::from("BTC-20240101-50000-C.DERIBIT"),
underlying_price: Some(50500.0),
..Default::default()
};
assert!(tracker.update_from_option_greeks(&greeks));
assert_eq!(tracker.atm_price().unwrap(), Price::from("50500.00"));
}
#[rstest]
fn test_atm_tracker_forward_ignores_none_underlying() {
let mut tracker = AtmTracker::new();
let greeks = OptionGreeks {
instrument_id: InstrumentId::from("BTC-20240101-50000-C.DERIBIT"),
underlying_price: None,
..Default::default()
};
assert!(!tracker.update_from_option_greeks(&greeks));
assert!(tracker.atm_price().is_none());
}
#[rstest]
fn test_atm_tracker_set_initial_price() {
let mut tracker = AtmTracker::new();
tracker.set_initial_price(Price::from("50000.00"));
assert_eq!(tracker.atm_price().unwrap(), Price::from("50000.00"));
}
#[rstest]
fn test_atm_tracker_set_forward_precision() {
let mut tracker = AtmTracker::new();
tracker.set_forward_precision(4);
let greeks = OptionGreeks {
instrument_id: InstrumentId::from("BTC-20240101-50000-C.DERIBIT"),
underlying_price: Some(50500.1234),
..Default::default()
};
assert!(tracker.update_from_option_greeks(&greeks));
assert_eq!(tracker.atm_price().unwrap(), Price::from("50500.1234"));
}
}