use std::{cell::RefCell, rc::Rc};
use nautilus_common::{
actor::data_actor::{DataActor, DataActorConfig, DataActorCore},
cache::Cache,
component::Component,
greeks::{GreeksCalculator, InstrumentGreeksParams, PortfolioGreeksParams},
live::clock::LiveClock,
nautilus_actor,
};
use nautilus_model::{
data::{
CustomData,
greeks::{GreeksData, PortfolioGreeks},
},
enums::PositionSide,
identifiers::{InstrumentId, TraderId},
};
const TRADER_ID: &str = "TRADER-001";
const INSTRUMENT_ID: &str = "SPY.AMEX";
const GREEKS_UNDERLYING: &str = "SPY";
#[derive(Debug)]
struct GreeksActor {
core: DataActorCore,
greeks_calculator: Option<GreeksCalculator>,
}
impl GreeksActor {
pub(crate) fn new(config: DataActorConfig) -> Self {
let core = DataActorCore::new(config);
Self {
core,
greeks_calculator: None,
}
}
pub(crate) fn calculate_instrument_greeks(
&self,
instrument_id: InstrumentId,
) -> anyhow::Result<GreeksData> {
InstrumentGreeksParams::builder()
.instrument_id(instrument_id)
.cache_greeks(true)
.publish_greeks(true)
.ts_event(self.clock().timestamp_ns())
.build()
.calculate(self.calculator()?)
}
pub(crate) fn calculate_portfolio_greeks(&self) -> anyhow::Result<PortfolioGreeks> {
PortfolioGreeksParams::builder()
.side(PositionSide::NoPositionSide)
.cache_greeks(true)
.publish_greeks(true)
.build()
.calculate(self.calculator()?)
}
pub(crate) fn subscribe_to_greeks(&self, underlying: &str) -> anyhow::Result<()> {
self.calculator()?
.subscribe_greeks(underlying, Some(Self::handle_greeks as fn(&GreeksData)));
Ok(())
}
fn handle_greeks(greeks: &GreeksData) {
println!("Received greeks data: {greeks:?}");
}
fn calculator(&self) -> anyhow::Result<&GreeksCalculator> {
let Some(calculator) = &self.greeks_calculator else {
anyhow::bail!("GreeksActor must be started before calculating greeks");
};
Ok(calculator)
}
}
nautilus_actor!(GreeksActor);
impl DataActor for GreeksActor {
fn on_start(&mut self) -> anyhow::Result<()> {
self.greeks_calculator = Some(GreeksCalculator::from_actor(self));
self.subscribe_to_greeks(GREEKS_UNDERLYING)
}
fn on_stop(&mut self) -> anyhow::Result<()> {
Ok(())
}
fn on_data(&mut self, data: &CustomData) -> anyhow::Result<()> {
println!("Received custom data: {}", data.data_type);
Ok(())
}
}
fn main() -> anyhow::Result<()> {
let cache = Rc::new(RefCell::new(Cache::default()));
let clock = Rc::new(RefCell::new(LiveClock::default()));
let config = DataActorConfig::default();
let trader_id = TraderId::from(TRADER_ID);
let mut actor = GreeksActor::new(config);
actor.register(trader_id, clock, cache).unwrap();
actor.start()?;
let instrument_id = InstrumentId::from(INSTRUMENT_ID);
match actor.calculate_instrument_greeks(instrument_id) {
Ok(greeks) => println!("Calculated greeks for {instrument_id}: {greeks:?}"),
Err(e) => println!("Error calculating greeks: {e}"),
}
match actor.calculate_portfolio_greeks() {
Ok(portfolio_greeks) => println!("Portfolio greeks: {portfolio_greeks:?}"),
Err(e) => println!("Error calculating portfolio greeks: {e}"),
}
actor.stop()?;
Ok(())
}