dbn/
record.rs

1//! Market data types for encoding different Databento [`Schema`](crate::enums::Schema)s
2//! in the most recent DBN version, as well as conversion functions.
3
4pub(crate) mod conv;
5mod impl_default;
6mod methods;
7
8use std::{ffi::CStr, mem, os::raw::c_char, ptr::NonNull, slice};
9
10// Dummy derive macro to get around `cfg_attr` incompatibility of several
11// of pyo3's attribute macros. See https://github.com/PyO3/pyo3/issues/780
12#[cfg(not(feature = "python"))]
13use dbn_macros::MockPyo3;
14
15use crate::{
16    enums::rtype,
17    macros::{dbn_record, CsvSerialize, JsonSerialize, RecordDebug},
18    Action, Error, FlagSet, InstrumentClass, MatchAlgorithm, Publisher, RType, Result,
19    SecurityUpdateAction, Side, StatType, StatUpdateAction, UserDefinedInstrument, ASSET_CSTR_LEN,
20    SYMBOL_CSTR_LEN,
21};
22pub(crate) use conv::as_u8_slice;
23#[cfg(feature = "serde")]
24pub(crate) use conv::cstr_serde;
25pub use conv::{
26    c_chars_to_str, str_to_c_chars, transmute_header_bytes, transmute_record,
27    transmute_record_bytes, transmute_record_mut, ts_to_dt,
28};
29
30/// Common data for all Databento records. Always found at the beginning of a record
31/// struct.
32#[repr(C)]
33#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
34#[cfg_attr(feature = "trivial_copy", derive(Copy))]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36#[cfg_attr(
37    feature = "python",
38    pyo3::pyclass(eq, get_all, dict, module = "databento_dbn"),
39    derive(crate::macros::PyFieldDesc)
40)]
41#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
42#[cfg_attr(test, derive(type_layout::TypeLayout))]
43pub struct RecordHeader {
44    /// The length of the record in 32-bit words.
45    #[dbn(skip)]
46    pub(crate) length: u8,
47    /// The record type; with `0xe0..0x0F` specifying MBP levels size. Record types
48    /// implement the trait [`HasRType`], and the [`has_rtype`][HasRType::has_rtype]
49    /// function can be used to check if that type can be used to decode a message with
50    /// a given rtype. The set of possible values is defined in [`rtype`].
51    pub rtype: u8,
52    /// The publisher ID assigned by Databento, which denotes the dataset and venue.
53    ///
54    /// See [Publishers](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
55    #[pyo3(set)]
56    pub publisher_id: u16,
57    /// The numeric instrument ID. See [Instrument identifiers](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#instrument-identifiers).
58    #[pyo3(set)]
59    pub instrument_id: u32,
60    /// The matching-engine-received timestamp expressed as the number of nanoseconds
61    /// since the UNIX epoch.
62    ///
63    /// See [Instrument identifiers](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-event).
64    #[dbn(encode_order(0), unix_nanos)]
65    #[pyo3(set)]
66    pub ts_event: u64,
67}
68
69/// A market-by-order (MBO) tick message. The record of the
70/// [`Mbo`](crate::enums::Schema::Mbo) schema.
71#[repr(C)]
72#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
73#[cfg_attr(feature = "trivial_copy", derive(Copy))]
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75#[cfg_attr(
76    feature = "python",
77    pyo3::pyclass(dict, eq, module = "databento_dbn", name = "MBOMsg"),
78    derive(crate::macros::PyFieldDesc)
79)]
80#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
81#[cfg_attr(test, derive(type_layout::TypeLayout))]
82#[dbn_record(rtype::MBO)]
83pub struct MboMsg {
84    /// The common header.
85    #[pyo3(get)]
86    pub hd: RecordHeader,
87    /// The order ID assigned at the venue.
88    #[pyo3(get, set)]
89    pub order_id: u64,
90    /// The order price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
91    /// 0.000000001.
92    ///
93    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
94    #[dbn(encode_order(4), fixed_price)]
95    #[pyo3(get, set)]
96    pub price: i64,
97    /// The order quantity.
98    #[dbn(encode_order(5))]
99    #[pyo3(get, set)]
100    pub size: u32,
101    /// A bit field indicating event end, message characteristics, and data quality. See
102    /// [`enums::flags`](crate::enums::flags) for possible values.
103    #[pyo3(get, set)]
104    pub flags: FlagSet,
105    /// The channel ID assigned by Databento as an incrementing integer starting at
106    /// zero.
107    #[dbn(encode_order(6))]
108    #[pyo3(get, set)]
109    pub channel_id: u8,
110    /// The event action. Can be **A**dd, **C**ancel, **M**odify, clea**R** book, **T**rade,
111    /// **F**ill, or **N**one.
112    ///
113    /// See [Action](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#action).
114    #[dbn(c_char, encode_order(2))]
115    pub action: c_char,
116    /// The side that initiates the event. Can be **A**sk for a sell order
117    /// (or sell aggressor in a trade), **B**id for a buy order (or buy aggressor in a trade),
118    /// or **N**one where no side is specified.
119    ///
120    /// See [Side](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#side).
121    #[dbn(c_char, encode_order(3))]
122    pub side: c_char,
123    /// The capture-server-received timestamp expressed as the number of nanoseconds
124    /// since the UNIX epoch.
125    ///
126    /// See [ts_recv](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-recv).
127    #[dbn(encode_order(0), index_ts, unix_nanos)]
128    #[pyo3(get, set)]
129    pub ts_recv: u64,
130    /// The matching-engine-sending timestamp expressed as the number of nanoseconds before
131    /// `ts_recv`.
132    ///
133    /// See [ts_in_delta](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-in-delta).
134    #[pyo3(get, set)]
135    pub ts_in_delta: i32,
136    /// The message sequence number assigned at the venue.
137    #[pyo3(get, set)]
138    pub sequence: u32,
139}
140
141/// A level.
142#[repr(C)]
143#[derive(Clone, JsonSerialize, RecordDebug, PartialEq, Eq, Hash)]
144#[cfg_attr(feature = "trivial_copy", derive(Copy))]
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146#[cfg_attr(
147    feature = "python",
148    pyo3::pyclass(eq, get_all, set_all, dict, module = "databento_dbn"),
149    derive(crate::macros::PyFieldDesc)
150)]
151#[cfg_attr(test, derive(type_layout::TypeLayout))]
152pub struct BidAskPair {
153    /// The bid price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
154    /// 0.000000001.
155    ///
156    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
157    #[dbn(fixed_price)]
158    pub bid_px: i64,
159    /// The ask price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
160    /// 0.000000001.
161    ///
162    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
163    #[dbn(fixed_price)]
164    pub ask_px: i64,
165    /// The bid size.
166    pub bid_sz: u32,
167    /// The ask size.
168    pub ask_sz: u32,
169    /// The bid order count.
170    pub bid_ct: u32,
171    /// The ask order count.
172    pub ask_ct: u32,
173}
174
175/// A price level consolidated from multiple venues.
176#[repr(C)]
177#[derive(Clone, JsonSerialize, RecordDebug, PartialEq, Eq, Hash)]
178#[cfg_attr(feature = "trivial_copy", derive(Copy))]
179#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
180#[cfg_attr(
181    feature = "python",
182    pyo3::pyclass(eq, get_all, set_all, dict, module = "databento_dbn"),
183    derive(crate::macros::PyFieldDesc)
184)]
185#[cfg_attr(test, derive(type_layout::TypeLayout))]
186pub struct ConsolidatedBidAskPair {
187    /// The bid price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
188    /// 0.000000001.
189    ///
190    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
191    #[dbn(fixed_price)]
192    pub bid_px: i64,
193    /// The ask price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
194    /// 0.000000001.
195    ///
196    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
197    #[dbn(fixed_price)]
198    pub ask_px: i64,
199    /// The bid size.
200    pub bid_sz: u32,
201    /// The ask size.
202    pub ask_sz: u32,
203    /// The publisher ID indicating the venue containing the best bid.
204    ///
205    /// See [Publishers](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
206    #[dbn(fmt_method)]
207    pub bid_pb: u16,
208    // Reserved for later usage.
209    #[doc(hidden)]
210    #[cfg_attr(feature = "serde", serde(skip))]
211    pub _reserved1: [c_char; 2],
212    /// The publisher ID indicating the venue containing the best ask.
213    ///
214    /// See [Publishers](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
215    #[dbn(fmt_method)]
216    pub ask_pb: u16,
217    // Reserved for later usage.
218    #[doc(hidden)]
219    #[cfg_attr(feature = "serde", serde(skip))]
220    pub _reserved2: [c_char; 2],
221}
222
223/// Market by price implementation with a book depth of 0. Equivalent to
224/// MBP-0. The record of the [`Trades`](crate::enums::Schema::Trades) schema.
225#[repr(C)]
226#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
227#[cfg_attr(feature = "trivial_copy", derive(Copy))]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229#[cfg_attr(
230    feature = "python",
231    pyo3::pyclass(dict, eq, module = "databento_dbn"),
232    derive(crate::macros::PyFieldDesc)
233)]
234#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
235#[cfg_attr(test, derive(type_layout::TypeLayout))]
236#[dbn_record(rtype::MBP_0)]
237pub struct TradeMsg {
238    /// The common header.
239    #[pyo3(get)]
240    pub hd: RecordHeader,
241    /// The order price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
242    /// 0.000000001.
243    ///
244    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
245    #[dbn(fixed_price)]
246    #[pyo3(get, set)]
247    pub price: i64,
248    /// The order quantity.
249    #[pyo3(get, set)]
250    pub size: u32,
251    /// The event action. Always **T**rade in the trades schema.
252    ///
253    /// See [Action](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#action).
254    #[dbn(c_char, encode_order(2))]
255    pub action: c_char,
256    /// The side that initiates the trade. Can be **A**sk for a sell aggressor in a trade,
257    /// **B**id for a buy aggressor in a trade, or **N**one where no side is specified.
258    ///
259    /// See [Side](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#side).
260    #[dbn(c_char, encode_order(3))]
261    pub side: c_char,
262    /// A bit field indicating event end, message characteristics, and data quality. See
263    /// [`enums::flags`](crate::enums::flags) for possible values.
264    #[pyo3(get, set)]
265    pub flags: FlagSet,
266    /// The book level where the update event occurred.
267    #[dbn(encode_order(4))]
268    #[pyo3(get, set)]
269    pub depth: u8,
270    /// The capture-server-received timestamp expressed as the number of nanoseconds
271    /// since the UNIX epoch.
272    ///
273    /// See [ts_recv](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-recv).
274    #[dbn(encode_order(0), index_ts, unix_nanos)]
275    #[pyo3(get, set)]
276    pub ts_recv: u64,
277    /// The matching-engine-sending timestamp expressed as the number of nanoseconds before
278    /// `ts_recv`.
279    ///
280    /// See [ts_in_delta](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-in-delta).
281    #[pyo3(get, set)]
282    pub ts_in_delta: i32,
283    /// The message sequence number assigned at the venue.
284    #[pyo3(get, set)]
285    pub sequence: u32,
286}
287
288/// Market by price implementation with a known book depth of 1. The record of the
289/// [`Mbp1`](crate::enums::Schema::Mbp1) schema.
290#[repr(C)]
291#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
292#[cfg_attr(feature = "trivial_copy", derive(Copy))]
293#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
294#[cfg_attr(
295    feature = "python",
296    pyo3::pyclass(dict, eq, module = "databento_dbn", name = "MBP1Msg"),
297    derive(crate::macros::PyFieldDesc)
298)]
299#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
300#[cfg_attr(test, derive(type_layout::TypeLayout))]
301#[dbn_record(rtype::MBP_1)]
302pub struct Mbp1Msg {
303    /// The common header.
304    #[pyo3(get)]
305    pub hd: RecordHeader,
306    /// The order price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
307    /// 0.000000001.
308    ///
309    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
310    #[dbn(fixed_price)]
311    #[pyo3(get, set)]
312    pub price: i64,
313    /// The order quantity.
314    #[pyo3(get, set)]
315    pub size: u32,
316    /// The event action. Can be **A**dd, **C**ancel, **M**odify, clea**R** book, or **T**rade.
317    ///
318    /// See [Action](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#action).
319    #[dbn(c_char, encode_order(2))]
320    pub action: c_char,
321    /// The side that initiates the event. Can be **A**sk for a sell order
322    /// (or sell aggressor in a trade), **B**id for a buy order (or buy aggressor in a trade),
323    /// or **N**one where no side is specified.
324    ///
325    /// See [Side](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#side).
326    #[dbn(c_char, encode_order(3))]
327    pub side: c_char,
328    /// A bit field indicating event end, message characteristics, and data quality. See
329    /// [`enums::flags`](crate::enums::flags) for possible values.
330    #[pyo3(get, set)]
331    pub flags: FlagSet,
332    /// The book level where the update event occurred.
333    #[dbn(encode_order(4))]
334    #[pyo3(get, set)]
335    pub depth: u8,
336    /// The capture-server-received timestamp expressed as the number of nanoseconds
337    /// since the UNIX epoch.
338    ///
339    /// See [ts_recv](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-recv).
340    #[dbn(encode_order(0), index_ts, unix_nanos)]
341    #[pyo3(get, set)]
342    pub ts_recv: u64,
343    /// The matching-engine-sending timestamp expressed as the number of nanoseconds before
344    /// `ts_recv`.
345    ///
346    /// See [ts_in_delta](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-in-delta).
347    #[pyo3(get, set)]
348    pub ts_in_delta: i32,
349    /// The message sequence number assigned at the venue.
350    #[pyo3(get, set)]
351    pub sequence: u32,
352    /// The top of the order book.
353    #[pyo3(get, set)]
354    pub levels: [BidAskPair; 1],
355}
356
357/// Market by price implementation with a known book depth of 10. The record of the
358/// [`Mbp10`](crate::enums::Schema::Mbp10) schema.
359#[repr(C)]
360#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
361#[cfg_attr(feature = "trivial_copy", derive(Copy))]
362#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
363#[cfg_attr(
364    feature = "python",
365    pyo3::pyclass(dict, eq, module = "databento_dbn", name = "MBP10Msg"),
366    derive(crate::macros::PyFieldDesc)
367)]
368#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
369#[cfg_attr(test, derive(type_layout::TypeLayout))]
370#[dbn_record(rtype::MBP_10)]
371pub struct Mbp10Msg {
372    /// The common header.
373    #[pyo3(get)]
374    pub hd: RecordHeader,
375    /// The order price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
376    /// 0.000000001.
377    ///
378    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
379    #[dbn(fixed_price)]
380    #[pyo3(get, set)]
381    pub price: i64,
382    /// The order quantity.
383    #[pyo3(get, set)]
384    pub size: u32,
385    /// The event action. Can be **A**dd, **C**ancel, **M**odify, clea**R** book, or **T**rade.
386    ///
387    /// See [Action](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#action).
388    #[dbn(c_char, encode_order(2))]
389    pub action: c_char,
390    /// The side that initiates the event. Can be **A**sk for a sell order
391    /// (or sell aggressor in a trade), **B**id for a buy order (or buy aggressor in a trade),
392    /// or **N**one where no side is specified.
393    ///
394    /// See [Side](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#side).
395    #[dbn(c_char, encode_order(3))]
396    pub side: c_char,
397    /// A bit field indicating event end, message characteristics, and data quality. See
398    /// [`enums::flags`](crate::enums::flags) for possible values.
399    #[pyo3(get, set)]
400    pub flags: FlagSet,
401    /// The book level where the update event occurred.
402    #[dbn(encode_order(4))]
403    #[pyo3(get, set)]
404    pub depth: u8,
405    /// The capture-server-received timestamp expressed as the number of nanoseconds
406    /// since the UNIX epoch.
407    ///
408    /// See [ts_recv](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-recv).
409    #[dbn(encode_order(0), index_ts, unix_nanos)]
410    #[pyo3(get, set)]
411    pub ts_recv: u64,
412    /// The matching-engine-sending timestamp expressed as the number of nanoseconds before
413    /// `ts_recv`.
414    ///
415    /// See [ts_in_delta](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-in-delta).
416    #[pyo3(get, set)]
417    pub ts_in_delta: i32,
418    /// The message sequence number assigned at the venue.
419    #[pyo3(get, set)]
420    pub sequence: u32,
421    /// The top 10 levels of the order book.
422    #[pyo3(get, set)]
423    pub levels: [BidAskPair; 10],
424}
425
426/// Subsampled market by price with a known book depth of 1. The record of the
427/// [`Bbo1S`](crate::Schema::Bbo1S) and [`Bbo1M`](crate::Schema::Bbo1M) schemas.
428#[repr(C)]
429#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
430#[cfg_attr(feature = "trivial_copy", derive(Copy))]
431#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
432#[cfg_attr(
433    feature = "python",
434    pyo3::pyclass(dict, eq, module = "databento_dbn", name = "BBOMsg"),
435    derive(crate::macros::PyFieldDesc)
436)]
437#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
438#[cfg_attr(test, derive(type_layout::TypeLayout))]
439#[dbn_record(rtype::BBO_1S, rtype::BBO_1M)]
440pub struct BboMsg {
441    /// The common header.
442    #[pyo3(get)]
443    pub hd: RecordHeader,
444    /// The last trade price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
445    /// 0.000000001. Will be [`UNDEF_PRICE`](crate::UNDEF_PRICE) if there was no last trade in
446    /// the session.
447    ///
448    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
449    #[dbn(fixed_price)]
450    #[pyo3(get, set)]
451    pub price: i64,
452    /// The last trade quantity.
453    #[pyo3(get, set)]
454    pub size: u32,
455    // Reserved for later usage.
456    #[doc(hidden)]
457    #[cfg_attr(feature = "serde", serde(skip))]
458    pub _reserved1: u8,
459    /// The side that initiated the last trade. Can be **A**sk for a sell aggressor, **B**id
460    /// for a buy aggressor, or **N**one where no side is specified.
461    ///
462    /// See [Side](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#side).
463    #[dbn(c_char, encode_order(2))]
464    pub side: c_char,
465    /// A bit field indicating event end, message characteristics, and data quality. See
466    /// [`enums::flags`](crate::enums::flags) for possible values.
467    #[pyo3(get, set)]
468    pub flags: FlagSet,
469    // Reserved for later usage.
470    #[doc(hidden)]
471    #[cfg_attr(feature = "serde", serde(skip))]
472    pub _reserved2: u8,
473    /// The end timestamp of the interval, clamped to the second/minute boundary, expressed
474    /// as the number of nanoseconds since the UNIX epoch.
475    #[dbn(encode_order(0), index_ts, unix_nanos)]
476    #[pyo3(get, set)]
477    pub ts_recv: u64,
478    // Reserved for later usage.
479    #[doc(hidden)]
480    #[cfg_attr(feature = "serde", serde(skip))]
481    pub _reserved3: [u8; 4],
482    /// The sequence number assigned at the venue of the last update.
483    #[pyo3(get, set)]
484    pub sequence: u32,
485    /// The top of the order book.
486    #[pyo3(get, set)]
487    pub levels: [BidAskPair; 1],
488}
489
490/// Consolidated market by price implementation with a known book depth of 1. The record of the
491/// [`Cmbp1`](crate::Schema::Cmbp1) schema.
492#[repr(C)]
493#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
494#[cfg_attr(feature = "trivial_copy", derive(Copy))]
495#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
496#[cfg_attr(
497    feature = "python",
498    pyo3::pyclass(dict, eq, module = "databento_dbn", name = "CMBP1Msg"),
499    derive(crate::macros::PyFieldDesc)
500)]
501#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
502#[cfg_attr(test, derive(type_layout::TypeLayout))]
503#[dbn_record(rtype::CMBP1, rtype::TCBBO)]
504pub struct Cmbp1Msg {
505    /// The common header.
506    #[pyo3(get)]
507    pub hd: RecordHeader,
508    /// The order price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
509    /// 0.000000001.
510    ///
511    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
512    #[dbn(fixed_price)]
513    #[pyo3(get, set)]
514    pub price: i64,
515    /// The order quantity.
516    #[pyo3(get, set)]
517    pub size: u32,
518    /// The event action. Can be **A**dd, **C**ancel, **M**odify, clea**R** book, or
519    /// **T**rade.
520    ///
521    /// See [Action](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#action).
522    #[dbn(c_char, encode_order(2))]
523    pub action: c_char,
524    /// The side that initiates the event. Can be **A**sk for a sell order
525    /// (or sell aggressor in a trade), **B**id for a buy order (or buy aggressor in a trade),
526    /// or **N**one where no side is specified.
527    ///
528    /// See [Side](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#side).
529    #[dbn(c_char, encode_order(3))]
530    pub side: c_char,
531    /// A bit field indicating event end, message characteristics, and data quality. See
532    /// [`enums::flags`](crate::enums::flags) for possible values.
533    #[pyo3(get, set)]
534    pub flags: FlagSet,
535    // Reserved for future usage.
536    #[doc(hidden)]
537    #[cfg_attr(feature = "serde", serde(skip))]
538    pub _reserved1: [c_char; 1],
539    /// The capture-server-received timestamp expressed as the number of nanoseconds
540    /// since the UNIX epoch.
541    ///
542    /// See [ts_recv](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-recv).
543    #[dbn(encode_order(0), index_ts, unix_nanos)]
544    #[pyo3(get, set)]
545    pub ts_recv: u64,
546    /// The matching-engine-sending timestamp expressed as the number of nanoseconds before
547    /// `ts_recv`.
548    ///
549    /// See [ts_in_delta](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-in-delta).
550    #[pyo3(get, set)]
551    pub ts_in_delta: i32,
552    #[doc(hidden)]
553    #[cfg_attr(feature = "serde", serde(skip))]
554    pub _reserved2: [c_char; 4],
555    /// The top of the order book.
556    #[pyo3(get, set)]
557    pub levels: [ConsolidatedBidAskPair; 1],
558}
559
560/// Subsampled consolidated market by price with a known book depth of 1. The record of the
561/// [`Cbbo1S`](crate::Schema::Cbbo1S) and [`Cbbo1M`](crate::Schema::Cbbo1M) schemas.
562#[repr(C)]
563#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
564#[cfg_attr(feature = "trivial_copy", derive(Copy))]
565#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
566#[cfg_attr(
567    feature = "python",
568    pyo3::pyclass(dict, eq, module = "databento_dbn", name = "CBBOMsg"),
569    derive(crate::macros::PyFieldDesc)
570)]
571#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
572#[cfg_attr(test, derive(type_layout::TypeLayout))]
573#[dbn_record(rtype::CBBO_1S, rtype::CBBO_1M)]
574pub struct CbboMsg {
575    /// The common header.
576    #[pyo3(get)]
577    pub hd: RecordHeader,
578    /// The last trade price where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
579    /// 0.000000001. Will be [`UNDEF_PRICE`](crate::UNDEF_PRICE) if there was no last trade in
580    /// the session.
581    ///
582    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
583    #[dbn(fixed_price)]
584    #[pyo3(get, set)]
585    pub price: i64,
586    /// The quantity of the last trade.
587    #[pyo3(get, set)]
588    pub size: u32,
589    // Reserved for later usage.
590    #[doc(hidden)]
591    #[cfg_attr(feature = "serde", serde(skip))]
592    pub _reserved1: u8,
593    /// The side that initiated the last trade. Can be **A**sk for a sell aggressor, **B**id
594    /// for a buy aggressor, or **N**one where no side is specified.
595    ///
596    /// See [Side](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#side).
597    #[dbn(c_char, encode_order(2))]
598    pub side: c_char,
599    /// A bit field indicating event end, message characteristics, and data quality. See
600    /// [`enums::flags`](crate::enums::flags) for possible values.
601    #[pyo3(get, set)]
602    pub flags: FlagSet,
603    // Reserved for later usage.
604    #[doc(hidden)]
605    #[cfg_attr(feature = "serde", serde(skip))]
606    pub _reserved2: u8,
607    /// The end timestamp of the interval, clamped to the second/minute boundary, expressed
608    /// as the number of nanoseconds since the UNIX epoch.
609    #[dbn(encode_order(0), index_ts, unix_nanos)]
610    #[pyo3(get, set)]
611    pub ts_recv: u64,
612    // Reserved for later usage.
613    #[doc(hidden)]
614    #[cfg_attr(feature = "serde", serde(skip))]
615    pub _reserved3: [u8; 8],
616    /// The top of the order book.
617    #[pyo3(get, set)]
618    pub levels: [ConsolidatedBidAskPair; 1],
619}
620
621/// The record of the [`Tbbo`](crate::enums::Schema::Tbbo) schema.
622pub type TbboMsg = Mbp1Msg;
623/// The record of the [`Bbo1S`](crate::enums::Schema::Bbo1S) schema.
624pub type Bbo1SMsg = BboMsg;
625/// The record of the [`Bbo1M`](crate::enums::Schema::Bbo1M) schema.
626pub type Bbo1MMsg = BboMsg;
627
628/// The record of the [`Tcbbo`](crate::enums::Schema::Tcbbo) schema.
629pub type TcbboMsg = Cmbp1Msg;
630/// The record of the [`Cbbo1S`](crate::enums::Schema::Cbbo1S) schema.
631pub type Cbbo1SMsg = CbboMsg;
632/// The record of the [`Cbbo1M`](crate::enums::Schema::Cbbo1M) schema.
633pub type Cbbo1MMsg = CbboMsg;
634
635/// Open, high, low, close, and volume. The record of the following schemas:
636/// - [`Ohlcv1S`](crate::enums::Schema::Ohlcv1S)
637/// - [`Ohlcv1M`](crate::enums::Schema::Ohlcv1M)
638/// - [`Ohlcv1H`](crate::enums::Schema::Ohlcv1H)
639/// - [`Ohlcv1D`](crate::enums::Schema::Ohlcv1D)
640/// - [`OhlcvEod`](crate::enums::Schema::OhlcvEod)
641#[repr(C)]
642#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
643#[cfg_attr(feature = "trivial_copy", derive(Copy))]
644#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
645#[cfg_attr(
646    feature = "python",
647    pyo3::pyclass(dict, eq, get_all, module = "databento_dbn", name = "OHLCVMsg"),
648    derive(crate::macros::PyFieldDesc)
649)]
650#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
651#[cfg_attr(test, derive(type_layout::TypeLayout))]
652#[dbn_record(
653    rtype::OHLCV_1S,
654    rtype::OHLCV_1M,
655    rtype::OHLCV_1H,
656    rtype::OHLCV_1D,
657    rtype::OHLCV_EOD,
658    rtype::OHLCV_DEPRECATED
659)]
660pub struct OhlcvMsg {
661    /// The common header.
662    pub hd: RecordHeader,
663    /// The open price for the bar where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
664    /// 0.000000001.
665    ///
666    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
667    #[dbn(fixed_price)]
668    #[pyo3(set)]
669    pub open: i64,
670    /// The high price for the bar where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
671    /// 0.000000001.
672    ///
673    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
674    #[dbn(fixed_price)]
675    #[pyo3(set)]
676    pub high: i64,
677    /// The low price for the bar where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
678    /// 0.000000001.
679    ///
680    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
681    #[dbn(fixed_price)]
682    #[pyo3(set)]
683    pub low: i64,
684    /// The close price for the bar where every 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or
685    /// 0.000000001.
686    ///
687    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
688    #[dbn(fixed_price)]
689    #[pyo3(set)]
690    pub close: i64,
691    /// The total volume traded during the aggregation period.
692    #[pyo3(set)]
693    pub volume: u64,
694}
695
696/// A trading status update message. The record of the
697/// [`Status`](crate::enums::Schema::Status) schema.
698#[repr(C)]
699#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
700#[cfg_attr(feature = "trivial_copy", derive(Copy))]
701#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
702#[cfg_attr(
703    feature = "python",
704    pyo3::pyclass(dict, eq, module = "databento_dbn"),
705    derive(crate::macros::PyFieldDesc)
706)]
707#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
708#[cfg_attr(test, derive(type_layout::TypeLayout))]
709#[dbn_record(rtype::STATUS)]
710pub struct StatusMsg {
711    /// The common header.
712    #[pyo3(get)]
713    pub hd: RecordHeader,
714    /// The capture-server-received timestamp expressed as the number of nanoseconds
715    /// since the UNIX epoch.
716    ///
717    /// See [ts_recv](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-recv).
718    #[dbn(encode_order(0), index_ts, unix_nanos)]
719    #[pyo3(get, set)]
720    pub ts_recv: u64,
721    /// The type of status change.
722    #[dbn(fmt_method)]
723    #[pyo3(get, set)]
724    pub action: u16,
725    /// Additional details about the cause of the status change.
726    #[dbn(fmt_method)]
727    #[pyo3(get, set)]
728    pub reason: u16,
729    /// Further information about the status change and its effect on trading.
730    #[dbn(fmt_method)]
731    #[pyo3(get, set)]
732    pub trading_event: u16,
733    /// The best-efforts state of trading in the instrument, either `Y`, `N` or `~`.
734    #[dbn(c_char)]
735    #[pyo3(get, set)]
736    pub is_trading: c_char,
737    /// The best-efforts state of quoting in the instrument, either `Y`, `N` or `~`.
738    #[dbn(c_char)]
739    #[pyo3(get, set)]
740    pub is_quoting: c_char,
741    /// The best-efforts state of short sell restrictions for the instrument (if applicable),
742    /// either `Y`, `N`, or `~`.
743    #[dbn(c_char)]
744    #[pyo3(get, set)]
745    pub is_short_sell_restricted: c_char,
746    // Filler for alignment.
747    #[doc(hidden)]
748    #[cfg_attr(feature = "serde", serde(skip))]
749    pub _reserved: [u8; 7],
750}
751
752/// Definition of an instrument. The record of the
753/// [`Definition`](crate::enums::Schema::Definition) schema.
754#[repr(C)]
755#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
756#[cfg_attr(feature = "trivial_copy", derive(Copy))]
757#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
758#[cfg_attr(
759    feature = "python",
760    pyo3::pyclass(dict, eq, module = "databento_dbn"),
761    derive(crate::macros::PyFieldDesc)
762)]
763#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
764#[cfg_attr(test, derive(type_layout::TypeLayout))]
765#[dbn_record(rtype::INSTRUMENT_DEF)]
766pub struct InstrumentDefMsg {
767    /// The common header.
768    #[pyo3(get, set)]
769    pub hd: RecordHeader,
770    /// The capture-server-received timestamp expressed as the number of nanoseconds
771    /// since the UNIX epoch.
772    ///
773    /// See [ts_recv](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-recv).
774    #[dbn(encode_order(0), index_ts, unix_nanos)]
775    #[pyo3(get, set)]
776    pub ts_recv: u64,
777    /// The minimum constant tick for the instrument where every 1 unit corresponds to 1e-9, i.e.
778    /// 1/1,000,000,000 or 0.000000001.
779    ///
780    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
781    #[dbn(fixed_price)]
782    #[pyo3(get, set)]
783    pub min_price_increment: i64,
784    /// The multiplier to convert the venue’s display price to the conventional price where every
785    /// 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or 0.000000001.
786    #[dbn(fixed_price)]
787    #[pyo3(get, set)]
788    pub display_factor: i64,
789    /// The last eligible trade time expressed as the number of nanoseconds since the
790    /// UNIX epoch.
791    ///
792    /// Will be [`crate::UNDEF_TIMESTAMP`] when null, such as for equities. Some publishers
793    /// only provide date-level granularity.
794    #[dbn(unix_nanos)]
795    #[pyo3(get, set)]
796    pub expiration: u64,
797    /// The time of instrument activation expressed as the number of nanoseconds since the
798    /// UNIX epoch.
799    ///
800    /// Will be [`crate::UNDEF_TIMESTAMP`] when null, such as for equities. Some publishers
801    /// only provide date-level granularity.
802    #[dbn(unix_nanos)]
803    #[pyo3(get, set)]
804    pub activation: u64,
805    /// The allowable high limit price for the trading day where every 1 unit corresponds to
806    /// 1e-9, i.e. 1/1,000,000,000 or 0.000000001.
807    ///
808    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
809    #[dbn(fixed_price)]
810    #[pyo3(get, set)]
811    pub high_limit_price: i64,
812    /// The allowable low limit price for the trading day where every 1 unit corresponds to
813    /// 1e-9, i.e. 1/1,000,000,000 or 0.000000001.
814    ///
815    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
816    #[dbn(fixed_price)]
817    #[pyo3(get, set)]
818    pub low_limit_price: i64,
819    /// The differential value for price banding where every 1 unit corresponds to 1e-9,
820    /// i.e. 1/1,000,000,000 or 0.000000001.
821    ///
822    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
823    #[dbn(fixed_price)]
824    #[pyo3(get, set)]
825    pub max_price_variation: i64,
826    /// The contract size for each instrument, in combination with `unit_of_measure`, where every
827    /// 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or 0.000000001.
828    #[dbn(fixed_price)]
829    #[pyo3(get, set)]
830    pub unit_of_measure_qty: i64,
831    /// The value currently under development by the venue where every 1 unit corresponds to 1e-9,
832    /// i.e. 1/1,000,000,000 or 0.000000001.
833    ///
834    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
835    #[dbn(fixed_price)]
836    #[pyo3(get, set)]
837    pub min_price_increment_amount: i64,
838    /// The value used for price calculation in spread and leg pricing where every 1 unit
839    /// corresponds to 1e-9, i.e. 1/1,000,000,000 or 0.000000001.
840    #[dbn(fixed_price)]
841    #[pyo3(get, set)]
842    pub price_ratio: i64,
843    /// The strike price of the option where every 1 unit corresponds to 1e-9, i.e.
844    /// 1/1,000,000,000 or 0.000000001.
845    ///
846    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
847    #[dbn(fixed_price, encode_order(54))]
848    #[pyo3(get, set)]
849    pub strike_price: i64,
850    /// The instrument ID assigned by the publisher. May be the same as `instrument_id`.
851    ///
852    /// See [Instrument identifiers](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#instrument-identifiers)
853    #[dbn(encode_order(20))]
854    #[pyo3(get, set)]
855    pub raw_instrument_id: u64,
856    /// The tied price (if any) of the leg.
857    #[dbn(fixed_price, encode_order(165))]
858    #[pyo3(get, set)]
859    pub leg_price: i64,
860    /// The associated delta (if any) of the leg.
861    #[dbn(fixed_price, encode_order(166))]
862    #[pyo3(get, set)]
863    pub leg_delta: i64,
864    /// A bitmap of instrument eligibility attributes.
865    #[dbn(fmt_binary)]
866    #[pyo3(get, set)]
867    pub inst_attrib_value: i32,
868    /// The `instrument_id` of the first underlying instrument.
869    ///
870    /// See [Instrument identifiers](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#instrument-identifiers)
871    #[pyo3(get, set)]
872    pub underlying_id: u32,
873    /// The implied book depth on the price level data feed.
874    #[pyo3(get, set)]
875    pub market_depth_implied: i32,
876    /// The (outright) book depth on the price level data feed.
877    #[pyo3(get, set)]
878    pub market_depth: i32,
879    /// The market segment of the instrument.
880    #[pyo3(get, set)]
881    pub market_segment_id: u32,
882    /// The maximum trading volume for the instrument.
883    #[pyo3(get, set)]
884    pub max_trade_vol: u32,
885    /// The minimum order entry quantity for the instrument.
886    #[pyo3(get, set)]
887    pub min_lot_size: i32,
888    /// The minimum quantity required for a block trade of the instrument.
889    #[pyo3(get, set)]
890    pub min_lot_size_block: i32,
891    /// The minimum quantity required for a round lot of the instrument. Multiples of
892    /// this quantity are also round lots.
893    #[pyo3(get, set)]
894    pub min_lot_size_round_lot: i32,
895    /// The minimum trading volume for the instrument.
896    #[pyo3(get, set)]
897    pub min_trade_vol: u32,
898    /// The number of deliverables per instrument, i.e. peak days.
899    #[pyo3(get, set)]
900    pub contract_multiplier: i32,
901    /// The quantity that a contract will decay daily, after `decay_start_date` has
902    /// been reached.
903    #[pyo3(get, set)]
904    pub decay_quantity: i32,
905    /// The fixed contract value assigned to each instrument.
906    #[pyo3(get, set)]
907    pub original_contract_size: i32,
908    /// The numeric ID assigned to the leg instrument.
909    ///
910    /// See [Instrument identifiers](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#instrument-identifiers)
911    #[dbn(encode_order(160))]
912    #[pyo3(get, set)]
913    pub leg_instrument_id: u32,
914    /// The numerator of the price ratio of the leg within the spread.
915    #[dbn(encode_order(167))]
916    #[pyo3(get, set)]
917    pub leg_ratio_price_numerator: i32,
918    /// The denominator of the price ratio of the leg within the spread.
919    #[dbn(encode_order(168))]
920    #[pyo3(get, set)]
921    pub leg_ratio_price_denominator: i32,
922    /// The numerator of the quantity ratio of the leg within the spread.
923    #[dbn(encode_order(169))]
924    #[pyo3(get, set)]
925    pub leg_ratio_qty_numerator: i32,
926    /// The denominator of the quantity ratio of the leg within the spread.
927    #[dbn(encode_order(170))]
928    #[pyo3(get, set)]
929    pub leg_ratio_qty_denominator: i32,
930    /// The numeric ID of the leg instrument's underlying instrument.
931    ///
932    /// See [Instrument identifiers](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#instrument-identifiers)
933    #[dbn(encode_order(171))]
934    #[pyo3(get, set)]
935    pub leg_underlying_id: u32,
936    /// The channel ID assigned at the venue.
937    #[pyo3(get, set)]
938    pub appl_id: i16,
939    /// The calendar year reflected in the instrument symbol.
940    #[pyo3(get, set)]
941    pub maturity_year: u16,
942    /// The date at which a contract will begin to decay.
943    #[pyo3(get, set)]
944    pub decay_start_date: u16,
945    /// The channel ID assigned by Databento as an incrementing integer starting at
946    /// zero.
947    #[pyo3(get, set)]
948    pub channel_id: u16,
949    /// The number of legs in the strategy or spread. Will be 0 for outrights.
950    #[dbn(encode_order(158))]
951    #[pyo3(get, set)]
952    pub leg_count: u16,
953    /// The 0-based index of the leg.
954    #[dbn(encode_order(159))]
955    #[pyo3(get, set)]
956    pub leg_index: u16,
957    /// The currency used for price fields.
958    #[dbn(fmt_method)]
959    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
960    pub currency: [c_char; 4],
961    /// The currency used for settlement, if different from `currency`.
962    #[dbn(fmt_method)]
963    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
964    pub settl_currency: [c_char; 4],
965    /// The strategy type of the spread.
966    #[dbn(fmt_method)]
967    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
968    pub secsubtype: [c_char; 6],
969    /// The instrument raw symbol assigned by the publisher.
970    #[dbn(encode_order(2), fmt_method)]
971    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
972    pub raw_symbol: [c_char; SYMBOL_CSTR_LEN],
973    /// The security group code of the instrument.
974    #[dbn(fmt_method)]
975    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
976    pub group: [c_char; 21],
977    /// The exchange used to identify the instrument.
978    #[dbn(fmt_method)]
979    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
980    pub exchange: [c_char; 5],
981    /// The underlying asset code (product code) of the instrument.
982    #[dbn(fmt_method)]
983    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
984    pub asset: [c_char; ASSET_CSTR_LEN],
985    /// The ISO standard instrument categorization code.
986    #[dbn(fmt_method)]
987    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
988    pub cfi: [c_char; 7],
989    /// The security type of the instrument, e.g. FUT for future or future spread.
990    ///
991    /// See [Security type](https://databento.com/docs/schemas-and-data-formats/instrument-definitions#security-type).
992    #[dbn(fmt_method)]
993    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
994    pub security_type: [c_char; 7],
995    /// The unit of measure for the instrument’s original contract size, e.g. USD or LBS.
996    #[dbn(fmt_method)]
997    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
998    pub unit_of_measure: [c_char; 31],
999    /// The symbol of the first underlying instrument.
1000    #[dbn(fmt_method)]
1001    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
1002    pub underlying: [c_char; 21],
1003    /// The currency of [`strike_price`](Self::strike_price).
1004    #[dbn(fmt_method)]
1005    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
1006    pub strike_price_currency: [c_char; 4],
1007    /// The leg instrument's raw symbol assigned by the publisher.
1008    #[dbn(encode_order(161), fmt_method)]
1009    #[cfg_attr(feature = "serde", serde(with = "crate::record::cstr_serde"))]
1010    #[pyo3(get)]
1011    pub leg_raw_symbol: [c_char; SYMBOL_CSTR_LEN],
1012    /// The classification of the instrument.
1013    ///
1014    /// See [Instrument class](https://databento.com/docs/schemas-and-data-formats/instrument-definitions#instrument-class).
1015    #[dbn(c_char, encode_order(4))]
1016    #[pyo3(set)]
1017    pub instrument_class: c_char,
1018    /// The matching algorithm used for the instrument, typically **F**IFO.
1019    ///
1020    /// See [Matching algorithm](https://databento.com/docs/schemas-and-data-formats/instrument-definitions#matching-algorithm).
1021    #[dbn(c_char)]
1022    #[pyo3(set)]
1023    pub match_algorithm: c_char,
1024    /// The price denominator of the main fraction.
1025    #[pyo3(get, set)]
1026    pub main_fraction: u8,
1027    /// The number of digits to the right of the tick mark, to display fractional prices.
1028    #[pyo3(get, set)]
1029    pub price_display_format: u8,
1030    /// The price denominator of the sub fraction.
1031    #[pyo3(get, set)]
1032    pub sub_fraction: u8,
1033    /// The product complex of the instrument.
1034    #[pyo3(get, set)]
1035    pub underlying_product: u8,
1036    /// Indicates if the instrument definition has been added, modified, or deleted.
1037    #[dbn(c_char, encode_order(3))]
1038    pub security_update_action: c_char,
1039    /// The calendar month reflected in the instrument symbol.
1040    #[pyo3(get, set)]
1041    pub maturity_month: u8,
1042    /// The calendar day reflected in the instrument symbol, or 0.
1043    #[pyo3(get, set)]
1044    pub maturity_day: u8,
1045    /// The calendar week reflected in the instrument symbol, or 0.
1046    #[pyo3(get, set)]
1047    pub maturity_week: u8,
1048    /// Indicates if the instrument is user defined: **Y**es or **N**o.
1049    #[dbn(c_char)]
1050    pub user_defined_instrument: c_char,
1051    /// The type of `contract_multiplier`. Either `1` for hours, or `2` for days.
1052    #[pyo3(get, set)]
1053    pub contract_multiplier_unit: i8,
1054    /// The schedule for delivering electricity.
1055    #[pyo3(get, set)]
1056    pub flow_schedule_type: i8,
1057    /// The tick rule of the spread.
1058    #[pyo3(get, set)]
1059    pub tick_rule: u8,
1060    /// The classification of the leg instrument.
1061    #[dbn(c_char, encode_order(163))]
1062    pub leg_instrument_class: c_char,
1063    /// The side taken for the leg when purchasing the spread.
1064    #[dbn(c_char, encode_order(164))]
1065    pub leg_side: c_char,
1066    // Filler for alignment.
1067    #[doc(hidden)]
1068    #[cfg_attr(feature = "serde", serde(skip))]
1069    pub _reserved: [u8; 17],
1070}
1071
1072/// An auction imbalance message.
1073#[repr(C)]
1074#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
1075#[cfg_attr(feature = "trivial_copy", derive(Copy))]
1076#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1077#[cfg_attr(
1078    feature = "python",
1079    pyo3::pyclass(dict, eq, module = "databento_dbn"),
1080    derive(crate::macros::PyFieldDesc)
1081)]
1082#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
1083#[cfg_attr(test, derive(type_layout::TypeLayout))]
1084#[dbn_record(rtype::IMBALANCE)]
1085pub struct ImbalanceMsg {
1086    /// The common header.
1087    #[pyo3(get)]
1088    pub hd: RecordHeader,
1089    /// The capture-server-received timestamp expressed as the number of nanoseconds
1090    /// since the UNIX epoch.
1091    ///
1092    /// See [ts_recv](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-recv).
1093    #[dbn(encode_order(0), index_ts, unix_nanos)]
1094    #[pyo3(get, set)]
1095    pub ts_recv: u64,
1096    /// The price at which the imbalance shares are calculated, where every 1 unit corresponds
1097    /// to 1e-9, i.e. 1/1,000,000,000 or 0.000000001.
1098    ///
1099    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
1100    #[dbn(fixed_price)]
1101    #[pyo3(get, set)]
1102    pub ref_price: i64,
1103    /// Reserved for future use.
1104    #[pyo3(get, set)]
1105    pub auction_time: u64,
1106    /// The hypothetical auction-clearing price for both cross and continuous orders where every
1107    /// 1 unit corresponds to 1e-9, i.e. 1/1,000,000,000 or 0.000000001.
1108    ///
1109    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
1110    #[dbn(fixed_price)]
1111    #[pyo3(get, set)]
1112    pub cont_book_clr_price: i64,
1113    /// The hypothetical auction-clearing price for cross orders only where every 1 unit corresponds
1114    /// to 1e-9, i.e. 1/1,000,000,000 or 0.000000001.
1115    ///
1116    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices).
1117    #[dbn(fixed_price)]
1118    #[pyo3(get, set)]
1119    pub auct_interest_clr_price: i64,
1120    /// Reserved for future use.
1121    #[dbn(fixed_price)]
1122    #[pyo3(get, set)]
1123    pub ssr_filling_price: i64,
1124    /// Reserved for future use.
1125    #[dbn(fixed_price)]
1126    #[pyo3(get, set)]
1127    pub ind_match_price: i64,
1128    /// Reserved for future use.
1129    #[dbn(fixed_price)]
1130    #[pyo3(get, set)]
1131    pub upper_collar: i64,
1132    /// Reserved for future use.
1133    #[dbn(fixed_price)]
1134    #[pyo3(get, set)]
1135    pub lower_collar: i64,
1136    /// The quantity of shares that are eligible to be matched at `ref_price`.
1137    #[pyo3(get, set)]
1138    pub paired_qty: u32,
1139    /// The quantity of shares that are not paired at `ref_price`.
1140    #[pyo3(get, set)]
1141    pub total_imbalance_qty: u32,
1142    /// Reserved for future use.
1143    #[pyo3(get, set)]
1144    pub market_imbalance_qty: u32,
1145    /// Reserved for future use.
1146    #[pyo3(get, set)]
1147    pub unpaired_qty: u32,
1148    /// Venue-specific character code indicating the auction type.
1149    #[dbn(c_char)]
1150    pub auction_type: c_char,
1151    /// The market side of the `total_imbalance_qty`. Can be **A**sk, **B**id, or **N**one.
1152    ///
1153    /// See [Side](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#side).
1154    #[dbn(c_char)]
1155    pub side: c_char,
1156    /// Reserved for future use.
1157    #[pyo3(get, set)]
1158    pub auction_status: u8,
1159    /// Reserved for future use.
1160    #[pyo3(get, set)]
1161    pub freeze_status: u8,
1162    /// Reserved for future use.
1163    #[pyo3(get, set)]
1164    pub num_extensions: u8,
1165    /// Reserved for future use.
1166    #[dbn(c_char)]
1167    pub unpaired_side: c_char,
1168    /// Venue-specific character code. For Nasdaq, contains the raw Price Variation Indicator.
1169    #[dbn(c_char)]
1170    pub significant_imbalance: c_char,
1171    // Filler for alignment.
1172    #[doc(hidden)]
1173    #[cfg_attr(feature = "serde", serde(skip))]
1174    pub _reserved: [u8; 1],
1175}
1176
1177/// A statistics message. A catchall for various data disseminated by publishers. The
1178/// [`stat_type`](Self::stat_type) indicates the statistic contained in the message.
1179#[repr(C)]
1180#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
1181#[cfg_attr(feature = "trivial_copy", derive(Copy))]
1182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1183#[cfg_attr(
1184    feature = "python",
1185    pyo3::pyclass(dict, eq, get_all, module = "databento_dbn"),
1186    derive(crate::macros::PyFieldDesc)
1187)]
1188#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
1189#[cfg_attr(test, derive(type_layout::TypeLayout))]
1190#[dbn_record(rtype::STATISTICS)]
1191pub struct StatMsg {
1192    /// The common header.
1193    pub hd: RecordHeader,
1194    /// The capture-server-received timestamp expressed as the number of nanoseconds
1195    /// since the UNIX epoch.
1196    ///
1197    /// See [ts_recv](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-recv).
1198    #[dbn(encode_order(0), index_ts, unix_nanos)]
1199    #[pyo3(set)]
1200    pub ts_recv: u64,
1201    /// The reference timestamp of the statistic value expressed as the number of
1202    /// nanoseconds since the UNIX epoch. Will be [`crate::UNDEF_TIMESTAMP`] when
1203    /// unused.
1204    #[dbn(unix_nanos)]
1205    #[pyo3(set)]
1206    pub ts_ref: u64,
1207    /// The value for price statistics where every 1 unit corresponds to 1e-9, i.e.
1208    /// 1/1,000,000,000 or 0.000000001. Will be [`UNDEF_PRICE`](crate::UNDEF_PRICE)
1209    /// when unused.
1210    ///
1211    /// See [Prices](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#prices)
1212    #[dbn(fixed_price)]
1213    #[pyo3(set)]
1214    pub price: i64,
1215    /// The value for non-price statistics. Will be [`crate::UNDEF_STAT_QUANTITY`] when
1216    /// unused.
1217    #[pyo3(set)]
1218    pub quantity: i64,
1219    /// The message sequence number assigned at the venue.
1220    #[pyo3(set)]
1221    pub sequence: u32,
1222    /// The matching-engine-sending timestamp expressed as the number of nanoseconds
1223    /// before `ts_recv`.
1224    ///
1225    /// See [ts_in_delta](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-in-delta).
1226    #[pyo3(set)]
1227    pub ts_in_delta: i32,
1228    /// The type of statistic value contained in the message. Refer to the
1229    /// [`StatType`](crate::enums::StatType) enum for possible variants.
1230    #[dbn(fmt_method)]
1231    #[pyo3(set)]
1232    pub stat_type: u16,
1233    /// The channel ID assigned by Databento as an incrementing integer starting at
1234    /// zero.
1235    #[pyo3(set)]
1236    pub channel_id: u16,
1237    /// Indicates if the statistic is newly added (1) or deleted (2). (Deleted is only
1238    /// used with some stat types)
1239    #[dbn(fmt_method)]
1240    #[pyo3(set)]
1241    pub update_action: u8,
1242    /// Additional flags associate with certain stat types.
1243    #[dbn(fmt_binary)]
1244    #[pyo3(set)]
1245    pub stat_flags: u8,
1246    // Filler for alignment
1247    #[doc(hidden)]
1248    #[cfg_attr(feature = "serde", serde(skip))]
1249    pub _reserved: [u8; 18],
1250}
1251
1252/// An error message from the Databento Live Subscription Gateway (LSG).
1253#[repr(C)]
1254#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
1255#[cfg_attr(feature = "trivial_copy", derive(Copy))]
1256#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1257#[cfg_attr(
1258    feature = "python",
1259    pyo3::pyclass(dict, eq, module = "databento_dbn"),
1260    derive(crate::macros::PyFieldDesc)
1261)]
1262#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
1263#[cfg_attr(test, derive(type_layout::TypeLayout))]
1264#[dbn_record(rtype::ERROR)]
1265pub struct ErrorMsg {
1266    /// The common header.
1267    #[pyo3(get)]
1268    pub hd: RecordHeader,
1269    /// The error message.
1270    #[dbn(fmt_method)]
1271    #[cfg_attr(feature = "serde", serde(with = "conv::cstr_serde"))]
1272    pub err: [c_char; 302],
1273    /// The error code. See the [`ErrorCode`](crate::enums::ErrorCode) enum
1274    /// for possible values.
1275    #[pyo3(get, set)]
1276    pub code: u8,
1277    /// Sometimes multiple errors are sent together. This field will be non-zero for the
1278    /// last error.
1279    #[pyo3(get, set)]
1280    pub is_last: u8,
1281}
1282
1283/// A symbol mapping message which maps a symbol of one [`SType`](crate::enums::SType)
1284/// to another.
1285#[repr(C)]
1286#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
1287#[cfg_attr(feature = "trivial_copy", derive(Copy))]
1288#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1289#[cfg_attr(
1290    feature = "python",
1291    pyo3::pyclass(dict, eq, module = "databento_dbn"),
1292    derive(crate::macros::PyFieldDesc)
1293)]
1294#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
1295#[cfg_attr(test, derive(type_layout::TypeLayout))]
1296#[dbn_record(rtype::SYMBOL_MAPPING)]
1297pub struct SymbolMappingMsg {
1298    /// The common header.
1299    #[pyo3(get, set)]
1300    pub hd: RecordHeader,
1301    // TODO(carter): special serialization to string?
1302    /// The input symbology type of `stype_in_symbol`.
1303    #[dbn(fmt_method)]
1304    #[pyo3(get, set)]
1305    pub stype_in: u8,
1306    /// The input symbol.
1307    #[dbn(fmt_method)]
1308    #[cfg_attr(feature = "serde", serde(with = "conv::cstr_serde"))]
1309    pub stype_in_symbol: [c_char; SYMBOL_CSTR_LEN],
1310    /// The output symbology type of `stype_out_symbol`.
1311    #[dbn(fmt_method)]
1312    #[pyo3(get, set)]
1313    pub stype_out: u8,
1314    /// The output symbol.
1315    #[dbn(fmt_method)]
1316    #[cfg_attr(feature = "serde", serde(with = "conv::cstr_serde"))]
1317    pub stype_out_symbol: [c_char; SYMBOL_CSTR_LEN],
1318    /// The start of the mapping interval expressed as the number of nanoseconds since
1319    /// the UNIX epoch.
1320    #[dbn(unix_nanos)]
1321    #[pyo3(get, set)]
1322    pub start_ts: u64,
1323    /// The end of the mapping interval expressed as the number of nanoseconds since
1324    /// the UNIX epoch.
1325    #[dbn(unix_nanos)]
1326    #[pyo3(get, set)]
1327    pub end_ts: u64,
1328}
1329
1330/// A non-error message from the Databento Live Subscription Gateway (LSG). Also used
1331/// for heartbeating.
1332#[repr(C)]
1333#[derive(Clone, CsvSerialize, JsonSerialize, PartialEq, Eq, Hash)]
1334#[cfg_attr(feature = "trivial_copy", derive(Copy))]
1335#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1336#[cfg_attr(
1337    feature = "python",
1338    pyo3::pyclass(dict, eq, module = "databento_dbn"),
1339    derive(crate::macros::PyFieldDesc)
1340)]
1341#[cfg_attr(not(feature = "python"), derive(MockPyo3))] // bring `pyo3` attribute into scope
1342#[cfg_attr(test, derive(type_layout::TypeLayout))]
1343#[dbn_record(rtype::SYSTEM)]
1344pub struct SystemMsg {
1345    /// The common header.
1346    #[pyo3(get, set)]
1347    pub hd: RecordHeader,
1348    /// The message from the Databento Live Subscription Gateway (LSG).
1349    #[dbn(fmt_method)]
1350    #[cfg_attr(feature = "serde", serde(with = "conv::cstr_serde"))]
1351    pub msg: [c_char; 303],
1352    /// Type of system message. See the [`SystemCode`](crate::enums::SystemCode) enum
1353    /// for possible values.
1354    #[pyo3(get, set)]
1355    pub code: u8,
1356}
1357
1358/// Used for polymorphism around types all beginning with a [`RecordHeader`] where
1359/// `rtype` is the discriminant used for indicating the type of record.
1360pub trait Record: AsRef<[u8]> {
1361    /// Returns a reference to the `RecordHeader` that comes at the beginning of all
1362    /// record types.
1363    fn header(&self) -> &RecordHeader;
1364
1365    /// Returns the size of the record in bytes.
1366    fn record_size(&self) -> usize {
1367        self.header().record_size()
1368    }
1369
1370    /// Tries to convert the raw record type into an enum which is useful for exhaustive
1371    /// pattern matching.
1372    ///
1373    /// # Errors
1374    /// This function returns an error if the `rtype` field does not
1375    /// contain a valid, known [`RType`].
1376    fn rtype(&self) -> crate::Result<RType> {
1377        self.header().rtype()
1378    }
1379
1380    /// Tries to convert the raw `publisher_id` into an enum which is useful for
1381    /// exhaustive pattern matching.
1382    ///
1383    /// # Errors
1384    /// This function returns an error if the `publisher_id` does not correspond with
1385    /// any known [`Publisher`].
1386    fn publisher(&self) -> crate::Result<Publisher> {
1387        self.header().publisher()
1388    }
1389
1390    /// Returns the raw primary timestamp for the record.
1391    ///
1392    /// This timestamp should be used for sorting records as well as indexing into any
1393    /// symbology data structure.
1394    fn raw_index_ts(&self) -> u64 {
1395        self.header().ts_event
1396    }
1397
1398    /// Returns the primary timestamp for the record. Returns `None` if the primary
1399    /// timestamp contains the sentinel value for a null timestamp.
1400    ///
1401    /// This timestamp should be used for sorting records as well as indexing into any
1402    /// symbology data structure.
1403    fn index_ts(&self) -> Option<time::OffsetDateTime> {
1404        ts_to_dt(self.raw_index_ts())
1405    }
1406
1407    /// Returns the primary date for the record; the date component of the primary
1408    /// timestamp (`index_ts()`). Returns `None` if the primary timestamp contains the
1409    /// sentinel value for a null timestamp.
1410    fn index_date(&self) -> Option<time::Date> {
1411        self.index_ts().map(|dt| dt.date())
1412    }
1413}
1414
1415/// Used for polymorphism around mutable types beginning with a [`RecordHeader`].
1416pub trait RecordMut {
1417    /// Returns a mutable reference to the `RecordHeader` that comes at the beginning of
1418    /// all record types.
1419    fn header_mut(&mut self) -> &mut RecordHeader;
1420}
1421
1422/// An extension of the [`Record`] trait for types with a static [`RType`]. Used for
1423/// determining if a rtype matches a type.
1424pub trait HasRType: Record + RecordMut {
1425    /// Returns `true` if `rtype` matches the value associated with the implementing type.
1426    fn has_rtype(rtype: u8) -> bool;
1427}
1428
1429/// Wrapper object for records that include the live gateway send timestamp (`ts_out`).
1430#[repr(C)]
1431#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1432#[cfg_attr(feature = "trivial_copy", derive(Copy))]
1433#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1434pub struct WithTsOut<T: HasRType> {
1435    /// The inner record.
1436    pub rec: T,
1437    /// The live gateway send timestamp expressed as the number of nanoseconds since the
1438    /// UNIX epoch.
1439    ///
1440    /// See [ts_out](https://databento.com/docs/standards-and-conventions/common-fields-enums-types#ts-out).
1441    pub ts_out: u64,
1442}
1443
1444#[cfg(test)]
1445mod tests {
1446    use mem::offset_of;
1447    use rstest::rstest;
1448    use type_layout::{Field, TypeLayout};
1449
1450    use crate::Schema;
1451    use crate::UNDEF_TIMESTAMP;
1452
1453    use super::*;
1454
1455    const OHLCV_MSG: OhlcvMsg = OhlcvMsg {
1456        hd: RecordHeader {
1457            length: 56,
1458            rtype: rtype::OHLCV_1S,
1459            publisher_id: 1,
1460            instrument_id: 5482,
1461            ts_event: 1609160400000000000,
1462        },
1463        open: 372025000000000,
1464        high: 372050000000000,
1465        low: 372025000000000,
1466        close: 372050000000000,
1467        volume: 57,
1468    };
1469
1470    #[test]
1471    fn test_transmute_record_bytes() {
1472        unsafe {
1473            let ohlcv_bytes = std::slice::from_raw_parts(
1474                &OHLCV_MSG as *const OhlcvMsg as *const u8,
1475                mem::size_of::<OhlcvMsg>(),
1476            )
1477            .to_vec();
1478            let ohlcv = transmute_record_bytes::<OhlcvMsg>(ohlcv_bytes.as_slice()).unwrap();
1479            assert_eq!(*ohlcv, OHLCV_MSG);
1480        };
1481    }
1482
1483    #[test]
1484    #[should_panic]
1485    fn test_transmute_record_bytes_small_buffer() {
1486        let source = OHLCV_MSG;
1487        unsafe {
1488            let slice = std::slice::from_raw_parts(
1489                &source as *const OhlcvMsg as *const u8,
1490                mem::size_of::<OhlcvMsg>() - 5,
1491            );
1492            transmute_record_bytes::<OhlcvMsg>(slice);
1493        };
1494    }
1495
1496    #[test]
1497    fn test_transmute_record() {
1498        let source = Box::new(OHLCV_MSG);
1499        let ohlcv_ref: &OhlcvMsg = unsafe { transmute_record(&source.hd) }.unwrap();
1500        assert_eq!(*ohlcv_ref, OHLCV_MSG);
1501    }
1502
1503    #[test]
1504    fn test_transmute_record_mut() {
1505        let mut source = Box::new(OHLCV_MSG);
1506        let ohlcv_ref: &OhlcvMsg = unsafe { transmute_record_mut(&mut source.hd) }.unwrap();
1507        assert_eq!(*ohlcv_ref, OHLCV_MSG);
1508    }
1509
1510    #[rstest]
1511    #[case::header(RecordHeader::default::<MboMsg>(rtype::MBO), 16)]
1512    #[case::mbo(MboMsg::default(), 56)]
1513    #[case::ba_pair(BidAskPair::default(), 32)]
1514    #[case::cba_pair(ConsolidatedBidAskPair::default(), mem::size_of::<BidAskPair>())]
1515    #[case::trade(TradeMsg::default(), 48)]
1516    #[case::mbp1(Mbp1Msg::default(), mem::size_of::<TradeMsg>() + mem::size_of::<BidAskPair>())]
1517    #[case::mbp10(Mbp10Msg::default(), mem::size_of::<TradeMsg>() + mem::size_of::<BidAskPair>() * 10)]
1518    #[case::bbo(BboMsg::default_for_schema(Schema::Bbo1S), mem::size_of::<Mbp1Msg>())]
1519    #[case::cmbp1(Cmbp1Msg::default_for_schema(Schema::Cmbp1), mem::size_of::<Mbp1Msg>())]
1520    #[case::cbbo(CbboMsg::default_for_schema(Schema::Cbbo1S), mem::size_of::<Mbp1Msg>())]
1521    #[case::ohlcv(OhlcvMsg::default_for_schema(Schema::Ohlcv1S), 56)]
1522    #[case::status(StatusMsg::default(), 40)]
1523    #[case::definition(InstrumentDefMsg::default(), 520)]
1524    #[case::imbalance(ImbalanceMsg::default(), 112)]
1525    #[case::stat(StatMsg::default(), 80)]
1526    #[case::error(ErrorMsg::default(), 320)]
1527    #[case::symbol_mapping(SymbolMappingMsg::default(), 176)]
1528    #[case::system(SystemMsg::default(), 320)]
1529    #[case::with_ts_out(WithTsOut::new(SystemMsg::default(), 0), mem::size_of::<SystemMsg>() + 8)]
1530    fn test_sizes<R: Sized>(#[case] _rec: R, #[case] exp: usize) {
1531        assert_eq!(mem::size_of::<R>(), exp);
1532        assert!(mem::size_of::<R>() <= crate::MAX_RECORD_LEN);
1533    }
1534
1535    #[rstest]
1536    #[case::header(RecordHeader::default::<MboMsg>(rtype::MBO))]
1537    #[case::mbo(MboMsg::default())]
1538    #[case::ba_pair(BidAskPair::default())]
1539    #[case::cba_pair(ConsolidatedBidAskPair::default())]
1540    #[case::trade(TradeMsg::default())]
1541    #[case::mbp1(Mbp1Msg::default())]
1542    #[case::mbp10(Mbp10Msg::default())]
1543    #[case::bbo(BboMsg::default_for_schema(crate::Schema::Bbo1S))]
1544    #[case::cmbp1(Cmbp1Msg::default_for_schema(crate::Schema::Cmbp1))]
1545    #[case::cbbo(CbboMsg::default_for_schema(crate::Schema::Cbbo1S))]
1546    #[case::ohlcv(OhlcvMsg::default_for_schema(Schema::Ohlcv1S))]
1547    #[case::status(StatusMsg::default())]
1548    #[case::definition(InstrumentDefMsg::default())]
1549    #[case::imbalance(ImbalanceMsg::default())]
1550    #[case::stat(StatMsg::default())]
1551    #[case::error(ErrorMsg::default())]
1552    #[case::symbol_mapping(SymbolMappingMsg::default())]
1553    #[case::system(SystemMsg::default())]
1554    fn test_alignment_and_no_padding<R: TypeLayout>(#[case] _rec: R) {
1555        let layout = R::type_layout();
1556        assert_eq!(layout.alignment, 8, "Unexpected alignment: {layout}");
1557        for field in layout.fields.iter() {
1558            assert!(
1559                matches!(field, Field::Field { .. }),
1560                "Detected padding: {layout}"
1561            );
1562        }
1563    }
1564
1565    #[test]
1566    fn test_bbo_alignment_matches_mbp1() {
1567        assert_eq!(offset_of!(BboMsg, hd), offset_of!(Mbp1Msg, hd));
1568        assert_eq!(offset_of!(BboMsg, price), offset_of!(Mbp1Msg, price));
1569        assert_eq!(offset_of!(BboMsg, size), offset_of!(Mbp1Msg, size));
1570        assert_eq!(offset_of!(BboMsg, side), offset_of!(Mbp1Msg, side));
1571        assert_eq!(offset_of!(BboMsg, flags), offset_of!(Mbp1Msg, flags));
1572        assert_eq!(offset_of!(BboMsg, ts_recv), offset_of!(Mbp1Msg, ts_recv));
1573        assert_eq!(offset_of!(BboMsg, sequence), offset_of!(Mbp1Msg, sequence));
1574        assert_eq!(offset_of!(BboMsg, levels), offset_of!(Mbp1Msg, levels));
1575    }
1576
1577    #[test]
1578    fn test_mbo_index_ts() {
1579        let rec = MboMsg {
1580            ts_recv: 1,
1581            ..Default::default()
1582        };
1583        assert_eq!(rec.raw_index_ts(), 1);
1584    }
1585
1586    #[test]
1587    fn test_def_index_ts() {
1588        let rec = InstrumentDefMsg {
1589            ts_recv: 1,
1590            ..Default::default()
1591        };
1592        assert_eq!(rec.raw_index_ts(), 1);
1593    }
1594
1595    #[test]
1596    fn test_db_ts_always_valid_time_offsetdatetime() {
1597        assert!(time::OffsetDateTime::from_unix_timestamp_nanos(0).is_ok());
1598        assert!(time::OffsetDateTime::from_unix_timestamp_nanos((u64::MAX - 1) as i128).is_ok());
1599        assert!(time::OffsetDateTime::from_unix_timestamp_nanos(UNDEF_TIMESTAMP as i128).is_ok());
1600    }
1601
1602    #[test]
1603    fn test_record_object_safe() {
1604        let _record: Box<dyn Record> = Box::new(ErrorMsg::new(1, None, "Boxed record", true));
1605    }
1606}