Skip to main content

nautilus_live/execution/
config.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Configuration for execution reconciliation decisions and cache retention.
17//!
18//! Thresholds, retry limits, filters, and lookbacks govern manager decisions. Startup enablement
19//! and polling intervals belong to the live node's execution-engine configuration.
20
21use indexmap::IndexSet;
22use nautilus_common::config::{ConfigError, ConfigErrorCollector, ConfigResult};
23use nautilus_core::{DurationNanos, datetime::checked_mins_to_secs};
24use nautilus_model::identifiers::{ClientOrderId, InstrumentId, TraderId};
25
26use super::submission::SubmissionRecoveryPolicy;
27
28/// Configuration for execution manager.
29#[expect(
30    clippy::struct_excessive_bools,
31    reason = "config flags mirror the live execution engine configuration surface"
32)]
33#[derive(Debug, Clone)]
34pub struct ExecutionManagerConfig {
35    /// The trader ID for generated orders.
36    pub trader_id: TraderId,
37    /// Number of minutes to look back during reconciliation.
38    pub lookback_mins: Option<u64>,
39    /// Instrument IDs to include during reconciliation (empty => all).
40    pub reconciliation_instrument_ids: IndexSet<InstrumentId>,
41    /// Whether to filter unclaimed external orders.
42    pub filter_unclaimed_external: bool,
43    /// Whether to filter position status reports during reconciliation.
44    pub filter_position_reports: bool,
45    /// Client order IDs excluded from reconciliation.
46    pub filtered_client_order_ids: IndexSet<ClientOrderId>,
47    /// Whether to generate missing orders from reports.
48    pub generate_missing_orders: bool,
49    /// Threshold in milliseconds for inflight order checks.
50    pub inflight_threshold_ms: u64,
51    /// Maximum number of retries for inflight checks.
52    pub inflight_max_retries: u32,
53    /// Policy when a submitted order exhausts automatic recovery.
54    pub submission_recovery_policy: SubmissionRecoveryPolicy,
55    /// The lookback minutes for open order checks.
56    pub open_check_lookback_mins: Option<u64>,
57    /// Threshold before acting on venue discrepancies for open orders.
58    pub open_check_threshold_ns: DurationNanos,
59    /// Maximum retries before resolving an open order missing at the venue.
60    pub open_check_missing_retries: u32,
61    /// Whether open-order polling should only request open orders from the venue.
62    pub open_check_open_only: bool,
63    /// The maximum number of single-order queries per consistency check cycle.
64    pub max_single_order_queries_per_cycle: u32,
65    /// The delay (milliseconds) between consecutive single-order queries.
66    pub single_order_query_delay_ms: u32,
67    /// The lookback minutes for position consistency checks.
68    pub position_check_lookback_mins: u64,
69    /// Threshold before acting on venue discrepancies for positions.
70    pub position_check_threshold_ns: DurationNanos,
71    /// Maximum retries before stopping position discrepancy reconciliation.
72    pub position_check_retries: u32,
73    /// The time buffer (minutes) before closed orders can be purged.
74    pub purge_closed_orders_buffer_mins: Option<u32>,
75    /// The time buffer (minutes) before closed positions can be purged.
76    pub purge_closed_positions_buffer_mins: Option<u32>,
77    /// The time buffer (minutes) before account events can be purged.
78    pub purge_account_events_lookback_mins: Option<u32>,
79    /// If purge operations should also delete from the backing database.
80    pub purge_from_database: bool,
81}
82
83impl Default for ExecutionManagerConfig {
84    fn default() -> Self {
85        Self {
86            trader_id: TraderId::default(),
87            lookback_mins: Some(60),
88            reconciliation_instrument_ids: IndexSet::new(),
89            filter_unclaimed_external: false,
90            filter_position_reports: false,
91            filtered_client_order_ids: IndexSet::new(),
92            generate_missing_orders: true,
93            inflight_threshold_ms: 5_000,
94            inflight_max_retries: 5,
95            submission_recovery_policy: SubmissionRecoveryPolicy::default(),
96            open_check_lookback_mins: Some(60),
97            open_check_threshold_ns: DurationNanos::from_secs(5),
98            open_check_missing_retries: 5,
99            open_check_open_only: true,
100            max_single_order_queries_per_cycle: 5,
101            single_order_query_delay_ms: 100,
102            position_check_lookback_mins: 60,
103            position_check_threshold_ns: DurationNanos::from_mins(1),
104            position_check_retries: 3,
105            purge_closed_orders_buffer_mins: None,
106            purge_closed_positions_buffer_mins: None,
107            purge_account_events_lookback_mins: None,
108            purge_from_database: false,
109        }
110    }
111}
112
113impl ExecutionManagerConfig {
114    /// Validates the execution manager configuration, collecting every field violation.
115    ///
116    /// # Errors
117    ///
118    /// Returns an error if any field fails validation. Multiple validation failures are collected
119    /// into one error.
120    pub fn validate(&self) -> ConfigResult<()> {
121        let mut errors = ConfigErrorCollector::with_capacity(3);
122
123        if let Some(mins) = self.lookback_mins {
124            errors.check(
125                checked_mins_to_secs(mins).is_some(),
126                ConfigError::range(
127                    "ExecutionManagerConfig.lookback_mins",
128                    format!("{mins} minutes (must fit in `u64` seconds)"),
129                ),
130            );
131        }
132
133        if let Some(mins) = self.open_check_lookback_mins {
134            errors.check(
135                DurationNanos::try_from_mins(mins).is_ok(),
136                ConfigError::range(
137                    "ExecutionManagerConfig.open_check_lookback_mins",
138                    format!("{mins} minutes (must fit in `u64` nanoseconds)"),
139                ),
140            );
141        }
142
143        errors.check(
144            DurationNanos::try_from_mins(self.position_check_lookback_mins).is_ok(),
145            ConfigError::range(
146                "ExecutionManagerConfig.position_check_lookback_mins",
147                format!(
148                    "{} minutes (must fit in `u64` nanoseconds)",
149                    self.position_check_lookback_mins
150                ),
151            ),
152        );
153
154        errors.into_result()
155    }
156
157    /// Sets the trader ID on the configuration.
158    #[must_use]
159    pub fn with_trader_id(mut self, trader_id: TraderId) -> Self {
160        self.trader_id = trader_id;
161        self
162    }
163}