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