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_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 = "validate_data_sequence")]
147 const fn py_validate_data_sequence(&self) -> bool {
148 self.validate_data_sequence
149 }
150
151 #[getter]
152 #[pyo3(name = "buffer_deltas")]
153 const fn py_buffer_deltas(&self) -> bool {
154 self.buffer_deltas
155 }
156
157 #[getter]
158 #[pyo3(name = "emit_quotes_from_book")]
159 const fn py_emit_quotes_from_book(&self) -> bool {
160 self.emit_quotes_from_book
161 }
162
163 #[getter]
164 #[pyo3(name = "emit_quotes_from_book_depths")]
165 const fn py_emit_quotes_from_book_depths(&self) -> bool {
166 self.emit_quotes_from_book_depths
167 }
168
169 #[getter]
170 #[pyo3(name = "disable_historical_cache")]
171 const fn py_disable_historical_cache(&self) -> bool {
172 self.disable_historical_cache
173 }
174
175 #[getter]
176 #[pyo3(name = "debug")]
177 const fn py_debug(&self) -> bool {
178 self.debug
179 }
180
181 fn __repr__(&self) -> String {
182 format!("{self:?}")
183 }
184
185 fn __str__(&self) -> String {
186 format!("{self:?}")
187 }
188}