Skip to main content

dbn/
enums.rs

1//! Enums used in Databento APIs.
2#![allow(deprecated)] // TODO: remove with SType::Smart
3#![allow(clippy::manual_non_exhaustive)] // false positive
4
5mod methods;
6
7use std::fmt::{self, Display, Formatter};
8
9// Dummy derive macro to get around `cfg_attr` incompatibility of several
10// of pyo3's attribute macros. See https://github.com/PyO3/pyo3/issues/780
11#[cfg(not(feature = "python"))]
12use dbn_macros::MockPyo3;
13use num_enum::{IntoPrimitive, TryFromPrimitive};
14
15/// A [record type](https://databento.com/docs/standards-and-conventions/common-fields-enums-types),
16/// i.e. a sentinel for different types implementing [`HasRType`](crate::record::HasRType).
17///
18/// Use in [`RecordHeader`](crate::RecordHeader) to indicate the type of record,
19/// which is useful when working with DBN streams containing multiple record types.
20#[derive(
21    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
22)]
23#[cfg_attr(
24    feature = "python",
25    derive(strum::EnumIter),
26    pyo3::pyclass(from_py_object, module = "databento_dbn")
27)]
28#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
29#[repr(u8)]
30pub enum RType {
31    /// Denotes a market-by-price record with a book depth of 0 (used for the
32    /// [`Trades`](super::Schema::Trades) schema).
33    #[pyo3(name = "MBP_0")]
34    Mbp0 = 0x00,
35    /// Denotes a market-by-price record with a book depth of 1 (also used for the
36    /// [`Tbbo`](super::Schema::Tbbo) schema).
37    #[pyo3(name = "MBP_1")]
38    Mbp1 = 0x01,
39    /// Denotes a market-by-price record with a book depth of 10.
40    #[pyo3(name = "MBP_10")]
41    Mbp10 = 0x0A,
42    /// Denotes an open, high, low, close, and volume record at an unspecified cadence.
43    #[deprecated(
44        since = "0.3.3",
45        note = "Separated into separate rtypes for each OHLCV schema."
46    )]
47    #[pyo3(name = "OHLCV_DEPRECATED")]
48    OhlcvDeprecated = 0x11,
49    /// Denotes an open, high, low, close, and volume record at a 1-second cadence.
50    #[pyo3(name = "OHLCV_1S")]
51    Ohlcv1S = 0x20,
52    /// Denotes an open, high, low, close, and volume record at a 1-minute cadence.
53    #[pyo3(name = "OHLCV_1M")]
54    Ohlcv1M = 0x21,
55    /// Denotes an open, high, low, close, and volume record at an hourly cadence.
56    #[pyo3(name = "OHLCV_1H")]
57    Ohlcv1H = 0x22,
58    /// Denotes an open, high, low, close, and volume record at a daily cadence
59    /// based on the UTC date.
60    #[pyo3(name = "OHLCV_1D")]
61    Ohlcv1D = 0x23,
62    /// Denotes an open, high, low, close, and volume record at a daily cadence
63    /// based on the end of the trading session.
64    #[pyo3(name = "OHLCV_EOD")]
65    OhlcvEod = 0x24,
66    /// Denotes an exchange status record.
67    #[pyo3(name = "STATUS")]
68    Status = 0x12,
69    /// Denotes an instrument definition record.
70    #[pyo3(name = "INSTRUMENT_DEF")]
71    InstrumentDef = 0x13,
72    /// Denotes an order imbalance record.
73    #[pyo3(name = "IMBALANCE")]
74    Imbalance = 0x14,
75    /// Denotes an error from gateway.
76    #[pyo3(name = "ERROR")]
77    Error = 0x15,
78    /// Denotes a symbol mapping record.
79    #[pyo3(name = "SYMBOL_MAPPING")]
80    SymbolMapping = 0x16,
81    /// Denotes a non-error message from the gateway. Also used for heartbeats.
82    #[pyo3(name = "SYSTEM")]
83    System = 0x17,
84    /// Denotes a statistics record from the publisher (not calculated by Databento).
85    #[pyo3(name = "STATISTICS")]
86    Statistics = 0x18,
87    /// Denotes a market-by-order record.
88    #[pyo3(name = "MBO")]
89    Mbo = 0xA0,
90    /// Denotes a consolidated best bid and offer record.
91    #[pyo3(name = "CMBP_1")]
92    Cmbp1 = 0xB1,
93    /// Denotes a consolidated best bid and offer record subsampled on a one-second
94    /// interval.
95    #[pyo3(name = "CBBO_1S")]
96    Cbbo1S = 0xC0,
97    /// Denotes a consolidated best bid and offer record subsampled on a one-minute
98    /// interval.
99    #[pyo3(name = "CBBO_1M")]
100    Cbbo1M = 0xC1,
101    /// Denotes a consolidated best bid and offer trade record containing the
102    /// consolidated BBO before the trade
103    #[pyo3(name = "TCBBO")]
104    Tcbbo = 0xC2,
105    /// Denotes a best bid and offer record subsampled on a one-second interval.
106    #[pyo3(name = "BBO_1S")]
107    Bbo1S = 0xC3,
108    /// Denotes a best bid and offer record subsampled on a one-minute interval.
109    #[pyo3(name = "BBO_1M")]
110    Bbo1M = 0xC4,
111}
112
113/// Record types, possible values for [`RecordHeader::rtype`][crate::RecordHeader::rtype].
114pub mod rtype {
115    use super::*;
116    /// Denotes a market-by-price record with a book depth of 0 (used for the
117    /// [`Trades`](super::Schema::Trades) schema).
118    pub const MBP_0: u8 = RType::Mbp0 as u8;
119    /// Denotes a market-by-price record with a book depth of 1 (also used for the
120    /// [`Tbbo`](super::Schema::Tbbo) schema).
121    pub const MBP_1: u8 = RType::Mbp1 as u8;
122    /// Denotes a market-by-price record with a book depth of 10.
123    pub const MBP_10: u8 = RType::Mbp10 as u8;
124    /// Denotes an open, high, low, close, and volume record at an unspecified cadence.#[deprecated(since = "0.3.3", note = "Separated into separate rtypes for each OHLCV schema.")]
125    pub const OHLCV_DEPRECATED: u8 = RType::OhlcvDeprecated as u8;
126    /// Denotes an open, high, low, close, and volume record at a 1-second cadence.
127    pub const OHLCV_1S: u8 = RType::Ohlcv1S as u8;
128    /// Denotes an open, high, low, close, and volume record at a 1-minute cadence.
129    pub const OHLCV_1M: u8 = RType::Ohlcv1M as u8;
130    /// Denotes an open, high, low, close, and volume record at an hourly cadence.
131    pub const OHLCV_1H: u8 = RType::Ohlcv1H as u8;
132    /// Denotes an open, high, low, close, and volume record at a daily cadence
133    /// based on the UTC date.
134    pub const OHLCV_1D: u8 = RType::Ohlcv1D as u8;
135    /// Denotes an open, high, low, close, and volume record at a daily cadence
136    /// based on the end of the trading session.
137    pub const OHLCV_EOD: u8 = RType::OhlcvEod as u8;
138    /// Denotes an exchange status record.
139    pub const STATUS: u8 = RType::Status as u8;
140    /// Denotes an instrument definition record.
141    pub const INSTRUMENT_DEF: u8 = RType::InstrumentDef as u8;
142    /// Denotes an order imbalance record.
143    pub const IMBALANCE: u8 = RType::Imbalance as u8;
144    /// Denotes an error from gateway.
145    pub const ERROR: u8 = RType::Error as u8;
146    /// Denotes a symbol mapping record.
147    pub const SYMBOL_MAPPING: u8 = RType::SymbolMapping as u8;
148    /// Denotes a non-error message from the gateway. Also used for heartbeats.
149    pub const SYSTEM: u8 = RType::System as u8;
150    /// Denotes a statistics record from the publisher (not calculated by Databento).
151    pub const STATISTICS: u8 = RType::Statistics as u8;
152    /// Denotes a market-by-order record.
153    pub const MBO: u8 = RType::Mbo as u8;
154    /// Denotes a consolidated best bid and offer record.
155    pub const CMBP_1: u8 = RType::Cmbp1 as u8;
156    /// Denotes a consolidated best bid and offer record subsampled on a one-second
157    /// interval.
158    pub const CBBO_1S: u8 = RType::Cbbo1S as u8;
159    /// Denotes a consolidated best bid and offer record subsampled on a one-minute
160    /// interval.
161    pub const CBBO_1M: u8 = RType::Cbbo1M as u8;
162    /// Denotes a consolidated best bid and offer trade record containing the
163    /// consolidated BBO before the trade
164    pub const TCBBO: u8 = RType::Tcbbo as u8;
165    /// Denotes a best bid and offer record subsampled on a one-second interval.
166    pub const BBO_1S: u8 = RType::Bbo1S as u8;
167    /// Denotes a best bid and offer record subsampled on a one-minute interval.
168    pub const BBO_1M: u8 = RType::Bbo1M as u8;
169}
170
171impl std::str::FromStr for RType {
172    type Err = crate::Error;
173
174    fn from_str(s: &str) -> Result<Self, Self::Err> {
175        match s {
176            "mbp-0" => Ok(Self::Mbp0),
177            "mbp-1" => Ok(Self::Mbp1),
178            "mbp-10" => Ok(Self::Mbp10),
179            #[allow(deprecated)]
180            "ohlcv-deprecated" => Ok(Self::OhlcvDeprecated),
181            "ohlcv-1s" => Ok(Self::Ohlcv1S),
182            "ohlcv-1m" => Ok(Self::Ohlcv1M),
183            "ohlcv-1h" => Ok(Self::Ohlcv1H),
184            "ohlcv-1d" => Ok(Self::Ohlcv1D),
185            "ohlcv-eod" => Ok(Self::OhlcvEod),
186            "status" => Ok(Self::Status),
187            "instrument-def" => Ok(Self::InstrumentDef),
188            "imbalance" => Ok(Self::Imbalance),
189            "error" => Ok(Self::Error),
190            "symbol-mapping" => Ok(Self::SymbolMapping),
191            "system" => Ok(Self::System),
192            "statistics" => Ok(Self::Statistics),
193            "mbo" => Ok(Self::Mbo),
194            "cmbp-1" => Ok(Self::Cmbp1),
195            "cbbo-1s" => Ok(Self::Cbbo1S),
196            "cbbo-1m" => Ok(Self::Cbbo1M),
197            "tcbbo" => Ok(Self::Tcbbo),
198            "bbo-1s" => Ok(Self::Bbo1S),
199            "bbo-1m" => Ok(Self::Bbo1M),
200            _ => Err(crate::Error::conversion::<Self>(s.to_owned())),
201        }
202    }
203}
204
205impl AsRef<str> for RType {
206    fn as_ref(&self) -> &str {
207        self.as_str()
208    }
209}
210
211impl RType {
212    /// Converts the `RType` to its `str` representation.
213    pub const fn as_str(&self) -> &'static str {
214        match self {
215            Self::Mbp0 => "mbp-0",
216            Self::Mbp1 => "mbp-1",
217            Self::Mbp10 => "mbp-10",
218            #[allow(deprecated)]
219            Self::OhlcvDeprecated => "ohlcv-deprecated",
220            Self::Ohlcv1S => "ohlcv-1s",
221            Self::Ohlcv1M => "ohlcv-1m",
222            Self::Ohlcv1H => "ohlcv-1h",
223            Self::Ohlcv1D => "ohlcv-1d",
224            Self::OhlcvEod => "ohlcv-eod",
225            Self::Status => "status",
226            Self::InstrumentDef => "instrument-def",
227            Self::Imbalance => "imbalance",
228            Self::Error => "error",
229            Self::SymbolMapping => "symbol-mapping",
230            Self::System => "system",
231            Self::Statistics => "statistics",
232            Self::Mbo => "mbo",
233            Self::Cmbp1 => "cmbp-1",
234            Self::Cbbo1S => "cbbo-1s",
235            Self::Cbbo1M => "cbbo-1m",
236            Self::Tcbbo => "tcbbo",
237            Self::Bbo1S => "bbo-1s",
238            Self::Bbo1M => "bbo-1m",
239        }
240    }
241}
242
243impl Display for RType {
244    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
245        f.write_str(self.as_str())
246    }
247}
248
249/// A [side](https://databento.com/docs/standards-and-conventions/common-fields-enums-types)
250/// of the market. The side of the market for resting orders, or the side of the
251/// aggressor for trades.
252#[derive(
253    Debug,
254    Clone,
255    Copy,
256    Default,
257    PartialEq,
258    Eq,
259    PartialOrd,
260    Ord,
261    Hash,
262    TryFromPrimitive,
263    IntoPrimitive,
264)]
265#[cfg_attr(
266    feature = "python",
267    derive(strum::EnumIter),
268    pyo3::pyclass(from_py_object, module = "databento_dbn")
269)]
270#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
271#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
272#[repr(u8)]
273pub enum Side {
274    /// A sell order or sell aggressor in a trade.
275    #[pyo3(name = "ASK")]
276    Ask = b'A',
277    /// A buy order or a buy aggressor in a trade.
278    #[pyo3(name = "BID")]
279    Bid = b'B',
280    /// No side specified by the original source.
281    #[default]
282    #[pyo3(name = "NONE")]
283    None = b'N',
284}
285
286impl From<Side> for char {
287    fn from(value: Side) -> Self {
288        u8::from(value) as char
289    }
290}
291
292/// An [order event or order book operation](https://databento.com/docs/api-reference-historical/basics/schemas-and-conventions).
293///
294/// For example usage see:
295/// - [Order actions](https://databento.com/docs/examples/order-book/order-actions)
296/// - [Order tracking](https://databento.com/docs/examples/order-book/order-tracking)
297#[derive(
298    Debug,
299    Clone,
300    Copy,
301    Default,
302    PartialEq,
303    Eq,
304    PartialOrd,
305    Ord,
306    Hash,
307    TryFromPrimitive,
308    IntoPrimitive,
309)]
310#[cfg_attr(
311    feature = "python",
312    derive(strum::EnumIter),
313    pyo3::pyclass(from_py_object, module = "databento_dbn")
314)]
315#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
316#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
317#[repr(u8)]
318pub enum Action {
319    /// An existing order was modified: price and/or size.
320    #[pyo3(name = "MODIFY")]
321    Modify = b'M',
322    /// An aggressing order traded. Does not affect the book.
323    #[pyo3(name = "TRADE")]
324    Trade = b'T',
325    /// An existing order was filled. Does not affect the book.
326    #[pyo3(name = "FILL")]
327    Fill = b'F',
328    /// An order was fully or partially cancelled.
329    #[pyo3(name = "CANCEL")]
330    Cancel = b'C',
331    /// A new order was added to the book.
332    #[pyo3(name = "ADD")]
333    Add = b'A',
334    /// Reset the book; clear all orders for an instrument.
335    #[pyo3(name = "CLEAR")]
336    Clear = b'R',
337    /// Has no effect on the book, but may carry `flags` or other information.
338    #[default]
339    #[pyo3(name = "NONE")]
340    None = b'N',
341}
342
343impl From<Action> for char {
344    fn from(value: Action) -> Self {
345        u8::from(value) as char
346    }
347}
348
349/// The class of instrument.
350///
351/// For example usage see
352/// [Getting options with their underlying](https://databento.com/docs/examples/options/options-and-futures).
353#[derive(
354    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
355)]
356#[cfg_attr(
357    feature = "python",
358    derive(strum::EnumIter),
359    pyo3::pyclass(from_py_object, module = "databento_dbn")
360)]
361#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
362#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
363#[non_exhaustive]
364#[repr(u8)]
365pub enum InstrumentClass {
366    /// A bond.
367    #[pyo3(name = "BOND")]
368    Bond = b'B',
369    /// A call option.
370    #[pyo3(name = "CALL")]
371    Call = b'C',
372    /// A future.
373    #[pyo3(name = "FUTURE")]
374    Future = b'F',
375    /// An index.
376    #[pyo3(name = "INDEX")]
377    Index = b'I',
378    /// A stock.
379    #[pyo3(name = "STOCK")]
380    Stock = b'K',
381    /// A spread composed of multiple instrument classes.
382    #[pyo3(name = "MIXED_SPREAD")]
383    MixedSpread = b'M',
384    /// A put option.
385    #[pyo3(name = "PUT")]
386    Put = b'P',
387    /// A spread composed of futures.
388    #[pyo3(name = "FUTURE_SPREAD")]
389    FutureSpread = b'S',
390    /// A spread composed of options.
391    #[pyo3(name = "OPTION_SPREAD")]
392    OptionSpread = b'T',
393    /// A foreign exchange spot.
394    #[pyo3(name = "FX_SPOT")]
395    FxSpot = b'X',
396    /// A commodity being traded for immediate delivery.
397    #[pyo3(name = "COMMODITY_SPOT")]
398    CommoditySpot = b'Y',
399}
400
401impl From<InstrumentClass> for char {
402    fn from(value: InstrumentClass) -> Self {
403        u8::from(value) as char
404    }
405}
406
407/// The type of matching algorithm used for the instrument at the exchange.
408#[derive(
409    Debug,
410    Clone,
411    Copy,
412    Default,
413    PartialEq,
414    Eq,
415    PartialOrd,
416    Ord,
417    Hash,
418    TryFromPrimitive,
419    IntoPrimitive,
420)]
421#[cfg_attr(
422    feature = "python",
423    derive(strum::EnumIter),
424    pyo3::pyclass(from_py_object, module = "databento_dbn")
425)]
426#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
427#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
428#[repr(u8)]
429pub enum MatchAlgorithm {
430    /// No matching algorithm was specified.
431    #[default]
432    #[pyo3(name = "UNDEFINED")]
433    Undefined = b' ',
434    /// First-in-first-out matching.
435    #[pyo3(name = "FIFO")]
436    Fifo = b'F',
437    /// A configurable match algorithm.
438    #[pyo3(name = "CONFIGURABLE")]
439    Configurable = b'K',
440    /// Trade quantity is allocated to resting orders based on a pro-rata percentage:
441    /// resting order quantity divided by total quantity.
442    #[pyo3(name = "PRO_RATA")]
443    ProRata = b'C',
444    /// Like [`Self::Fifo`] but with LMM allocations prior to FIFO allocations.
445    #[pyo3(name = "FIFO_LMM")]
446    FifoLmm = b'T',
447    /// Like [`Self::ProRata`] but includes a configurable allocation to the first order that
448    /// improves the market.
449    #[pyo3(name = "THRESHOLD_PRO_RATA")]
450    ThresholdProRata = b'O',
451    /// Like [`Self::FifoLmm`] but includes a configurable allocation to the first order that
452    /// improves the market.
453    #[pyo3(name = "FIFO_TOP_LMM")]
454    FifoTopLmm = b'S',
455    /// Like [`Self::ThresholdProRata`] but includes a special priority to LMMs.
456    #[pyo3(name = "THRESHOLD_PRO_RATA_LMM")]
457    ThresholdProRataLmm = b'Q',
458    /// Special variant used only for Eurodollar futures on CME.
459    #[pyo3(name = "EURODOLLAR_FUTURES")]
460    EurodollarFutures = b'Y',
461    /// Trade quantity is shared between all orders at the best price. Orders with the
462    /// highest time priority receive a higher matched quantity.
463    #[pyo3(name = "TIME_PRO_RATA")]
464    TimeProRata = b'P',
465    /// A two-pass FIFO algorithm. The first pass fills the Institutional Group the aggressing
466    /// order is associated with. The second pass matches orders without an Institutional Group
467    /// association. See [CME documentation](https://cmegroupclientsite.atlassian.net/wiki/spaces/EPICSANDBOX/pages/457217267#InstitutionalPrioritizationMatchAlgorithm).
468    #[pyo3(name = "INSTITUTIONAL_PRIORITIZATION")]
469    InstitutionalPrioritization = b'V',
470}
471
472impl From<MatchAlgorithm> for char {
473    fn from(value: MatchAlgorithm) -> Self {
474        u8::from(value) as char
475    }
476}
477
478/// Whether the instrument is user-defined.
479///
480/// For example usage see
481/// [Getting options with their underlying](https://databento.com/docs/examples/options/options-and-futures).
482#[derive(
483    Debug,
484    Clone,
485    Copy,
486    Default,
487    PartialEq,
488    Eq,
489    PartialOrd,
490    Ord,
491    Hash,
492    TryFromPrimitive,
493    IntoPrimitive,
494)]
495#[cfg_attr(
496    feature = "python",
497    derive(strum::EnumIter),
498    pyo3::pyclass(from_py_object, module = "databento_dbn")
499)]
500#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
501#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
502#[repr(u8)]
503pub enum UserDefinedInstrument {
504    /// The instrument is not user-defined.
505    #[default]
506    #[pyo3(name = "NO")]
507    No = b'N',
508    /// The instrument is user-defined.
509    #[pyo3(name = "YES")]
510    Yes = b'Y',
511}
512
513impl From<UserDefinedInstrument> for char {
514    fn from(value: UserDefinedInstrument) -> Self {
515        u8::from(value) as char
516    }
517}
518
519/// The type of [`InstrumentDefMsg`](crate::record::InstrumentDefMsg) update.
520#[derive(
521    Debug,
522    Clone,
523    Copy,
524    Default,
525    PartialEq,
526    Eq,
527    PartialOrd,
528    Ord,
529    Hash,
530    TryFromPrimitive,
531    IntoPrimitive,
532)]
533#[cfg_attr(
534    feature = "python",
535    derive(strum::EnumIter),
536    pyo3::pyclass(from_py_object, module = "databento_dbn")
537)]
538#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
539#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
540#[repr(u8)]
541pub enum SecurityUpdateAction {
542    /// A new instrument definition.
543    #[default]
544    #[pyo3(name = "ADD")]
545    Add = b'A',
546    /// A modified instrument definition of an existing one.
547    #[pyo3(name = "MODIFY")]
548    Modify = b'M',
549    /// Removal of an instrument definition.
550    #[pyo3(name = "DELETE")]
551    Delete = b'D',
552    #[doc(hidden)]
553    #[deprecated(since = "0.3.0", note = "Still present in legacy files.")]
554    #[pyo3(name = "INVALID")]
555    Invalid = b'~',
556}
557
558impl From<SecurityUpdateAction> for char {
559    fn from(value: SecurityUpdateAction) -> Self {
560        u8::from(value) as char
561    }
562}
563
564/// A symbology type. Refer to the
565/// [symbology documentation](https://databento.com/docs/api-reference-historical/basics/symbology)
566/// for more information.
567#[derive(
568    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
569)]
570#[cfg_attr(
571    feature = "python",
572    derive(strum::EnumIter),
573    pyo3::pyclass(from_py_object, module = "databento_dbn")
574)]
575#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
576#[repr(u8)]
577pub enum SType {
578    /// Symbology using a unique numeric ID.
579    #[pyo3(name = "INSTRUMENT_ID")]
580    InstrumentId = 0,
581    /// Symbology using the original symbols provided by the publisher.
582    #[pyo3(name = "RAW_SYMBOL")]
583    RawSymbol = 1,
584    /// A set of Databento-specific symbologies for referring to groups of symbols.
585    #[deprecated(since = "0.5.0", note = "Smart was split into continuous and parent.")]
586    #[pyo3(name = "SMART")]
587    Smart = 2,
588    /// A Databento-specific symbology where one symbol may point to different
589    /// instruments at different points of time, e.g. to always refer to the front month
590    /// future.
591    #[pyo3(name = "CONTINUOUS")]
592    Continuous = 3,
593    /// A Databento-specific symbology for referring to a group of symbols by one
594    /// "parent" symbol, e.g. ES.FUT to refer to all ES futures.
595    #[pyo3(name = "PARENT")]
596    Parent = 4,
597    /// Symbology for US equities using NASDAQ Integrated suffix conventions.
598    #[pyo3(name = "NASDAQ_SYMBOL")]
599    NasdaqSymbol = 5,
600    /// Symbology for US equities using CMS suffix conventions.
601    #[pyo3(name = "CMS_SYMBOL")]
602    CmsSymbol = 6,
603    /// Symbology using International Security Identification Numbers (ISIN) - ISO 6166.
604    #[pyo3(name = "ISIN")]
605    Isin = 7,
606    /// Symbology using US domestic Committee on Uniform Securities Identification Procedure (CUSIP) codes.
607    #[pyo3(name = "US_CODE")]
608    UsCode = 8,
609    /// Symbology using Bloomberg composite global IDs.
610    #[pyo3(name = "BBG_COMP_ID")]
611    BbgCompId = 9,
612    /// Symbology using Bloomberg composite tickers.
613    #[pyo3(name = "BBG_COMP_TICKER")]
614    BbgCompTicker = 10,
615    /// Symbology using Bloomberg FIGI exchange level IDs.
616    #[pyo3(name = "FIGI")]
617    Figi = 11,
618    /// Symbology using Bloomberg exchange level tickers.
619    #[pyo3(name = "FIGI_TICKER")]
620    FigiTicker = 12,
621}
622
623impl std::str::FromStr for SType {
624    type Err = crate::Error;
625
626    fn from_str(s: &str) -> Result<Self, Self::Err> {
627        match s {
628            "instrument_id" | "product_id" => Ok(Self::InstrumentId),
629            "raw_symbol" | "native" => Ok(Self::RawSymbol),
630            #[allow(deprecated)]
631            "smart" => Ok(Self::Smart),
632            "continuous" => Ok(Self::Continuous),
633            "parent" => Ok(Self::Parent),
634            "nasdaq_symbol" | "nasdaq" => Ok(Self::NasdaqSymbol),
635            "cms_symbol" | "cms" => Ok(Self::CmsSymbol),
636            "isin" => Ok(Self::Isin),
637            "us_code" => Ok(Self::UsCode),
638            "bbg_comp_id" => Ok(Self::BbgCompId),
639            "bbg_comp_ticker" => Ok(Self::BbgCompTicker),
640            "figi" => Ok(Self::Figi),
641            "figi_ticker" => Ok(Self::FigiTicker),
642            _ => Err(crate::Error::conversion::<Self>(s.to_owned())),
643        }
644    }
645}
646
647impl AsRef<str> for SType {
648    fn as_ref(&self) -> &str {
649        self.as_str()
650    }
651}
652
653impl SType {
654    /// Converts the `SType` to its `str` representation.
655    pub const fn as_str(&self) -> &'static str {
656        match self {
657            Self::InstrumentId => "instrument_id",
658            Self::RawSymbol => "raw_symbol",
659            #[allow(deprecated)]
660            Self::Smart => "smart",
661            Self::Continuous => "continuous",
662            Self::Parent => "parent",
663            Self::NasdaqSymbol => "nasdaq_symbol",
664            Self::CmsSymbol => "cms_symbol",
665            Self::Isin => "isin",
666            Self::UsCode => "us_code",
667            Self::BbgCompId => "bbg_comp_id",
668            Self::BbgCompTicker => "bbg_comp_ticker",
669            Self::Figi => "figi",
670            Self::FigiTicker => "figi_ticker",
671        }
672    }
673}
674
675impl Display for SType {
676    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
677        f.write_str(self.as_str())
678    }
679}
680
681/// A data record schema.
682///
683/// Each schema has a particular [record](crate::record) type associated with it.
684///
685/// See [List of supported market data schemas](https://databento.com/docs/schemas-and-data-formats/whats-a-schema)
686/// for an overview of the differences and use cases of each schema.
687#[derive(
688    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
689)]
690#[cfg_attr(
691    feature = "python",
692    derive(strum::EnumIter),
693    pyo3::pyclass(from_py_object, module = "databento_dbn")
694)]
695#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
696#[repr(u16)]
697pub enum Schema {
698    /// Market by order.
699    #[pyo3(name = "MBO")]
700    Mbo = 0,
701    /// Market by price with a book depth of 1.
702    #[pyo3(name = "MBP_1")]
703    Mbp1 = 1,
704    /// Market by price with a book depth of 10.
705    #[pyo3(name = "MBP_10")]
706    Mbp10 = 2,
707    /// All trade events with the best bid and offer (BBO) immediately **before** the effect of
708    /// the trade.
709    #[pyo3(name = "TBBO")]
710    Tbbo = 3,
711    /// All trade events.
712    #[pyo3(name = "TRADES")]
713    Trades = 4,
714    /// Open, high, low, close, and volume at a one-second interval.
715    #[pyo3(name = "OHLCV_1S")]
716    Ohlcv1S = 5,
717    /// Open, high, low, close, and volume at a one-minute interval.
718    #[pyo3(name = "OHLCV_1M")]
719    Ohlcv1M = 6,
720    /// Open, high, low, close, and volume at an hourly interval.
721    #[pyo3(name = "OHLCV_1H")]
722    Ohlcv1H = 7,
723    /// Open, high, low, close, and volume at a daily interval based on the UTC date.
724    #[pyo3(name = "OHLCV_1D")]
725    Ohlcv1D = 8,
726    /// Instrument definitions.
727    #[pyo3(name = "DEFINITION")]
728    Definition = 9,
729    /// Additional data disseminated by publishers.
730    #[pyo3(name = "STATISTICS")]
731    Statistics = 10,
732    /// Trading status events.
733    #[pyo3(name = "STATUS")]
734    Status = 11,
735    /// Auction imbalance events.
736    #[pyo3(name = "IMBALANCE")]
737    Imbalance = 12,
738    /// Open, high, low, close, and volume at a daily cadence based on the end of the trading
739    /// session.
740    #[pyo3(name = "OHLCV_EOD")]
741    OhlcvEod = 13,
742    /// Consolidated best bid and offer.
743    #[pyo3(name = "CMBP_1")]
744    Cmbp1 = 14,
745    /// Consolidated best bid and offer subsampled at one-second intervals, in addition to
746    /// trades.
747    #[pyo3(name = "CBBO_1S")]
748    Cbbo1S = 15,
749    /// Consolidated best bid and offer subsampled at one-minute intervals, in addition to
750    /// trades.
751    #[pyo3(name = "CBBO_1M")]
752    Cbbo1M = 16,
753    /// All trade events with the consolidated best bid and offer (CBBO) immediately **before**
754    /// the effect of the trade.
755    #[pyo3(name = "TCBBO")]
756    Tcbbo = 17,
757    /// Best bid and offer subsampled at one-second intervals, in addition to trades.
758    #[pyo3(name = "BBO_1S")]
759    Bbo1S = 18,
760    /// Best bid and offer subsampled at one-minute intervals, in addition to trades.
761    #[pyo3(name = "BBO_1M")]
762    Bbo1M = 19,
763}
764
765impl std::str::FromStr for Schema {
766    type Err = crate::Error;
767
768    fn from_str(s: &str) -> Result<Self, Self::Err> {
769        match s {
770            "mbo" => Ok(Self::Mbo),
771            "mbp-1" => Ok(Self::Mbp1),
772            "mbp-10" => Ok(Self::Mbp10),
773            "tbbo" => Ok(Self::Tbbo),
774            "trades" => Ok(Self::Trades),
775            "ohlcv-1s" => Ok(Self::Ohlcv1S),
776            "ohlcv-1m" => Ok(Self::Ohlcv1M),
777            "ohlcv-1h" => Ok(Self::Ohlcv1H),
778            "ohlcv-1d" => Ok(Self::Ohlcv1D),
779            "definition" => Ok(Self::Definition),
780            "statistics" => Ok(Self::Statistics),
781            "status" => Ok(Self::Status),
782            "imbalance" => Ok(Self::Imbalance),
783            "ohlcv-eod" => Ok(Self::OhlcvEod),
784            "cmbp-1" => Ok(Self::Cmbp1),
785            "cbbo-1s" => Ok(Self::Cbbo1S),
786            "cbbo-1m" => Ok(Self::Cbbo1M),
787            "tcbbo" => Ok(Self::Tcbbo),
788            "bbo-1s" => Ok(Self::Bbo1S),
789            "bbo-1m" => Ok(Self::Bbo1M),
790            _ => Err(crate::Error::conversion::<Self>(s.to_owned())),
791        }
792    }
793}
794
795impl AsRef<str> for Schema {
796    fn as_ref(&self) -> &str {
797        self.as_str()
798    }
799}
800
801impl Schema {
802    /// Converts the `Schema` to its `str` representation.
803    pub const fn as_str(&self) -> &'static str {
804        match self {
805            Self::Mbo => "mbo",
806            Self::Mbp1 => "mbp-1",
807            Self::Mbp10 => "mbp-10",
808            Self::Tbbo => "tbbo",
809            Self::Trades => "trades",
810            Self::Ohlcv1S => "ohlcv-1s",
811            Self::Ohlcv1M => "ohlcv-1m",
812            Self::Ohlcv1H => "ohlcv-1h",
813            Self::Ohlcv1D => "ohlcv-1d",
814            Self::Definition => "definition",
815            Self::Statistics => "statistics",
816            Self::Status => "status",
817            Self::Imbalance => "imbalance",
818            Self::OhlcvEod => "ohlcv-eod",
819            Self::Cmbp1 => "cmbp-1",
820            Self::Cbbo1S => "cbbo-1s",
821            Self::Cbbo1M => "cbbo-1m",
822            Self::Tcbbo => "tcbbo",
823            Self::Bbo1S => "bbo-1s",
824            Self::Bbo1M => "bbo-1m",
825        }
826    }
827
828    /// The number of Schema variants.
829    pub const COUNT: usize = 20;
830}
831
832impl Display for Schema {
833    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
834        f.write_str(self.as_str())
835    }
836}
837
838/// A data encoding format.
839#[derive(
840    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
841)]
842#[cfg_attr(
843    feature = "python",
844    derive(strum::EnumIter),
845    pyo3::pyclass(from_py_object, module = "databento_dbn")
846)]
847#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
848#[repr(u8)]
849pub enum Encoding {
850    /// Databento Binary Encoding.
851    #[pyo3(name = "DBN")]
852    Dbn = 0,
853    /// Comma-separated values.
854    #[pyo3(name = "CSV")]
855    Csv = 1,
856    /// JavaScript object notation.
857    #[pyo3(name = "JSON")]
858    Json = 2,
859}
860
861impl std::str::FromStr for Encoding {
862    type Err = crate::Error;
863
864    fn from_str(s: &str) -> Result<Self, Self::Err> {
865        match s {
866            "dbn" | "dbz" => Ok(Self::Dbn),
867            "csv" => Ok(Self::Csv),
868            "json" => Ok(Self::Json),
869            _ => Err(crate::Error::conversion::<Self>(s.to_owned())),
870        }
871    }
872}
873
874impl AsRef<str> for Encoding {
875    fn as_ref(&self) -> &str {
876        self.as_str()
877    }
878}
879
880impl Encoding {
881    /// Converts the `Encoding` to its `str` representation.
882    pub const fn as_str(&self) -> &'static str {
883        match self {
884            Self::Dbn => "dbn",
885            Self::Csv => "csv",
886            Self::Json => "json",
887        }
888    }
889}
890
891impl Display for Encoding {
892    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
893        f.write_str(self.as_str())
894    }
895}
896
897/// A compression format or none if uncompressed.
898#[derive(
899    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
900)]
901#[cfg_attr(
902    feature = "python",
903    derive(strum::EnumIter),
904    pyo3::pyclass(from_py_object, module = "databento_dbn")
905)]
906#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
907#[repr(u8)]
908pub enum Compression {
909    /// Uncompressed.
910    #[pyo3(name = "NONE")]
911    None = 0,
912    /// Zstandard compressed.
913    #[pyo3(name = "ZSTD")]
914    Zstd = 1,
915}
916
917impl std::str::FromStr for Compression {
918    type Err = crate::Error;
919
920    fn from_str(s: &str) -> Result<Self, Self::Err> {
921        match s {
922            "none" => Ok(Self::None),
923            "zstd" => Ok(Self::Zstd),
924            _ => Err(crate::Error::conversion::<Self>(s.to_owned())),
925        }
926    }
927}
928
929impl AsRef<str> for Compression {
930    fn as_ref(&self) -> &str {
931        self.as_str()
932    }
933}
934
935impl Compression {
936    /// Converts the `Compression` to its `str` representation.
937    pub const fn as_str(&self) -> &'static str {
938        match self {
939            Self::None => "none",
940            Self::Zstd => "zstd",
941        }
942    }
943}
944
945impl Display for Compression {
946    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
947        f.write_str(self.as_str())
948    }
949}
950
951/// The type of statistic contained in a [`StatMsg`](crate::record::StatMsg).
952#[derive(
953    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
954)]
955#[cfg_attr(
956    feature = "python",
957    derive(strum::EnumIter),
958    pyo3::pyclass(from_py_object, module = "databento_dbn")
959)]
960#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
961#[non_exhaustive]
962#[repr(u16)]
963pub enum StatType {
964    /// The price of the first trade of an instrument. `price` will be set.
965    /// `quantity` will be set when provided by the venue.
966    #[pyo3(name = "OPENING_PRICE")]
967    OpeningPrice = 1,
968    /// The probable price of the first trade of an instrument published during pre-
969    /// open. Both `price` and `quantity` will be set.
970    #[pyo3(name = "INDICATIVE_OPENING_PRICE")]
971    IndicativeOpeningPrice = 2,
972    /// The settlement price of an instrument. `price` will be set and `flags` indicate
973    /// whether the price is final or preliminary and actual or theoretical. `ts_ref`
974    /// will indicate the trading date of the settlement price.
975    #[pyo3(name = "SETTLEMENT_PRICE")]
976    SettlementPrice = 3,
977    /// The lowest trade price of an instrument during the trading session. `price` will
978    /// be set.
979    #[pyo3(name = "TRADING_SESSION_LOW_PRICE")]
980    TradingSessionLowPrice = 4,
981    /// The highest trade price of an instrument during the trading session. `price` will
982    /// be set.
983    #[pyo3(name = "TRADING_SESSION_HIGH_PRICE")]
984    TradingSessionHighPrice = 5,
985    /// The number of contracts cleared for an instrument on the previous trading date.
986    /// `quantity` will be set. `ts_ref` will indicate the trading date of the volume.
987    #[pyo3(name = "CLEARED_VOLUME")]
988    ClearedVolume = 6,
989    /// The lowest offer price for an instrument during the trading session. `price`
990    /// will be set.
991    #[pyo3(name = "LOWEST_OFFER")]
992    LowestOffer = 7,
993    /// The highest bid price for an instrument during the trading session. `price`
994    /// will be set.
995    #[pyo3(name = "HIGHEST_BID")]
996    HighestBid = 8,
997    /// The current number of outstanding contracts of an instrument. `quantity` will
998    /// be set. `ts_ref` will indicate the trading date for which the open interest was
999    /// calculated.
1000    #[pyo3(name = "OPEN_INTEREST")]
1001    OpenInterest = 9,
1002    /// The volume-weighted average price (VWAP) for a fixing period. `price` will be
1003    /// set.
1004    #[pyo3(name = "FIXING_PRICE")]
1005    FixingPrice = 10,
1006    /// The last trade price during a trading session. `price` will be set.
1007    /// `quantity` will be set when provided by the venue.
1008    #[pyo3(name = "CLOSE_PRICE")]
1009    ClosePrice = 11,
1010    /// The change in price from the close price of the previous trading session to the
1011    /// most recent trading session. `price` will be set.
1012    #[pyo3(name = "NET_CHANGE")]
1013    NetChange = 12,
1014    /// The volume-weighted average price (VWAP) during the trading session.
1015    /// `price` will be set to the VWAP while `quantity` will be the traded
1016    /// volume.
1017    #[pyo3(name = "VWAP")]
1018    Vwap = 13,
1019    /// The implied volatility associated with the settlement price. `price` will be set
1020    /// with the standard precision.
1021    #[pyo3(name = "VOLATILITY")]
1022    Volatility = 14,
1023    /// The option delta associated with the settlement price. `price` will be set with
1024    /// the standard precision.
1025    #[pyo3(name = "DELTA")]
1026    Delta = 15,
1027    /// The auction uncrossing price. This is used for auctions that are neither the
1028    /// official opening auction nor the official closing auction. `price` will be set.
1029    /// `quantity` will be set when provided by the venue.
1030    #[pyo3(name = "UNCROSSING_PRICE")]
1031    UncrossingPrice = 16,
1032    /// The exchange defined upper price limit. `price` will be set with the standard precision.
1033    #[pyo3(name = "UPPER_PRICE_LIMIT")]
1034    UpperPriceLimit = 17,
1035    /// The exchange defined lower price limit. `price` will be set with the standard precision.
1036    #[pyo3(name = "LOWER_PRICE_LIMIT")]
1037    LowerPriceLimit = 18,
1038    /// The number of Block contracts cleared for an instrument on the previous trading date.
1039    /// `quantity` will be set. `ts_ref` will indicate the trading date of the volume.
1040    #[pyo3(name = "BLOCK_VOLUME")]
1041    BlockVolume = 19,
1042    /// The probable price of the last trade of an instrument published during the trading
1043    /// session. `price` will be set.
1044    #[pyo3(name = "INDICATIVE_CLOSE_PRICE")]
1045    IndicativeClosePrice = 20,
1046    /// A venue specific volume statistic. Refer to the venue documentation for more information.
1047    /// `quantity` will be set.
1048    #[pyo3(name = "VENUE_SPECIFIC_VOLUME_1")]
1049    VenueSpecificVolume1 = 10001,
1050    /// A venue specific price statistic. Refer to the venue documentation for more information.
1051    /// `price` will be set.
1052    #[pyo3(name = "VENUE_SPECIFIC_PRICE_1")]
1053    VenueSpecificPrice1 = 10002,
1054}
1055
1056/// The type of [`StatMsg`](crate::record::StatMsg) update.
1057#[derive(
1058    Debug,
1059    Clone,
1060    Copy,
1061    Default,
1062    PartialEq,
1063    Eq,
1064    PartialOrd,
1065    Ord,
1066    Hash,
1067    TryFromPrimitive,
1068    IntoPrimitive,
1069)]
1070#[cfg_attr(
1071    feature = "python",
1072    derive(strum::EnumIter),
1073    pyo3::pyclass(from_py_object, module = "databento_dbn")
1074)]
1075#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
1076#[non_exhaustive]
1077#[repr(u8)]
1078pub enum StatUpdateAction {
1079    /// A new statistic.
1080    #[default]
1081    #[pyo3(name = "NEW")]
1082    New = 1,
1083    /// A removal of a statistic.
1084    #[pyo3(name = "DELETE")]
1085    Delete = 2,
1086}
1087
1088/// The primary enum for the type of [`StatusMsg`](crate::record::StatusMsg) update.
1089#[derive(
1090    Debug,
1091    Clone,
1092    Copy,
1093    Default,
1094    PartialEq,
1095    Eq,
1096    PartialOrd,
1097    Ord,
1098    Hash,
1099    TryFromPrimitive,
1100    IntoPrimitive,
1101)]
1102#[cfg_attr(
1103    feature = "python",
1104    derive(strum::EnumIter),
1105    pyo3::pyclass(from_py_object, module = "databento_dbn")
1106)]
1107#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
1108#[non_exhaustive]
1109#[repr(u16)]
1110pub enum StatusAction {
1111    /// No change.
1112    #[default]
1113    #[pyo3(name = "NONE")]
1114    None = 0,
1115    /// The instrument is in a pre-open period.
1116    #[pyo3(name = "PRE_OPEN")]
1117    PreOpen = 1,
1118    /// The instrument is in a pre-cross period.
1119    #[pyo3(name = "PRE_CROSS")]
1120    PreCross = 2,
1121    /// The instrument is quoting but not trading.
1122    #[pyo3(name = "QUOTING")]
1123    Quoting = 3,
1124    /// The instrument is in a cross/auction.
1125    #[pyo3(name = "CROSS")]
1126    Cross = 4,
1127    /// The instrument is being opened through a trading rotation.
1128    #[pyo3(name = "ROTATION")]
1129    Rotation = 5,
1130    /// A new price indication is available for the instrument.
1131    #[pyo3(name = "NEW_PRICE_INDICATION")]
1132    NewPriceIndication = 6,
1133    /// The instrument is trading.
1134    #[pyo3(name = "TRADING")]
1135    Trading = 7,
1136    /// Trading in the instrument has been halted.
1137    #[pyo3(name = "HALT")]
1138    Halt = 8,
1139    /// Trading in the instrument has been paused.
1140    #[pyo3(name = "PAUSE")]
1141    Pause = 9,
1142    /// Trading in the instrument has been suspended.
1143    #[pyo3(name = "SUSPEND")]
1144    Suspend = 10,
1145    /// The instrument is in a pre-close period.
1146    #[pyo3(name = "PRE_CLOSE")]
1147    PreClose = 11,
1148    /// Trading in the instrument has closed.
1149    #[pyo3(name = "CLOSE")]
1150    Close = 12,
1151    /// The instrument is in a post-close period.
1152    #[pyo3(name = "POST_CLOSE")]
1153    PostClose = 13,
1154    /// A change in short-selling restrictions.
1155    #[pyo3(name = "SSR_CHANGE")]
1156    SsrChange = 14,
1157    /// The instrument is not available for trading, either trading has closed or been halted.
1158    #[pyo3(name = "NOT_AVAILABLE_FOR_TRADING")]
1159    NotAvailableForTrading = 15,
1160}
1161
1162/// The secondary enum for a [`StatusMsg`](crate::record::StatusMsg) update, explains
1163/// the cause of a halt or other change in `action`.
1164#[derive(
1165    Debug,
1166    Clone,
1167    Copy,
1168    Default,
1169    PartialEq,
1170    Eq,
1171    PartialOrd,
1172    Ord,
1173    Hash,
1174    TryFromPrimitive,
1175    IntoPrimitive,
1176)]
1177#[cfg_attr(
1178    feature = "python",
1179    derive(strum::EnumIter),
1180    pyo3::pyclass(from_py_object, module = "databento_dbn")
1181)]
1182#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
1183#[non_exhaustive]
1184#[repr(u16)]
1185pub enum StatusReason {
1186    /// No reason is given.
1187    #[default]
1188    #[pyo3(name = "NONE")]
1189    None = 0,
1190    /// The change in status occurred as scheduled.
1191    #[pyo3(name = "SCHEDULED")]
1192    Scheduled = 1,
1193    /// The instrument stopped due to a market surveillance intervention.
1194    #[pyo3(name = "SURVEILLANCE_INTERVENTION")]
1195    SurveillanceIntervention = 2,
1196    /// The status changed due to activity in the market.
1197    #[pyo3(name = "MARKET_EVENT")]
1198    MarketEvent = 3,
1199    /// The derivative instrument began trading.
1200    #[pyo3(name = "INSTRUMENT_ACTIVATION")]
1201    InstrumentActivation = 4,
1202    /// The derivative instrument expired.
1203    #[pyo3(name = "INSTRUMENT_EXPIRATION")]
1204    InstrumentExpiration = 5,
1205    /// Recovery in progress.
1206    #[pyo3(name = "RECOVERY_IN_PROCESS")]
1207    RecoveryInProcess = 6,
1208    /// The status change was caused by a regulatory action.
1209    #[pyo3(name = "REGULATORY")]
1210    Regulatory = 10,
1211    /// The status change was caused by an administrative action.
1212    #[pyo3(name = "ADMINISTRATIVE")]
1213    Administrative = 11,
1214    /// The status change was caused by the issuer not being compliance with regulatory
1215    /// requirements.
1216    #[pyo3(name = "NON_COMPLIANCE")]
1217    NonCompliance = 12,
1218    /// Trading halted because the issuer's filings are not current.
1219    #[pyo3(name = "FILINGS_NOT_CURRENT")]
1220    FilingsNotCurrent = 13,
1221    /// Trading halted due to an SEC trading suspension.
1222    #[pyo3(name = "SEC_TRADING_SUSPENSION")]
1223    SecTradingSuspension = 14,
1224    /// The status changed because a new issue is available.
1225    #[pyo3(name = "NEW_ISSUE")]
1226    NewIssue = 15,
1227    /// The status changed because an issue is available.
1228    #[pyo3(name = "ISSUE_AVAILABLE")]
1229    IssueAvailable = 16,
1230    /// The status changed because the issue(s) were reviewed.
1231    #[pyo3(name = "ISSUES_REVIEWED")]
1232    IssuesReviewed = 17,
1233    /// The status changed because the filing requirements were satisfied.
1234    #[pyo3(name = "FILING_REQS_SATISFIED")]
1235    FilingReqsSatisfied = 18,
1236    /// Relevant news is pending.
1237    #[pyo3(name = "NEWS_PENDING")]
1238    NewsPending = 30,
1239    /// Relevant news was released.
1240    #[pyo3(name = "NEWS_RELEASED")]
1241    NewsReleased = 31,
1242    /// The news has been fully disseminated and times are available for the resumption
1243    /// in quoting and trading.
1244    #[pyo3(name = "NEWS_AND_RESUMPTION_TIMES")]
1245    NewsAndResumptionTimes = 32,
1246    /// The relevant news was not forthcoming.
1247    #[pyo3(name = "NEWS_NOT_FORTHCOMING")]
1248    NewsNotForthcoming = 33,
1249    /// Halted for order imbalance.
1250    #[pyo3(name = "ORDER_IMBALANCE")]
1251    OrderImbalance = 40,
1252    /// The instrument hit limit up or limit down.
1253    #[pyo3(name = "LULD_PAUSE")]
1254    LuldPause = 50,
1255    /// An operational issue occurred with the venue.
1256    #[pyo3(name = "OPERATIONAL")]
1257    Operational = 60,
1258    /// The status changed until the exchange receives additional information.
1259    #[pyo3(name = "ADDITIONAL_INFORMATION_REQUESTED")]
1260    AdditionalInformationRequested = 70,
1261    /// Trading halted due to merger becoming effective.
1262    #[pyo3(name = "MERGER_EFFECTIVE")]
1263    MergerEffective = 80,
1264    /// Trading is halted in an ETF due to conditions with the component securities.
1265    #[pyo3(name = "ETF")]
1266    Etf = 90,
1267    /// Trading is halted for a corporate action.
1268    #[pyo3(name = "CORPORATE_ACTION")]
1269    CorporateAction = 100,
1270    /// Trading is halted because the instrument is a new offering.
1271    #[pyo3(name = "NEW_SECURITY_OFFERING")]
1272    NewSecurityOffering = 110,
1273    /// Halted due to the market-wide circuit breaker level 1.
1274    #[pyo3(name = "MARKET_WIDE_HALT_LEVEL1")]
1275    MarketWideHaltLevel1 = 120,
1276    /// Halted due to the market-wide circuit breaker level 2.
1277    #[pyo3(name = "MARKET_WIDE_HALT_LEVEL2")]
1278    MarketWideHaltLevel2 = 121,
1279    /// Halted due to the market-wide circuit breaker level 3.
1280    #[pyo3(name = "MARKET_WIDE_HALT_LEVEL3")]
1281    MarketWideHaltLevel3 = 122,
1282    /// Halted due to the carryover of a market-wide circuit breaker from the previous
1283    /// trading day.
1284    #[pyo3(name = "MARKET_WIDE_HALT_CARRYOVER")]
1285    MarketWideHaltCarryover = 123,
1286    /// Resumption due to the end of a market-wide circuit breaker halt.
1287    #[pyo3(name = "MARKET_WIDE_HALT_RESUMPTION")]
1288    MarketWideHaltResumption = 124,
1289    /// Halted because quotation is not available.
1290    #[pyo3(name = "QUOTATION_NOT_AVAILABLE")]
1291    QuotationNotAvailable = 130,
1292}
1293
1294/// Further information about a status update.
1295#[derive(
1296    Debug,
1297    Clone,
1298    Copy,
1299    Default,
1300    PartialEq,
1301    Eq,
1302    PartialOrd,
1303    Ord,
1304    Hash,
1305    TryFromPrimitive,
1306    IntoPrimitive,
1307)]
1308#[cfg_attr(
1309    feature = "python",
1310    derive(strum::EnumIter),
1311    pyo3::pyclass(from_py_object, module = "databento_dbn")
1312)]
1313#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
1314#[non_exhaustive]
1315#[repr(u16)]
1316pub enum TradingEvent {
1317    /// No additional information given.
1318    #[default]
1319    #[pyo3(name = "NONE")]
1320    None = 0,
1321    /// Order entry is allowed. Modification and cancellation are not allowed.
1322    #[pyo3(name = "NO_CANCEL")]
1323    NoCancel = 1,
1324    /// A change of trading session occurred. Daily statistics are reset.
1325    #[pyo3(name = "CHANGE_TRADING_SESSION")]
1326    ChangeTradingSession = 2,
1327    /// Implied matching is available.
1328    #[pyo3(name = "IMPLIED_MATCHING_ON")]
1329    ImpliedMatchingOn = 3,
1330    /// Implied matching is not available.
1331    #[pyo3(name = "IMPLIED_MATCHING_OFF")]
1332    ImpliedMatchingOff = 4,
1333}
1334
1335/// An enum for representing unknown, true, or false values. Equivalent to
1336/// `Option<bool>` but with a human-readable repr.
1337#[derive(
1338    Debug,
1339    Clone,
1340    Copy,
1341    Default,
1342    PartialEq,
1343    Eq,
1344    PartialOrd,
1345    Ord,
1346    Hash,
1347    TryFromPrimitive,
1348    IntoPrimitive,
1349)]
1350#[cfg_attr(
1351    feature = "python",
1352    derive(strum::EnumIter),
1353    pyo3::pyclass(from_py_object, module = "databento_dbn")
1354)]
1355#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
1356#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1357#[repr(u8)]
1358pub enum TriState {
1359    /// The value is not applicable or not known.
1360    #[default]
1361    #[pyo3(name = "NOT_AVAILABLE")]
1362    NotAvailable = b'~',
1363    /// False.
1364    #[pyo3(name = "NO")]
1365    No = b'N',
1366    /// True.
1367    #[pyo3(name = "YES")]
1368    Yes = b'Y',
1369}
1370
1371impl From<TriState> for char {
1372    fn from(value: TriState) -> Self {
1373        u8::from(value) as char
1374    }
1375}
1376
1377/// How to handle decoding DBN data from other versions.
1378#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1379#[cfg_attr(
1380    feature = "python",
1381    derive(strum::EnumIter),
1382    pyo3::pyclass(from_py_object, module = "databento_dbn")
1383)]
1384#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
1385pub enum VersionUpgradePolicy {
1386    /// Decode data from all supported versions (less than or equal to
1387    /// [`DBN_VERSION`](crate::DBN_VERSION)) as-is.
1388    #[pyo3(name = "AS_IS")]
1389    AsIs,
1390    /// Decode and convert data from DBN versions prior to version 2 to that version.
1391    /// Attempting to decode data from newer versions will fail.
1392    #[pyo3(name = "UPGRADE_TO_V2")]
1393    UpgradeToV2,
1394    /// Decode and convert data from DBN versions prior to version 3 to that version.
1395    /// Attempting to decode data from newer versions (when they're introduced) will
1396    /// fail.
1397    #[default]
1398    #[pyo3(name = "UPGRADE_TO_V3")]
1399    UpgradeToV3,
1400}
1401
1402/// An error code from the live subscription gateway.
1403#[derive(
1404    Debug,
1405    Clone,
1406    Copy,
1407    Default,
1408    PartialEq,
1409    Eq,
1410    PartialOrd,
1411    Ord,
1412    Hash,
1413    TryFromPrimitive,
1414    IntoPrimitive,
1415)]
1416#[cfg_attr(
1417    feature = "python",
1418    derive(strum::EnumIter),
1419    pyo3::pyclass(from_py_object, module = "databento_dbn")
1420)]
1421#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
1422#[non_exhaustive]
1423#[repr(u8)]
1424pub enum ErrorCode {
1425    /// The authentication step failed.
1426    #[pyo3(name = "AUTH_FAILED")]
1427    AuthFailed = 1,
1428    /// The user account or API key were deactivated.
1429    #[pyo3(name = "API_KEY_DEACTIVATED")]
1430    ApiKeyDeactivated = 2,
1431    /// The user has exceeded their open connection limit.
1432    #[pyo3(name = "CONNECTION_LIMIT_EXCEEDED")]
1433    ConnectionLimitExceeded = 3,
1434    /// One or more symbols failed to resolve.
1435    #[pyo3(name = "SYMBOL_RESOLUTION_FAILED")]
1436    SymbolResolutionFailed = 4,
1437    /// There was an issue with a subscription request (other than symbol resolution).
1438    #[pyo3(name = "INVALID_SUBSCRIPTION")]
1439    InvalidSubscription = 5,
1440    /// An error occurred in the gateway.
1441    #[pyo3(name = "INTERNAL_ERROR")]
1442    InternalError = 6,
1443    /// A slow client was detected and records were skipped by the gateway to allow catching up.
1444    #[pyo3(name = "SKIPPED_RECORDS_AFTER_SLOW_READING")]
1445    SkippedRecordsAfterSlowReading = 7,
1446    /// No error code was specified or this record was upgraded from a version 1 struct where the code field didn't exist.
1447    #[default]
1448    #[pyo3(name = "UNSET")]
1449    Unset = 255,
1450}
1451
1452impl std::str::FromStr for ErrorCode {
1453    type Err = crate::Error;
1454
1455    fn from_str(s: &str) -> Result<Self, Self::Err> {
1456        match s {
1457            "auth_failed" => Ok(Self::AuthFailed),
1458            "api_key_deactivated" => Ok(Self::ApiKeyDeactivated),
1459            "connection_limit_exceeded" => Ok(Self::ConnectionLimitExceeded),
1460            "symbol_resolution_failed" => Ok(Self::SymbolResolutionFailed),
1461            "invalid_subscription" => Ok(Self::InvalidSubscription),
1462            "internal_error" => Ok(Self::InternalError),
1463            "skipped_records_after_slow_reading" => Ok(Self::SkippedRecordsAfterSlowReading),
1464            "unset" => Ok(Self::Unset),
1465            _ => Err(crate::Error::conversion::<Self>(s.to_owned())),
1466        }
1467    }
1468}
1469
1470impl AsRef<str> for ErrorCode {
1471    fn as_ref(&self) -> &str {
1472        self.as_str()
1473    }
1474}
1475
1476impl ErrorCode {
1477    /// Converts the `ErrorCode` to its `str` representation.
1478    pub const fn as_str(&self) -> &'static str {
1479        match self {
1480            Self::AuthFailed => "auth_failed",
1481            Self::ApiKeyDeactivated => "api_key_deactivated",
1482            Self::ConnectionLimitExceeded => "connection_limit_exceeded",
1483            Self::SymbolResolutionFailed => "symbol_resolution_failed",
1484            Self::InvalidSubscription => "invalid_subscription",
1485            Self::InternalError => "internal_error",
1486            Self::SkippedRecordsAfterSlowReading => "skipped_records_after_slow_reading",
1487            Self::Unset => "unset",
1488        }
1489    }
1490}
1491
1492impl Display for ErrorCode {
1493    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1494        f.write_str(self.as_str())
1495    }
1496}
1497
1498/// A [`SystemMsg`](crate::SystemMsg) code indicating the type of message from the live
1499/// subscription gateway.
1500#[derive(
1501    Debug,
1502    Clone,
1503    Copy,
1504    Default,
1505    PartialEq,
1506    Eq,
1507    PartialOrd,
1508    Ord,
1509    Hash,
1510    TryFromPrimitive,
1511    IntoPrimitive,
1512)]
1513#[cfg_attr(
1514    feature = "python",
1515    derive(strum::EnumIter),
1516    pyo3::pyclass(from_py_object, module = "databento_dbn")
1517)]
1518#[cfg_attr(not(feature = "python"), derive(MockPyo3))]
1519#[non_exhaustive]
1520#[repr(u8)]
1521pub enum SystemCode {
1522    /// A message sent in the absence of other records to indicate the connection
1523    /// remains open.
1524    #[pyo3(name = "HEARTBEAT")]
1525    Heartbeat = 0,
1526    /// An acknowledgement of a subscription request.
1527    #[pyo3(name = "SUBSCRIPTION_ACK")]
1528    SubscriptionAck = 1,
1529    /// The gateway has detected this session is falling behind real-time.
1530    #[pyo3(name = "SLOW_READER_WARNING")]
1531    SlowReaderWarning = 2,
1532    /// Indicates a replay subscription has caught up with real-time data.
1533    #[pyo3(name = "REPLAY_COMPLETED")]
1534    ReplayCompleted = 3,
1535    /// Signals that all records for interval-based schemas have been published for the given timestamp.
1536    #[pyo3(name = "END_OF_INTERVAL")]
1537    EndOfInterval = 4,
1538    /// No system code was specified or this record was upgraded from a version 1 struct where
1539    /// the code field didn't exist.
1540    #[default]
1541    #[pyo3(name = "UNSET")]
1542    Unset = 255,
1543}
1544
1545impl std::str::FromStr for SystemCode {
1546    type Err = crate::Error;
1547
1548    fn from_str(s: &str) -> Result<Self, Self::Err> {
1549        match s {
1550            "heartbeat" => Ok(Self::Heartbeat),
1551            "subscription_ack" => Ok(Self::SubscriptionAck),
1552            "slow_reader_warning" => Ok(Self::SlowReaderWarning),
1553            "replay_completed" => Ok(Self::ReplayCompleted),
1554            "end_of_interval" => Ok(Self::EndOfInterval),
1555            "unset" => Ok(Self::Unset),
1556            _ => Err(crate::Error::conversion::<Self>(s.to_owned())),
1557        }
1558    }
1559}
1560
1561impl AsRef<str> for SystemCode {
1562    fn as_ref(&self) -> &str {
1563        self.as_str()
1564    }
1565}
1566
1567impl SystemCode {
1568    /// Converts the `SystemCode` to its `str` representation.
1569    pub const fn as_str(&self) -> &'static str {
1570        match self {
1571            Self::Heartbeat => "heartbeat",
1572            Self::SubscriptionAck => "subscription_ack",
1573            Self::SlowReaderWarning => "slow_reader_warning",
1574            Self::ReplayCompleted => "replay_completed",
1575            Self::EndOfInterval => "end_of_interval",
1576            Self::Unset => "unset",
1577        }
1578    }
1579}
1580
1581impl Display for SystemCode {
1582    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1583        f.write_str(self.as_str())
1584    }
1585}
1586
1587#[cfg(feature = "serde")]
1588mod deserialize {
1589    use std::str::FromStr;
1590
1591    use serde::{de, Deserialize, Deserializer, Serialize};
1592
1593    use super::*;
1594
1595    impl<'de> Deserialize<'de> for RType {
1596        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1597            let str = String::deserialize(deserializer)?;
1598            FromStr::from_str(&str).map_err(de::Error::custom)
1599        }
1600    }
1601
1602    impl Serialize for RType {
1603        fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1604        where
1605            S: serde::Serializer,
1606        {
1607            self.as_str().serialize(serializer)
1608        }
1609    }
1610
1611    impl<'de> Deserialize<'de> for SType {
1612        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1613            let str = String::deserialize(deserializer)?;
1614            FromStr::from_str(&str).map_err(de::Error::custom)
1615        }
1616    }
1617
1618    impl Serialize for SType {
1619        fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1620        where
1621            S: serde::Serializer,
1622        {
1623            self.as_str().serialize(serializer)
1624        }
1625    }
1626
1627    impl<'de> Deserialize<'de> for Schema {
1628        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1629            let str = String::deserialize(deserializer)?;
1630            FromStr::from_str(&str).map_err(de::Error::custom)
1631        }
1632    }
1633
1634    impl Serialize for Schema {
1635        fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1636        where
1637            S: serde::Serializer,
1638        {
1639            self.as_str().serialize(serializer)
1640        }
1641    }
1642
1643    impl<'de> Deserialize<'de> for Encoding {
1644        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1645            let str = String::deserialize(deserializer)?;
1646            FromStr::from_str(&str).map_err(de::Error::custom)
1647        }
1648    }
1649
1650    impl Serialize for Encoding {
1651        fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1652        where
1653            S: serde::Serializer,
1654        {
1655            self.as_str().serialize(serializer)
1656        }
1657    }
1658
1659    impl<'de> Deserialize<'de> for Compression {
1660        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1661            let str = String::deserialize(deserializer)?;
1662            FromStr::from_str(&str).map_err(de::Error::custom)
1663        }
1664    }
1665
1666    impl Serialize for Compression {
1667        fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1668        where
1669            S: serde::Serializer,
1670        {
1671            self.as_str().serialize(serializer)
1672        }
1673    }
1674
1675    impl<'de> Deserialize<'de> for ErrorCode {
1676        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1677            let str = String::deserialize(deserializer)?;
1678            FromStr::from_str(&str).map_err(de::Error::custom)
1679        }
1680    }
1681
1682    impl Serialize for ErrorCode {
1683        fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1684        where
1685            S: serde::Serializer,
1686        {
1687            self.as_str().serialize(serializer)
1688        }
1689    }
1690
1691    impl<'de> Deserialize<'de> for SystemCode {
1692        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1693            let str = String::deserialize(deserializer)?;
1694            FromStr::from_str(&str).map_err(de::Error::custom)
1695        }
1696    }
1697
1698    impl Serialize for SystemCode {
1699        fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1700        where
1701            S: serde::Serializer,
1702        {
1703            self.as_str().serialize(serializer)
1704        }
1705    }
1706}