Skip to main content

nautilus_databento/
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::{collections::HashMap, ffi::c_char, sync::Arc};
17
18use databento::dbn;
19use nautilus_core::UnixNanos;
20use nautilus_model::{
21    data::{HasTsInit, custom::CustomDataTrait},
22    enums::OrderSide,
23    identifiers::InstrumentId,
24    types::{Price, Quantity},
25};
26use serde::{Deserialize, Serialize};
27use ustr::Ustr;
28
29use super::enums::{DatabentoStatisticType, DatabentoStatisticUpdateAction};
30
31/// Subscription acknowledgement event from the Databento gateway.
32#[derive(Debug, Clone)]
33pub struct SubscriptionAckEvent {
34    /// The schema that was acknowledged.
35    pub schema: String,
36    /// The raw message from the gateway.
37    pub message: String,
38    /// Timestamp when the ack was received.
39    pub ts_received: UnixNanos,
40}
41
42/// Represents a Databento publisher ID.
43pub type PublisherId = u16;
44
45/// Represents a Databento dataset ID.
46pub type Dataset = Ustr;
47
48/// Represents a Databento publisher.
49#[cfg_attr(
50    feature = "python",
51    pyo3::pyclass(
52        module = "nautilus_trader.core.nautilus_pyo3.databento",
53        from_py_object
54    )
55)]
56#[cfg_attr(
57    feature = "python",
58    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.databento")
59)]
60#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)]
61pub struct DatabentoPublisher {
62    /// The publisher ID assigned by Databento, which denotes the dataset and venue.
63    pub publisher_id: PublisherId,
64    /// The Databento dataset ID for the publisher.
65    pub dataset: dbn::Dataset,
66    /// The venue for the publisher.
67    pub venue: dbn::Venue,
68    /// The publisher description.
69    pub description: String,
70}
71
72/// Represents an auction imbalance.
73///
74/// This data type includes the populated data fields provided by `Databento`,
75/// excluding `publisher_id` and `instrument_id`.
76#[cfg_attr(
77    feature = "python",
78    pyo3::pyclass(
79        module = "nautilus_trader.core.nautilus_pyo3.databento",
80        from_py_object
81    )
82)]
83#[cfg_attr(
84    feature = "python",
85    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.databento")
86)]
87#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
88pub struct DatabentoImbalance {
89    // The instrument ID for the imbalance data.
90    pub instrument_id: InstrumentId,
91    // The reference price at which the imbalance shares are calculated.
92    pub ref_price: Price,
93    // The hypothetical auction-clearing price for both cross and continuous orders.
94    pub cont_book_clr_price: Price,
95    // The hypothetical auction-clearing price for cross orders only.
96    pub auct_interest_clr_price: Price,
97    // The quantity of shares which are eligible to be matched at `ref_price`.
98    pub paired_qty: Quantity,
99    // The quantity of shares which are not paired at `ref_price`.
100    pub total_imbalance_qty: Quantity,
101    // The market side of the `total_imbalance_qty` (can be `NO_ORDER_SIDE`).
102    pub side: OrderSide,
103    // A venue-specific character code. For Nasdaq, contains the raw Price Variation Indicator.
104    pub significant_imbalance: c_char,
105    // UNIX timestamp (nanoseconds) when the data event occurred.
106    pub ts_event: UnixNanos,
107    // UNIX timestamp (nanoseconds) when the data object was received by Databento.
108    pub ts_recv: UnixNanos,
109    // UNIX timestamp (nanoseconds) when the data object was initialized.
110    pub ts_init: UnixNanos,
111}
112
113impl DatabentoImbalance {
114    /// Returns the metadata for the type, for use with serialization formats.
115    #[must_use]
116    pub fn get_metadata(
117        instrument_id: &InstrumentId,
118        price_precision: u8,
119        size_precision: u8,
120    ) -> HashMap<String, String> {
121        let mut metadata = HashMap::new();
122        metadata.insert("instrument_id".to_string(), instrument_id.to_string());
123        metadata.insert("price_precision".to_string(), price_precision.to_string());
124        metadata.insert("size_precision".to_string(), size_precision.to_string());
125        metadata
126    }
127
128    /// Creates a new [`DatabentoImbalance`] instance.
129    #[expect(clippy::too_many_arguments)]
130    #[must_use]
131    pub const fn new(
132        instrument_id: InstrumentId,
133        ref_price: Price,
134        cont_book_clr_price: Price,
135        auct_interest_clr_price: Price,
136        paired_qty: Quantity,
137        total_imbalance_qty: Quantity,
138        side: OrderSide,
139        significant_imbalance: c_char,
140        ts_event: UnixNanos,
141        ts_recv: UnixNanos,
142        ts_init: UnixNanos,
143    ) -> Self {
144        Self {
145            instrument_id,
146            ref_price,
147            cont_book_clr_price,
148            auct_interest_clr_price,
149            paired_qty,
150            total_imbalance_qty,
151            side,
152            significant_imbalance,
153            ts_event,
154            ts_recv,
155            ts_init,
156        }
157    }
158}
159
160impl HasTsInit for DatabentoImbalance {
161    fn ts_init(&self) -> UnixNanos {
162        self.ts_init
163    }
164}
165
166impl CustomDataTrait for DatabentoImbalance {
167    fn type_name(&self) -> &'static str {
168        "DatabentoImbalance"
169    }
170
171    fn as_any(&self) -> &dyn std::any::Any {
172        self
173    }
174
175    fn ts_event(&self) -> UnixNanos {
176        self.ts_event
177    }
178
179    fn to_json(&self) -> anyhow::Result<String> {
180        Ok(serde_json::to_string(self)?)
181    }
182
183    fn clone_arc(&self) -> Arc<dyn CustomDataTrait> {
184        Arc::new(self.clone())
185    }
186
187    fn eq_arc(&self, other: &dyn CustomDataTrait) -> bool {
188        if let Some(o) = other.as_any().downcast_ref::<Self>() {
189            self == o
190        } else {
191            false
192        }
193    }
194
195    #[cfg(feature = "python")]
196    fn to_pyobject(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<pyo3::Py<pyo3::PyAny>> {
197        nautilus_model::data::custom::clone_pyclass_to_pyobject(self, py)
198    }
199
200    fn type_name_static() -> &'static str {
201        "DatabentoImbalance"
202    }
203
204    fn from_json(value: serde_json::Value) -> anyhow::Result<Arc<dyn CustomDataTrait>> {
205        let parsed: Self = serde_json::from_value(value)?;
206        Ok(Arc::new(parsed))
207    }
208}
209
210/// Represents a market statistics snapshot.
211///
212/// This data type includes the populated data fields provided by `Databento`,
213/// excluding `publisher_id` and `instrument_id`.
214#[cfg_attr(
215    feature = "python",
216    pyo3::pyclass(
217        module = "nautilus_trader.core.nautilus_pyo3.databento",
218        from_py_object
219    )
220)]
221#[cfg_attr(
222    feature = "python",
223    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.databento")
224)]
225#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
226pub struct DatabentoStatistics {
227    // The instrument ID for the statistics message.
228    pub instrument_id: InstrumentId,
229    // The type of statistic value contained in the message.
230    pub stat_type: DatabentoStatisticType,
231    // Indicates if the statistic is newly added (1) or deleted (2). (Deleted is only used with some stat_types).
232    pub update_action: DatabentoStatisticUpdateAction,
233    // The statistics price.
234    pub price: Option<Price>,
235    // The value for non-price statistics.
236    pub quantity: Option<Quantity>,
237    // The channel ID within the venue.
238    pub channel_id: u16,
239    // Additional flags associated with certain stat types.
240    pub stat_flags: u8,
241    // The message sequence number assigned at the venue.
242    pub sequence: u32,
243    // UNIX timestamp (nanoseconds) Databento `ts_ref` reference timestamp).
244    pub ts_ref: UnixNanos,
245    // The matching-engine-sending timestamp expressed as the number of nanoseconds before the Databento `ts_recv`.
246    pub ts_in_delta: i32,
247    // UNIX timestamp (nanoseconds) when the data event occurred.
248    pub ts_event: UnixNanos,
249    // UNIX timestamp (nanoseconds) when the data object was received by Databento.
250    pub ts_recv: UnixNanos,
251    // UNIX timestamp (nanoseconds) when the data object was initialized.
252    pub ts_init: UnixNanos,
253}
254
255impl DatabentoStatistics {
256    /// Returns the metadata for the type, for use with serialization formats.
257    #[must_use]
258    pub fn get_metadata(
259        instrument_id: &InstrumentId,
260        price_precision: u8,
261        size_precision: u8,
262    ) -> HashMap<String, String> {
263        let mut metadata = HashMap::new();
264        metadata.insert("instrument_id".to_string(), instrument_id.to_string());
265        metadata.insert("price_precision".to_string(), price_precision.to_string());
266        metadata.insert("size_precision".to_string(), size_precision.to_string());
267        metadata
268    }
269
270    /// Creates a new [`DatabentoStatistics`] instance.
271    #[expect(clippy::too_many_arguments)]
272    #[must_use]
273    pub const fn new(
274        instrument_id: InstrumentId,
275        stat_type: DatabentoStatisticType,
276        update_action: DatabentoStatisticUpdateAction,
277        price: Option<Price>,
278        quantity: Option<Quantity>,
279        channel_id: u16,
280        stat_flags: u8,
281        sequence: u32,
282        ts_ref: UnixNanos,
283        ts_in_delta: i32,
284        ts_event: UnixNanos,
285        ts_recv: UnixNanos,
286        ts_init: UnixNanos,
287    ) -> Self {
288        Self {
289            instrument_id,
290            stat_type,
291            update_action,
292            price,
293            quantity,
294            channel_id,
295            stat_flags,
296            sequence,
297            ts_ref,
298            ts_in_delta,
299            ts_event,
300            ts_recv,
301            ts_init,
302        }
303    }
304}
305
306impl HasTsInit for DatabentoStatistics {
307    fn ts_init(&self) -> UnixNanos {
308        self.ts_init
309    }
310}
311
312impl CustomDataTrait for DatabentoStatistics {
313    fn type_name(&self) -> &'static str {
314        "DatabentoStatistics"
315    }
316
317    fn as_any(&self) -> &dyn std::any::Any {
318        self
319    }
320
321    fn ts_event(&self) -> UnixNanos {
322        self.ts_event
323    }
324
325    fn to_json(&self) -> anyhow::Result<String> {
326        Ok(serde_json::to_string(self)?)
327    }
328
329    fn clone_arc(&self) -> Arc<dyn CustomDataTrait> {
330        Arc::new(self.clone())
331    }
332
333    fn eq_arc(&self, other: &dyn CustomDataTrait) -> bool {
334        if let Some(o) = other.as_any().downcast_ref::<Self>() {
335            self == o
336        } else {
337            false
338        }
339    }
340
341    #[cfg(feature = "python")]
342    fn to_pyobject(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<pyo3::Py<pyo3::PyAny>> {
343        nautilus_model::data::custom::clone_pyclass_to_pyobject(self, py)
344    }
345
346    fn type_name_static() -> &'static str {
347        "DatabentoStatistics"
348    }
349
350    fn from_json(value: serde_json::Value) -> anyhow::Result<Arc<dyn CustomDataTrait>> {
351        let parsed: Self = serde_json::from_value(value)?;
352        Ok(Arc::new(parsed))
353    }
354}