1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
//! Bit set flags used in Databento market data.
use std::fmt;
#[cfg(feature = "python")]
use pyo3::prelude::*;
/// Indicates it's the last record in the event from the venue for a given
/// `instrument_id`.
pub const LAST: u8 = 1 << 7;
/// Indicates a top-of-book record, not an individual order.
pub const TOB: u8 = 1 << 6;
/// Indicates the record was sourced from a replay, such as a snapshot server.
pub const SNAPSHOT: u8 = 1 << 5;
/// Indicates an aggregated price level record, not an individual order.
pub const MBP: u8 = 1 << 4;
/// Indicates the `ts_recv` value is inaccurate due to clock issues or packet
/// reordering.
pub const BAD_TS_RECV: u8 = 1 << 3;
/// Indicates an unrecoverable gap was detected in the channel.
pub const MAYBE_BAD_BOOK: u8 = 1 << 2;
/// A transparent wrapper around the bit field used in several DBN record types,
/// namely [`MboMsg`](crate::MboMsg) and record types derived from it.
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, Default, Hash)]
#[cfg_attr(feature = "python", derive(FromPyObject), pyo3(transparent))]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(transparent)
)]
pub struct FlagSet {
raw: u8,
}
impl fmt::Debug for FlagSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut has_written_flag = false;
for (flag, name) in [
(LAST, stringify!(LAST)),
(TOB, stringify!(TOB)),
(SNAPSHOT, stringify!(SNAPSHOT)),
(MBP, stringify!(MBP)),
(BAD_TS_RECV, stringify!(BAD_TS_RECV)),
(MAYBE_BAD_BOOK, stringify!(MAYBE_BAD_BOOK)),
] {
if (self.raw() & flag) > 0 {
if has_written_flag {
write!(f, " | {name}")?;
} else {
write!(f, "{name}")?;
has_written_flag = true;
}
}
}
if has_written_flag {
write!(f, " ({})", self.raw())
} else {
write!(f, "{}", self.raw())
}
}
}
impl From<u8> for FlagSet {
fn from(raw: u8) -> Self {
Self { raw }
}
}
impl FlagSet {
/// Returns an empty [`FlagSet`]: one with no flags set.
pub const fn empty() -> Self {
Self { raw: 0 }
}
/// Creates a new flag set from `raw`.
pub const fn new(raw: u8) -> Self {
Self { raw }
}
/// Turns all flags off, i.e. to `false`.
pub fn clear(&mut self) -> &mut Self {
self.raw = 0;
self
}
/// Returns the raw value.
pub const fn raw(&self) -> u8 {
self.raw
}
/// Sets the flags directly with a raw `u8`.
pub fn set_raw(&mut self, raw: u8) {
self.raw = raw;
}
/// Returns `true` if any of the flags are on or set to true.
pub const fn any(&self) -> bool {
self.raw > 0
}
/// Returns `true` if all flags are unset/false.
pub fn is_empty(&self) -> bool {
self.raw == 0
}
/// Returns `true` if it's the last record in the event from the venue for a given
/// `instrument_id`.
pub const fn is_last(&self) -> bool {
(self.raw & LAST) > 0
}
/// Sets the `LAST` bit flag to `true` to indicate this is the last record in the
/// event for a given instrument.
pub fn set_last(&mut self) -> Self {
self.raw |= LAST;
*self
}
/// Returns `true` if it's a top-of-book record, not an individual order.
pub const fn is_tob(&self) -> bool {
(self.raw & TOB) > 0
}
/// Sets the `TOB` bit flag to `true` to indicate this is a top-of-book record.
pub fn set_tob(&mut self) -> Self {
self.raw |= TOB;
*self
}
/// Returns `true` if this record was sourced from a replay, such as a snapshot
/// server.
pub const fn is_snapshot(&self) -> bool {
(self.raw & SNAPSHOT) > 0
}
/// Sets the `SNAPSHOT` bit flag to `true` to indicate this record was sourced from
/// a replay.
pub fn set_snapshot(&mut self) -> Self {
self.raw |= SNAPSHOT;
*self
}
/// Returns `true` if this record is an aggregated price level record, not an
/// individual order.
pub const fn is_mbp(&self) -> bool {
(self.raw & MBP) > 0
}
/// Sets the `MBP` bit flag to `true` to indicate this record is an aggregated price
/// level record.
pub fn set_mbp(&mut self) -> Self {
self.raw |= MBP;
*self
}
/// Returns `true` if this record has an inaccurate `ts_recv` value due to clock
/// issues or packet reordering.
pub const fn is_bad_ts_recv(&self) -> bool {
(self.raw & BAD_TS_RECV) > 0
}
/// Sets the `BAD_TS_RECV` bit flag to `true` to indicate this record has an
/// inaccurate `ts_recv` value.
pub fn set_bad_ts_recv(&mut self) -> Self {
self.raw |= BAD_TS_RECV;
*self
}
/// Returns `true` if this record is from a channel where an unrecoverable gap was
/// detected.
pub const fn is_maybe_bad_book(&self) -> bool {
(self.raw & MAYBE_BAD_BOOK) > 0
}
/// Sets the `MAYBE_BAD_BOOK` bit flag to `true` to indicate this record is from a
/// channel where an unrecoverable gap was detected.
pub fn set_maybe_bad_book(&mut self) -> Self {
self.raw |= MAYBE_BAD_BOOK;
*self
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::*;
#[rstest]
#[case::empty(FlagSet::empty(), "0")]
#[case::one_set(FlagSet::empty().set_mbp(), "MBP (16)")]
#[case::three_set(FlagSet::empty().set_tob().set_snapshot().set_maybe_bad_book(), "TOB | SNAPSHOT | MAYBE_BAD_BOOK (100)")]
#[case::reserved_set(
FlagSet::new(255),
"LAST | TOB | SNAPSHOT | MBP | BAD_TS_RECV | MAYBE_BAD_BOOK (255)"
)]
fn dbg(#[case] target: FlagSet, #[case] exp: &str) {
assert_eq!(format!("{target:?}"), exp);
}
}