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::{
22    IntoPyObjectNautilusExt,
23    serialization::{from_dict_pyo3, to_dict_pyo3},
24};
25use nautilus_model::{
26    enums::OrderSide,
27    identifiers::InstrumentId,
28    types::{Price, Quantity},
29};
30use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
31
32use crate::{
33    enums::{DatabentoStatisticType, DatabentoStatisticUpdateAction},
34    types::{DatabentoImbalance, DatabentoStatistics},
35};
36
37#[pymethods]
38#[pyo3_stub_gen::derive::gen_stub_pymethods]
39impl DatabentoImbalance {
40    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
41        match op {
42            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
43            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
44            _ => py.NotImplemented(),
45        }
46    }
47
48    fn __hash__(&self) -> isize {
49        let mut hasher = DefaultHasher::new();
50        self.hash(&mut hasher);
51        hasher.finish() as isize
52    }
53
54    fn __repr__(&self) -> String {
55        format!(
56            "{}(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={})",
57            stringify!(DatabentoImbalance),
58            self.instrument_id,
59            self.ref_price,
60            self.cont_book_clr_price,
61            self.auct_interest_clr_price,
62            self.paired_qty,
63            self.total_imbalance_qty,
64            self.side,
65            self.significant_imbalance,
66            self.ts_event,
67            self.ts_recv,
68            self.ts_init,
69        )
70    }
71
72    fn __str__(&self) -> String {
73        self.__repr__()
74    }
75
76    #[getter]
77    #[pyo3(name = "instrument_id")]
78    const fn py_instrument_id(&self) -> InstrumentId {
79        self.instrument_id
80    }
81
82    #[getter]
83    #[pyo3(name = "ref_price")]
84    const fn py_ref_price(&self) -> Price {
85        self.ref_price
86    }
87
88    #[getter]
89    #[pyo3(name = "cont_book_clr_price")]
90    const fn py_cont_book_clr_price(&self) -> Price {
91        self.cont_book_clr_price
92    }
93
94    #[getter]
95    #[pyo3(name = "auct_interest_clr_price")]
96    const fn py_auct_interest_clr_price(&self) -> Price {
97        self.auct_interest_clr_price
98    }
99
100    #[getter]
101    #[pyo3(name = "paired_qty")]
102    const fn py_paired_qty(&self) -> Quantity {
103        self.paired_qty
104    }
105
106    #[getter]
107    #[pyo3(name = "total_imbalance_qty")]
108    const fn py_total_imbalance_qty(&self) -> Quantity {
109        self.total_imbalance_qty
110    }
111
112    #[getter]
113    #[pyo3(name = "side")]
114    const fn py_side(&self) -> OrderSide {
115        self.side
116    }
117
118    #[getter]
119    #[pyo3(name = "significant_imbalance")]
120    fn py_significant_imbalance(&self) -> String {
121        self.significant_imbalance.to_string()
122    }
123
124    #[getter]
125    #[pyo3(name = "ts_event")]
126    const fn py_ts_event(&self) -> u64 {
127        self.ts_event.as_u64()
128    }
129
130    #[getter]
131    #[pyo3(name = "ts_recv")]
132    const fn py_ts_recv(&self) -> u64 {
133        self.ts_recv.as_u64()
134    }
135
136    #[getter]
137    #[pyo3(name = "ts_init")]
138    const fn py_ts_init(&self) -> u64 {
139        self.ts_init.as_u64()
140    }
141
142    #[staticmethod]
143    #[pyo3(name = "from_dict")]
144    fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
145        from_dict_pyo3(py, values)
146    }
147
148    /// Return a dictionary representation of the object.
149    #[pyo3(name = "to_dict")]
150    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
151        to_dict_pyo3(py, self)
152    }
153}
154
155#[pymethods]
156#[pyo3_stub_gen::derive::gen_stub_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    /// Return a dictionary representation of the object.
283    #[pyo3(name = "to_dict")]
284    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
285        to_dict_pyo3(py, self)
286    }
287}
288
289/// Subscription acknowledgement from the Databento gateway.
290#[cfg_attr(
291    feature = "python",
292    pyo3::pyclass(
293        module = "nautilus_trader.core.nautilus_pyo3.databento",
294        from_py_object
295    )
296)]
297#[cfg_attr(
298    feature = "python",
299    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.databento")
300)]
301#[derive(Debug, Clone)]
302pub struct DatabentoSubscriptionAck {
303    #[pyo3(get)]
304    pub schema: String,
305    #[pyo3(get)]
306    pub message: String,
307    #[pyo3(get)]
308    pub ts_received: u64,
309}
310
311impl From<crate::types::SubscriptionAckEvent> for DatabentoSubscriptionAck {
312    fn from(event: crate::types::SubscriptionAckEvent) -> Self {
313        Self {
314            schema: event.schema,
315            message: event.message,
316            ts_received: event.ts_received.as_u64(),
317        }
318    }
319}