1use 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct LiveDataEngineConfig {
37 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
55pub struct LiveRiskEngineConfig {
56 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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74pub struct LiveExecEngineConfig {
75 pub reconciliation: bool,
77 pub reconciliation_startup_delay_secs: f64,
79 pub reconciliation_lookback_mins: Option<u32>,
81 pub reconciliation_instrument_ids: Option<Vec<String>>,
83 pub filter_unclaimed_external_orders: bool,
85 pub filter_position_reports: bool,
87 pub filtered_client_order_ids: Option<Vec<String>>,
89 pub generate_missing_orders: bool,
91 pub inflight_check_interval_ms: u32,
93 pub inflight_check_threshold_ms: u32,
95 pub inflight_check_retries: u32,
97 pub open_check_interval_secs: Option<f64>,
99 pub open_check_lookback_mins: Option<u32>,
101 pub open_check_threshold_ms: u32,
103 pub open_check_missing_retries: u32,
105 pub open_check_open_only: bool,
107 pub max_single_order_queries_per_cycle: u32,
109 pub single_order_query_delay_ms: u32,
111 pub position_check_interval_secs: Option<f64>,
113 pub position_check_lookback_mins: u32,
115 pub position_check_threshold_ms: u32,
117 pub purge_closed_orders_interval_mins: Option<u32>,
119 pub purge_closed_orders_buffer_mins: Option<u32>,
121 pub purge_closed_positions_interval_mins: Option<u32>,
123 pub purge_closed_positions_buffer_mins: Option<u32>,
125 pub purge_account_events_interval_mins: Option<u32>,
127 pub purge_account_events_lookback_mins: Option<u32>,
129 pub purge_from_database: bool,
131 pub own_books_audit_interval_secs: Option<f64>,
133 pub graceful_shutdown_on_error: bool,
135 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#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
185pub struct RoutingConfig {
186 pub default: bool,
188 pub venues: Option<Vec<String>>,
190}
191
192#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
194pub struct InstrumentProviderConfig {
195 pub load_all: bool,
197 pub load_ids: bool,
199 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#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
215pub struct LiveDataClientConfig {
216 pub handle_revised_bars: bool,
218 pub instrument_provider: InstrumentProviderConfig,
220 pub routing: RoutingConfig,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
226pub struct LiveExecClientConfig {
227 pub instrument_provider: InstrumentProviderConfig,
229 pub routing: RoutingConfig,
231}
232
233#[derive(Debug, Clone)]
235pub struct LiveNodeConfig {
236 pub environment: Environment,
238 pub trader_id: TraderId,
240 pub load_state: bool,
242 pub save_state: bool,
244 pub logging: LoggerConfig,
246 pub instance_id: Option<UUID4>,
248 pub timeout_connection: Duration,
250 pub timeout_reconciliation: Duration,
252 pub timeout_portfolio: Duration,
254 pub timeout_disconnection: Duration,
256 pub delay_post_stop: Duration,
258 pub timeout_shutdown: Duration,
260 pub cache: Option<CacheConfig>,
262 pub msgbus: Option<MessageBusConfig>,
264 pub portfolio: Option<PortfolioConfig>,
266 pub streaming: Option<StreamingConfig>,
268 pub data_engine: LiveDataEngineConfig,
270 pub risk_engine: LiveRiskEngineConfig,
272 pub exec_engine: LiveExecEngineConfig,
274 pub data_clients: HashMap<String, LiveDataClientConfig>,
276 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#[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}