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    /// Reserved for future use; the runtime currently resolves locally for both variants.
55    pub submission_recovery_policy: SubmissionRecoveryPolicy,
56    /// The lookback minutes for open order checks.
57    pub open_check_lookback_mins: Option<u64>,
58    /// Threshold before acting on venue discrepancies for open orders.
59    pub open_check_threshold_ns: DurationNanos,
60    /// Maximum retries before resolving an open order missing at the venue.
61    pub open_check_missing_retries: u32,
62    /// Whether open-order polling should only request open orders from the venue.
63    pub open_check_open_only: bool,
64    /// The maximum number of single-order queries per consistency check cycle.
65    pub max_single_order_queries_per_cycle: u32,
66    /// The delay (milliseconds) between consecutive single-order queries.
67    pub single_order_query_delay_ms: u32,
68    /// The lookback minutes for position consistency checks.
69    pub position_check_lookback_mins: u64,
70    /// Threshold before acting on venue discrepancies for positions.
71    pub position_check_threshold_ns: DurationNanos,
72    /// Maximum retries before stopping position discrepancy reconciliation.
73    pub position_check_retries: u32,
74    /// The time buffer (minutes) before closed orders can be purged.
75    pub purge_closed_orders_buffer_mins: Option<u32>,
76    /// The time buffer (minutes) before closed positions can be purged.
77    pub purge_closed_positions_buffer_mins: Option<u32>,
78    /// The time buffer (minutes) before account events can be purged.
79    pub purge_account_events_lookback_mins: Option<u32>,
80    /// If purge operations should also delete from the backing database.
81    pub purge_from_database: bool,
82}
83
84impl Default for ExecutionManagerConfig {
85    fn default() -> Self {
86        Self {
87            trader_id: TraderId::default(),
88            lookback_mins: Some(60),
89            reconciliation_instrument_ids: IndexSet::new(),
90            filter_unclaimed_external: false,
91            filter_position_reports: false,
92            filtered_client_order_ids: IndexSet::new(),
93            generate_missing_orders: true,
94            inflight_threshold_ms: 5_000,
95            inflight_max_retries: 5,
96            submission_recovery_policy: SubmissionRecoveryPolicy::default(),
97            open_check_lookback_mins: Some(60),
98            open_check_threshold_ns: DurationNanos::from_secs(5),
99            open_check_missing_retries: 5,
100            open_check_open_only: true,
101            max_single_order_queries_per_cycle: 5,
102            single_order_query_delay_ms: 100,
103            position_check_lookback_mins: 60,
104            position_check_threshold_ns: DurationNanos::from_mins(1),
105            position_check_retries: 3,
106            purge_closed_orders_buffer_mins: None,
107            purge_closed_positions_buffer_mins: None,
108            purge_account_events_lookback_mins: None,
109            purge_from_database: false,
110        }
111    }
112}
113
114impl ExecutionManagerConfig {
115    /// Validates the execution manager configuration, collecting every field violation.
116    ///
117    /// # Errors
118    ///
119    /// Returns a [`ConfigError`] (a [`ConfigError::Multiple`] when more than one field is
120    /// invalid) if any field fails validation.
121    pub fn validate(&self) -> ConfigResult<()> {
122        let mut errors = ConfigErrorCollector::with_capacity(3);
123
124        if let Some(mins) = self.lookback_mins {
125            errors.check(
126                checked_mins_to_secs(mins).is_some(),
127                ConfigError::range(
128                    "ExecutionManagerConfig.lookback_mins",
129                    format!("{mins} minutes (must fit in `u64` seconds)"),
130                ),
131            );
132        }
133
134        if let Some(mins) = self.open_check_lookback_mins {
135            errors.check(
136                DurationNanos::try_from_mins(mins).is_ok(),
137                ConfigError::range(
138                    "ExecutionManagerConfig.open_check_lookback_mins",
139                    format!("{mins} minutes (must fit in `u64` nanoseconds)"),
140                ),
141            );
142        }
143
144        errors.check(
145            DurationNanos::try_from_mins(self.position_check_lookback_mins).is_ok(),
146            ConfigError::range(
147                "ExecutionManagerConfig.position_check_lookback_mins",
148                format!(
149                    "{} minutes (must fit in `u64` nanoseconds)",
150                    self.position_check_lookback_mins
151                ),
152            ),
153        );
154
155        errors.into_result()
156    }
157
158    /// Sets the trader ID on the configuration.
159    #[must_use]
160    pub fn with_trader_id(mut self, trader_id: TraderId) -> Self {
161        self.trader_id = trader_id;
162        self
163    }
164}