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_portfolio::config::PortfolioConfig;
29use nautilus_risk::engine::config::RiskEngineConfig;
30use nautilus_system::config::{NautilusKernelConfig, StreamingConfig};
31use serde::{Deserialize, Serialize};
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct LiveDataEngineConfig {
36 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54pub struct LiveRiskEngineConfig {
55 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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct LiveExecEngineConfig {
74 pub reconciliation: bool,
76 pub reconciliation_startup_delay_secs: f64,
78 pub reconciliation_lookback_mins: Option<u32>,
80 pub reconciliation_instrument_ids: Option<Vec<String>>,
82 pub filter_unclaimed_external_orders: bool,
84 pub filter_position_reports: bool,
86 pub filtered_client_order_ids: Option<Vec<String>>,
88 pub generate_missing_orders: bool,
90 pub inflight_check_interval_ms: u32,
92 pub inflight_check_threshold_ms: u32,
94 pub inflight_check_retries: u32,
96 pub open_check_interval_secs: Option<f64>,
98 pub open_check_lookback_mins: Option<u32>,
100 pub open_check_threshold_ms: u32,
102 pub open_check_missing_retries: u32,
104 pub open_check_open_only: bool,
106 pub max_single_order_queries_per_cycle: u32,
108 pub single_order_query_delay_ms: u32,
110 pub position_check_interval_secs: Option<f64>,
112 pub position_check_lookback_mins: u32,
114 pub position_check_threshold_ms: u32,
116 pub purge_closed_orders_interval_mins: Option<u32>,
118 pub purge_closed_orders_buffer_mins: Option<u32>,
120 pub purge_closed_positions_interval_mins: Option<u32>,
122 pub purge_closed_positions_buffer_mins: Option<u32>,
124 pub purge_account_events_interval_mins: Option<u32>,
126 pub purge_account_events_lookback_mins: Option<u32>,
128 pub purge_from_database: bool,
130 pub own_books_audit_interval_secs: Option<f64>,
132 pub graceful_shutdown_on_error: bool,
134 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#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
184pub struct RoutingConfig {
185 pub default: bool,
187 pub venues: Option<Vec<String>>,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct InstrumentProviderConfig {
194 pub load_all: bool,
196 pub load_ids: bool,
198 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#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
214pub struct LiveDataClientConfig {
215 pub handle_revised_bars: bool,
217 pub instrument_provider: InstrumentProviderConfig,
219 pub routing: RoutingConfig,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
225pub struct LiveExecClientConfig {
226 pub instrument_provider: InstrumentProviderConfig,
228 pub routing: RoutingConfig,
230}
231
232#[derive(Debug, Clone)]
234pub struct LiveNodeConfig {
235 pub environment: Environment,
237 pub trader_id: TraderId,
239 pub load_state: bool,
241 pub save_state: bool,
243 pub logging: LoggerConfig,
245 pub instance_id: Option<UUID4>,
247 pub timeout_connection: Duration,
249 pub timeout_reconciliation: Duration,
251 pub timeout_portfolio: Duration,
253 pub timeout_disconnection: Duration,
255 pub delay_post_stop: Duration,
257 pub timeout_shutdown: Duration,
259 pub cache: Option<CacheConfig>,
261 pub msgbus: Option<MessageBusConfig>,
263 pub portfolio: Option<PortfolioConfig>,
265 pub streaming: Option<StreamingConfig>,
267 pub data_engine: LiveDataEngineConfig,
269 pub risk_engine: LiveRiskEngineConfig,
271 pub exec_engine: LiveExecEngineConfig,
273 pub data_clients: HashMap<String, LiveDataClientConfig>,
275 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}