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