nautilus_trading/strategy/
config.rs1use std::collections::HashMap;
17
18use nautilus_core::serialization::{default_false, default_true};
19use nautilus_model::{
20 enums::{OmsType, TimeInForce},
21 identifiers::{InstrumentId, StrategyId},
22};
23use serde::{Deserialize, Serialize};
24
25#[derive(Clone, Debug, Deserialize, Serialize, bon::Builder)]
27#[serde(deny_unknown_fields)]
28#[cfg_attr(
29 feature = "python",
30 pyo3::pyclass(
31 module = "nautilus_trader.core.nautilus_pyo3.trading",
32 subclass,
33 from_py_object
34 )
35)]
36#[cfg_attr(
37 feature = "python",
38 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.trading")
39)]
40pub struct StrategyConfig {
41 pub strategy_id: Option<StrategyId>,
43 pub order_id_tag: Option<String>,
46 #[serde(default = "default_false")]
48 #[builder(default)]
49 pub use_uuid_client_order_ids: bool,
50 #[serde(default = "default_true")]
52 #[builder(default = true)]
53 pub use_hyphens_in_client_order_ids: bool,
54 pub oms_type: Option<OmsType>,
57 pub external_order_claims: Option<Vec<InstrumentId>>,
60 #[serde(default = "default_false")]
63 #[builder(default)]
64 pub manage_contingent_orders: bool,
65 #[serde(default = "default_false")]
68 #[builder(default)]
69 pub manage_gtd_expiry: bool,
70 #[serde(default = "default_false")]
74 #[builder(default)]
75 pub manage_stop: bool,
76 #[serde(default = "default_market_exit_interval_ms")]
79 #[builder(default = 100)]
80 pub market_exit_interval_ms: u64,
81 #[serde(default = "default_market_exit_max_attempts")]
85 #[builder(default = 100)]
86 pub market_exit_max_attempts: u64,
87 #[serde(default = "default_market_exit_time_in_force")]
89 #[builder(default = TimeInForce::Gtc)]
90 pub market_exit_time_in_force: TimeInForce,
91 #[serde(default = "default_true")]
93 #[builder(default = true)]
94 pub market_exit_reduce_only: bool,
95 #[serde(default = "default_true")]
98 #[builder(default = true)]
99 pub log_events: bool,
100 #[serde(default = "default_true")]
102 #[builder(default = true)]
103 pub log_commands: bool,
104 #[serde(default = "default_true")]
106 #[builder(default = true)]
107 pub log_rejected_due_post_only_as_warning: bool,
108}
109
110const fn default_market_exit_interval_ms() -> u64 {
111 100
112}
113
114const fn default_market_exit_max_attempts() -> u64 {
115 100
116}
117
118const fn default_market_exit_time_in_force() -> TimeInForce {
119 TimeInForce::Gtc
120}
121
122#[derive(Debug, Clone, Deserialize, Serialize)]
124#[serde(deny_unknown_fields)]
125#[cfg_attr(
126 feature = "python",
127 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.trading", from_py_object)
128)]
129#[cfg_attr(
130 feature = "python",
131 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.trading")
132)]
133pub struct ImportableStrategyConfig {
134 pub strategy_path: String,
136 pub config_path: String,
138 pub config: HashMap<String, serde_json::Value>,
140}
141
142impl Default for StrategyConfig {
143 fn default() -> Self {
144 Self::builder().build()
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use rstest::rstest;
151
152 use super::*;
153
154 #[rstest]
155 fn test_strategy_config_default() {
156 let config = StrategyConfig::default();
157
158 assert!(config.strategy_id.is_none());
159 assert!(config.order_id_tag.is_none());
160 assert!(!config.use_uuid_client_order_ids);
161 assert!(config.use_hyphens_in_client_order_ids);
162 assert!(config.oms_type.is_none());
163 assert!(config.external_order_claims.is_none());
164 assert!(!config.manage_contingent_orders);
165 assert!(!config.manage_gtd_expiry);
166 assert!(!config.manage_stop);
167 assert_eq!(config.market_exit_interval_ms, 100);
168 assert_eq!(config.market_exit_max_attempts, 100);
169 assert_eq!(config.market_exit_time_in_force, TimeInForce::Gtc);
170 assert!(config.market_exit_reduce_only);
171 assert!(config.log_events);
172 assert!(config.log_commands);
173 assert!(config.log_rejected_due_post_only_as_warning);
174 }
175
176 #[rstest]
177 fn test_strategy_config_with_strategy_id() {
178 let strategy_id = StrategyId::from("TEST-001");
179 let config = StrategyConfig {
180 strategy_id: Some(strategy_id),
181 ..Default::default()
182 };
183
184 assert_eq!(config.strategy_id, Some(strategy_id));
185 }
186
187 #[rstest]
188 fn test_strategy_config_serialization() {
189 let config = StrategyConfig {
190 strategy_id: Some(StrategyId::from("TEST-001")),
191 order_id_tag: Some("TAG1".to_string()),
192 use_uuid_client_order_ids: true,
193 ..Default::default()
194 };
195
196 let json = serde_json::to_string(&config).unwrap();
197 let deserialized: StrategyConfig = serde_json::from_str(&json).unwrap();
198
199 assert_eq!(config.strategy_id, deserialized.strategy_id);
200 assert_eq!(config.order_id_tag, deserialized.order_id_tag);
201 assert_eq!(
202 config.use_uuid_client_order_ids,
203 deserialized.use_uuid_client_order_ids
204 );
205 }
206}