Skip to main content

nautilus_databento/python/
types.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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]
35#[pyo3_stub_gen::derive::gen_stub_pymethods]
36impl DatabentoImbalance {
37    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
38        match op {
39            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
40            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
41            _ => py.NotImplemented(),
42        }
43    }
44
45    fn __hash__(&self) -> isize {
46        let mut hasher = DefaultHasher::new();
47        self.hash(&mut hasher);
48        hasher.finish() as isize
49    }
50
51    fn __repr__(&self) -> String {
52        format!(
53            "{}(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={})",
54            stringify!(DatabentoImbalance),
55            self.instrument_id,
56            self.ref_price,
57            self.cont_book_clr_price,
58            self.auct_interest_clr_price,
59            self.paired_qty,
60            self.total_imbalance_qty,
61            self.side,
62            self.significant_imbalance,
63            self.ts_event,
64            self.ts_recv,
65            self.ts_init,
66        )
67    }
68
69    fn __str__(&self) -> String {
70        self.__repr__()
71    }
72
73    #[getter]
74    #[pyo3(name = "instrument_id")]
75    const fn py_instrument_id(&self) -> InstrumentId {
76        self.instrument_id
77    }
78
79    #[getter]
80    #[pyo3(name = "ref_price")]
81    const fn py_ref_price(&self) -> Price {
82        self.ref_price
83    }
84
85    #[getter]
86    #[pyo3(name = "cont_book_clr_price")]
87    const fn py_cont_book_clr_price(&self) -> Price {
88        self.cont_book_clr_price
89    }
90
91    #[getter]
92    #[pyo3(name = "auct_interest_clr_price")]
93    const fn py_auct_interest_clr_price(&self) -> Price {
94        self.auct_interest_clr_price
95    }
96
97    #[getter]
98    #[pyo3(name = "paired_qty")]
99    const fn py_paired_qty(&self) -> Quantity {
100        self.paired_qty
101    }
102
103    #[getter]
104    #[pyo3(name = "total_imbalance_qty")]
105    const fn py_total_imbalance_qty(&self) -> Quantity {
106        self.total_imbalance_qty
107    }
108
109    #[getter]
110    #[pyo3(name = "side")]
111    const fn py_side(&self) -> OrderSide {
112        self.side
113    }
114
115    #[getter]
116    #[pyo3(name = "significant_imbalance")]
117    fn py_significant_imbalance(&self) -> String {
118        self.significant_imbalance.to_string()
119    }
120
121    #[getter]
122    #[pyo3(name = "ts_event")]
123    const fn py_ts_event(&self) -> u64 {
124        self.ts_event.as_u64()
125    }
126
127    #[getter]
128    #[pyo3(name = "ts_recv")]
129    const fn py_ts_recv(&self) -> u64 {
130        self.ts_recv.as_u64()
131    }
132
133    #[getter]
134    #[pyo3(name = "ts_init")]
135    const fn py_ts_init(&self) -> u64 {
136        self.ts_init.as_u64()
137    }
138
139    #[staticmethod]
140    #[pyo3(name = "from_dict")]
141    fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
142        from_dict_pyo3(py, values)
143    }
144
145    // TODO
146    /// # Errors
147    ///
148    /// Returns a `PyErr` if generating the Python dictionary fails.
149    #[pyo3(name = "to_dict")]
150    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
151        let dict = PyDict::new(py);
152        dict.set_item("type", stringify!(DatabentoImbalance))?;
153        Ok(dict.into())
154    }
155}
156
157#[pymethods]
158#[pyo3_stub_gen::derive::gen_stub_pymethods]
159impl DatabentoStatistics {
160    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
161        match op {
162            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
163            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
164            _ => py.NotImplemented(),
165        }
166    }
167
168    fn __hash__(&self) -> isize {
169        let mut hasher = DefaultHasher::new();
170        self.hash(&mut hasher);
171        hasher.finish() as isize
172    }
173
174    fn __repr__(&self) -> String {
175        format!(
176            "{}(instrument_id={}, stat_type={}, update_action={}, price={}, quantity={}, channel_id={}, stat_flags={}, sequence={}, ts_ref={}, ts_in_delta={}, ts_event={}, ts_recv={}, ts_init={})",
177            stringify!(DatabentoStatistics),
178            self.instrument_id,
179            self.stat_type,
180            self.update_action,
181            self.price
182                .map_or_else(|| "None".to_string(), |p| format!("{p}")),
183            self.quantity
184                .map_or_else(|| "None".to_string(), |q| format!("{q}")),
185            self.channel_id,
186            self.stat_flags,
187            self.sequence,
188            self.ts_ref,
189            self.ts_in_delta,
190            self.ts_event,
191            self.ts_recv,
192            self.ts_init,
193        )
194    }
195
196    fn __str__(&self) -> String {
197        self.__repr__()
198    }
199
200    #[getter]
201    #[pyo3(name = "instrument_id")]
202    const fn py_instrument_id(&self) -> InstrumentId {
203        self.instrument_id
204    }
205
206    #[getter]
207    #[pyo3(name = "stat_type")]
208    const fn py_stat_type(&self) -> DatabentoStatisticType {
209        self.stat_type
210    }
211
212    #[getter]
213    #[pyo3(name = "update_action")]
214    const fn py_update_action(&self) -> DatabentoStatisticUpdateAction {
215        self.update_action
216    }
217
218    #[getter]
219    #[pyo3(name = "price")]
220    const fn py_price(&self) -> Option<Price> {
221        self.price
222    }
223
224    #[getter]
225    #[pyo3(name = "quantity")]
226    const fn py_quantity(&self) -> Option<Quantity> {
227        self.quantity
228    }
229
230    #[getter]
231    #[pyo3(name = "channel_id")]
232    const fn py_channel_id(&self) -> u16 {
233        self.channel_id
234    }
235
236    #[getter]
237    #[pyo3(name = "stat_flags")]
238    const fn py_stat_flags(&self) -> u8 {
239        self.stat_flags
240    }
241
242    #[getter]
243    #[pyo3(name = "sequence")]
244    const fn py_sequence(&self) -> u32 {
245        self.sequence
246    }
247
248    #[getter]
249    #[pyo3(name = "ts_ref")]
250    const fn py_ts_ref(&self) -> u64 {
251        self.ts_ref.as_u64()
252    }
253
254    #[getter]
255    #[pyo3(name = "ts_in_delta")]
256    const fn py_ts_in_delta(&self) -> i32 {
257        self.ts_in_delta
258    }
259
260    #[getter]
261    #[pyo3(name = "ts_event")]
262    const fn py_ts_event(&self) -> u64 {
263        self.ts_event.as_u64()
264    }
265
266    #[pyo3(name = "ts_recv")]
267    #[getter]
268    const fn py_ts_recv(&self) -> u64 {
269        self.ts_recv.as_u64()
270    }
271
272    #[pyo3(name = "ts_init")]
273    #[getter]
274    const fn py_ts_init(&self) -> u64 {
275        self.ts_init.as_u64()
276    }
277
278    #[staticmethod]
279    #[pyo3(name = "from_dict")]
280    fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
281        from_dict_pyo3(py, values)
282    }
283
284    // TODO
285    /// # Errors
286    ///
287    /// Returns a `PyErr` if generating the Python dictionary fails.
288    #[pyo3(name = "to_dict")]
289    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
290        let dict = PyDict::new(py);
291        dict.set_item("type", stringify!(DatabentoStatistics))?;
292        Ok(dict.into())
293    }
294}
295
296/// Subscription acknowledgement from the Databento gateway.
297#[cfg_attr(
298    feature = "python",
299    pyo3::pyclass(
300        module = "nautilus_trader.core.nautilus_pyo3.databento",
301        from_py_object
302    )
303)]
304#[cfg_attr(
305    feature = "python",
306    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.databento")
307)]
308#[derive(Debug, Clone)]
309pub struct DatabentoSubscriptionAck {
310    #[pyo3(get)]
311    pub schema: String,
312    #[pyo3(get)]
313    pub message: String,
314    #[pyo3(get)]
315    pub ts_received: u64,
316}
317
318impl From<crate::types::SubscriptionAckEvent> for DatabentoSubscriptionAck {
319    fn from(event: crate::types::SubscriptionAckEvent) -> Self {
320        Self {
321            schema: event.schema,
322            message: event.message,
323            ts_received: event.ts_received.as_u64(),
324        }
325    }
326}