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}