nautilus_live/
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 types for live Nautilus system nodes.
17
18use std::{collections::HashMap, time::Duration};
19
20use nautilus_common::{
21    cache::CacheConfig, enums::Environment, logging::logger::LoggerConfig,
22    msgbus::database::MessageBusConfig,
23};
24use nautilus_core::UUID4;
25use nautilus_data::engine::config::DataEngineConfig;
26use nautilus_execution::engine::config::ExecutionEngineConfig;
27use nautilus_model::identifiers::TraderId;
28use nautilus_portfolio::config::PortfolioConfig;
29use nautilus_risk::engine::config::RiskEngineConfig;
30use nautilus_system::config::{NautilusKernelConfig, StreamingConfig};
31use serde::{Deserialize, Serialize};
32
33/// Configuration for live data engines.
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct LiveDataEngineConfig {
36    /// The queue size for the engine's internal queue buffers.
37    pub qsize: u32,
38}
39
40impl Default for LiveDataEngineConfig {
41    fn default() -> Self {
42        Self { qsize: 100_000 }
43    }
44}
45
46impl From<LiveDataEngineConfig> for DataEngineConfig {
47    fn from(_config: LiveDataEngineConfig) -> Self {
48        Self::default()
49    }
50}
51
52/// Configuration for live risk engines.
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54pub struct LiveRiskEngineConfig {
55    /// The queue size for the engine's internal queue buffers.
56    pub qsize: u32,
57}
58
59impl Default for LiveRiskEngineConfig {
60    fn default() -> Self {
61        Self { qsize: 100_000 }
62    }
63}
64
65impl From<LiveRiskEngineConfig> for RiskEngineConfig {
66    fn from(_config: LiveRiskEngineConfig) -> Self {
67        Self::default()
68    }
69}
70
71/// Configuration for live execution engines.
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct LiveExecEngineConfig {
74    /// If reconciliation is active at start-up.
75    pub reconciliation: bool,
76    /// The delay (seconds) before starting reconciliation at startup.
77    pub reconciliation_startup_delay_secs: f64,
78    /// The maximum lookback minutes to reconcile state for.
79    pub reconciliation_lookback_mins: Option<u32>,
80    /// Specific instrument IDs to reconcile (if None, reconciles all).
81    pub reconciliation_instrument_ids: Option<Vec<String>>,
82    /// If unclaimed order events with an EXTERNAL strategy ID should be filtered/dropped.
83    pub filter_unclaimed_external_orders: bool,
84    /// If position status reports are filtered from reconciliation.
85    pub filter_position_reports: bool,
86    /// Client order IDs to filter from reconciliation.
87    pub filtered_client_order_ids: Option<Vec<String>>,
88    /// If MARKET order events will be generated during reconciliation to align discrepancies.
89    pub generate_missing_orders: bool,
90    /// The interval (milliseconds) between checking whether in-flight orders have exceeded their threshold.
91    pub inflight_check_interval_ms: u32,
92    /// The threshold (milliseconds) beyond which an in-flight order's status is checked with the venue.
93    pub inflight_check_threshold_ms: u32,
94    /// The number of retry attempts for verifying in-flight order status.
95    pub inflight_check_retries: u32,
96    /// The interval (seconds) between checks for open orders at the venue.
97    pub open_check_interval_secs: Option<f64>,
98    /// The lookback minutes for open order checks.
99    pub open_check_lookback_mins: Option<u32>,
100    /// The minimum elapsed time (milliseconds) since an order update before acting on discrepancies.
101    pub open_check_threshold_ms: u32,
102    /// The number of retries for missing open orders.
103    pub open_check_missing_retries: u32,
104    /// If the `check_open_orders` requests only currently open orders from the venue.
105    pub open_check_open_only: bool,
106    /// The maximum number of single-order queries per consistency check cycle.
107    pub max_single_order_queries_per_cycle: u32,
108    /// The delay (milliseconds) between consecutive single-order queries.
109    pub single_order_query_delay_ms: u32,
110    /// The interval (seconds) between checks for open positions at the venue.
111    pub position_check_interval_secs: Option<f64>,
112    /// The lookback minutes for position consistency checks.
113    pub position_check_lookback_mins: u32,
114    /// The minimum elapsed time (milliseconds) since a position update before acting on discrepancies.
115    pub position_check_threshold_ms: u32,
116    /// The interval (minutes) between purging closed orders from the in-memory cache.
117    pub purge_closed_orders_interval_mins: Option<u32>,
118    /// The time buffer (minutes) before closed orders can be purged.
119    pub purge_closed_orders_buffer_mins: Option<u32>,
120    /// The interval (minutes) between purging closed positions from the in-memory cache.
121    pub purge_closed_positions_interval_mins: Option<u32>,
122    /// The time buffer (minutes) before closed positions can be purged.
123    pub purge_closed_positions_buffer_mins: Option<u32>,
124    /// The interval (minutes) between purging account events from the in-memory cache.
125    pub purge_account_events_interval_mins: Option<u32>,
126    /// The time buffer (minutes) before account events can be purged.
127    pub purge_account_events_lookback_mins: Option<u32>,
128    /// If purge operations should also delete from the backing database.
129    pub purge_from_database: bool,
130    /// The interval (seconds) between auditing own books against public order books.
131    pub own_books_audit_interval_secs: Option<f64>,
132    /// If the engine should gracefully shutdown when queue processing encounters unexpected errors.
133    pub graceful_shutdown_on_error: bool,
134    /// The queue size for the engine's internal queue buffers.
135    pub qsize: u32,
136}
137
138impl Default for LiveExecEngineConfig {
139    fn default() -> Self {
140        Self {
141            reconciliation: true,
142            reconciliation_startup_delay_secs: 10.0,
143            reconciliation_lookback_mins: None,
144            reconciliation_instrument_ids: None,
145            filter_unclaimed_external_orders: false,
146            filter_position_reports: false,
147            filtered_client_order_ids: None,
148            generate_missing_orders: true,
149            inflight_check_interval_ms: 2_000,
150            inflight_check_threshold_ms: 5_000,
151            inflight_check_retries: 5,
152            open_check_interval_secs: None,
153            open_check_lookback_mins: Some(60),
154            open_check_threshold_ms: 5_000,
155            open_check_missing_retries: 5,
156            open_check_open_only: true,
157            max_single_order_queries_per_cycle: 5,
158            single_order_query_delay_ms: 100,
159            position_check_interval_secs: None,
160            position_check_lookback_mins: 60,
161            position_check_threshold_ms: 60_000,
162            purge_closed_orders_interval_mins: None,
163            purge_closed_orders_buffer_mins: None,
164            purge_closed_positions_interval_mins: None,
165            purge_closed_positions_buffer_mins: None,
166            purge_account_events_interval_mins: None,
167            purge_account_events_lookback_mins: None,
168            purge_from_database: false,
169            own_books_audit_interval_secs: None,
170            graceful_shutdown_on_error: false,
171            qsize: 100_000,
172        }
173    }
174}
175
176impl From<LiveExecEngineConfig> for ExecutionEngineConfig {
177    fn from(_config: LiveExecEngineConfig) -> Self {
178        Self::default()
179    }
180}
181
182/// Configuration for live client message routing.
183#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
184pub struct RoutingConfig {
185    /// If the client should be registered as the default routing client.
186    pub default: bool,
187    /// The venues to register for routing.
188    pub venues: Option<Vec<String>>,
189}
190
191/// Configuration for instrument providers.
192#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct InstrumentProviderConfig {
194    /// Whether to load all instruments on startup.
195    pub load_all: bool,
196    /// Whether to load instrument IDs only.
197    pub load_ids: bool,
198    /// Filters for loading specific instruments.
199    pub filters: HashMap<String, String>,
200}
201
202impl Default for InstrumentProviderConfig {
203    fn default() -> Self {
204        Self {
205            load_all: false,
206            load_ids: true,
207            filters: HashMap::new(),
208        }
209    }
210}
211
212/// Configuration for live data clients.
213#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
214pub struct LiveDataClientConfig {
215    /// If `DataClient` will emit bar updates when a new bar opens.
216    pub handle_revised_bars: bool,
217    /// The client's instrument provider configuration.
218    pub instrument_provider: InstrumentProviderConfig,
219    /// The client's message routing configuration.
220    pub routing: RoutingConfig,
221}
222
223/// Configuration for live execution clients.
224#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
225pub struct LiveExecClientConfig {
226    /// The client's instrument provider configuration.
227    pub instrument_provider: InstrumentProviderConfig,
228    /// The client's message routing configuration.
229    pub routing: RoutingConfig,
230}
231
232/// Configuration for live Nautilus system nodes.
233#[derive(Debug, Clone)]
234pub struct LiveNodeConfig {
235    /// The trading environment.
236    pub environment: Environment,
237    /// The trader ID for the node.
238    pub trader_id: TraderId,
239    /// If trading strategy state should be loaded from the database on start.
240    pub load_state: bool,
241    /// If trading strategy state should be saved to the database on stop.
242    pub save_state: bool,
243    /// The logging configuration for the kernel.
244    pub logging: LoggerConfig,
245    /// The unique instance identifier for the kernel
246    pub instance_id: Option<UUID4>,
247    /// The timeout for all clients to connect and initialize.
248    pub timeout_connection: Duration,
249    /// The timeout for execution state to reconcile.
250    pub timeout_reconciliation: Duration,
251    /// The timeout for portfolio to initialize margins and unrealized pnls.
252    pub timeout_portfolio: Duration,
253    /// The timeout for all engine clients to disconnect.
254    pub timeout_disconnection: Duration,
255    /// The delay after stopping the node to await residual events before final shutdown.
256    pub delay_post_stop: Duration,
257    /// The timeout to await pending tasks cancellation during shutdown.
258    pub timeout_shutdown: Duration,
259    /// The cache configuration.
260    pub cache: Option<CacheConfig>,
261    /// The message bus configuration.
262    pub msgbus: Option<MessageBusConfig>,
263    /// The portfolio configuration.
264    pub portfolio: Option<PortfolioConfig>,
265    /// The configuration for streaming to feather files.
266    pub streaming: Option<StreamingConfig>,
267    /// The live data engine configuration.
268    pub data_engine: LiveDataEngineConfig,
269    /// The live risk engine configuration.
270    pub risk_engine: LiveRiskEngineConfig,
271    /// The live execution engine configuration.
272    pub exec_engine: LiveExecEngineConfig,
273    /// The data client configurations.
274    pub data_clients: HashMap<String, LiveDataClientConfig>,
275    /// The execution client configurations.
276    pub exec_clients: HashMap<String, LiveExecClientConfig>,
277}
278
279impl Default for LiveNodeConfig {
280    fn default() -> Self {
281        Self {
282            environment: Environment::Live,
283            trader_id: TraderId::from("TRADER-001"),
284            load_state: false,
285            save_state: false,
286            logging: LoggerConfig::default(),
287            instance_id: None,
288            timeout_connection: Duration::from_secs(60),
289            timeout_reconciliation: Duration::from_secs(30),
290            timeout_portfolio: Duration::from_secs(10),
291            timeout_disconnection: Duration::from_secs(10),
292            delay_post_stop: Duration::from_secs(10),
293            timeout_shutdown: Duration::from_secs(5),
294            cache: None,
295            msgbus: None,
296            portfolio: None,
297            streaming: None,
298            data_engine: LiveDataEngineConfig::default(),
299            risk_engine: LiveRiskEngineConfig::default(),
300            exec_engine: LiveExecEngineConfig::default(),
301            data_clients: HashMap::new(),
302            exec_clients: HashMap::new(),
303        }
304    }
305}
306
307impl NautilusKernelConfig for LiveNodeConfig {
308    fn environment(&self) -> Environment {
309        self.environment
310    }
311
312    fn trader_id(&self) -> TraderId {
313        self.trader_id
314    }
315
316    fn load_state(&self) -> bool {
317        self.load_state
318    }
319
320    fn save_state(&self) -> bool {
321        self.save_state
322    }
323
324    fn logging(&self) -> LoggerConfig {
325        self.logging.clone()
326    }
327
328    fn instance_id(&self) -> Option<UUID4> {
329        self.instance_id
330    }
331
332    fn timeout_connection(&self) -> Duration {
333        self.timeout_connection
334    }
335
336    fn timeout_reconciliation(&self) -> Duration {
337        self.timeout_reconciliation
338    }
339
340    fn timeout_portfolio(&self) -> Duration {
341        self.timeout_portfolio
342    }
343
344    fn timeout_disconnection(&self) -> Duration {
345        self.timeout_disconnection
346    }
347
348    fn delay_post_stop(&self) -> Duration {
349        self.delay_post_stop
350    }
351
352    fn timeout_shutdown(&self) -> Duration {
353        self.timeout_shutdown
354    }
355
356    fn cache(&self) -> Option<CacheConfig> {
357        self.cache.clone()
358    }
359
360    fn msgbus(&self) -> Option<MessageBusConfig> {
361        self.msgbus.clone()
362    }
363
364    fn data_engine(&self) -> Option<DataEngineConfig> {
365        Some(self.data_engine.clone().into())
366    }
367
368    fn risk_engine(&self) -> Option<RiskEngineConfig> {
369        Some(self.risk_engine.clone().into())
370    }
371
372    fn exec_engine(&self) -> Option<ExecutionEngineConfig> {
373        Some(self.exec_engine.clone().into())
374    }
375
376    fn portfolio(&self) -> Option<PortfolioConfig> {
377        self.portfolio.clone()
378    }
379
380    fn streaming(&self) -> Option<StreamingConfig> {
381        self.streaming.clone()
382    }
383}
384
385#[cfg(test)]
386mod tests {
387    use rstest::rstest;
388
389    use super::*;
390
391    #[rstest]
392    fn test_trading_node_config_default() {
393        let config = LiveNodeConfig::default();
394
395        assert_eq!(config.environment, Environment::Live);
396        assert_eq!(config.trader_id, TraderId::from("TRADER-001"));
397        assert_eq!(config.data_engine.qsize, 100_000);
398        assert_eq!(config.risk_engine.qsize, 100_000);
399        assert_eq!(config.exec_engine.qsize, 100_000);
400        assert!(config.exec_engine.reconciliation);
401        assert!(!config.exec_engine.filter_unclaimed_external_orders);
402        assert!(config.data_clients.is_empty());
403        assert!(config.exec_clients.is_empty());
404    }
405
406    #[rstest]
407    fn test_trading_node_config_as_kernel_config() {
408        let config = LiveNodeConfig::default();
409
410        assert_eq!(config.environment(), Environment::Live);
411        assert_eq!(config.trader_id(), TraderId::from("TRADER-001"));
412        assert!(config.data_engine().is_some());
413        assert!(config.risk_engine().is_some());
414        assert!(config.exec_engine().is_some());
415        assert!(!config.load_state());
416        assert!(!config.save_state());
417    }
418
419    #[rstest]
420    fn test_live_exec_engine_config_defaults() {
421        let config = LiveExecEngineConfig::default();
422
423        assert!(config.reconciliation);
424        assert_eq!(config.reconciliation_startup_delay_secs, 10.0);
425        assert_eq!(config.reconciliation_lookback_mins, None);
426        assert_eq!(config.reconciliation_instrument_ids, None);
427        assert_eq!(config.filtered_client_order_ids, None);
428        assert!(!config.filter_unclaimed_external_orders);
429        assert!(!config.filter_position_reports);
430        assert!(config.generate_missing_orders);
431        assert_eq!(config.inflight_check_interval_ms, 2_000);
432        assert_eq!(config.inflight_check_threshold_ms, 5_000);
433        assert_eq!(config.inflight_check_retries, 5);
434        assert_eq!(config.open_check_threshold_ms, 5_000);
435        assert_eq!(config.open_check_lookback_mins, Some(60));
436        assert_eq!(config.open_check_missing_retries, 5);
437        assert!(config.open_check_open_only);
438        assert!(!config.purge_from_database);
439        assert!(!config.graceful_shutdown_on_error);
440        assert_eq!(config.qsize, 100_000);
441        assert_eq!(config.reconciliation_startup_delay_secs, 10.0);
442    }
443
444    #[rstest]
445    fn test_routing_config_default() {
446        let config = RoutingConfig::default();
447
448        assert!(!config.default);
449        assert_eq!(config.venues, None);
450    }
451
452    #[rstest]
453    fn test_live_data_client_config_default() {
454        let config = LiveDataClientConfig::default();
455
456        assert!(!config.handle_revised_bars);
457        assert!(!config.instrument_provider.load_all);
458        assert!(config.instrument_provider.load_ids);
459        assert!(!config.routing.default);
460    }
461}