nautilus_trading/strategy/
config.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 nautilus_model::{
17    enums::OmsType,
18    identifiers::{InstrumentId, StrategyId},
19};
20use serde::{Deserialize, Serialize};
21
22/// The base model for all trading strategy configurations.
23#[derive(Clone, Debug, Deserialize, Serialize)]
24#[cfg_attr(
25    feature = "python",
26    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.trading")
27)]
28pub struct StrategyConfig {
29    /// The unique ID for the strategy. Will become the strategy ID if not None.
30    pub strategy_id: Option<StrategyId>,
31    /// The unique order ID tag for the strategy. Must be unique
32    /// amongst all running strategies for a particular trader ID.
33    pub order_id_tag: Option<String>,
34    /// If UUID4's should be used for client order ID values.
35    #[serde(default = "default_false")]
36    pub use_uuid_client_order_ids: bool,
37    /// If hyphens should be used in generated client order ID values.
38    #[serde(default = "default_true")]
39    pub use_hyphens_in_client_order_ids: bool,
40    /// The order management system type for the strategy. This will determine
41    /// how the `ExecutionEngine` handles position IDs.
42    pub oms_type: Option<OmsType>,
43    /// The external order claim instrument IDs.
44    /// External orders for matching instrument IDs will be associated with (claimed by) the strategy.
45    pub external_order_claims: Option<Vec<InstrumentId>>,
46    /// If OUO and OCO **open** contingent orders should be managed automatically by the strategy.
47    /// Any emulated orders which are active local will be managed by the `OrderEmulator` instead.
48    #[serde(default = "default_false")]
49    pub manage_contingent_orders: bool,
50    /// If all order GTD time in force expirations should be managed by the strategy.
51    /// If True, then will ensure open orders have their GTD timers re-activated on start.
52    #[serde(default = "default_false")]
53    pub manage_gtd_expiry: bool,
54    /// If events should be logged by the strategy.
55    /// If False, then only warning events and above are logged.
56    #[serde(default = "default_true")]
57    pub log_events: bool,
58    /// If commands should be logged by the strategy.
59    #[serde(default = "default_true")]
60    pub log_commands: bool,
61    /// If order rejected events where `due_post_only` is True should be logged as warnings.
62    #[serde(default = "default_true")]
63    pub log_rejected_due_post_only_as_warning: bool,
64}
65
66impl Default for StrategyConfig {
67    fn default() -> Self {
68        Self {
69            strategy_id: None,
70            order_id_tag: None,
71            use_uuid_client_order_ids: false,
72            use_hyphens_in_client_order_ids: true,
73            oms_type: None,
74            external_order_claims: None,
75            manage_contingent_orders: false,
76            manage_gtd_expiry: false,
77            log_events: true,
78            log_commands: true,
79            log_rejected_due_post_only_as_warning: true,
80        }
81    }
82}
83
84fn default_true() -> bool {
85    true
86}
87
88fn default_false() -> bool {
89    false
90}
91
92#[cfg(test)]
93mod tests {
94    use rstest::rstest;
95
96    use super::*;
97
98    #[rstest]
99    fn test_strategy_config_default() {
100        let config = StrategyConfig::default();
101
102        assert!(config.strategy_id.is_none());
103        assert!(config.order_id_tag.is_none());
104        assert!(!config.use_uuid_client_order_ids);
105        assert!(config.use_hyphens_in_client_order_ids);
106        assert!(config.oms_type.is_none());
107        assert!(config.external_order_claims.is_none());
108        assert!(!config.manage_contingent_orders);
109        assert!(!config.manage_gtd_expiry);
110        assert!(config.log_events);
111        assert!(config.log_commands);
112        assert!(config.log_rejected_due_post_only_as_warning);
113    }
114
115    #[rstest]
116    fn test_strategy_config_with_strategy_id() {
117        let strategy_id = StrategyId::from("TEST-001");
118        let config = StrategyConfig {
119            strategy_id: Some(strategy_id),
120            ..Default::default()
121        };
122
123        assert_eq!(config.strategy_id, Some(strategy_id));
124    }
125
126    #[rstest]
127    fn test_strategy_config_serialization() {
128        let config = StrategyConfig {
129            strategy_id: Some(StrategyId::from("TEST-001")),
130            order_id_tag: Some("TAG1".to_string()),
131            use_uuid_client_order_ids: true,
132            ..Default::default()
133        };
134
135        let json = serde_json::to_string(&config).unwrap();
136        let deserialized: StrategyConfig = serde_json::from_str(&json).unwrap();
137
138        assert_eq!(config.strategy_id, deserialized.strategy_id);
139        assert_eq!(config.order_id_tag, deserialized.order_id_tag);
140        assert_eq!(
141            config.use_uuid_client_order_ids,
142            deserialized.use_uuid_client_order_ids
143        );
144    }
145}