pub mod fx_rollover;
use std::fmt::Display;
use ahash::AHashMap;
pub use fx_rollover::FXRolloverInterestModule;
use indexmap::IndexMap;
use nautilus_common::cache::Cache;
use nautilus_core::UnixNanos;
use nautilus_execution::matching_engine::OrderMatchingEngine;
use nautilus_model::{
data::Data,
identifiers::{InstrumentId, Venue},
instruments::InstrumentAny,
types::{Currency, Money},
};
#[derive(Debug)]
pub struct ExchangeContext<'a> {
pub venue: Venue,
pub base_currency: Option<Currency>,
pub instruments: &'a AHashMap<InstrumentId, InstrumentAny>,
pub matching_engines: &'a IndexMap<InstrumentId, OrderMatchingEngine>,
pub cache: &'a Cache,
}
#[derive(Debug, Clone)]
pub enum SimulationModuleAny {
FXRolloverInterest(FXRolloverInterestModule),
}
impl SimulationModule for SimulationModuleAny {
fn pre_process(&self, data: &Data) {
match self {
Self::FXRolloverInterest(module) => module.pre_process(data),
}
}
fn process(&self, ts_now: UnixNanos, ctx: &ExchangeContext) -> SimulationModuleResult {
match self {
Self::FXRolloverInterest(module) => module.process(ts_now, ctx),
}
}
fn acknowledge(&self, outcomes: &[AccountAdjustmentOutcome]) {
match self {
Self::FXRolloverInterest(module) => module.acknowledge(outcomes),
}
}
fn log_diagnostics(&self) {
match self {
Self::FXRolloverInterest(module) => module.log_diagnostics(),
}
}
fn reset(&self) {
match self {
Self::FXRolloverInterest(module) => module.reset(),
}
}
}
impl From<SimulationModuleAny> for Box<dyn SimulationModule> {
fn from(value: SimulationModuleAny) -> Self {
match value {
SimulationModuleAny::FXRolloverInterest(module) => Box::new(module),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SimulationModuleResult {
NotReady,
Completed(Vec<Money>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccountAdjustmentError {
TotalOverflow(Currency),
FreeBalanceOverflow(Currency),
MissingBalance(Currency),
MissingAccount(Venue),
AccountStateGeneration(String),
}
impl Display for AccountAdjustmentError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TotalOverflow(currency) => {
write!(
f,
"Cannot adjust account: {currency} total exceeds Money bounds"
)
}
Self::FreeBalanceOverflow(currency) => write!(
f,
"Cannot adjust account: {currency} free balance exceeds Money bounds"
),
Self::MissingBalance(currency) => {
write!(
f,
"Cannot adjust account: no balance for currency {currency}"
)
}
Self::MissingAccount(venue) => {
write!(f, "Cannot adjust account: no account for venue {venue}")
}
Self::AccountStateGeneration(error) => {
write!(
f,
"Cannot adjust account: failed to generate account state: {error}"
)
}
}
}
}
impl std::error::Error for AccountAdjustmentError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccountAdjustmentOutcome {
Applied,
Failed(AccountAdjustmentError),
}
pub trait SimulationModule {
fn pre_process(&self, data: &Data);
fn process(&self, ts_now: UnixNanos, ctx: &ExchangeContext) -> SimulationModuleResult;
fn acknowledge(&self, outcomes: &[AccountAdjustmentOutcome]);
fn log_diagnostics(&self);
fn reset(&self);
}