use indexmap::IndexSet;
use nautilus_common::config::{ConfigError, ConfigErrorCollector, ConfigResult};
use nautilus_core::{DurationNanos, datetime::checked_mins_to_secs};
use nautilus_model::identifiers::{ClientOrderId, InstrumentId, TraderId};
#[expect(
clippy::struct_excessive_bools,
reason = "config flags mirror the live execution engine configuration surface"
)]
#[derive(Debug, Clone)]
pub struct ExecutionManagerConfig {
pub trader_id: TraderId,
pub lookback_mins: Option<u64>,
pub reconciliation_instrument_ids: IndexSet<InstrumentId>,
pub filter_unclaimed_external: bool,
pub filter_position_reports: bool,
pub filtered_client_order_ids: IndexSet<ClientOrderId>,
pub generate_missing_orders: bool,
pub inflight_threshold_ms: u64,
pub inflight_max_retries: u32,
pub open_check_lookback_mins: Option<u64>,
pub open_check_threshold_ns: DurationNanos,
pub open_check_missing_retries: u32,
pub open_check_open_only: bool,
pub max_single_order_queries_per_cycle: u32,
pub single_order_query_delay_ms: u32,
pub position_check_lookback_mins: u64,
pub position_check_threshold_ns: DurationNanos,
pub position_check_retries: u32,
pub purge_closed_orders_buffer_mins: Option<u32>,
pub purge_closed_positions_buffer_mins: Option<u32>,
pub purge_account_events_lookback_mins: Option<u32>,
pub purge_from_database: bool,
}
impl Default for ExecutionManagerConfig {
fn default() -> Self {
Self {
trader_id: TraderId::default(),
lookback_mins: Some(60),
reconciliation_instrument_ids: IndexSet::new(),
filter_unclaimed_external: false,
filter_position_reports: false,
filtered_client_order_ids: IndexSet::new(),
generate_missing_orders: true,
inflight_threshold_ms: 5_000,
inflight_max_retries: 5,
open_check_lookback_mins: Some(60),
open_check_threshold_ns: DurationNanos::from_secs(5),
open_check_missing_retries: 5,
open_check_open_only: true,
max_single_order_queries_per_cycle: 5,
single_order_query_delay_ms: 100,
position_check_lookback_mins: 60,
position_check_threshold_ns: DurationNanos::from_mins(1),
position_check_retries: 3,
purge_closed_orders_buffer_mins: None,
purge_closed_positions_buffer_mins: None,
purge_account_events_lookback_mins: None,
purge_from_database: false,
}
}
}
impl ExecutionManagerConfig {
pub fn validate(&self) -> ConfigResult<()> {
let mut errors = ConfigErrorCollector::with_capacity(3);
if let Some(mins) = self.lookback_mins {
errors.check(
checked_mins_to_secs(mins).is_some(),
ConfigError::range(
"ExecutionManagerConfig.lookback_mins",
format!("{mins} minutes (must fit in `u64` seconds)"),
),
);
}
if let Some(mins) = self.open_check_lookback_mins {
errors.check(
DurationNanos::try_from_mins(mins).is_ok(),
ConfigError::range(
"ExecutionManagerConfig.open_check_lookback_mins",
format!("{mins} minutes (must fit in `u64` nanoseconds)"),
),
);
}
errors.check(
DurationNanos::try_from_mins(self.position_check_lookback_mins).is_ok(),
ConfigError::range(
"ExecutionManagerConfig.position_check_lookback_mins",
format!(
"{} minutes (must fit in `u64` nanoseconds)",
self.position_check_lookback_mins
),
),
);
errors.into_result()
}
#[must_use]
pub fn with_trader_id(mut self, trader_id: TraderId) -> Self {
self.trader_id = trader_id;
self
}
}