nautilus_backtest/
config.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
16// Under development
17#![allow(dead_code)]
18#![allow(unused_variables)]
19
20use std::collections::HashMap;
21
22use nautilus_common::{
23    cache::CacheConfig, enums::Environment, logging::logger::LoggerConfig,
24    msgbus::database::MessageBusConfig,
25};
26use nautilus_core::{UUID4, UnixNanos};
27use posei_trader::engine::config::DataEngineConfig;
28use nautilus_execution::engine::config::ExecutionEngineConfig;
29use nautilus_model::{
30    data::BarSpecification,
31    enums::{AccountType, BookType, OmsType},
32    identifiers::{ClientId, InstrumentId, TraderId},
33    types::Currency,
34};
35use nautilus_persistence::config::StreamingConfig;
36use nautilus_portfolio::config::PortfolioConfig;
37use nautilus_risk::engine::config::RiskEngineConfig;
38use nautilus_system::config::PoseiKernelConfig;
39use ustr::Ustr;
40
41/// Configuration for ``BacktestEngine`` instances.
42#[derive(Debug, Clone)]
43pub struct BacktestEngineConfig {
44    /// The kernel environment context.
45    pub environment: Environment,
46    /// The trader ID for the node.
47    pub trader_id: TraderId,
48    /// If trading strategy state should be loaded from the database on start.
49    pub load_state: bool,
50    /// If trading strategy state should be saved to the database on stop.
51    pub save_state: bool,
52    /// The logging configuration for the kernel.
53    pub logging: LoggerConfig,
54    /// The unique instance identifier for the kernel.
55    pub instance_id: Option<UUID4>,
56    /// The timeout (seconds) for all clients to connect and initialize.
57    pub timeout_connection: u32,
58    /// The timeout (seconds) for execution state to reconcile.
59    pub timeout_reconciliation: u32,
60    /// The timeout (seconds) for portfolio to initialize margins and unrealized pnls.
61    pub timeout_portfolio: u32,
62    /// The timeout (seconds) for all engine clients to disconnect.
63    pub timeout_disconnection: u32,
64    /// The timeout (seconds) after stopping the node to await residual events before final shutdown.
65    pub timeout_post_stop: u32,
66    /// The timeout (seconds) to await pending tasks cancellation during shutdown.
67    pub timeout_shutdown: u32,
68    /// The cache configuration.
69    pub cache: Option<CacheConfig>,
70    /// The message bus configuration.
71    pub msgbus: Option<MessageBusConfig>,
72    /// The data engine configuration.
73    pub data_engine: Option<DataEngineConfig>,
74    /// The risk engine configuration.
75    pub risk_engine: Option<RiskEngineConfig>,
76    /// The execution engine configuration.
77    pub exec_engine: Option<ExecutionEngineConfig>,
78    /// The portfolio configuration.
79    pub portfolio: Option<PortfolioConfig>,
80    /// The configuration for streaming to feather files.
81    pub streaming: Option<StreamingConfig>,
82    /// If logging should be bypassed.
83    pub bypass_logging: bool,
84    /// If post backtest performance analysis should be run.
85    pub run_analysis: bool,
86}
87
88impl BacktestEngineConfig {
89    #[must_use]
90    #[allow(clippy::too_many_arguments)]
91    pub fn new(
92        environment: Environment,
93        trader_id: TraderId,
94        load_state: Option<bool>,
95        save_state: Option<bool>,
96        bypass_logging: Option<bool>,
97        run_analysis: Option<bool>,
98        timeout_connection: Option<u32>,
99        timeout_reconciliation: Option<u32>,
100        timeout_portfolio: Option<u32>,
101        timeout_disconnection: Option<u32>,
102        timeout_post_stop: Option<u32>,
103        timeout_shutdown: Option<u32>,
104        logging: Option<LoggerConfig>,
105        instance_id: Option<UUID4>,
106        cache: Option<CacheConfig>,
107        msgbus: Option<MessageBusConfig>,
108        data_engine: Option<DataEngineConfig>,
109        risk_engine: Option<RiskEngineConfig>,
110        exec_engine: Option<ExecutionEngineConfig>,
111        portfolio: Option<PortfolioConfig>,
112        streaming: Option<StreamingConfig>,
113    ) -> Self {
114        Self {
115            environment,
116            trader_id,
117            load_state: load_state.unwrap_or(false),
118            save_state: save_state.unwrap_or(false),
119            logging: logging.unwrap_or_default(),
120            instance_id,
121            timeout_connection: timeout_connection.unwrap_or(60),
122            timeout_reconciliation: timeout_reconciliation.unwrap_or(30),
123            timeout_portfolio: timeout_portfolio.unwrap_or(10),
124            timeout_disconnection: timeout_disconnection.unwrap_or(10),
125            timeout_post_stop: timeout_post_stop.unwrap_or(10),
126            timeout_shutdown: timeout_shutdown.unwrap_or(5),
127            cache,
128            msgbus,
129            data_engine,
130            risk_engine,
131            exec_engine,
132            portfolio,
133            streaming,
134            bypass_logging: bypass_logging.unwrap_or(false),
135            run_analysis: run_analysis.unwrap_or(true),
136        }
137    }
138}
139
140impl PoseiKernelConfig for BacktestEngineConfig {
141    fn environment(&self) -> Environment {
142        self.environment
143    }
144
145    fn trader_id(&self) -> TraderId {
146        self.trader_id
147    }
148
149    fn load_state(&self) -> bool {
150        self.load_state
151    }
152
153    fn save_state(&self) -> bool {
154        self.save_state
155    }
156
157    fn logging(&self) -> LoggerConfig {
158        self.logging.clone()
159    }
160
161    fn instance_id(&self) -> Option<UUID4> {
162        self.instance_id
163    }
164
165    fn timeout_connection(&self) -> u32 {
166        self.timeout_connection
167    }
168
169    fn timeout_reconciliation(&self) -> u32 {
170        self.timeout_reconciliation
171    }
172
173    fn timeout_portfolio(&self) -> u32 {
174        self.timeout_portfolio
175    }
176
177    fn timeout_disconnection(&self) -> u32 {
178        self.timeout_disconnection
179    }
180
181    fn timeout_post_stop(&self) -> u32 {
182        self.timeout_post_stop
183    }
184
185    fn timeout_shutdown(&self) -> u32 {
186        self.timeout_shutdown
187    }
188
189    fn cache(&self) -> Option<CacheConfig> {
190        self.cache.clone()
191    }
192
193    fn msgbus(&self) -> Option<MessageBusConfig> {
194        self.msgbus.clone()
195    }
196
197    fn data_engine(&self) -> Option<DataEngineConfig> {
198        self.data_engine.clone()
199    }
200
201    fn risk_engine(&self) -> Option<RiskEngineConfig> {
202        self.risk_engine.clone()
203    }
204
205    fn exec_engine(&self) -> Option<ExecutionEngineConfig> {
206        self.exec_engine.clone()
207    }
208
209    fn portfolio(&self) -> Option<PortfolioConfig> {
210        self.portfolio.clone()
211    }
212
213    fn streaming(&self) -> Option<StreamingConfig> {
214        self.streaming.clone()
215    }
216}
217
218impl Default for BacktestEngineConfig {
219    fn default() -> Self {
220        Self {
221            environment: Environment::Backtest,
222            trader_id: TraderId::default(),
223            load_state: false,
224            save_state: false,
225            logging: LoggerConfig::default(),
226            instance_id: None,
227            timeout_connection: 60,
228            timeout_reconciliation: 30,
229            timeout_portfolio: 10,
230            timeout_disconnection: 10,
231            timeout_post_stop: 10,
232            timeout_shutdown: 5,
233            cache: None,
234            msgbus: None,
235            data_engine: None,
236            risk_engine: None,
237            exec_engine: None,
238            portfolio: None,
239            streaming: None,
240            bypass_logging: false,
241            run_analysis: true,
242        }
243    }
244}
245
246/// Represents a venue configuration for one specific backtest engine.
247#[derive(Debug, Clone)]
248pub struct BacktestVenueConfig {
249    /// The name of the venue.
250    name: Ustr,
251    /// The order management system type for the exchange. If ``HEDGING`` will generate new position IDs.
252    oms_type: OmsType,
253    /// The account type for the exchange.
254    account_type: AccountType,
255    /// The default order book type.
256    book_type: BookType,
257    /// The starting account balances (specify one for a single asset account).
258    starting_balances: Vec<String>,
259    /// If multi-venue routing should be enabled for the execution client.
260    routing: bool,
261    /// If the account for this exchange is frozen (balances will not change).
262    frozen_account: bool,
263    /// If stop orders are rejected on submission if trigger price is in the market.
264    reject_stop_orders: bool,
265    /// If orders with GTD time in force will be supported by the venue.
266    support_gtd_orders: bool,
267    /// If contingent orders will be supported/respected by the venue.
268    /// If False, then it's expected the strategy will be managing any contingent orders.
269    support_contingent_orders: bool,
270    /// If venue position IDs will be generated on order fills.
271    use_position_ids: bool,
272    /// If all venue generated identifiers will be random UUID4's.
273    use_random_ids: bool,
274    /// If the `reduce_only` execution instruction on orders will be honored.
275    use_reduce_only: bool,
276    /// If bars should be processed by the matching engine(s) (and move the market).
277    bar_execution: bool,
278    /// Determines whether the processing order of bar prices is adaptive based on a heuristic.
279    /// This setting is only relevant when `bar_execution` is True.
280    /// If False, bar prices are always processed in the fixed order: Open, High, Low, Close.
281    /// If True, the processing order adapts with the heuristic:
282    /// - If High is closer to Open than Low then the processing order is Open, High, Low, Close.
283    /// - If Low is closer to Open than High then the processing order is Open, Low, High, Close.
284    bar_adaptive_high_low_ordering: bool,
285    /// If trades should be processed by the matching engine(s) (and move the market).
286    trade_execution: bool,
287    /// The account base currency for the exchange. Use `None` for multi-currency accounts.
288    base_currency: Option<Currency>,
289    /// The account default leverage (for margin accounts).
290    default_leverage: Option<f64>,
291    /// The instrument specific leverage configuration (for margin accounts).
292    leverages: Option<HashMap<Currency, f64>>,
293}
294
295impl BacktestVenueConfig {
296    #[allow(clippy::too_many_arguments)]
297    #[must_use]
298    pub fn new(
299        name: Ustr,
300        oms_type: OmsType,
301        account_type: AccountType,
302        book_type: BookType,
303        routing: Option<bool>,
304        frozen_account: Option<bool>,
305        reject_stop_orders: Option<bool>,
306        support_gtd_orders: Option<bool>,
307        support_contingent_orders: Option<bool>,
308        use_position_ids: Option<bool>,
309        use_random_ids: Option<bool>,
310        use_reduce_only: Option<bool>,
311        bar_execution: Option<bool>,
312        bar_adaptive_high_low_ordering: Option<bool>,
313        trade_execution: Option<bool>,
314        starting_balances: Vec<String>,
315        base_currency: Option<Currency>,
316        default_leverage: Option<f64>,
317        leverages: Option<HashMap<Currency, f64>>,
318    ) -> Self {
319        Self {
320            name,
321            oms_type,
322            account_type,
323            book_type,
324            routing: routing.unwrap_or(false),
325            frozen_account: frozen_account.unwrap_or(false),
326            reject_stop_orders: reject_stop_orders.unwrap_or(true),
327            support_gtd_orders: support_gtd_orders.unwrap_or(true),
328            support_contingent_orders: support_contingent_orders.unwrap_or(true),
329            use_position_ids: use_position_ids.unwrap_or(true),
330            use_random_ids: use_random_ids.unwrap_or(false),
331            use_reduce_only: use_reduce_only.unwrap_or(true),
332            bar_execution: bar_execution.unwrap_or(true),
333            bar_adaptive_high_low_ordering: bar_adaptive_high_low_ordering.unwrap_or(false),
334            trade_execution: trade_execution.unwrap_or(false),
335            starting_balances,
336            base_currency,
337            default_leverage,
338            leverages,
339        }
340    }
341}
342
343#[derive(Debug, Clone)]
344/// Represents the data configuration for one specific backtest run.
345pub struct BacktestDataConfig {
346    /// The path to the data catalog.
347    catalog_path: String,
348    /// The `fsspec` filesystem protocol for the catalog.
349    catalog_fs_protocol: Option<String>,
350    /// The instrument ID for the data configuration.
351    instrument_id: Option<InstrumentId>,
352    /// The start time for the data configuration.
353    start_time: Option<UnixNanos>,
354    /// The end time for the data configuration.
355    end_time: Option<UnixNanos>,
356    /// The additional filter expressions for the data catalog query.
357    filter_expr: Option<String>,
358    /// The client ID for the data configuration.
359    client_id: Option<ClientId>,
360    /// The metadata for the data catalog query.
361    metadata: Option<HashMap<String, String>>,
362    /// The bar specification for the data catalog query.
363    bar_spec: Option<BarSpecification>,
364}
365
366impl BacktestDataConfig {
367    #[allow(clippy::too_many_arguments)]
368    #[must_use]
369    pub const fn new(
370        catalog_path: String,
371        catalog_fs_protocol: Option<String>,
372        instrument_id: Option<InstrumentId>,
373        start_time: Option<UnixNanos>,
374        end_time: Option<UnixNanos>,
375        filter_expr: Option<String>,
376        client_id: Option<ClientId>,
377        metadata: Option<HashMap<String, String>>,
378        bar_spec: Option<BarSpecification>,
379    ) -> Self {
380        Self {
381            catalog_path,
382            catalog_fs_protocol,
383            instrument_id,
384            start_time,
385            end_time,
386            filter_expr,
387            client_id,
388            metadata,
389            bar_spec,
390        }
391    }
392}
393
394/// Represents the configuration for one specific backtest run.
395/// This includes a backtest engine with its actors and strategies, with the external inputs of venues and data.
396#[derive(Debug, Clone)]
397pub struct BacktestRunConfig {
398    /// The venue configurations for the backtest run.
399    venues: Vec<BacktestVenueConfig>,
400    /// The data configurations for the backtest run.
401    data: Vec<BacktestDataConfig>,
402    /// The backtest engine configuration (the core system kernel).
403    engine: BacktestEngineConfig,
404    /// The number of data points to process in each chunk during streaming mode.
405    /// If `None`, the backtest will run without streaming, loading all data at once.
406    chunk_size: Option<usize>,
407    /// If the backtest engine should be disposed on completion of the run.
408    /// If `True`, then will drop data and all state.
409    /// If `False`, then will *only* drop data.
410    dispose_on_completion: bool,
411    /// The start datetime (UTC) for the backtest run.
412    /// If `None` engine runs from the start of the data.
413    start: Option<UnixNanos>,
414    /// The end datetime (UTC) for the backtest run.
415    /// If `None` engine runs to the end of the data.
416    end: Option<UnixNanos>,
417}
418
419impl BacktestRunConfig {
420    #[must_use]
421    pub fn new(
422        venues: Vec<BacktestVenueConfig>,
423        data: Vec<BacktestDataConfig>,
424        engine: BacktestEngineConfig,
425        chunk_size: Option<usize>,
426        dispose_on_completion: Option<bool>,
427        start: Option<UnixNanos>,
428        end: Option<UnixNanos>,
429    ) -> Self {
430        Self {
431            venues,
432            data,
433            engine,
434            chunk_size,
435            dispose_on_completion: dispose_on_completion.unwrap_or(true),
436            start,
437            end,
438        }
439    }
440}