nautilus_live/
config.rs

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