nautilus_databento/python/
types.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::{
17    collections::hash_map::DefaultHasher,
18    hash::{Hash, Hasher},
19};
20
21use nautilus_core::python::{IntoPyObjectNautilusExt, serialization::from_dict_pyo3};
22use nautilus_model::{
23    enums::OrderSide,
24    identifiers::InstrumentId,
25    types::{Price, Quantity},
26};
27use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
28
29use crate::{
30    enums::{DatabentoStatisticType, DatabentoStatisticUpdateAction},
31    types::{DatabentoImbalance, DatabentoStatistics},
32};
33
34#[pymethods]
35impl DatabentoImbalance {
36    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
37        match op {
38            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
39            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
40            _ => py.NotImplemented(),
41        }
42    }
43
44    fn __hash__(&self) -> isize {
45        let mut hasher = DefaultHasher::new();
46        self.hash(&mut hasher);
47        hasher.finish() as isize
48    }
49
50    fn __repr__(&self) -> String {
51        format!(
52            "{}(instrument_id={}, ref_price={}, cont_book_clr_price={}, auct_interest_clr_price={}, paired_qty={}, total_imbalance_qty={}, side={}, significant_imbalance={}, ts_event={}, ts_recv={}, ts_init={})",
53            stringify!(DatabentoImbalance),
54            self.instrument_id,
55            self.ref_price,
56            self.cont_book_clr_price,
57            self.auct_interest_clr_price,
58            self.paired_qty,
59            self.total_imbalance_qty,
60            self.side,
61            self.significant_imbalance,
62            self.ts_event,
63            self.ts_recv,
64            self.ts_init,
65        )
66    }
67
68    fn __str__(&self) -> String {
69        self.__repr__()
70    }
71
72    #[getter]
73    #[pyo3(name = "instrument_id")]
74    const fn py_instrument_id(&self) -> InstrumentId {
75        self.instrument_id
76    }
77
78    #[getter]
79    #[pyo3(name = "ref_price")]
80    const fn py_ref_price(&self) -> Price {
81        self.ref_price
82    }
83
84    #[getter]
85    #[pyo3(name = "cont_book_clr_price")]
86    const fn py_cont_book_clr_price(&self) -> Price {
87        self.cont_book_clr_price
88    }
89
90    #[getter]
91    #[pyo3(name = "auct_interest_clr_price")]
92    const fn py_auct_interest_clr_price(&self) -> Price {
93        self.auct_interest_clr_price
94    }
95
96    #[getter]
97    #[pyo3(name = "paired_qty")]
98    const fn py_paired_qty(&self) -> Quantity {
99        self.paired_qty
100    }
101
102    #[getter]
103    #[pyo3(name = "total_imbalance_qty")]
104    const fn py_total_imbalance_qty(&self) -> Quantity {
105        self.total_imbalance_qty
106    }
107
108    #[getter]
109    #[pyo3(name = "side")]
110    const fn py_side(&self) -> OrderSide {
111        self.side
112    }
113
114    #[getter]
115    #[pyo3(name = "significant_imbalance")]
116    fn py_significant_imbalance(&self) -> String {
117        self.significant_imbalance.to_string()
118    }
119
120    #[getter]
121    #[pyo3(name = "ts_event")]
122    const fn py_ts_event(&self) -> u64 {
123        self.ts_event.as_u64()
124    }
125
126    #[getter]
127    #[pyo3(name = "ts_recv")]
128    const fn py_ts_recv(&self) -> u64 {
129        self.ts_recv.as_u64()
130    }
131
132    #[getter]
133    #[pyo3(name = "ts_init")]
134    const fn py_ts_init(&self) -> u64 {
135        self.ts_init.as_u64()
136    }
137
138    #[staticmethod]
139    #[pyo3(name = "from_dict")]
140    fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
141        from_dict_pyo3(py, values)
142    }
143
144    // TODO
145    /// # Errors
146    ///
147    /// Returns a `PyErr` if generating the Python dictionary fails.
148    #[pyo3(name = "to_dict")]
149    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
150        let dict = PyDict::new(py);
151        dict.set_item("type", stringify!(DatabentoImbalance))?;
152        Ok(dict.into())
153    }
154}
155
156#[pymethods]
157impl DatabentoStatistics {
158    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
159        match op {
160            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
161            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
162            _ => py.NotImplemented(),
163        }
164    }
165
166    fn __hash__(&self) -> isize {
167        let mut hasher = DefaultHasher::new();
168        self.hash(&mut hasher);
169        hasher.finish() as isize
170    }
171
172    fn __repr__(&self) -> String {
173        format!(
174            "{}(instrument_id={}, stat_type={}, update_action={}, price={}, quantity={}, channel_id={}, stat_flags={}, sequence={}, ts_ref={}, ts_in_delta={}, ts_event={}, ts_recv={}, ts_init={})",
175            stringify!(DatabentoStatistics),
176            self.instrument_id,
177            self.stat_type,
178            self.update_action,
179            self.price
180                .map_or_else(|| "None".to_string(), |p| format!("{p}")),
181            self.quantity
182                .map_or_else(|| "None".to_string(), |q| format!("{q}")),
183            self.channel_id,
184            self.stat_flags,
185            self.sequence,
186            self.ts_ref,
187            self.ts_in_delta,
188            self.ts_event,
189            self.ts_recv,
190            self.ts_init,
191        )
192    }
193
194    fn __str__(&self) -> String {
195        self.__repr__()
196    }
197
198    #[getter]
199    #[pyo3(name = "instrument_id")]
200    const fn py_instrument_id(&self) -> InstrumentId {
201        self.instrument_id
202    }
203
204    #[getter]
205    #[pyo3(name = "stat_type")]
206    const fn py_stat_type(&self) -> DatabentoStatisticType {
207        self.stat_type
208    }
209
210    #[getter]
211    #[pyo3(name = "update_action")]
212    const fn py_update_action(&self) -> DatabentoStatisticUpdateAction {
213        self.update_action
214    }
215
216    #[getter]
217    #[pyo3(name = "price")]
218    const fn py_price(&self) -> Option<Price> {
219        self.price
220    }
221
222    #[getter]
223    #[pyo3(name = "quantity")]
224    const fn py_quantity(&self) -> Option<Quantity> {
225        self.quantity
226    }
227
228    #[getter]
229    #[pyo3(name = "channel_id")]
230    const fn py_channel_id(&self) -> u16 {
231        self.channel_id
232    }
233
234    #[getter]
235    #[pyo3(name = "stat_flags")]
236    const fn py_stat_flags(&self) -> u8 {
237        self.stat_flags
238    }
239
240    #[getter]
241    #[pyo3(name = "sequence")]
242    const fn py_sequence(&self) -> u32 {
243        self.sequence
244    }
245
246    #[getter]
247    #[pyo3(name = "ts_ref")]
248    const fn py_ts_ref(&self) -> u64 {
249        self.ts_ref.as_u64()
250    }
251
252    #[getter]
253    #[pyo3(name = "ts_in_delta")]
254    const fn py_ts_in_delta(&self) -> i32 {
255        self.ts_in_delta
256    }
257
258    #[getter]
259    #[pyo3(name = "ts_event")]
260    const fn py_ts_event(&self) -> u64 {
261        self.ts_event.as_u64()
262    }
263
264    #[pyo3(name = "ts_recv")]
265    #[getter]
266    const fn py_ts_recv(&self) -> u64 {
267        self.ts_recv.as_u64()
268    }
269
270    #[pyo3(name = "ts_init")]
271    #[getter]
272    const fn py_ts_init(&self) -> u64 {
273        self.ts_init.as_u64()
274    }
275
276    #[staticmethod]
277    #[pyo3(name = "from_dict")]
278    fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
279        from_dict_pyo3(py, values)
280    }
281
282    // TODO
283    /// # Errors
284    ///
285    /// Returns a `PyErr` if generating the Python dictionary fails.
286    #[pyo3(name = "to_dict")]
287    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
288        let dict = PyDict::new(py);
289        dict.set_item("type", stringify!(DatabentoStatistics))?;
290        Ok(dict.into())
291    }
292}