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#[pyo3_stub_gen::derive::gen_stub_pymethods]
54#[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_origin_offset = 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 disable_historical_cache = None,
73 ))]
74 fn py_new(
75 time_bars_build_with_no_updates: Option<bool>,
76 time_bars_timestamp_on_close: Option<bool>,
77 time_bars_skip_first_non_full_bar: Option<bool>,
78 time_bars_interval_type: Option<Py<PyAny>>,
79 time_bars_build_delay: Option<u64>,
80 time_bars_origin_offset: Option<HashMap<BarAggregation, u64>>,
81 validate_data_sequence: Option<bool>,
82 buffer_deltas: Option<bool>,
83 emit_quotes_from_book: Option<bool>,
84 emit_quotes_from_book_depths: Option<bool>,
85 external_clients: Option<Vec<ClientId>>,
86 debug: Option<bool>,
87 disable_historical_cache: Option<bool>,
88 ) -> PyResult<Self> {
89 let time_bars_interval_type = match time_bars_interval_type {
90 Some(value) => Some(coerce_bar_interval_type(&value)?),
91 None => None,
92 };
93 let time_bars_origin_offset = time_bars_origin_offset.map(|map| {
94 map.into_iter()
95 .map(|(agg, nanos)| (agg, Duration::from_nanos(nanos)))
96 .collect()
97 });
98 Ok(Self::builder()
99 .maybe_time_bars_build_with_no_updates(time_bars_build_with_no_updates)
100 .maybe_time_bars_timestamp_on_close(time_bars_timestamp_on_close)
101 .maybe_time_bars_skip_first_non_full_bar(time_bars_skip_first_non_full_bar)
102 .maybe_time_bars_interval_type(time_bars_interval_type)
103 .maybe_time_bars_build_delay(time_bars_build_delay)
104 .maybe_time_bars_origin_offset(time_bars_origin_offset)
105 .maybe_validate_data_sequence(validate_data_sequence)
106 .maybe_buffer_deltas(buffer_deltas)
107 .maybe_emit_quotes_from_book(emit_quotes_from_book)
108 .maybe_emit_quotes_from_book_depths(emit_quotes_from_book_depths)
109 .maybe_disable_historical_cache(disable_historical_cache)
110 .maybe_external_clients(external_clients)
111 .maybe_debug(debug)
112 .build())
113 }
114
115 #[getter]
116 #[pyo3(name = "time_bars_build_with_no_updates")]
117 const fn py_time_bars_build_with_no_updates(&self) -> bool {
118 self.time_bars_build_with_no_updates
119 }
120
121 #[getter]
122 #[pyo3(name = "time_bars_timestamp_on_close")]
123 const fn py_time_bars_timestamp_on_close(&self) -> bool {
124 self.time_bars_timestamp_on_close
125 }
126
127 #[getter]
128 #[pyo3(name = "time_bars_skip_first_non_full_bar")]
129 const fn py_time_bars_skip_first_non_full_bar(&self) -> bool {
130 self.time_bars_skip_first_non_full_bar
131 }
132
133 #[getter]
134 #[pyo3(name = "time_bars_interval_type")]
135 const fn py_time_bars_interval_type(&self) -> BarIntervalType {
136 self.time_bars_interval_type
137 }
138
139 #[getter]
140 #[pyo3(name = "time_bars_build_delay")]
141 const fn py_time_bars_build_delay(&self) -> u64 {
142 self.time_bars_build_delay
143 }
144
145 #[getter]
146 #[pyo3(name = "time_bars_origin_offset")]
147 fn py_time_bars_origin_offset(&self) -> HashMap<BarAggregation, u64> {
148 self.time_bars_origin_offset
149 .iter()
150 .map(|(aggregation, offset)| (*aggregation, offset.as_nanos() as u64))
151 .collect()
152 }
153
154 #[getter]
155 #[pyo3(name = "validate_data_sequence")]
156 const fn py_validate_data_sequence(&self) -> bool {
157 self.validate_data_sequence
158 }
159
160 #[getter]
161 #[pyo3(name = "buffer_deltas")]
162 const fn py_buffer_deltas(&self) -> bool {
163 self.buffer_deltas
164 }
165
166 #[getter]
167 #[pyo3(name = "emit_quotes_from_book")]
168 const fn py_emit_quotes_from_book(&self) -> bool {
169 self.emit_quotes_from_book
170 }
171
172 #[getter]
173 #[pyo3(name = "emit_quotes_from_book_depths")]
174 const fn py_emit_quotes_from_book_depths(&self) -> bool {
175 self.emit_quotes_from_book_depths
176 }
177
178 #[getter]
179 #[pyo3(name = "disable_historical_cache")]
180 const fn py_disable_historical_cache(&self) -> bool {
181 self.disable_historical_cache
182 }
183
184 #[getter]
185 #[pyo3(name = "external_clients")]
186 fn py_external_clients(&self) -> Option<Vec<ClientId>> {
187 self.external_clients.clone()
188 }
189
190 #[getter]
191 #[pyo3(name = "debug")]
192 const fn py_debug(&self) -> bool {
193 self.debug
194 }
195
196 fn __repr__(&self) -> String {
197 format!("{self:?}")
198 }
199
200 fn __str__(&self) -> String {
201 format!("{self:?}")
202 }
203}