nautilus_trading/examples/strategies/delta_neutral_vol/
config.rs1use nautilus_model::{
19 enums::TimeInForce,
20 identifiers::{ClientId, InstrumentId, StrategyId},
21};
22
23use crate::strategy::StrategyConfig;
24
25#[derive(Debug, Clone)]
31#[cfg_attr(
32 feature = "python",
33 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.trading", from_py_object)
34)]
35pub struct DeltaNeutralVolConfig {
36 pub base: StrategyConfig,
38 pub option_family: String,
40 pub hedge_instrument_id: InstrumentId,
42 pub client_id: ClientId,
44 pub target_call_delta: f64,
46 pub target_put_delta: f64,
48 pub contracts: u64,
50 pub rehedge_delta_threshold: f64,
52 pub rehedge_interval_secs: u64,
54 pub expiry_filter: Option<String>,
56 pub enter_strangle: bool,
59 pub entry_iv_offset: f64,
62 pub entry_time_in_force: TimeInForce,
64 pub iv_param_key: String,
67}
68
69impl DeltaNeutralVolConfig {
70 #[must_use]
72 pub fn new(
73 option_family: String,
74 hedge_instrument_id: InstrumentId,
75 client_id: ClientId,
76 ) -> Self {
77 Self {
78 base: StrategyConfig {
79 strategy_id: Some(StrategyId::from("DELTA_NEUTRAL_VOL-001")),
80 order_id_tag: Some("001".to_string()),
81 ..Default::default()
82 },
83 option_family,
84 hedge_instrument_id,
85 client_id,
86 target_call_delta: 0.20,
87 target_put_delta: -0.20,
88 contracts: 1,
89 rehedge_delta_threshold: 0.5,
90 rehedge_interval_secs: 30,
91 expiry_filter: None,
92 enter_strangle: true,
93 entry_iv_offset: 0.0,
94 entry_time_in_force: TimeInForce::Gtc,
95 iv_param_key: "px_vol".to_string(),
96 }
97 }
98
99 #[must_use]
100 pub fn with_target_call_delta(mut self, delta: f64) -> Self {
101 self.target_call_delta = delta;
102 self
103 }
104
105 #[must_use]
106 pub fn with_target_put_delta(mut self, delta: f64) -> Self {
107 self.target_put_delta = delta;
108 self
109 }
110
111 #[must_use]
112 pub fn with_contracts(mut self, contracts: u64) -> Self {
113 self.contracts = contracts;
114 self
115 }
116
117 #[must_use]
118 pub fn with_rehedge_delta_threshold(mut self, threshold: f64) -> Self {
119 self.rehedge_delta_threshold = threshold;
120 self
121 }
122
123 #[must_use]
124 pub fn with_rehedge_interval_secs(mut self, secs: u64) -> Self {
125 self.rehedge_interval_secs = secs;
126 self
127 }
128
129 #[must_use]
130 pub fn with_expiry_filter(mut self, expiry: String) -> Self {
131 self.expiry_filter = Some(expiry);
132 self
133 }
134
135 #[must_use]
136 pub fn with_enter_strangle(mut self, enter: bool) -> Self {
137 self.enter_strangle = enter;
138 self
139 }
140
141 #[must_use]
142 pub fn with_entry_iv_offset(mut self, offset: f64) -> Self {
143 self.entry_iv_offset = offset;
144 self
145 }
146
147 #[must_use]
148 pub fn with_entry_time_in_force(mut self, tif: TimeInForce) -> Self {
149 self.entry_time_in_force = tif;
150 self
151 }
152
153 #[must_use]
154 pub fn with_strategy_id(mut self, strategy_id: StrategyId) -> Self {
155 self.base.strategy_id = Some(strategy_id);
156 self
157 }
158
159 #[must_use]
160 pub fn with_order_id_tag(mut self, tag: String) -> Self {
161 self.base.order_id_tag = Some(tag);
162 self
163 }
164
165 #[must_use]
166 pub fn with_iv_param_key(mut self, key: String) -> Self {
167 self.iv_param_key = key;
168 self
169 }
170}
171
172#[cfg(feature = "python")]
173#[pyo3::pymethods]
174impl DeltaNeutralVolConfig {
175 #[new]
176 #[pyo3(signature = (
177 option_family,
178 hedge_instrument_id,
179 client_id,
180 strategy_id=None,
181 order_id_tag=None,
182 target_call_delta=0.20,
183 target_put_delta=-0.20,
184 contracts=1,
185 rehedge_delta_threshold=0.5,
186 rehedge_interval_secs=30,
187 expiry_filter=None,
188 enter_strangle=true,
189 entry_iv_offset=0.0,
190 entry_time_in_force=TimeInForce::Gtc,
191 iv_param_key="px_vol",
192 ))]
193 #[expect(clippy::too_many_arguments)]
194 fn py_new(
195 option_family: String,
196 hedge_instrument_id: InstrumentId,
197 client_id: ClientId,
198 strategy_id: Option<StrategyId>,
199 order_id_tag: Option<String>,
200 target_call_delta: f64,
201 target_put_delta: f64,
202 contracts: u64,
203 rehedge_delta_threshold: f64,
204 rehedge_interval_secs: u64,
205 expiry_filter: Option<String>,
206 enter_strangle: bool,
207 entry_iv_offset: f64,
208 entry_time_in_force: TimeInForce,
209 iv_param_key: &str,
210 ) -> Self {
211 let mut config = Self::new(option_family, hedge_instrument_id, client_id)
212 .with_target_call_delta(target_call_delta)
213 .with_target_put_delta(target_put_delta)
214 .with_contracts(contracts)
215 .with_rehedge_delta_threshold(rehedge_delta_threshold)
216 .with_rehedge_interval_secs(rehedge_interval_secs)
217 .with_enter_strangle(enter_strangle)
218 .with_entry_iv_offset(entry_iv_offset)
219 .with_entry_time_in_force(entry_time_in_force)
220 .with_iv_param_key(iv_param_key.to_string());
221
222 if let Some(id) = strategy_id {
223 config.base.strategy_id = Some(id);
224 }
225
226 if let Some(tag) = order_id_tag {
227 config.base.order_id_tag = Some(tag);
228 }
229
230 if let Some(expiry) = expiry_filter {
231 config.expiry_filter = Some(expiry);
232 }
233
234 config
235 }
236
237 #[getter]
238 fn option_family(&self) -> &str {
239 &self.option_family
240 }
241
242 #[getter]
243 fn hedge_instrument_id(&self) -> InstrumentId {
244 self.hedge_instrument_id
245 }
246
247 #[getter]
248 fn client_id(&self) -> ClientId {
249 self.client_id
250 }
251
252 #[getter]
253 fn target_call_delta(&self) -> f64 {
254 self.target_call_delta
255 }
256
257 #[getter]
258 fn target_put_delta(&self) -> f64 {
259 self.target_put_delta
260 }
261
262 #[getter]
263 fn contracts(&self) -> u64 {
264 self.contracts
265 }
266
267 #[getter]
268 fn rehedge_delta_threshold(&self) -> f64 {
269 self.rehedge_delta_threshold
270 }
271
272 #[getter]
273 fn rehedge_interval_secs(&self) -> u64 {
274 self.rehedge_interval_secs
275 }
276
277 #[getter]
278 fn expiry_filter(&self) -> Option<&str> {
279 self.expiry_filter.as_deref()
280 }
281
282 #[getter]
283 fn enter_strangle(&self) -> bool {
284 self.enter_strangle
285 }
286
287 #[getter]
288 fn entry_iv_offset(&self) -> f64 {
289 self.entry_iv_offset
290 }
291
292 #[getter]
293 fn entry_time_in_force(&self) -> TimeInForce {
294 self.entry_time_in_force
295 }
296}