1use std::time::Duration;
17
18use nautilus_common::{cache::CacheConfig, enums::Environment, logging::logger::LoggerConfig};
19use nautilus_core::UUID4;
20use nautilus_data::engine::config::DataEngineConfig;
21use nautilus_execution::engine::config::ExecutionEngineConfig;
22use nautilus_model::identifiers::TraderId;
23use nautilus_portfolio::config::PortfolioConfig;
24use nautilus_risk::engine::config::RiskEngineConfig;
25
26use crate::{config::KernelConfig, kernel::NautilusKernel};
27
28#[derive(Debug)]
33pub struct NautilusKernelBuilder {
34 name: String,
35 trader_id: TraderId,
36 environment: Environment,
37 instance_id: Option<UUID4>,
38 load_state: bool,
39 save_state: bool,
40 logging: Option<LoggerConfig>,
41 timeout_connection: Duration,
42 timeout_reconciliation: Duration,
43 timeout_portfolio: Duration,
44 timeout_disconnection: Duration,
45 delay_post_stop: Duration,
46 timeout_shutdown: Duration,
47 cache: Option<CacheConfig>,
48 data_engine: Option<DataEngineConfig>,
49 risk_engine: Option<RiskEngineConfig>,
50 exec_engine: Option<ExecutionEngineConfig>,
51 portfolio: Option<PortfolioConfig>,
52}
53
54impl NautilusKernelBuilder {
55 #[must_use]
57 pub const fn new(name: String, trader_id: TraderId, environment: Environment) -> Self {
58 Self {
59 name,
60 trader_id,
61 environment,
62 instance_id: None,
63 load_state: true,
64 save_state: true,
65 logging: None,
66 timeout_connection: Duration::from_secs(60),
67 timeout_reconciliation: Duration::from_secs(30),
68 timeout_portfolio: Duration::from_secs(10),
69 timeout_disconnection: Duration::from_secs(10),
70 delay_post_stop: Duration::from_secs(10),
71 timeout_shutdown: Duration::from_secs(5),
72 cache: None,
73 data_engine: None,
74 risk_engine: None,
75 exec_engine: None,
76 portfolio: None,
77 }
78 }
79
80 #[must_use]
82 pub const fn with_instance_id(mut self, instance_id: UUID4) -> Self {
83 self.instance_id = Some(instance_id);
84 self
85 }
86
87 #[must_use]
89 pub const fn with_load_state(mut self, load_state: bool) -> Self {
90 self.load_state = load_state;
91 self
92 }
93
94 #[must_use]
96 pub const fn with_save_state(mut self, save_state: bool) -> Self {
97 self.save_state = save_state;
98 self
99 }
100
101 #[must_use]
103 pub fn with_logging_config(mut self, config: LoggerConfig) -> Self {
104 self.logging = Some(config);
105 self
106 }
107
108 #[must_use]
110 pub const fn with_timeout_connection(mut self, timeout_secs: u64) -> Self {
111 self.timeout_connection = Duration::from_secs(timeout_secs);
112 self
113 }
114
115 #[must_use]
117 pub const fn with_timeout_reconciliation(mut self, timeout_secs: u64) -> Self {
118 self.timeout_reconciliation = Duration::from_secs(timeout_secs);
119 self
120 }
121
122 #[must_use]
124 pub const fn with_timeout_portfolio(mut self, timeout_secs: u64) -> Self {
125 self.timeout_portfolio = Duration::from_secs(timeout_secs);
126 self
127 }
128
129 #[must_use]
131 pub const fn with_timeout_disconnection(mut self, timeout_secs: u64) -> Self {
132 self.timeout_disconnection = Duration::from_secs(timeout_secs);
133 self
134 }
135
136 #[must_use]
138 pub const fn with_delay_post_stop(mut self, delay_secs: u64) -> Self {
139 self.delay_post_stop = Duration::from_secs(delay_secs);
140 self
141 }
142
143 #[must_use]
145 pub const fn with_timeout_shutdown(mut self, timeout_secs: u64) -> Self {
146 self.timeout_shutdown = Duration::from_secs(timeout_secs);
147 self
148 }
149
150 #[must_use]
152 pub fn with_cache_config(mut self, config: CacheConfig) -> Self {
153 self.cache = Some(config);
154 self
155 }
156
157 #[must_use]
159 pub fn with_data_engine_config(mut self, config: DataEngineConfig) -> Self {
160 self.data_engine = Some(config);
161 self
162 }
163
164 #[must_use]
166 pub fn with_risk_engine_config(mut self, config: RiskEngineConfig) -> Self {
167 self.risk_engine = Some(config);
168 self
169 }
170
171 #[must_use]
173 pub fn with_exec_engine_config(mut self, config: ExecutionEngineConfig) -> Self {
174 self.exec_engine = Some(config);
175 self
176 }
177
178 #[must_use]
180 pub const fn with_portfolio_config(mut self, config: PortfolioConfig) -> Self {
181 self.portfolio = Some(config);
182 self
183 }
184
185 pub fn build(self) -> anyhow::Result<NautilusKernel> {
191 let config = KernelConfig {
192 environment: self.environment,
193 trader_id: self.trader_id,
194 load_state: self.load_state,
195 save_state: self.save_state,
196 logging: self.logging.unwrap_or_default(),
197 instance_id: self.instance_id,
198 timeout_connection: self.timeout_connection,
199 timeout_reconciliation: self.timeout_reconciliation,
200 timeout_portfolio: self.timeout_portfolio,
201 timeout_disconnection: self.timeout_disconnection,
202 delay_post_stop: self.delay_post_stop,
203 timeout_shutdown: self.timeout_shutdown,
204 cache: self.cache,
205 msgbus: None, data_engine: self.data_engine,
207 risk_engine: self.risk_engine,
208 exec_engine: self.exec_engine,
209 portfolio: self.portfolio,
210 streaming: None, };
212
213 NautilusKernel::new(self.name, config)
214 }
215}
216
217impl Default for NautilusKernelBuilder {
218 fn default() -> Self {
220 Self::new(
221 "NautilusKernel".to_string(),
222 TraderId::default(),
223 Environment::Backtest,
224 )
225 }
226}
227
228#[cfg(test)]
233mod tests {
234 use nautilus_model::identifiers::TraderId;
235 use rstest::*;
236
237 use super::*;
238
239 #[rstest]
240 fn test_builder_default() {
241 let builder = NautilusKernelBuilder::default();
242 assert_eq!(builder.name, "NautilusKernel");
243 assert_eq!(builder.environment, Environment::Backtest);
244 assert!(builder.load_state);
245 assert!(builder.save_state);
246 }
247
248 #[rstest]
249 fn test_builder_fluent_api() {
250 let trader_id = TraderId::from("TRADER-001");
251 let instance_id = UUID4::new();
252
253 let builder =
254 NautilusKernelBuilder::new("TestKernel".to_string(), trader_id, Environment::Live)
255 .with_instance_id(instance_id)
256 .with_load_state(false)
257 .with_save_state(false)
258 .with_timeout_connection(30);
259
260 assert_eq!(builder.name, "TestKernel");
261 assert_eq!(builder.trader_id, trader_id);
262 assert_eq!(builder.environment, Environment::Live);
263 assert_eq!(builder.instance_id, Some(instance_id));
264 assert!(!builder.load_state);
265 assert!(!builder.save_state);
266 assert_eq!(builder.timeout_connection, Duration::from_secs(30));
267 }
268
269 #[rstest]
270 fn test_builder_build() {
271 #[cfg(feature = "python")]
272 pyo3::prepare_freethreaded_python();
273
274 let result = NautilusKernelBuilder::default().build();
275 assert!(result.is_ok());
276
277 let kernel = result.unwrap();
278 assert_eq!(kernel.name(), "NautilusKernel".to_string());
279 assert_eq!(kernel.environment(), Environment::Backtest);
280 }
281
282 #[rstest]
283 fn test_builder_with_configs() {
284 let cache_config = CacheConfig::default();
285 let data_engine_config = DataEngineConfig::default();
286
287 let builder = NautilusKernelBuilder::default()
288 .with_cache_config(cache_config)
289 .with_data_engine_config(data_engine_config);
290
291 assert!(builder.cache.is_some());
292 assert!(builder.data_engine.is_some());
293 }
294}