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