nautilus_system/
builder.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
16use 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/// Builder for constructing a [`NautilusKernel`] with a fluent API.
29///
30/// Provides a convenient way to configure and build a kernel instance with
31/// optional components and settings.
32#[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    /// Creates a new [`NautilusKernelBuilder`] with required parameters.
56    #[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    /// Set the instance ID for the kernel.
81    #[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    /// Configure whether to load state on startup.
88    #[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    /// Configure whether to save state on shutdown.
95    #[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    /// Set the logging configuration.
102    #[must_use]
103    pub fn with_logging_config(mut self, config: LoggerConfig) -> Self {
104        self.logging = Some(config);
105        self
106    }
107
108    /// Set the connection timeout in seconds.
109    #[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    /// Set the reconciliation timeout in seconds.
116    #[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    /// Set the portfolio initialization timeout in seconds.
123    #[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    /// Set the disconnection timeout in seconds.
130    #[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    /// Set the post-stop delay in seconds.
137    #[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    /// Set the shutdown timeout in seconds.
144    #[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    /// Set the cache configuration.
151    #[must_use]
152    pub fn with_cache_config(mut self, config: CacheConfig) -> Self {
153        self.cache = Some(config);
154        self
155    }
156
157    /// Set the data engine configuration.
158    #[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    /// Set the risk engine configuration.
165    #[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    /// Set the execution engine configuration.
172    #[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    /// Set the portfolio configuration.
179    #[must_use]
180    pub const fn with_portfolio_config(mut self, config: PortfolioConfig) -> Self {
181        self.portfolio = Some(config);
182        self
183    }
184
185    /// Build the [`NautilusKernel`] with the configured settings.
186    ///
187    /// # Errors
188    ///
189    /// Returns an error if kernel initialization fails.
190    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, // msgbus config - not exposed in builder yet
206            data_engine: self.data_engine,
207            risk_engine: self.risk_engine,
208            exec_engine: self.exec_engine,
209            portfolio: self.portfolio,
210            streaming: None, // streaming config - not exposed in builder yet
211        };
212
213        NautilusKernel::new(self.name, config)
214    }
215}
216
217impl Default for NautilusKernelBuilder {
218    /// Create a default builder with minimal configuration for testing/development.
219    fn default() -> Self {
220        Self::new(
221            "NautilusKernel".to_string(),
222            TraderId::default(),
223            Environment::Backtest,
224        )
225    }
226}
227
228////////////////////////////////////////////////////////////////////////////////
229// Tests
230////////////////////////////////////////////////////////////////////////////////
231
232#[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}