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 own_books_audit_interval_secs: Option<f64>,
99 pub open_check_interval_secs: Option<f64>,
101 pub open_check_lookback_mins: Option<u32>,
103 pub open_check_threshold_ms: u32,
105 pub open_check_missing_retries: u32,
107 pub open_check_open_only: bool,
109 pub purge_closed_orders_interval_mins: Option<u32>,
111 pub purge_closed_orders_buffer_mins: Option<u32>,
113 pub purge_closed_positions_interval_mins: Option<u32>,
115 pub purge_closed_positions_buffer_mins: Option<u32>,
117 pub purge_account_events_interval_mins: Option<u32>,
119 pub purge_account_events_lookback_mins: Option<u32>,
121 pub purge_from_database: bool,
123 pub qsize: u32,
125 pub graceful_shutdown_on_exception: bool,
127}
128
129impl Default for LiveExecEngineConfig {
130 fn default() -> Self {
131 Self {
132 reconciliation: true,
133 reconciliation_startup_delay_secs: 10.0,
134 reconciliation_lookback_mins: None,
135 reconciliation_instrument_ids: None,
136 filter_unclaimed_external_orders: false,
137 filter_position_reports: false,
138 filtered_client_order_ids: None,
139 generate_missing_orders: true,
140 inflight_check_interval_ms: 2_000,
141 inflight_check_threshold_ms: 5_000,
142 inflight_check_retries: 5,
143 own_books_audit_interval_secs: None,
144 open_check_interval_secs: None,
145 open_check_lookback_mins: Some(60),
146 open_check_threshold_ms: 5_000,
147 open_check_missing_retries: 5,
148 open_check_open_only: true,
149 purge_closed_orders_interval_mins: None,
150 purge_closed_orders_buffer_mins: None,
151 purge_closed_positions_interval_mins: None,
152 purge_closed_positions_buffer_mins: None,
153 purge_account_events_interval_mins: None,
154 purge_account_events_lookback_mins: None,
155 purge_from_database: false,
156 qsize: 100_000,
157 graceful_shutdown_on_exception: false,
158 }
159 }
160}
161
162impl From<LiveExecEngineConfig> for ExecutionEngineConfig {
163 fn from(_config: LiveExecEngineConfig) -> Self {
164 Self::default()
165 }
166}
167
168#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
170pub struct RoutingConfig {
171 pub default: bool,
173 pub venues: Option<Vec<String>>,
175}
176
177#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
179pub struct InstrumentProviderConfig {
180 pub load_all: bool,
182 pub load_ids: bool,
184 pub filters: HashMap<String, String>,
186}
187
188impl Default for InstrumentProviderConfig {
189 fn default() -> Self {
190 Self {
191 load_all: false,
192 load_ids: true,
193 filters: HashMap::new(),
194 }
195 }
196}
197
198#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
200pub struct LiveDataClientConfig {
201 pub handle_revised_bars: bool,
203 pub instrument_provider: InstrumentProviderConfig,
205 pub routing: RoutingConfig,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
211pub struct LiveExecClientConfig {
212 pub instrument_provider: InstrumentProviderConfig,
214 pub routing: RoutingConfig,
216}
217
218#[derive(Debug, Clone)]
220pub struct LiveNodeConfig {
221 pub environment: Environment,
223 pub trader_id: TraderId,
225 pub load_state: bool,
227 pub save_state: bool,
229 pub logging: LoggerConfig,
231 pub instance_id: Option<UUID4>,
233 pub timeout_connection: Duration,
235 pub timeout_reconciliation: Duration,
237 pub timeout_portfolio: Duration,
239 pub timeout_disconnection: Duration,
241 pub delay_post_stop: Duration,
243 pub timeout_shutdown: Duration,
245 pub cache: Option<CacheConfig>,
247 pub msgbus: Option<MessageBusConfig>,
249 pub portfolio: Option<PortfolioConfig>,
251 pub streaming: Option<StreamingConfig>,
253 pub data_engine: LiveDataEngineConfig,
255 pub risk_engine: LiveRiskEngineConfig,
257 pub exec_engine: LiveExecEngineConfig,
259 pub data_clients: HashMap<String, LiveDataClientConfig>,
261 pub exec_clients: HashMap<String, LiveExecClientConfig>,
263}
264
265impl Default for LiveNodeConfig {
266 fn default() -> Self {
267 Self {
268 environment: Environment::Live,
269 trader_id: TraderId::from("TRADER-001"),
270 load_state: false,
271 save_state: false,
272 logging: LoggerConfig::default(),
273 instance_id: None,
274 timeout_connection: Duration::from_secs(60),
275 timeout_reconciliation: Duration::from_secs(30),
276 timeout_portfolio: Duration::from_secs(10),
277 timeout_disconnection: Duration::from_secs(10),
278 delay_post_stop: Duration::from_secs(10),
279 timeout_shutdown: Duration::from_secs(5),
280 cache: None,
281 msgbus: None,
282 portfolio: None,
283 streaming: None,
284 data_engine: LiveDataEngineConfig::default(),
285 risk_engine: LiveRiskEngineConfig::default(),
286 exec_engine: LiveExecEngineConfig::default(),
287 data_clients: HashMap::new(),
288 exec_clients: HashMap::new(),
289 }
290 }
291}
292
293impl NautilusKernelConfig for LiveNodeConfig {
294 fn environment(&self) -> Environment {
295 self.environment
296 }
297
298 fn trader_id(&self) -> TraderId {
299 self.trader_id
300 }
301
302 fn load_state(&self) -> bool {
303 self.load_state
304 }
305
306 fn save_state(&self) -> bool {
307 self.save_state
308 }
309
310 fn logging(&self) -> LoggerConfig {
311 self.logging.clone()
312 }
313
314 fn instance_id(&self) -> Option<UUID4> {
315 self.instance_id
316 }
317
318 fn timeout_connection(&self) -> Duration {
319 self.timeout_connection
320 }
321
322 fn timeout_reconciliation(&self) -> Duration {
323 self.timeout_reconciliation
324 }
325
326 fn timeout_portfolio(&self) -> Duration {
327 self.timeout_portfolio
328 }
329
330 fn timeout_disconnection(&self) -> Duration {
331 self.timeout_disconnection
332 }
333
334 fn delay_post_stop(&self) -> Duration {
335 self.delay_post_stop
336 }
337
338 fn timeout_shutdown(&self) -> Duration {
339 self.timeout_shutdown
340 }
341
342 fn cache(&self) -> Option<CacheConfig> {
343 self.cache.clone()
344 }
345
346 fn msgbus(&self) -> Option<MessageBusConfig> {
347 self.msgbus.clone()
348 }
349
350 fn data_engine(&self) -> Option<DataEngineConfig> {
351 Some(self.data_engine.clone().into())
352 }
353
354 fn risk_engine(&self) -> Option<RiskEngineConfig> {
355 Some(self.risk_engine.clone().into())
356 }
357
358 fn exec_engine(&self) -> Option<ExecutionEngineConfig> {
359 Some(self.exec_engine.clone().into())
360 }
361
362 fn portfolio(&self) -> Option<PortfolioConfig> {
363 self.portfolio.clone()
364 }
365
366 fn streaming(&self) -> Option<nautilus_persistence::config::StreamingConfig> {
367 self.streaming.clone()
368 }
369}
370
371#[cfg(test)]
376mod tests {
377 use rstest::rstest;
378
379 use super::*;
380
381 #[rstest]
382 fn test_trading_node_config_default() {
383 let config = LiveNodeConfig::default();
384
385 assert_eq!(config.environment, Environment::Live);
386 assert_eq!(config.trader_id, TraderId::from("TRADER-001"));
387 assert_eq!(config.data_engine.qsize, 100_000);
388 assert_eq!(config.risk_engine.qsize, 100_000);
389 assert_eq!(config.exec_engine.qsize, 100_000);
390 assert!(config.exec_engine.reconciliation);
391 assert!(!config.exec_engine.filter_unclaimed_external_orders);
392 assert!(config.data_clients.is_empty());
393 assert!(config.exec_clients.is_empty());
394 }
395
396 #[rstest]
397 fn test_trading_node_config_as_kernel_config() {
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!(config.data_engine().is_some());
403 assert!(config.risk_engine().is_some());
404 assert!(config.exec_engine().is_some());
405 assert!(!config.load_state());
406 assert!(!config.save_state());
407 }
408
409 #[rstest]
410 fn test_live_exec_engine_config_defaults() {
411 let config = LiveExecEngineConfig::default();
412
413 assert!(config.reconciliation);
414 assert_eq!(config.reconciliation_startup_delay_secs, 10.0);
415 assert_eq!(config.reconciliation_lookback_mins, None);
416 assert_eq!(config.reconciliation_instrument_ids, None);
417 assert_eq!(config.filtered_client_order_ids, None);
418 assert!(!config.filter_unclaimed_external_orders);
419 assert!(!config.filter_position_reports);
420 assert!(config.generate_missing_orders);
421 assert_eq!(config.inflight_check_interval_ms, 2_000);
422 assert_eq!(config.inflight_check_threshold_ms, 5_000);
423 assert_eq!(config.inflight_check_retries, 5);
424 assert_eq!(config.open_check_threshold_ms, 5_000);
425 assert_eq!(config.open_check_lookback_mins, Some(60));
426 assert_eq!(config.open_check_missing_retries, 5);
427 assert!(config.open_check_open_only);
428 assert!(!config.purge_from_database);
429 assert!(!config.graceful_shutdown_on_exception);
430 assert_eq!(config.qsize, 100_000);
431 assert_eq!(config.reconciliation_startup_delay_secs, 10.0);
432 }
433
434 #[rstest]
435 fn test_routing_config_default() {
436 let config = RoutingConfig::default();
437
438 assert!(!config.default);
439 assert_eq!(config.venues, None);
440 }
441
442 #[rstest]
443 fn test_live_data_client_config_default() {
444 let config = LiveDataClientConfig::default();
445
446 assert!(!config.handle_revised_bars);
447 assert!(!config.instrument_provider.load_all);
448 assert!(config.instrument_provider.load_ids);
449 assert!(!config.routing.default);
450 }
451}