1use nautilus_common::python::config_error_to_pyvalue_err;
19use nautilus_model::{
20 data::BarType,
21 enums::TimeInForce,
22 identifiers::{ActorId, ClientId, InstrumentId, StrategyId},
23 types::Quantity,
24};
25use pyo3::prelude::*;
26
27use crate::examples::{
28 actors::BookImbalanceActorConfig,
29 strategies::{
30 CompositeMarketMakerConfig, DeltaNeutralVolConfig, EmaCrossConfig, GridMarketMakerConfig,
31 HurstVpinDirectionalConfig,
32 },
33};
34
35macro_rules! impl_strategy_config_base_getters {
36 ($type:ty) => {
37 #[pyo3_stub_gen::derive::gen_stub_pymethods]
38 #[pymethods]
39 impl $type {
40 #[getter]
41 #[pyo3(name = "strategy_id")]
42 fn py_strategy_id(&self) -> Option<StrategyId> {
43 self.base.strategy_id
44 }
45
46 #[getter]
47 #[pyo3(name = "order_id_tag")]
48 fn py_order_id_tag(&self) -> Option<&str> {
49 self.base.order_id_tag.as_deref()
50 }
51 }
52 };
53}
54
55impl_strategy_config_base_getters!(CompositeMarketMakerConfig);
56impl_strategy_config_base_getters!(GridMarketMakerConfig);
57impl_strategy_config_base_getters!(EmaCrossConfig);
58impl_strategy_config_base_getters!(DeltaNeutralVolConfig);
59impl_strategy_config_base_getters!(HurstVpinDirectionalConfig);
60
61#[pymethods]
62#[pyo3_stub_gen::derive::gen_stub_pymethods]
63impl CompositeMarketMakerConfig {
64 #[new]
66 #[pyo3(signature = (
67 instrument_id,
68 signal_instrument_id,
69 max_position,
70 strategy_id=None,
71 order_id_tag=None,
72 trade_size=None,
73 half_spread_bps=5,
74 inventory_skew_factor=0.0,
75 signal_skew_factor=0.0,
76 signal_baseline=None,
77 requote_threshold_bps=5,
78 expire_time_secs=None,
79 on_cancel_resubmit=false,
80 ))]
81 #[expect(clippy::too_many_arguments)]
82 fn py_new(
83 instrument_id: InstrumentId,
84 signal_instrument_id: InstrumentId,
85 max_position: Quantity,
86 strategy_id: Option<StrategyId>,
87 order_id_tag: Option<String>,
88 trade_size: Option<Quantity>,
89 half_spread_bps: u32,
90 inventory_skew_factor: f64,
91 signal_skew_factor: f64,
92 signal_baseline: Option<f64>,
93 requote_threshold_bps: u32,
94 expire_time_secs: Option<u64>,
95 on_cancel_resubmit: bool,
96 ) -> Self {
97 let mut config = Self::builder()
98 .instrument_id(instrument_id)
99 .signal_instrument_id(signal_instrument_id)
100 .max_position(max_position)
101 .half_spread_bps(half_spread_bps)
102 .inventory_skew_factor(inventory_skew_factor)
103 .signal_skew_factor(signal_skew_factor)
104 .requote_threshold_bps(requote_threshold_bps)
105 .on_cancel_resubmit(on_cancel_resubmit)
106 .maybe_trade_size(trade_size)
107 .maybe_signal_baseline(signal_baseline)
108 .maybe_expire_time_secs(expire_time_secs)
109 .build();
110
111 if let Some(id) = strategy_id {
112 config.base.strategy_id = Some(id);
113 }
114
115 if let Some(tag) = order_id_tag {
116 config.base.order_id_tag = Some(tag);
117 }
118
119 config
120 }
121
122 #[getter]
123 fn instrument_id(&self) -> InstrumentId {
124 self.instrument_id
125 }
126
127 #[getter]
128 fn signal_instrument_id(&self) -> InstrumentId {
129 self.signal_instrument_id
130 }
131
132 #[getter]
133 fn max_position(&self) -> Quantity {
134 self.max_position
135 }
136
137 #[getter]
138 fn trade_size(&self) -> Option<Quantity> {
139 self.trade_size
140 }
141
142 #[getter]
143 fn half_spread_bps(&self) -> u32 {
144 self.half_spread_bps
145 }
146
147 #[getter]
148 fn inventory_skew_factor(&self) -> f64 {
149 self.inventory_skew_factor
150 }
151
152 #[getter]
153 fn signal_skew_factor(&self) -> f64 {
154 self.signal_skew_factor
155 }
156
157 #[getter]
158 fn signal_baseline(&self) -> Option<f64> {
159 self.signal_baseline
160 }
161
162 #[getter]
163 fn requote_threshold_bps(&self) -> u32 {
164 self.requote_threshold_bps
165 }
166
167 #[getter]
168 fn expire_time_secs(&self) -> Option<u64> {
169 self.expire_time_secs
170 }
171
172 #[getter]
173 fn on_cancel_resubmit(&self) -> bool {
174 self.on_cancel_resubmit
175 }
176}
177
178#[pymethods]
179#[pyo3_stub_gen::derive::gen_stub_pymethods]
180impl GridMarketMakerConfig {
181 #[new]
183 #[pyo3(signature = (
184 instrument_id,
185 max_position,
186 strategy_id=None,
187 order_id_tag=None,
188 trade_size=None,
189 num_levels=3,
190 grid_step_bps=10,
191 skew_factor=0.0,
192 requote_threshold_bps=5,
193 expire_time_secs=None,
194 on_cancel_resubmit=false,
195 use_uuid_client_order_ids=false,
196 use_hyphens_in_client_order_ids=true,
197 ))]
198 #[expect(clippy::too_many_arguments)]
199 fn py_new(
200 instrument_id: InstrumentId,
201 max_position: Quantity,
202 strategy_id: Option<StrategyId>,
203 order_id_tag: Option<String>,
204 trade_size: Option<Quantity>,
205 num_levels: usize,
206 grid_step_bps: u32,
207 skew_factor: f64,
208 requote_threshold_bps: u32,
209 expire_time_secs: Option<u64>,
210 on_cancel_resubmit: bool,
211 use_uuid_client_order_ids: bool,
212 use_hyphens_in_client_order_ids: bool,
213 ) -> Self {
214 let mut config = Self::builder()
215 .instrument_id(instrument_id)
216 .max_position(max_position)
217 .num_levels(num_levels)
218 .grid_step_bps(grid_step_bps)
219 .skew_factor(skew_factor)
220 .requote_threshold_bps(requote_threshold_bps)
221 .on_cancel_resubmit(on_cancel_resubmit)
222 .maybe_trade_size(trade_size)
223 .maybe_expire_time_secs(expire_time_secs)
224 .build();
225
226 if let Some(id) = strategy_id {
227 config.base.strategy_id = Some(id);
228 }
229
230 if let Some(tag) = order_id_tag {
231 config.base.order_id_tag = Some(tag);
232 }
233
234 config.base.use_uuid_client_order_ids = use_uuid_client_order_ids;
235 config.base.use_hyphens_in_client_order_ids = use_hyphens_in_client_order_ids;
236
237 config
238 }
239
240 #[getter]
241 fn instrument_id(&self) -> InstrumentId {
242 self.instrument_id
243 }
244
245 #[getter]
246 fn max_position(&self) -> Quantity {
247 self.max_position
248 }
249
250 #[getter]
251 fn trade_size(&self) -> Option<Quantity> {
252 self.trade_size
253 }
254
255 #[getter]
256 fn num_levels(&self) -> usize {
257 self.num_levels
258 }
259
260 #[getter]
261 fn grid_step_bps(&self) -> u32 {
262 self.grid_step_bps
263 }
264
265 #[getter]
266 fn skew_factor(&self) -> f64 {
267 self.skew_factor
268 }
269
270 #[getter]
271 fn requote_threshold_bps(&self) -> u32 {
272 self.requote_threshold_bps
273 }
274
275 #[getter]
276 fn expire_time_secs(&self) -> Option<u64> {
277 self.expire_time_secs
278 }
279
280 #[getter]
281 fn on_cancel_resubmit(&self) -> bool {
282 self.on_cancel_resubmit
283 }
284
285 #[getter]
286 fn use_uuid_client_order_ids(&self) -> bool {
287 self.base.use_uuid_client_order_ids
288 }
289
290 #[getter]
291 fn use_hyphens_in_client_order_ids(&self) -> bool {
292 self.base.use_hyphens_in_client_order_ids
293 }
294}
295
296#[pymethods]
297#[pyo3_stub_gen::derive::gen_stub_pymethods]
298impl EmaCrossConfig {
299 #[new]
301 #[pyo3(signature = (
302 instrument_id,
303 trade_size,
304 fast_period=10,
305 slow_period=50,
306 strategy_id=None,
307 order_id_tag=None,
308 ))]
309 fn py_new(
310 instrument_id: InstrumentId,
311 trade_size: Quantity,
312 fast_period: usize,
313 slow_period: usize,
314 strategy_id: Option<StrategyId>,
315 order_id_tag: Option<String>,
316 ) -> Self {
317 let mut config = Self::builder()
318 .instrument_id(instrument_id)
319 .trade_size(trade_size)
320 .fast_period(fast_period)
321 .slow_period(slow_period)
322 .build();
323
324 if let Some(id) = strategy_id {
325 config.base.strategy_id = Some(id);
326 }
327
328 if let Some(tag) = order_id_tag {
329 config.base.order_id_tag = Some(tag);
330 }
331
332 config
333 }
334
335 #[getter]
336 fn instrument_id(&self) -> InstrumentId {
337 self.instrument_id
338 }
339
340 #[getter]
341 fn trade_size(&self) -> Quantity {
342 self.trade_size
343 }
344
345 #[getter]
346 fn fast_period(&self) -> usize {
347 self.fast_period
348 }
349
350 #[getter]
351 fn slow_period(&self) -> usize {
352 self.slow_period
353 }
354}
355
356#[pymethods]
357#[pyo3_stub_gen::derive::gen_stub_pymethods]
358impl DeltaNeutralVolConfig {
359 #[new]
365 #[pyo3(signature = (
366 option_family,
367 hedge_instrument_id,
368 client_id,
369 strategy_id=None,
370 order_id_tag=None,
371 target_call_delta=0.20,
372 target_put_delta=-0.20,
373 contracts=1,
374 rehedge_delta_threshold=0.5,
375 rehedge_interval_secs=30,
376 expiry_filter=None,
377 enter_strangle=true,
378 entry_iv_offset=0.0,
379 entry_time_in_force=TimeInForce::Gtc,
380 entry_premium_offset_ticks=None,
381 iv_param_key="px_vol",
382 ))]
383 #[expect(clippy::too_many_arguments)]
384 fn py_new(
385 option_family: String,
386 hedge_instrument_id: InstrumentId,
387 client_id: ClientId,
388 strategy_id: Option<StrategyId>,
389 order_id_tag: Option<String>,
390 target_call_delta: f64,
391 target_put_delta: f64,
392 contracts: u64,
393 rehedge_delta_threshold: f64,
394 rehedge_interval_secs: u64,
395 expiry_filter: Option<String>,
396 enter_strangle: bool,
397 entry_iv_offset: f64,
398 entry_time_in_force: TimeInForce,
399 entry_premium_offset_ticks: Option<i32>,
400 iv_param_key: &str,
401 ) -> Self {
402 let mut config = Self::builder()
403 .option_family(option_family)
404 .hedge_instrument_id(hedge_instrument_id)
405 .client_id(client_id)
406 .target_call_delta(target_call_delta)
407 .target_put_delta(target_put_delta)
408 .contracts(contracts)
409 .rehedge_delta_threshold(rehedge_delta_threshold)
410 .rehedge_interval_secs(rehedge_interval_secs)
411 .enter_strangle(enter_strangle)
412 .entry_iv_offset(entry_iv_offset)
413 .entry_time_in_force(entry_time_in_force)
414 .iv_param_key(iv_param_key.to_string())
415 .maybe_expiry_filter(expiry_filter)
416 .maybe_entry_premium_offset_ticks(entry_premium_offset_ticks)
417 .build();
418
419 if let Some(id) = strategy_id {
420 config.base.strategy_id = Some(id);
421 }
422
423 if let Some(tag) = order_id_tag {
424 config.base.order_id_tag = Some(tag);
425 }
426
427 config
428 }
429
430 #[getter]
431 fn option_family(&self) -> &str {
432 &self.option_family
433 }
434
435 #[getter]
436 fn hedge_instrument_id(&self) -> InstrumentId {
437 self.hedge_instrument_id
438 }
439
440 #[getter]
441 fn client_id(&self) -> ClientId {
442 self.client_id
443 }
444
445 #[getter]
446 fn target_call_delta(&self) -> f64 {
447 self.target_call_delta
448 }
449
450 #[getter]
451 fn target_put_delta(&self) -> f64 {
452 self.target_put_delta
453 }
454
455 #[getter]
456 fn contracts(&self) -> u64 {
457 self.contracts
458 }
459
460 #[getter]
461 fn rehedge_delta_threshold(&self) -> f64 {
462 self.rehedge_delta_threshold
463 }
464
465 #[getter]
466 fn rehedge_interval_secs(&self) -> u64 {
467 self.rehedge_interval_secs
468 }
469
470 #[getter]
471 fn expiry_filter(&self) -> Option<&str> {
472 self.expiry_filter.as_deref()
473 }
474
475 #[getter]
476 fn enter_strangle(&self) -> bool {
477 self.enter_strangle
478 }
479
480 #[getter]
481 fn entry_iv_offset(&self) -> f64 {
482 self.entry_iv_offset
483 }
484
485 #[getter]
486 fn entry_time_in_force(&self) -> TimeInForce {
487 self.entry_time_in_force
488 }
489
490 #[getter]
491 fn entry_premium_offset_ticks(&self) -> Option<i32> {
492 self.entry_premium_offset_ticks
493 }
494
495 #[getter]
496 #[pyo3(name = "iv_param_key")]
497 fn py_iv_param_key(&self) -> &str {
498 &self.iv_param_key
499 }
500}
501
502#[pymethods]
503#[pyo3_stub_gen::derive::gen_stub_pymethods]
504impl HurstVpinDirectionalConfig {
505 #[new]
513 #[pyo3(signature = (
514 instrument_id,
515 bar_type,
516 trade_size,
517 strategy_id=None,
518 order_id_tag=None,
519 hurst_window=128,
520 hurst_lags=None,
521 hurst_enter=0.55,
522 hurst_exit=0.50,
523 vpin_window=50,
524 vpin_threshold=0.30,
525 max_holding_secs=3600,
526 ))]
527 #[expect(clippy::too_many_arguments)]
528 fn py_new(
529 instrument_id: InstrumentId,
530 bar_type: BarType,
531 trade_size: Quantity,
532 strategy_id: Option<StrategyId>,
533 order_id_tag: Option<String>,
534 hurst_window: usize,
535 hurst_lags: Option<Vec<usize>>,
536 hurst_enter: f64,
537 hurst_exit: f64,
538 vpin_window: usize,
539 vpin_threshold: f64,
540 max_holding_secs: u64,
541 ) -> PyResult<Self> {
542 let mut config = Self::builder()
543 .instrument_id(instrument_id)
544 .bar_type(bar_type)
545 .trade_size(trade_size)
546 .hurst_window(hurst_window)
547 .maybe_hurst_lags(hurst_lags)
548 .hurst_enter(hurst_enter)
549 .hurst_exit(hurst_exit)
550 .vpin_window(vpin_window)
551 .vpin_threshold(vpin_threshold)
552 .max_holding_secs(max_holding_secs)
553 .build();
554
555 if let Some(id) = strategy_id {
556 config.base.strategy_id = Some(id);
557 }
558
559 if let Some(tag) = order_id_tag {
560 config.base.order_id_tag = Some(tag);
561 }
562
563 config.validate().map_err(config_error_to_pyvalue_err)?;
564 Ok(config)
565 }
566
567 #[getter]
568 fn instrument_id(&self) -> InstrumentId {
569 self.instrument_id
570 }
571
572 #[getter]
573 fn bar_type(&self) -> BarType {
574 self.bar_type
575 }
576
577 #[getter]
578 fn trade_size(&self) -> Quantity {
579 self.trade_size
580 }
581
582 #[getter]
583 fn hurst_window(&self) -> usize {
584 self.hurst_window
585 }
586
587 #[getter]
588 fn hurst_lags(&self) -> Vec<usize> {
589 self.hurst_lags.clone()
590 }
591
592 #[getter]
593 fn hurst_enter(&self) -> f64 {
594 self.hurst_enter
595 }
596
597 #[getter]
598 fn hurst_exit(&self) -> f64 {
599 self.hurst_exit
600 }
601
602 #[getter]
603 fn vpin_window(&self) -> usize {
604 self.vpin_window
605 }
606
607 #[getter]
608 fn vpin_threshold(&self) -> f64 {
609 self.vpin_threshold
610 }
611
612 #[getter]
613 fn max_holding_secs(&self) -> u64 {
614 self.max_holding_secs
615 }
616}
617
618#[pymethods]
619#[pyo3_stub_gen::derive::gen_stub_pymethods]
620impl BookImbalanceActorConfig {
621 #[new]
623 #[pyo3(signature = (instrument_ids, log_interval=100, actor_id=None))]
624 fn py_new(
625 instrument_ids: Vec<InstrumentId>,
626 log_interval: u64,
627 actor_id: Option<ActorId>,
628 ) -> Self {
629 Self::builder()
630 .instrument_ids(instrument_ids)
631 .log_interval(log_interval)
632 .maybe_actor_id(actor_id)
633 .build()
634 }
635
636 #[getter]
637 fn instrument_ids(&self) -> Vec<InstrumentId> {
638 self.instrument_ids.clone()
639 }
640
641 #[getter]
642 fn log_interval(&self) -> u64 {
643 self.log_interval
644 }
645
646 #[getter]
647 fn actor_id(&self) -> Option<ActorId> {
648 self.actor_id
649 }
650}