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}