nautilus_data/python/
config.rs1use std::{collections::HashMap, time::Duration};
19
20use nautilus_core::python::to_pyvalue_err;
21use nautilus_model::{
22 enums::{BarAggregation, BarIntervalType},
23 identifiers::ClientId,
24};
25use pyo3::{Py, PyAny, PyResult, Python, prelude::PyAnyMethods, pymethods};
26
27use crate::engine::config::DataEngineConfig;
28
29fn coerce_bar_interval_type(value: &Py<PyAny>) -> PyResult<BarIntervalType> {
33 Python::attach(|py| {
34 let bound = value.bind(py);
35 if let Ok(variant) = bound.extract::<BarIntervalType>() {
36 return Ok(variant);
37 }
38
39 let raw = bound.extract::<String>().map_err(|_| {
40 to_pyvalue_err("`time_bars_interval_type` must be a string or BarIntervalType")
41 })?;
42
43 match raw.to_ascii_uppercase().replace('-', "_").as_str() {
44 "LEFT_OPEN" => Ok(BarIntervalType::LeftOpen),
45 "RIGHT_OPEN" => Ok(BarIntervalType::RightOpen),
46 _ => Err(to_pyvalue_err(format!(
47 "invalid `time_bars_interval_type`: {raw:?} (expected 'left-open' or 'right-open')"
48 ))),
49 }
50 })
51}
52
53#[pymethods]
54#[pyo3_stub_gen::derive::gen_stub_pymethods]
55impl DataEngineConfig {
56 #[new]
58 #[expect(clippy::too_many_arguments)]
59 #[pyo3(signature = (
60 time_bars_build_with_no_updates = None,
61 time_bars_timestamp_on_close = None,
62 time_bars_skip_first_non_full_bar = None,
63 time_bars_interval_type = None,
64 time_bars_build_delay = None,
65 time_bars_origins = None,
66 validate_data_sequence = None,
67 buffer_deltas = None,
68 emit_quotes_from_book = None,
69 emit_quotes_from_book_depths = None,
70 external_clients = None,
71 debug = None,
72 ))]
73 fn py_new(
74 time_bars_build_with_no_updates: Option<bool>,
75 time_bars_timestamp_on_close: Option<bool>,
76 time_bars_skip_first_non_full_bar: Option<bool>,
77 time_bars_interval_type: Option<Py<PyAny>>,
78 time_bars_build_delay: Option<u64>,
79 time_bars_origins: Option<HashMap<BarAggregation, u64>>,
80 validate_data_sequence: Option<bool>,
81 buffer_deltas: Option<bool>,
82 emit_quotes_from_book: Option<bool>,
83 emit_quotes_from_book_depths: Option<bool>,
84 external_clients: Option<Vec<ClientId>>,
85 debug: Option<bool>,
86 ) -> PyResult<Self> {
87 let time_bars_interval_type = match time_bars_interval_type {
88 Some(value) => Some(coerce_bar_interval_type(&value)?),
89 None => None,
90 };
91 let time_bars_origins = time_bars_origins.map(|map| {
92 map.into_iter()
93 .map(|(agg, nanos)| (agg, Duration::from_nanos(nanos)))
94 .collect()
95 });
96 Ok(Self::builder()
97 .maybe_time_bars_build_with_no_updates(time_bars_build_with_no_updates)
98 .maybe_time_bars_timestamp_on_close(time_bars_timestamp_on_close)
99 .maybe_time_bars_skip_first_non_full_bar(time_bars_skip_first_non_full_bar)
100 .maybe_time_bars_interval_type(time_bars_interval_type)
101 .maybe_time_bars_build_delay(time_bars_build_delay)
102 .maybe_time_bars_origins(time_bars_origins)
103 .maybe_validate_data_sequence(validate_data_sequence)
104 .maybe_buffer_deltas(buffer_deltas)
105 .maybe_emit_quotes_from_book(emit_quotes_from_book)
106 .maybe_emit_quotes_from_book_depths(emit_quotes_from_book_depths)
107 .maybe_external_clients(external_clients)
108 .maybe_debug(debug)
109 .build())
110 }
111
112 #[getter]
113 #[pyo3(name = "time_bars_build_with_no_updates")]
114 const fn py_time_bars_build_with_no_updates(&self) -> bool {
115 self.time_bars_build_with_no_updates
116 }
117
118 #[getter]
119 #[pyo3(name = "time_bars_timestamp_on_close")]
120 const fn py_time_bars_timestamp_on_close(&self) -> bool {
121 self.time_bars_timestamp_on_close
122 }
123
124 #[getter]
125 #[pyo3(name = "time_bars_skip_first_non_full_bar")]
126 const fn py_time_bars_skip_first_non_full_bar(&self) -> bool {
127 self.time_bars_skip_first_non_full_bar
128 }
129
130 #[getter]
131 #[pyo3(name = "time_bars_interval_type")]
132 const fn py_time_bars_interval_type(&self) -> BarIntervalType {
133 self.time_bars_interval_type
134 }
135
136 #[getter]
137 #[pyo3(name = "time_bars_build_delay")]
138 const fn py_time_bars_build_delay(&self) -> u64 {
139 self.time_bars_build_delay
140 }
141
142 #[getter]
143 #[pyo3(name = "validate_data_sequence")]
144 const fn py_validate_data_sequence(&self) -> bool {
145 self.validate_data_sequence
146 }
147
148 #[getter]
149 #[pyo3(name = "buffer_deltas")]
150 const fn py_buffer_deltas(&self) -> bool {
151 self.buffer_deltas
152 }
153
154 #[getter]
155 #[pyo3(name = "emit_quotes_from_book")]
156 const fn py_emit_quotes_from_book(&self) -> bool {
157 self.emit_quotes_from_book
158 }
159
160 #[getter]
161 #[pyo3(name = "emit_quotes_from_book_depths")]
162 const fn py_emit_quotes_from_book_depths(&self) -> bool {
163 self.emit_quotes_from_book_depths
164 }
165
166 #[getter]
167 #[pyo3(name = "debug")]
168 const fn py_debug(&self) -> bool {
169 self.debug
170 }
171
172 fn __repr__(&self) -> String {
173 format!("{self:?}")
174 }
175
176 fn __str__(&self) -> String {
177 format!("{self:?}")
178 }
179}