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