nautilus_system/
builder.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.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 nautilus_common::{cache::CacheConfig, enums::Environment, logging::logger::LoggerConfig};
17use nautilus_core::UUID4;
18use posei_trader::engine::config::DataEngineConfig;
19use nautilus_execution::engine::config::ExecutionEngineConfig;
20use nautilus_model::identifiers::TraderId;
21use nautilus_portfolio::config::PortfolioConfig;
22use nautilus_risk::engine::config::RiskEngineConfig;
23
24use crate::{config::KernelConfig, kernel::PoseiKernel};
25
26/// Builder for constructing a [`PoseiKernel`] with a fluent API.
27///
28/// Provides a convenient way to configure and build a kernel instance with
29/// optional components and settings.
30#[derive(Debug)]
31pub struct PoseiKernelBuilder {
32    name: String,
33    trader_id: TraderId,
34    environment: Environment,
35    instance_id: Option<UUID4>,
36    load_state: bool,
37    save_state: bool,
38    logging: Option<LoggerConfig>,
39    timeout_connection: u32,
40    timeout_reconciliation: u32,
41    timeout_portfolio: u32,
42    timeout_disconnection: u32,
43    timeout_post_stop: u32,
44    timeout_shutdown: u32,
45    cache: Option<CacheConfig>,
46    data_engine: Option<DataEngineConfig>,
47    risk_engine: Option<RiskEngineConfig>,
48    exec_engine: Option<ExecutionEngineConfig>,
49    portfolio: Option<PortfolioConfig>,
50}
51
52impl PoseiKernelBuilder {
53    /// Creates a new [`PoseiKernelBuilder`] with required parameters.
54    #[must_use]
55    pub const fn new(name: String, trader_id: TraderId, environment: Environment) -> Self {
56        Self {
57            name,
58            trader_id,
59            environment,
60            instance_id: None,
61            load_state: true,
62            save_state: true,
63            logging: None,
64            timeout_connection: 60,
65            timeout_reconciliation: 30,
66            timeout_portfolio: 10,
67            timeout_disconnection: 10,
68            timeout_post_stop: 10,
69            timeout_shutdown: 5,
70            cache: None,
71            data_engine: None,
72            risk_engine: None,
73            exec_engine: None,
74            portfolio: None,
75        }
76    }
77
78    /// Set the instance ID for the kernel.
79    #[must_use]
80    pub const fn with_instance_id(mut self, instance_id: UUID4) -> Self {
81        self.instance_id = Some(instance_id);
82        self
83    }
84
85    /// Configure whether to load state on startup.
86    #[must_use]
87    pub const fn with_load_state(mut self, load_state: bool) -> Self {
88        self.load_state = load_state;
89        self
90    }
91
92    /// Configure whether to save state on shutdown.
93    #[must_use]
94    pub const fn with_save_state(mut self, save_state: bool) -> Self {
95        self.save_state = save_state;
96        self
97    }
98
99    /// Set the logging configuration.
100    #[must_use]
101    pub fn with_logging_config(mut self, config: LoggerConfig) -> Self {
102        self.logging = Some(config);
103        self
104    }
105
106    /// Set the connection timeout in seconds.
107    #[must_use]
108    pub const fn with_timeout_connection(mut self, timeout: u32) -> Self {
109        self.timeout_connection = timeout;
110        self
111    }
112
113    /// Set the reconciliation timeout in seconds.
114    #[must_use]
115    pub const fn with_timeout_reconciliation(mut self, timeout: u32) -> Self {
116        self.timeout_reconciliation = timeout;
117        self
118    }
119
120    /// Set the portfolio initialization timeout in seconds.
121    #[must_use]
122    pub const fn with_timeout_portfolio(mut self, timeout: u32) -> Self {
123        self.timeout_portfolio = timeout;
124        self
125    }
126
127    /// Set the disconnection timeout in seconds.
128    #[must_use]
129    pub const fn with_timeout_disconnection(mut self, timeout: u32) -> Self {
130        self.timeout_disconnection = timeout;
131        self
132    }
133
134    /// Set the post-stop timeout in seconds.
135    #[must_use]
136    pub const fn with_timeout_post_stop(mut self, timeout: u32) -> Self {
137        self.timeout_post_stop = timeout;
138        self
139    }
140
141    /// Set the shutdown timeout in seconds.
142    #[must_use]
143    pub const fn with_timeout_shutdown(mut self, timeout: u32) -> Self {
144        self.timeout_shutdown = timeout;
145        self
146    }
147
148    /// Set the cache configuration.
149    #[must_use]
150    pub fn with_cache_config(mut self, config: CacheConfig) -> Self {
151        self.cache = Some(config);
152        self
153    }
154
155    /// Set the data engine configuration.
156    #[must_use]
157    pub fn with_data_engine_config(mut self, config: DataEngineConfig) -> Self {
158        self.data_engine = Some(config);
159        self
160    }
161
162    /// Set the risk engine configuration.
163    #[must_use]
164    pub fn with_risk_engine_config(mut self, config: RiskEngineConfig) -> Self {
165        self.risk_engine = Some(config);
166        self
167    }
168
169    /// Set the execution engine configuration.
170    #[must_use]
171    pub const fn with_exec_engine_config(mut self, config: ExecutionEngineConfig) -> Self {
172        self.exec_engine = Some(config);
173        self
174    }
175
176    /// Set the portfolio configuration.
177    #[must_use]
178    pub const fn with_portfolio_config(mut self, config: PortfolioConfig) -> Self {
179        self.portfolio = Some(config);
180        self
181    }
182
183    /// Build the [`PoseiKernel`] with the configured settings.
184    ///
185    /// # Errors
186    ///
187    /// Returns an error if kernel initialization fails.
188    pub fn build(self) -> anyhow::Result<PoseiKernel> {
189        let config = KernelConfig {
190            environment: self.environment,
191            trader_id: self.trader_id,
192            load_state: self.load_state,
193            save_state: self.save_state,
194            logging: self.logging.unwrap_or_default(),
195            instance_id: self.instance_id,
196            timeout_connection: self.timeout_connection,
197            timeout_reconciliation: self.timeout_reconciliation,
198            timeout_portfolio: self.timeout_portfolio,
199            timeout_disconnection: self.timeout_disconnection,
200            timeout_post_stop: self.timeout_post_stop,
201            timeout_shutdown: self.timeout_shutdown,
202            cache: self.cache,
203            msgbus: None, // msgbus config - not exposed in builder yet
204            data_engine: self.data_engine,
205            risk_engine: self.risk_engine,
206            exec_engine: self.exec_engine,
207            portfolio: self.portfolio,
208            streaming: None, // streaming config - not exposed in builder yet
209        };
210
211        PoseiKernel::new(self.name, config)
212    }
213}
214
215impl Default for PoseiKernelBuilder {
216    /// Create a default builder with minimal configuration for testing/development.
217    fn default() -> Self {
218        Self::new(
219            "PoseiKernel".to_string(),
220            TraderId::default(),
221            Environment::Backtest,
222        )
223    }
224}
225
226////////////////////////////////////////////////////////////////////////////////
227// Tests
228////////////////////////////////////////////////////////////////////////////////
229
230#[cfg(test)]
231mod tests {
232    use nautilus_model::identifiers::TraderId;
233    use rstest::*;
234
235    use super::*;
236
237    #[rstest]
238    fn test_builder_default() {
239        let builder = PoseiKernelBuilder::default();
240        assert_eq!(builder.name, "PoseiKernel");
241        assert_eq!(builder.environment, Environment::Backtest);
242        assert!(builder.load_state);
243        assert!(builder.save_state);
244    }
245
246    #[rstest]
247    fn test_builder_fluent_api() {
248        let trader_id = TraderId::from("TRADER-001");
249        let instance_id = UUID4::new();
250
251        let builder =
252            PoseiKernelBuilder::new("TestKernel".to_string(), trader_id, Environment::Live)
253                .with_instance_id(instance_id)
254                .with_load_state(false)
255                .with_save_state(false)
256                .with_timeout_connection(30);
257
258        assert_eq!(builder.name, "TestKernel");
259        assert_eq!(builder.trader_id, trader_id);
260        assert_eq!(builder.environment, Environment::Live);
261        assert_eq!(builder.instance_id, Some(instance_id));
262        assert!(!builder.load_state);
263        assert!(!builder.save_state);
264        assert_eq!(builder.timeout_connection, 30);
265    }
266
267    #[rstest]
268    fn test_builder_build() {
269        #[cfg(feature = "python")]
270        pyo3::prepare_freethreaded_python();
271
272        let result = PoseiKernelBuilder::default().build();
273        assert!(result.is_ok());
274
275        let kernel = result.unwrap();
276        assert_eq!(kernel.name(), "PoseiKernel".to_string());
277        assert_eq!(kernel.environment(), Environment::Backtest);
278    }
279
280    #[rstest]
281    fn test_builder_with_configs() {
282        let cache_config = CacheConfig::default();
283        let data_engine_config = DataEngineConfig::default();
284
285        let builder = PoseiKernelBuilder::default()
286            .with_cache_config(cache_config)
287            .with_data_engine_config(data_engine_config);
288
289        assert!(builder.cache.is_some());
290        assert!(builder.data_engine.is_some());
291    }
292}