//! Definitions for types defined by the protocol plus some helper structs e.g., [`Symbol`]
//! See docs on each struct for description or see the official protocol spec.
//!
//! [Nasdaq TotalView-ITCH 5.0](https://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/NQTVITCHspecification.pdf).
use zerocopy::{
FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned,
network_endian::{U16, U32, U64},
};
/// Prices are integer fields, supplied with an associated precision. When converted to a decimal format, prices are in
/// fixed point format, where the precision defines the number of decimal places. For example, a field flagged as Price
/// (4) has an implied 4 decimal places. The maximum value of price (4) in TotalView ITCH is 200,000.0000 (decimal,
/// 77359400 hex).
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug, Copy, Clone)]
#[repr(transparent)]
pub struct Price4([u8; 4]);
impl Price4 {
#[inline]
pub const fn into_u32(&self) -> u32 {
u32::from_be_bytes(self.0)
}
#[inline]
pub const fn into_i64(&self) -> i64 {
self.into_u32() as i64
}
#[deprecated]
#[inline]
pub fn into_f64(&self) -> f64 {
f64::from(self.into_u32()) / 10000.0
}
}
/// Prices are integer fields, supplied with an associated precision. When converted to a decimal format, prices are in
/// fixed point format, where the precision defines the number of decimal places.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug, Copy, Clone)]
#[repr(transparent)]
pub struct Price8([u8; 8]);
impl Price8 {
#[inline]
pub const fn into_u64(&self) -> u64 {
u64::from_be_bytes(self.0)
}
#[deprecated]
#[inline]
pub const fn into_f64(&self) -> f64 {
self.into_u64() as f64 / 1_0000_0000.0
}
}
/// Timestamp is u48 representing nano-seconds since midnight of a particular trading day.
#[derive(
FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug, Copy, Clone, PartialEq,
)]
#[repr(transparent)]
pub struct Timestamp([u8; 6]);
impl Timestamp {
/// returns nanoseconds since midnight
#[inline]
pub const fn to_u64(&self) -> u64 {
u64::from_be_bytes([
0, 0, self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5],
])
}
#[inline]
pub const fn to_dur(&self) -> std::time::Duration {
let (secs, nanos) = self.to_secs_and_nanos();
std::time::Duration::new(secs, nanos)
}
#[inline]
pub const fn to_secs_and_nanos(&self) -> (u64, u32) {
let nanos = self.to_u64();
(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32)
}
#[cfg(feature = "chrono")]
#[inline]
pub const fn to_naive_time(&self) -> chrono::NaiveTime {
let (secs, nanos) = self.to_secs_and_nanos();
debug_assert!(secs < 86_400);
chrono::NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nanos).unwrap()
}
}
/// Symbol is a container of a stock's symbol. e.g., AAPL, TSLA, MSFT etc
///
/// Ticker symbols are fixed width (8) uppercased ASCII arrays, right padded with `b' '`.
#[derive(
FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug, Copy, Clone, PartialEq,
)]
#[repr(transparent)]
pub struct Symbol([u8; 8]);
impl Symbol {
/// Converts bytes of symbol to u64, big endian.
#[inline]
pub const fn to_u64(&self) -> u64 {
u64::from_be_bytes(self.0)
}
/// Converts u64 to [`Symbol`], big endian.
#[inline]
pub const fn from_u64(hash: u64) -> Symbol {
Symbol(hash.to_be_bytes())
}
/// Returns str representation of symbol or `"unknown"` if invalid utf-8
/// Useful for debugging.
#[inline]
pub fn as_str(&self) -> &str {
str::from_utf8(&self.0).unwrap_or("unknown")
}
}
impl std::str::FromStr for Symbol {
type Err = &'static str;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() == 0 {
return Err("empty input");
}
let mut arr = [b' '; 8];
let upper = s.to_uppercase();
let bytes = upper.as_bytes();
let len = bytes.len().min(8);
arr[..len].copy_from_slice(&bytes[..len]);
Ok(Self(arr))
}
}
/// System Event Message
/// The system event message type is used to signal a market or data feed handler event.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct SystemEvent {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
event_code: u8,
}
impl SystemEvent {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'S';
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
#[inline]
pub const fn event_code(&self) -> SystemEventCode {
SystemEventCode::from_byte(self.event_code)
}
}
/// Nasdaq supports the following event codes on a daily basis on the TotalView-ITCH data feed.
/// Code Explanation
/// "O" Start of Messages. Outside of time stamp messages, the start of day message is the first message sent in
/// any trading day.
/// "S" Start of System hours. This message indicates that NASDAQ is open and ready to start accepting orders.
/// "Q" Start of Market hours. This message is intended to indicate that Market Hours orders are available
/// for execution.
/// "M" End of Market hours. This message is intended to indicate that Market Hours orders are no longer
/// available for execution.
/// "E" End of System hours. It indicates that Nasdaq is now closed and will not accept any new orders today.
/// It is still possible to receive Broken Trade messages and Order Delete messages after the End of Day
/// ."C" End of Messages. This is always the last message sent in any trading day.
#[derive(Debug)]
#[repr(u8)]
pub enum SystemEventCode {
StartOfMessages = b'O',
StartOfSystemHours = b'S',
StartOfMarketHours = b'Q',
EndOfMarketHours = b'M',
EndOfSystemHours = b'E',
EndOfMessages = b'C',
Unknown(u8),
}
impl SystemEventCode {
#[inline]
pub const fn from_byte(b: u8) -> Self {
match b {
b'O' => Self::StartOfMessages,
b'S' => Self::StartOfSystemHours,
b'Q' => Self::StartOfMarketHours,
b'M' => Self::EndOfMarketHours,
b'E' => Self::EndOfSystemHours,
b'C' => Self::EndOfMessages,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for SystemEventCode {
#[inline]
fn from(value: u8) -> Self {
SystemEventCode::from_byte(value)
}
}
/// Stock Directory
/// At the start of each trading day, Nasdaq disseminates stock directory messages for all active symbols in the Nasdaq
/// execution system.
/// Market data redistributors should process this message to populate the Financial Status Indicator (required display field) and the Market Category (recommended display field) for Nasdaq listed issues.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct StockDirectory {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
stock: Symbol,
market_category: u8,
financial_status_indicator: u8,
round_lot_size: U32,
round_lots_only: u8,
issue_classification: u8,
issue_sub_type: [u8; 2],
authenticity: u8,
short_sale_threshold_indicator: u8,
ipo_flag: u8,
luld_reference_price_tier: u8,
etp_flag: u8,
etp_leverage_factor: U32,
inverse_indicator: u8,
}
impl StockDirectory {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'R';
/// Locate Code uniquely assigned to the security symbol for the day.
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Time at which the directory message was generated. Refer to Data Types for field processing notes.
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Denotes the security symbol for the issue in the Nasdaq execution system.
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// Indicates Listing market or listing market tier for the issue
#[inline]
pub const fn market_category(&self) -> MarketCategory {
MarketCategory::from_byte(self.market_category)
}
/// For Nasdaq listed issues, this field indicates when a firm is not in compliance with Nasdaq continued listing requirements
#[inline]
pub const fn financial_status_indicator(&self) -> FinancialStatusIndicator {
FinancialStatusIndicator::from_byte(self.financial_status_indicator)
}
/// Denotes the number of shares that represent a round lot for the issue
#[inline]
pub const fn round_lot_size(&self) -> u32 {
self.round_lot_size.get()
}
/// Indicates if Nasdaq system limits order entry for issue
#[inline]
pub const fn round_lots_only(&self) -> RoundLotsOnly {
RoundLotsOnly::from_byte(self.round_lots_only)
}
/// Identifies the security class for the issue as assigned by Nasdaq. See Appendix for allowable values.
#[inline]
pub const fn issue_classification(&self) -> IssueClassificationCode {
IssueClassificationCode::from_byte(self.issue_classification)
}
/// Identifies the security sub-type for the issue as assigned by Nasdaq. See Appendix for allowable values.
#[inline]
pub const fn issue_sub_type(&self) -> IssueSubTypeCode {
IssueSubTypeCode::from_bytes(self.issue_sub_type)
}
/// Denotes if an issue or quoting participant record is set-up in Nasdaq systems in a live/production, test, or demo state.
#[inline]
pub const fn authenticity(&self) -> Authenticity {
Authenticity::from_byte(self.authenticity)
}
/// Indicates if a security is subject to mandatory close-out of short sales under SEC Rule 203(b)(3).
#[inline]
pub const fn short_sale_threshold_indicator(&self) -> ShortSaleThresholdIndicator {
ShortSaleThresholdIndicator::from_byte(self.short_sale_threshold_indicator)
}
/// Indicates if the Nasdaq security is set up for IPO release.
#[inline]
pub const fn ipo_flag(&self) -> IpoFlag {
IpoFlag::from_byte(self.ipo_flag)
}
/// Indicates which Limit Up / Limit Down price band calculation parameter is to be used for the instrument.
#[inline]
pub const fn luld_reference_price_tier(&self) -> LuldReferencePriceTier {
LuldReferencePriceTier::from_byte(self.luld_reference_price_tier)
}
/// Indicates whether the security is an exchange traded product (ETP).
#[inline]
pub const fn etp_flag(&self) -> EtpFlag {
EtpFlag::from_byte(self.etp_flag)
}
/// Tracks the integral relationship of the ETP to the underlying index.
#[inline]
pub const fn etp_leverage_factor(&self) -> u32 {
self.etp_leverage_factor.get()
}
/// Indicates the directional relationship between the ETP and Underlying index.
#[inline]
pub const fn inverse_indicator(&self) -> InverseIndicator {
InverseIndicator::from_byte(self.inverse_indicator)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum MarketCategory {
NasdaqGlobalSelectMarket = b'Q',
NasdaqGlobalMarket = b'G',
NasdaqCapitalMarket = b'S',
NYSE = b'N',
NYSEAmerican = b'A',
NYSEArca = b'P',
BATSZExchange = b'Z',
InvestorsExchangeLLC = b'V',
NotAvailable = b' ',
Unknown(u8),
}
impl MarketCategory {
#[inline]
pub const fn from_byte(b: u8) -> Self {
match b {
b'Q' => Self::NasdaqGlobalSelectMarket,
b'G' => Self::NasdaqGlobalMarket,
b'S' => Self::NasdaqCapitalMarket,
b'N' => Self::NYSE,
b'A' => Self::NYSEAmerican,
b'P' => Self::NYSEArca,
b'Z' => Self::BATSZExchange,
b'V' => Self::InvestorsExchangeLLC,
b' ' => Self::NotAvailable,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for MarketCategory {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum FinancialStatusIndicator {
Deficient = b'D',
Delinquent = b'E',
Bankrupt = b'Q',
Suspended = b'S',
DeficientBankrupt = b'G',
DeficientDelinquent = b'H',
DelinquentBankrupt = b'J',
DeficientDelinquentBankrupt = b'K',
CreationsRedemptionsSuspendedETP = b'C',
Normal = b'N',
Unknown(u8),
}
impl FinancialStatusIndicator {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'D' => Self::Deficient,
b'E' => Self::Delinquent,
b'Q' => Self::Bankrupt,
b'S' => Self::Suspended,
b'G' => Self::DeficientBankrupt,
b'H' => Self::DeficientDelinquent,
b'J' => Self::DelinquentBankrupt,
b'K' => Self::DeficientDelinquentBankrupt,
b'C' => Self::CreationsRedemptionsSuspendedETP,
b'N' => Self::Normal,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for FinancialStatusIndicator {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum RoundLotsOnly {
Yes = b'Y',
No = b'N',
Unknown(u8),
}
impl RoundLotsOnly {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'Y' => Self::Yes,
b'N' => Self::No,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for RoundLotsOnly {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[repr(u8)]
pub enum Authenticity {
LiveProduction = b'P',
Test = b'T',
Unknown(u8),
}
impl Authenticity {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'P' => Self::LiveProduction,
b'T' => Self::Test,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for Authenticity {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum ShortSaleThresholdIndicator {
Restricted = b'Y',
NotRestricted = b'N',
NotAvailable = b' ',
Unknown(u8),
}
impl ShortSaleThresholdIndicator {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'Y' => Self::Restricted,
b'N' => Self::NotRestricted,
b' ' => Self::NotAvailable,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for ShortSaleThresholdIndicator {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum IpoFlag {
NewIPO = b'Y',
NotNewIPO = b'N',
NotAvailable = b' ',
Unknown(u8),
}
impl IpoFlag {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'Y' => Self::NewIPO,
b'N' => Self::NotNewIPO,
b' ' => Self::NotAvailable,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for IpoFlag {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum LuldReferencePriceTier {
Tier1 = b'1',
Tier2 = b'2',
NotAvailable = b' ',
Unknown(u8),
}
impl LuldReferencePriceTier {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'1' => Self::Tier1,
b'2' => Self::Tier2,
b' ' => Self::NotAvailable,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for LuldReferencePriceTier {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum EtpFlag {
IsETP = b'Y',
NotETP = b'N',
NotAvailable = b' ',
Unknown(u8),
}
impl EtpFlag {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'Y' => Self::IsETP,
b'N' => Self::NotETP,
b' ' => Self::NotAvailable,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for EtpFlag {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum InverseIndicator {
Inverse = b'Y',
NotInverse = b'N',
Unknown(u8),
}
impl InverseIndicator {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'Y' => Self::Inverse,
b'N' => Self::NotInverse,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for InverseIndicator {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Stock Trading Action Message
/// Nasdaq uses this administrative message to indicate the current trading status of a security to the trading
/// community.
/// Prior to the start of system hours, Nasdaq will send out a Trading Action spin. In the spin, Nasdaq will send out a
/// Stock Trading Action message with the "T" (Trading Resumption) for all Nasdaq--- and other exchange-•-listed
/// securities that are eligible for trading at the start of the system hours. If a security is absent from the pre-•-
/// opening Trading Action spin, firms should assume that the security is being treated as halted in the Nasdaq
/// platform at the start of the system hours. Please note that securities may be halted in the Nasdaq system for
/// regulatory or operational reasons.
/// After the start of system hours, Nasdaq will use the Trading Action message to relay changes in trading status for an
/// individual security. Messages will be sent when a stock is:
/// • Halted
/// • Paused*
/// • Released for quotation
/// • Released for trading
/// * The paused status will be disseminated for NASDAQ---listed securities only. Trading pauses on non---NASDAQ listed securities
/// will be treated simply as a halt.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct StockTradingAction {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
stock: Symbol,
trading_state: u8,
reserved: u8,
reason: [u8; 4],
}
impl StockTradingAction {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'H';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// Indicates the current trading state for the stock.
#[inline]
pub const fn trading_state(&self) -> TradingState {
TradingState::from_byte(self.trading_state)
}
/// Reserved.
#[inline]
pub const fn reserved(&self) -> u8 {
self.reserved
}
/// Trading Action reason.
#[inline]
pub const fn reason(&self) -> TradingActionReason {
match self.trading_state() {
TradingState::Halted => {
TradingActionReason::Halted(TradingHaltReason::from_bytes(&self.reason))
}
TradingState::Paused => {
TradingActionReason::Paused(TradingHaltReason::from_bytes(&self.reason))
}
TradingState::QuotationOnly => TradingActionReason::QuotationOnly(
TradingResumptionReason::from_bytes(&self.reason),
),
TradingState::Trading => {
TradingActionReason::Trading(TradingResumptionReason::from_bytes(&self.reason))
}
TradingState::Unknown(ident) => TradingActionReason::Unknown {
state: ident,
reason_bytes: self.reason,
},
}
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum TradingState {
/// Halted across all U.S. equity markets / SROs
Halted = b'H',
/// Paused across all U.S. equity markets / SROs (Nasdaq-listed securities only)
Paused = b'P',
/// Quotation only period for cross-SRO halt or pause
QuotationOnly = b'Q',
/// Trading on Nasdaq
Trading = b'T',
Unknown(u8),
}
impl TradingState {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'H' => Self::Halted,
b'P' => Self::Paused,
b'Q' => Self::QuotationOnly,
b'T' => Self::Trading,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for TradingState {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Reg SHO Short Sale Price Test Restricted Indicator
/// In February 2011, the Securities and Exchange Commission (SEC) implemented changes to Rule 201 of the
/// Regulation SHO (Reg SHO). For details, please refer to SEC Release Number 34-61595. In association with
/// the Reg SHO rule change, Nasdaq will introduce the following Reg SHO Short Sale Price Test Restricted
/// Indicator message format.
/// For Nasdaq-•-listed issues, Nasdaq supports a full pre-•-opening spin of Reg SHO Short Sale Price Test Restricted
/// Indicator messages indicating the Rule 201 status for all active issues. Nasdaq also sends the Reg SHO
/// Short Sale Price Test Restricted Indicator message in the event of an intraday status change.
/// For other exchange-•-listed issues, Nasdaq relays the Reg SHO Short Sale Price Test Restricted Indicator
/// message when it receives an update from the primary listing exchange.
/// Nasdaq processes orders based on the most Reg SHO Restriction status value.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct RegSHORestriction {
tag: u8,
locate_code: U16,
tracking_number: U16,
timestamp: Timestamp,
stock: Symbol,
reg_sho_action: u8,
}
impl RegSHORestriction {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'Y';
/// Locate code identifying the security
#[inline]
pub const fn locate_code(&self) -> u16 {
self.locate_code.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// Denotes the Reg SHO Short Sale Price Test Restriction status for the issue at the time of the message dissemination.
#[inline]
pub const fn reg_sho_action(&self) -> RegShoAction {
RegShoAction::from_byte(self.reg_sho_action)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum RegShoAction {
/// No price test in place
NoPriceTest = b'0',
/// Reg SHO Short Sale Price Test Restriction in effect due to an intra-day price drop in security
RestrictionInEffectIntradayDrop = b'1',
/// Reg SHO Short Sale Price Test Restriction remains in effect
RestrictionRemainsInEffect = b'2',
Unknown(u8),
}
impl RegShoAction {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'0' => Self::NoPriceTest,
b'1' => Self::RestrictionInEffectIntradayDrop,
b'2' => Self::RestrictionRemainsInEffect,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for RegShoAction {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Market Participant Position message
/// At the start of each trading day, Nasdaq disseminates a spin of market participant position messages. The
/// message provides the Primary Market Maker status, Market Maker mode and Market Participant state for
/// each Nasdaq market participant firm registered in an issue. Market participant firms may use these fields to
/// comply with certain marketplace rules.
/// Throughout the day, Nasdaq will send out this message only if Nasdaq Operations changes the status of a
/// market participant firm in an issue.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct MarketParticipantPosition {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
mpid: [u8; 4],
stock: Symbol,
primary_market_maker: u8,
market_maker_mode: u8,
market_participant_state: u8,
}
impl MarketParticipantPosition {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'L';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Denotes the market participant identifier for which the position message is being generated
#[inline]
pub const fn mpid(&self) -> &[u8] {
&self.mpid
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// Indicates if the market participant firm qualifies as a Primary Market Maker in accordance with Nasdaq marketplace rules
#[inline]
pub const fn primary_market_maker(&self) -> PrimaryMarketMaker {
PrimaryMarketMaker::from_byte(self.primary_market_maker)
}
/// Indicates the quoting participant's registration status in relation to SEC Rules 101 and 104 of Regulation M
#[inline]
pub const fn market_maker_mode(&self) -> MarketMakerMode {
MarketMakerMode::from_byte(self.market_maker_mode)
}
/// Indicates the market participant's current registration status in the issue
#[inline]
pub const fn market_participant_state(&self) -> MarketParticipantState {
MarketParticipantState::from_byte(self.market_participant_state)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum PrimaryMarketMaker {
/// primary market maker
Primary = b'Y',
/// non-primary market maker
NonPrimary = b'N',
Unknown(u8),
}
impl PrimaryMarketMaker {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'Y' => Self::Primary,
b'N' => Self::NonPrimary,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for PrimaryMarketMaker {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[repr(u8)]
pub enum MarketMakerMode {
/// normal
Normal = b'N',
/// passive
Passive = b'P',
/// syndicate
Syndicate = b'S',
/// pre-syndicate
PreSyndicate = b'R',
/// penalty
Penalty = b'L',
Unknown(u8),
}
impl MarketMakerMode {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'N' => Self::Normal,
b'P' => Self::Passive,
b'S' => Self::Syndicate,
b'R' => Self::PreSyndicate,
b'L' => Self::Penalty,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for MarketMakerMode {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum MarketParticipantState {
/// Active
Active = b'A',
/// Excused/Withdrawn
Deleted = b'D',
ExcusedWithdrawn = b'E',
/// Withdrawn
Withdrawn = b'W',
/// Suspended
Suspended = b'S',
/// Deleted
Unknown(u8),
}
impl MarketParticipantState {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'A' => Self::Active,
b'E' => Self::ExcusedWithdrawn,
b'W' => Self::Withdrawn,
b'S' => Self::Suspended,
b'D' => Self::Deleted,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for MarketParticipantState {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Market wide circuit breaker Decline Level Message
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct MWCBDeclineLevel {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
level_1: Price8,
level_2: Price8,
level_3: Price8,
}
impl MWCBDeclineLevel {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'V';
/// Always set to 0
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Time at which the MWCB Decline Level message was generated
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Denotes the MWCB Level 1 Value.
#[inline]
pub const fn level_1(&self) -> &Price8 {
&self.level_1
}
/// Denotes the MWCB Level 2 Value.
#[inline]
pub const fn level_2(&self) -> &Price8 {
&self.level_2
}
/// Denotes the MWCB Level 3 Value.
#[inline]
pub const fn level_3(&self) -> &Price8 {
&self.level_3
}
}
/// Market-Wide Circuit Breaker Status message
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct MWCBStatus {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
breached_level: u8,
}
impl MWCBStatus {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'W';
/// Always set to 0
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Time at which the MWCB Breaker Status message was generated
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Denotes the MWCB Level that was breached.
#[inline]
pub const fn breached_level(&self) -> BreachedLevel {
BreachedLevel::from_byte(self.breached_level)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum BreachedLevel {
/// Level 1
Level1 = b'1',
/// Level 2
Level2 = b'2',
/// Level 3
Level3 = b'3',
Unknown(u8),
}
impl BreachedLevel {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'1' => Self::Level1,
b'2' => Self::Level2,
b'3' => Self::Level3,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for BreachedLevel {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// IPO Quoting Period Update Message
/// Indicates the anticipated IPO quotation release time of a security.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct QuotingPeriodUpdate {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
stock: Symbol,
ipo_quotation_release_time: U32,
ipo_quotation_release_qualifier: u8,
ipo_price: Price4,
}
impl QuotingPeriodUpdate {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'K';
/// Always set to 0
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Time at which the IPO Quoting Period Update message was generated
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// Denotes the IPO release time, in seconds since midnight, for quotation to the nearest second.
#[inline]
pub const fn ipo_quotation_release_time(&self) -> u32 {
self.ipo_quotation_release_time.get()
}
/// Anticipated Quotation Release Time or IPO Release Canceled/Postponed
#[inline]
pub const fn ipo_quotation_release_qualifier(&self) -> IpoQuotationReleaseQualifier {
IpoQuotationReleaseQualifier::from_byte(self.ipo_quotation_release_qualifier)
}
/// Denotes the IPO Price to be used for intraday net change calculations.
#[inline]
pub const fn ipo_price(&self) -> &Price4 {
&self.ipo_price
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum IpoQuotationReleaseQualifier {
/// Anticipated Quotation Release Time: This value would be used when Nasdaq Market Operations initially enters the IPO instrument for release
Anticipated = b'A',
/// IPO Release Canceled/Postponed: This value would be used when Nasdaq Market Operations cancels or postpones the release of the new IPO instrument
CanceledPostponed = b'C',
Unknown(u8),
}
impl IpoQuotationReleaseQualifier {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'A' => Self::Anticipated,
b'C' => Self::CanceledPostponed,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for IpoQuotationReleaseQualifier {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Limit Up – Limit Down (LULD) Auction Collar
/// Indicates the auction collar thresholds within which a paused security can reopen following a LULD Trading pause.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct LULDAuctionCollar {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
stock: Symbol,
auction_collar_reference_price: Price4,
upper_auction_collar_price: Price4,
lower_auction_collar_price: Price4,
auction_collar_extension: Price4,
}
impl LULDAuctionCollar {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'J';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds past midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// Reference price used to set the Auction Collars
#[inline]
pub const fn auction_collar_reference_price(&self) -> &Price4 {
&self.auction_collar_reference_price
}
/// Indicates the price of the Upper Auction Collar Threshold
#[inline]
pub const fn upper_auction_collar_price(&self) -> &Price4 {
&self.upper_auction_collar_price
}
/// Indicates the price of the Lower Auction Collar Threshold
#[inline]
pub const fn lower_auction_collar_price(&self) -> &Price4 {
&self.lower_auction_collar_price
}
/// Indicates the number of the extensions to the Reopening Auction
#[inline]
pub const fn auction_collar_extension(&self) -> &Price4 {
&self.auction_collar_extension
}
}
/// Operational Halt Message
/// The Exchange uses this message to indicate the current Operational Status of a security to the trading
/// community. An Operational Halt means that there has been an interruption of service on the identified
/// security impacting only the designated Market Center. These Halts differ from the "Stock Trading
/// Action" message types since an Operational Halt is specific to the exchange for which it is declared, and
/// does not interrupt the ability of the trading community to trade the identified instrument on any other
/// marketplace.
/// Nasdaq uses this administrative message to indicate the current trading status of the three market centers
/// operated by Nasdaq.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct OperationalHalt {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
stock: Symbol,
market_code: u8,
operational_halt_action: u8,
}
impl OperationalHalt {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'h';
/// Locate code uniquely assigned to the security symbol for the day.
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Time at which the Operational Halt message was generated.
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Denotes the security symbol for the issue in Nasdaq execution system
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// Market Code
#[inline]
pub const fn market_code(&self) -> MarketCode {
MarketCode::from_byte(self.market_code)
}
/// Operational Halt Action
#[inline]
pub const fn operational_halt_action(&self) -> OperationalHaltAction {
OperationalHaltAction::from_byte(self.operational_halt_action)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum MarketCode {
/// Nasdaq
Nasdaq = b'Q',
/// BX
BX = b'B',
/// PSX
PSX = b'X',
Unknown(u8),
}
impl MarketCode {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'Q' => Self::Nasdaq,
b'B' => Self::BX,
b'X' => Self::PSX,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for MarketCode {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum OperationalHaltAction {
/// Operationally Halted on the identified Market
OperationallyHalted = b'H',
/// Operational Halt has been lifted and Trading resumed
TradingResumed = b'T',
Unknown(u8),
}
impl OperationalHaltAction {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'H' => Self::OperationallyHalted,
b'T' => Self::TradingResumed,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for OperationalHaltAction {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Add Order – No MPID Attribution Message
/// This message will be generated for unattributed orders accepted by the Nasdaq system. (Note: If a firm wants to display a MPID for unattributed orders, Nasdaq recommends that it use the MPID of "NSDQ".)
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct AddOrderNoMPIDAttribution {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
order_reference_number: U64,
buy_sell_indicator: u8,
shares: U32,
stock: Symbol,
price: Price4,
}
impl AddOrderNoMPIDAttribution {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'A';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight.
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The unique reference number assigned to the new order at the time of receipt.
#[inline]
pub const fn order_reference_number(&self) -> u64 {
self.order_reference_number.get()
}
/// The type of order being added.
#[inline]
pub const fn buy_sell_indicator(&self) -> BuySellIndicator {
BuySellIndicator::from_byte(self.buy_sell_indicator)
}
/// The total number of shares associated with the order being added to the book.
#[inline]
pub const fn shares(&self) -> u32 {
self.shares.get()
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// The display price of the new order. Refer to Data Types for field processing notes.
#[inline]
pub const fn price(&self) -> &Price4 {
&self.price
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum BuySellIndicator {
/// Buy Order
Buy = b'B',
/// Sell Order
Sell = b'S',
Unknown(u8),
}
impl BuySellIndicator {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'B' => Self::Buy,
b'S' => Self::Sell,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for BuySellIndicator {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Add Order - MPID Attribution Message
/// This message will be generated for attributed orders and quotations accepted by the Nasdaq system.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct AddOrderWithMPIDAttribution {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
order_reference_number: U64,
buy_sell_indicator: u8,
shares: U32,
stock: Symbol,
price: Price4,
attribution: [u8; 4],
}
impl AddOrderWithMPIDAttribution {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'F';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight.
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The unique reference number assigned to the new order at the time of receipt.
#[inline]
pub const fn order_reference_number(&self) -> u64 {
self.order_reference_number.get()
}
/// The type of order being added.
#[inline]
pub const fn buy_sell_indicator(&self) -> BuySellIndicator {
BuySellIndicator::from_byte(self.buy_sell_indicator)
}
/// The total number of shares associated with the order being added to the book
#[inline]
pub const fn shares(&self) -> u32 {
self.shares.get()
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// The display price of the new order. Refer to Data Types for field processing notes const.
#[inline]
pub const fn price(&self) -> &Price4 {
&self.price
}
/// Nasdaq Market participant identifier associated with the entered order
#[inline]
pub const fn attribution(&self) -> &[u8] {
&self.attribution
}
}
/// Order Executed Message
/// This message is sent whenever an order on the book is executed in whole or in part. It is possible to receive several
/// Order Executed Messages for the same order reference number if that order is executed in several parts. The
/// multiple Order Executed Messages on the same order are cumulative.
/// By combining the executions from both types of Order Executed Messages and the Trade Message, it is possible to
/// build a complete view of all non-•-cross executions that happen on Nasdaq. Cross execution information is available in one bulk print per symbol via the Cross Trade Message.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct OrderExecuted {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
order_reference_number: U64,
executed_shares: U32,
match_number: U64,
}
impl OrderExecuted {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'E';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The unique reference number assigned to the new order at the time of receipt
#[inline]
pub const fn order_reference_number(&self) -> u64 {
self.order_reference_number.get()
}
/// The number of shares executed
#[inline]
pub const fn executed_shares(&self) -> u32 {
self.executed_shares.get()
}
/// The Nasdaq generated day unique Match Number of this execution. The Match Number is also referenced in the Trade Break Message
#[inline]
pub const fn match_number(&self) -> u64 {
self.match_number.get()
}
}
/// Order Executed With Price Message
/// This message is sent whenever an order on the book is executed in whole or in part at a price different from the
/// initial display price. Since the execution price is different than the display price of the original Add Order, Nasdaq
/// includes a price field within this execution message.
/// It is possible to receive multiple Order Executed and Order Executed With Price messages for the same order if that
/// order is executed in several parts. The multiple Order Executed messages on the same order are cumulative.
/// These executions may be marked as non-•-printable. If the execution is marked as non-•-printed, it means that the
/// shares will be included into a later bulk print (e.g., in the case of cross executions). If a firm is looking to use the data
/// in time-•-and-•-sales displays or volume calculations, Nasdaq recommends that firms ignore messages marked as non-
/// -- printable to prevent double counting.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct OrderExecutedWithPrice {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
order_reference_number: U64,
executed_shares: U32,
match_number: U64,
printable: u8,
execution_price: Price4,
}
impl OrderExecutedWithPrice {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'C';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The unique reference number assigned to the new order at the time of receipt
#[inline]
pub const fn order_reference_number(&self) -> u64 {
self.order_reference_number.get()
}
/// The number of shares executed
#[inline]
pub const fn executed_shares(&self) -> u32 {
self.executed_shares.get()
}
/// The Nasdaq generated day unique Match Number of this execution. The Match Number is also referenced in the Trade Break Message
#[inline]
pub const fn match_number(&self) -> u64 {
self.match_number.get()
}
/// Indicates if the execution should be reflected on time and sales displays and volume calculations
#[inline]
pub const fn printable(&self) -> Printable {
Printable::from_byte(self.printable)
}
/// The Price at which the order execution occurred. Refer to Data Types for field processing notes
#[inline]
pub const fn execution_price(&self) -> &Price4 {
&self.execution_price
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum Printable {
/// Non-Printable
NonPrintable = b'N',
/// Printable
Printable = b'Y',
Unknown(u8),
}
impl Printable {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'N' => Self::NonPrintable,
b'Y' => Self::Printable,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for Printable {
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Order Cancel Message
/// This message is sent whenever an order on the book is modified as a result of a partial cancellation.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct OrderCancel {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
order_reference_number: U64,
cancelled_shares: U32,
}
impl OrderCancel {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'X';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The reference number of the order being canceled
#[inline]
pub const fn order_reference_number(&self) -> u64 {
self.order_reference_number.get()
}
/// The number of shares being removed from the display size of the order as a result of a cancellation
#[inline]
pub const fn cancelled_shares(&self) -> u32 {
self.cancelled_shares.get()
}
}
/// Order Delete Message
/// This message is sent whenever an order on the book is being cancelled. All remaining shares are no longer accessible so the order must be removed from the book.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct OrderDelete {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
order_reference_number: U64,
}
impl OrderDelete {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'D';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The reference number of the order being canceled
#[inline]
pub const fn order_reference_number(&self) -> u64 {
self.order_reference_number.get()
}
}
/// Order Replace Message
/// This message is sent whenever an order on the book has been cancel-replaced. All remaining shares from the original order are no longer accessible, and must be removed. The new order details are provided for the replacement, along with a new order reference number which will be used henceforth. Since the side, stock symbol and attribution (if any) cannot be changed by an Order Replace event, these fields are not included in the message. Firms should retain the side, stock symbol and MPID from the original Add Order message.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct OrderReplace {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
original_order_reference_number: U64,
new_order_reference_number: U64,
shares: U32,
price: Price4,
}
impl OrderReplace {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'U';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The original order reference number of the order being replaced
#[inline]
pub const fn original_order_reference_number(&self) -> u64 {
self.original_order_reference_number.get()
}
/// The new reference number for this order at time of replacement
#[inline]
pub const fn new_order_reference_number(&self) -> u64 {
self.new_order_reference_number.get()
}
/// The new total displayed quantity
#[inline]
pub const fn shares(&self) -> u32 {
self.shares.get()
}
/// The new display price for the order
#[inline]
pub const fn price(&self) -> &Price4 {
&self.price
}
}
/// Trade Message
/// The Trade Message is designed to provide execution details for normal match events involving non-displayable order types.
/// Since no Add Order Message is generated when a non-displayed order is initially received, Nasdaq cannot use the Order Executed messages for all matches. Therefore this message indicates when a match occurs between non-displayable order types. A Trade Message is transmitted each time a non-displayable order is executed in whole or in part. It is possible to receive multiple Trade Messages for the same order if that order is executed in several parts. Trade Messages for the same order are cumulative.
/// Trade Messages should be included in Nasdaq time-and-sales displays as well as volume and other market statistics. Since Trade Messages do not affect the book, however, they may be ignored by firms just looking to build and track the Nasdaq execution system display.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct Trade {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
order_reference_number: U64,
buy_sell_indicator: u8,
shares: U32,
stock: Symbol,
price: Price4,
match_number: U64,
}
impl Trade {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'P';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight.
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The unique reference number assigned to the order on the book being executed.
#[inline]
pub const fn order_reference_number(&self) -> u64 {
self.order_reference_number.get()
}
/// The type of non-display order on the book being matched
#[inline]
pub const fn buy_sell_indicator(&self) -> BuySellIndicator {
BuySellIndicator::from_byte(self.buy_sell_indicator)
}
/// The number of shares being matched in this execution
#[inline]
pub const fn shares(&self) -> u32 {
self.shares.get()
}
/// Stock Symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// The match price of the order
#[inline]
pub const fn price(&self) -> &Price4 {
&self.price
}
/// The Nasdaq generated session unique Match Number for this trade
#[inline]
pub const fn match_number(&self) -> u64 {
self.match_number.get()
}
}
/// Cross Trade Message
/// Cross Trade message indicates that Nasdaq has completed its cross process for a specific security. Nasdaq sends out
/// a Cross Trade message for all active issues in the system following the Opening, Closing and EMC cross events. Firms
/// may use the Cross Trade message to determine when the cross for each security has been completed.
///
/// For most issues, the Cross Trade message will indicate the bulk volume associated with the cross event. If the order
/// interest is insufficient to conduct a cross in a particular issue, however, the Cross Trade message may show the
/// shares as zero.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct CrossTrade {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
shares: U64,
stock: Symbol,
cross_price: Price4,
match_number: U64,
cross_type: u8,
}
impl CrossTrade {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'Q';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight.
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The number of shares matched in the Nasdaq Cross.
#[inline]
pub const fn shares(&self) -> u64 {
self.shares.get()
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// The price at which the cross occurred. Refer to Data Types for field processing notes.
#[inline]
pub const fn cross_price(&self) -> &Price4 {
&self.cross_price
}
/// The Nasdaq generated day-unique Match Number of this execution.
#[inline]
pub const fn match_number(&self) -> u64 {
self.match_number.get()
}
/// The Nasdaq cross session for which the message is being generated.
#[inline]
pub const fn cross_type(&self) -> CrossType {
CrossType::from_byte(self.cross_type)
}
}
/// Broken Trade Message
/// The Broken Trade Message is sent whenever an execution on Nasdaq is broken. An execution may be broken if it is
/// found to be "clearly erroneous" pursuant to Nasdaq's Clearly Erroneous Policy. A trade break is final; once a trade is
/// broken, it cannot be reinstated.
/// Firms that use the ITCH feed to create time-and-sales displays or calculate market statistics should be prepared
/// to process the broken trade message. If a firm is only using the ITCH feed to build a book, however, it may ignore
/// these messages as they have no impact on the current book.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct BrokenTrade {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
match_number: U64,
}
impl BrokenTrade {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'B';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight.
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The Nasdaq Match Number of the execution that was broken.
#[inline]
pub const fn match_number(&self) -> u64 {
self.match_number.get()
}
}
/// Net Order Imbalance Indicator (NOII) Message
/// Nasdaq begins disseminating Net Order Imbalance Indicators (NOII) at 9:25 a.m. for the Opening Cross and 3:50 p.m. for the Closing Cross.
/// Between 9:25 and 9:28 a.m. and 3:50 and 3:55 p.m., Nasdaq disseminates the NOII information every 10 seconds.
/// Between 9:28 and 9:30 a.m. and 3:55 and 4:00 p.m., Nasdaq disseminates the NOII information every second.
/// For Nasdaq Halt, IPO and Pauses, NOII messages will be disseminated at 1 second intervals starting 1 second after quoting period starts/trading action is released.
/// Nasdaq will also disseminate an Extended Trading Close (ETC) message from 4:00 p.m. to 4:05 p.m. at five second intervals.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct NetOrderImbalanceIndicator {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
paired_shares: U64,
imbalance_shares: U64,
imbalance_direction: u8,
stock: Symbol,
far_price: Price4,
near_price: Price4,
current_reference_price: Price4,
cross_type: u8,
price_variation_indicator: u8,
}
impl NetOrderImbalanceIndicator {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'I';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight.
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// The total number of shares that are eligible to be matched at the Current Reference Price.
#[inline]
pub const fn paired_shares(&self) -> u64 {
self.paired_shares.get()
}
/// The number of shares not paired at the Current Reference Price.
#[inline]
pub const fn imbalance_shares(&self) -> u64 {
self.imbalance_shares.get()
}
/// The market side of the order imbalance.
#[inline]
pub const fn imbalance_direction(&self) -> ImbalanceDirection {
ImbalanceDirection::from_byte(self.imbalance_direction)
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// A hypothetical auction-clearing price for cross orders only.
#[inline]
pub const fn far_price(&self) -> &Price4 {
&self.far_price
}
/// A hypothetical auction-clearing price for cross orders as well as continuous orders.
#[inline]
pub const fn near_price(&self) -> &Price4 {
&self.near_price
}
/// The price at which the NOII shares are being calculated.
#[inline]
pub const fn current_reference_price(&self) -> &Price4 {
&self.current_reference_price
}
/// The type of Nasdaq cross for which the NOII message is being generated
#[inline]
pub const fn cross_type(&self) -> CrossType {
CrossType::from_byte(self.cross_type)
}
/// This field indicates the absolute value of the percentage of deviation of the Near Indicative Clearing Price to the nearest Current Reference Price.
#[inline]
pub const fn price_variation_indicator(&self) -> PriceVariationIndicator {
PriceVariationIndicator::from_byte(self.price_variation_indicator)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum ImbalanceDirection {
/// buy imbalance
Buy = b'B',
/// sell imbalance
Sell = b'S',
/// no imbalance
NoImbalance = b'N',
/// Insufficient orders to calculate
InsufficientOrders = b'O',
/// Paused
Paused = b'P',
Unknown(u8),
}
impl ImbalanceDirection {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'B' => Self::Buy,
b'S' => Self::Sell,
b'N' => Self::NoImbalance,
b'O' => Self::InsufficientOrders,
b'P' => Self::Paused,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for ImbalanceDirection {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum CrossType {
/// Nasdaq Opening Cross
NasdaqOpeningCross = b'O',
/// Nasdaq Closing Cross
NasdaqClosingCross = b'C',
/// Cross for IPO and halted / paused securities
IpoHaltedPaused = b'H',
/// Extended Trading Close
ExtendedTradingClose = b'A',
Unknown(u8),
}
impl CrossType {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'O' => Self::NasdaqOpeningCross,
b'C' => Self::NasdaqClosingCross,
b'H' => Self::IpoHaltedPaused,
b'A' => Self::ExtendedTradingClose,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for CrossType {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum PriceVariationIndicator {
/// Less than 1%
LessThan1Percent = b'L',
/// 1 to 1.99%
Between1And1_99Percent = b'1',
/// 2 to 2.99%
Between2And2_99Percent = b'2',
/// 3 to 3.99%
Between3And3_99Percent = b'3',
/// 4 to 4.99%
Between4And4_99Percent = b'4',
/// 5 to 5.99%
Between5And5_99Percent = b'5',
/// 6 to 6.99%
Between6And6_99Percent = b'6',
/// 7 to 7.99%
Between7And7_99Percent = b'7',
/// 8 to 8.99%
Between8And8_99Percent = b'8',
/// 9 to 9.99%
Between9And9_99Percent = b'9',
/// 10 to 19.99%
Between10And19_99Percent = b'A',
/// 20 to 29.99%
Between20And29_99Percent = b'B',
/// 30% or greater
GreaterOrEqualTo30Percent = b'C',
/// Cannot be calculated
CannotBeCalculated = b' ',
Unknown(u8),
}
impl PriceVariationIndicator {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'L' => Self::LessThan1Percent,
b'1' => Self::Between1And1_99Percent,
b'2' => Self::Between2And2_99Percent,
b'3' => Self::Between3And3_99Percent,
b'4' => Self::Between4And4_99Percent,
b'5' => Self::Between5And5_99Percent,
b'6' => Self::Between6And6_99Percent,
b'7' => Self::Between7And7_99Percent,
b'8' => Self::Between8And8_99Percent,
b'9' => Self::Between9And9_99Percent,
b'A' => Self::Between10And19_99Percent,
b'B' => Self::Between20And29_99Percent,
b'C' => Self::GreaterOrEqualTo30Percent,
b' ' => Self::CannotBeCalculated,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for PriceVariationIndicator {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Retail Price Improvement Indicator (RPII)
/// Identifies a retail interest indication of the Bid, Ask or both the Bid and Ask for Nasdaq-listed securities.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct RetailPriceImprovementIndicator {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
stock: Symbol,
interest_flag: u8,
}
impl RetailPriceImprovementIndicator {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'N';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight.
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// Interest Flag
#[inline]
pub const fn interest_flag(&self) -> InterestFlag {
InterestFlag::from_byte(self.interest_flag)
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum InterestFlag {
/// RPI orders available on the buy side
BuySide = b'B',
/// RPI orders available on the sell side
SellSide = b'S',
/// RPI orders available on both sides (buy and sell)
BothSides = b'A',
/// No RPI orders available
NoneAvailable = b'N',
Unknown(u8),
}
impl InterestFlag {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'B' => Self::BuySide,
b'S' => Self::SellSide,
b'A' => Self::BothSides,
b'N' => Self::NoneAvailable,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for InterestFlag {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Direct Listing with Capital Raise Price Discovery Message
/// The following message is disseminated only for Direct Listing with Capital Raise (DLCR) securities. Nasdaq begins
/// disseminating messages once per second as soon as the DLCR volatility test has successfully passed.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Debug)]
#[repr(C, packed)]
pub struct DirectListingwithCapitalRaisePriceDiscovery {
tag: u8,
stock_locate: U16,
tracking_number: U16,
timestamp: Timestamp,
stock: Symbol,
open_eligibility_status: u8,
minimum_allowable_price: Price4,
maximum_allowable_price: Price4,
near_execution_price: Price4,
near_execution_time: U64,
lower_price_range_collar: Price4,
upper_price_range_collar: Price4,
}
impl DirectListingwithCapitalRaisePriceDiscovery {
pub const LEN: usize = size_of::<Self>();
pub const TAG: u8 = b'O';
/// Locate code identifying the security
#[inline]
pub const fn stock_locate(&self) -> u16 {
self.stock_locate.get()
}
/// Nasdaq internal tracking number
#[inline]
pub const fn tracking_number(&self) -> u16 {
self.tracking_number.get()
}
/// Nanoseconds since midnight
#[inline]
pub const fn timestamp(&self) -> &Timestamp {
&self.timestamp
}
/// Stock symbol, right padded with spaces
#[inline]
pub const fn stock(&self) -> &Symbol {
&self.stock
}
/// Indicates if the security is eligible to be released for trading
#[inline]
pub const fn open_eligibility_status(&self) -> OpenEligibilityStatus {
OpenEligibilityStatus::from_byte(self.open_eligibility_status)
}
/// 20% below Registration Statement Lower Price
#[inline]
pub const fn minimum_allowable_price(&self) -> &Price4 {
&self.minimum_allowable_price
}
/// 80% above Registration Statement Highest Price
#[inline]
pub const fn maximum_allowable_price(&self) -> &Price4 {
&self.maximum_allowable_price
}
/// The current reference price when the DLCR volatility test has successfully passed
#[inline]
pub const fn near_execution_price(&self) -> &Price4 {
&self.near_execution_price
}
/// The time at which the near execution price was set
#[inline]
pub const fn near_execution_time(&self) -> u64 {
self.near_execution_time.get()
}
/// Indicates the price of the Lower Auction Collar Threshold (10% below the Near Execution Price)
#[inline]
pub const fn lower_price_range_collar(&self) -> &Price4 {
&self.lower_price_range_collar
}
/// Indicates the price of the Upper Auction Collar Threshold (10% above the Near Execution Price)
#[inline]
pub const fn upper_price_range_collar(&self) -> &Price4 {
&self.upper_price_range_collar
}
}
#[derive(Debug)]
#[repr(u8)]
pub enum OpenEligibilityStatus {
/// Not Eligible
NotEligible = b'N',
/// Eligible
Eligible = b'Y',
Unknown(u8),
}
impl OpenEligibilityStatus {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'N' => Self::NotEligible,
b'Y' => Self::Eligible,
unknown => Self::Unknown(unknown),
}
}
}
impl From<u8> for OpenEligibilityStatus {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Idiomatic representation combining the trading state and its contextual reason.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TradingActionReason {
/// 'H' - Halted across all U.S. equity markets
Halted(TradingHaltReason),
/// 'P' - Paused across all U.S. equity markets
Paused(TradingHaltReason),
/// 'Q' - Quotation only period for cross-market resumption
QuotationOnly(TradingResumptionReason),
/// 'T' - Trading on Nasdaq
Trading(TradingResumptionReason),
/// Fallback for unrecognized states
Unknown { state: u8, reason_bytes: [u8; 4] },
}
/// A 4-byte space-padded ASCII reason code from a Trading Action message.
///
/// The raw bytes are stored as-is from the wire. Use [`TradingHaltReason::decode`]
/// to obtain the typed [`TradingHaltReasonCode`] variant.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TradingHaltReason {
/// T1 — Halt News Pending
HaltNewsPending,
/// T2 — Halt News Disseminated
HaltNewsDisseminated,
/// T5 — Single Security Trading Pause In Effect
SingleSecurityTradingPause,
/// T6 — Regulatory Halt: Extraordinary Market Activity
RegulatoryHaltExtraordinaryMarketActivity,
/// T8 — Halt ETF
HaltEtf,
/// T12 — Trading Halted; For Information Requested by Listing Market
HaltInformationRequestedByListingMarket,
/// H4 — Halt Non-Compliance
HaltNonCompliance,
/// H9 — Halt Filings Not Current
HaltFilingsNotCurrent,
/// H10 — Halt SEC Trading Suspension
HaltSecTradingSuspension,
/// H11 — Halt Regulatory Concern
HaltRegulatoryConcern,
/// O1 — Operations Halt; Contact Market Operations
OperationsHalt,
/// LUDP — Volatility Trading Pause
VolatilityTradingPause,
/// LUDS — Volatility Trading Pause: Straddle Condition
VolatilityTradingPauseStraddleCondition,
/// MWC1 — Market Wide Circuit Breaker Halt – Level 1
MarketWideCircuitBreakerLevel1,
/// MWC2 — Market Wide Circuit Breaker Halt – Level 2
MarketWideCircuitBreakerLevel2,
/// MWC3 — Market Wide Circuit Breaker Halt – Level 3
MarketWideCircuitBreakerLevel3,
/// MWC0 — Market Wide Circuit Breaker Halt – Carry over from previous day
MarketWideCircuitBreakerCarryOver,
/// IPO1 — IPO Issue Not Yet Trading
IpoNotYetTrading,
/// M1 — Corporate Action
CorporateAction,
/// M2 — Quotation Not Available
QuotationNotAvailable,
/// ` ` (four spaces) — Reason Not Available
ReasonNotAvailable,
/// Unrecognised code; raw bytes preserved for diagnostics.
Unknown([u8; 4]),
}
impl TradingHaltReason {
#[inline]
pub const fn from_bytes(value: &[u8; 4]) -> Self {
match value {
b"T1 " => TradingHaltReason::HaltNewsPending,
b"T2 " => TradingHaltReason::HaltNewsDisseminated,
b"T5 " => TradingHaltReason::SingleSecurityTradingPause,
b"T6 " => TradingHaltReason::RegulatoryHaltExtraordinaryMarketActivity,
b"T8 " => TradingHaltReason::HaltEtf,
b"T12 " => TradingHaltReason::HaltInformationRequestedByListingMarket,
b"H4 " => TradingHaltReason::HaltNonCompliance,
b"H9 " => TradingHaltReason::HaltFilingsNotCurrent,
b"H10 " => TradingHaltReason::HaltSecTradingSuspension,
b"H11 " => TradingHaltReason::HaltRegulatoryConcern,
b"O1 " => TradingHaltReason::OperationsHalt,
b"LUDP" => TradingHaltReason::VolatilityTradingPause,
b"LUDS" => TradingHaltReason::VolatilityTradingPauseStraddleCondition,
b"MWC1" => TradingHaltReason::MarketWideCircuitBreakerLevel1,
b"MWC2" => TradingHaltReason::MarketWideCircuitBreakerLevel2,
b"MWC3" => TradingHaltReason::MarketWideCircuitBreakerLevel3,
b"MWC0" => TradingHaltReason::MarketWideCircuitBreakerCarryOver,
b"IPO1" => TradingHaltReason::IpoNotYetTrading,
b"M1 " => TradingHaltReason::CorporateAction,
b"M2 " => TradingHaltReason::QuotationNotAvailable,
b" " => TradingHaltReason::ReasonNotAvailable,
_ => TradingHaltReason::Unknown(*value),
}
}
}
impl std::fmt::Display for TradingHaltReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::HaltNewsPending => "T1 – Halt News Pending",
Self::HaltNewsDisseminated => "T2 – Halt News Disseminated",
Self::SingleSecurityTradingPause => "T5 – Single Security Trading Pause In Effect",
Self::RegulatoryHaltExtraordinaryMarketActivity => {
"T6 – Regulatory Halt: Extraordinary Market Activity"
}
Self::HaltEtf => "T8 – Halt ETF",
Self::HaltInformationRequestedByListingMarket => {
"T12 – Trading Halted; For Information Requested by Listing Market"
}
Self::HaltNonCompliance => "H4 – Halt Non-Compliance",
Self::HaltFilingsNotCurrent => "H9 – Halt Filings Not Current",
Self::HaltSecTradingSuspension => "H10 – Halt SEC Trading Suspension",
Self::HaltRegulatoryConcern => "H11 – Halt Regulatory Concern",
Self::OperationsHalt => "O1 – Operations Halt; Contact Market Operations",
Self::VolatilityTradingPause => "LUDP – Volatility Trading Pause",
Self::VolatilityTradingPauseStraddleCondition => {
"LUDS – Volatility Trading Pause: Straddle Condition"
}
Self::MarketWideCircuitBreakerLevel1 => {
"MWC1 – Market Wide Circuit Breaker Halt – Level 1"
}
Self::MarketWideCircuitBreakerLevel2 => {
"MWC2 – Market Wide Circuit Breaker Halt – Level 2"
}
Self::MarketWideCircuitBreakerLevel3 => {
"MWC3 – Market Wide Circuit Breaker Halt – Level 3"
}
Self::MarketWideCircuitBreakerCarryOver => {
"MWC0 – Market Wide Circuit Breaker Halt – Carry over from previous day"
}
Self::IpoNotYetTrading => "IPO1 – IPO Issue Not Yet Trading",
Self::CorporateAction => "M1 – Corporate Action",
Self::QuotationNotAvailable => "M2 – Quotation Not Available",
Self::ReasonNotAvailable => " – Reason Not Available",
Self::Unknown(b) => {
return write!(f, "Unknown({:?})", core::str::from_utf8(b).unwrap_or("?"));
}
};
f.write_str(s)
}
}
/// A 4-byte space-padded ASCII reason code from a Quotation/Trading Resumption Action message.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TradingResumptionReason {
/// T3 — News and Resumption Times
NewsAndResumptionTimes,
/// T7 — Single Security Trading Pause / Quotation Only Period
SingleSecurityTradingPause,
/// R4 — Qualifications Issues Reviewed/Resolved; Quotations/Trading to Resume
QualificationsIssuesResolved,
/// R9 — Filing Requirements Satisfied/Resolved; Quotations/Trading To Resume
FilingRequirementsSatisfied,
/// C3 — Issuer News Not Forthcoming; Quotations/Trading To Resume
IssuerNewsNotForthcoming,
/// C4 — Qualifications Halt ended Maintenance Requirements Met; Resume
QualificationsHaltEnded,
/// C9 — Qualifications Halt Concluded; Filings Met; Quotes/Trades To Resume
QualificationsHaltConcluded,
/// C11 — Trade Halt Concluded By Other Regulatory Auth.; Quotes/Trades Resume
TradeHaltConcludedByOtherRegulatoryAuth,
/// MWCQ — Market Wide Circuit Breaker Resumption
MarketWideCircuitBreakerResumption,
/// R1 — New Issue Available
NewIssueAvailable,
/// R2 — Issue Available
IssueAvailable,
/// IPOQ — IPO Security Released for Quotation (Nasdaq Securities Only)
IpoSecurityReleasedForQuotation,
/// IPOE — IPO Security — Positioning Window Extension (Nasdaq Securities Only)
IpoSecurityPositioningWindowExtension,
/// ` ` (four spaces) — Reason Not Available
ReasonNotAvailable,
/// Unrecognised code; raw bytes preserved for diagnostics.
Unknown([u8; 4]),
}
impl TradingResumptionReason {
#[inline]
pub const fn from_bytes(value: &[u8; 4]) -> Self {
match value {
b"T3 " => TradingResumptionReason::NewsAndResumptionTimes,
b"T7 " => TradingResumptionReason::SingleSecurityTradingPause,
b"R4 " => TradingResumptionReason::QualificationsIssuesResolved,
b"R9 " => TradingResumptionReason::FilingRequirementsSatisfied,
b"C3 " => TradingResumptionReason::IssuerNewsNotForthcoming,
b"C4 " => TradingResumptionReason::QualificationsHaltEnded,
b"C9 " => TradingResumptionReason::QualificationsHaltConcluded,
b"C11 " => TradingResumptionReason::TradeHaltConcludedByOtherRegulatoryAuth,
b"MWCQ" => TradingResumptionReason::MarketWideCircuitBreakerResumption,
b"R1 " => TradingResumptionReason::NewIssueAvailable,
b"R2 " => TradingResumptionReason::IssueAvailable,
b"IPOQ" => TradingResumptionReason::IpoSecurityReleasedForQuotation,
b"IPOE" => TradingResumptionReason::IpoSecurityPositioningWindowExtension,
b" " => TradingResumptionReason::ReasonNotAvailable,
_ => TradingResumptionReason::Unknown(*value),
}
}
}
impl std::fmt::Display for TradingResumptionReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::NewsAndResumptionTimes => "T3 – News and Resumption Times",
Self::SingleSecurityTradingPause => {
"T7 – Single Security Trading Pause / Quotation Only Period"
}
Self::QualificationsIssuesResolved => {
"R4 – Qualifications Issues Reviewed/Resolved; Quotations/Trading to Resume"
}
Self::FilingRequirementsSatisfied => {
"R9 – Filing Requirements Satisfied/Resolved; Quotations/Trading To Resume"
}
Self::IssuerNewsNotForthcoming => {
"C3 – Issuer News Not Forthcoming; Quotations/Trading To Resume"
}
Self::QualificationsHaltEnded => {
"C4 – Qualifications Halt ended Maintenance Requirements Met; Resume"
}
Self::QualificationsHaltConcluded => {
"C9 – Qualifications Halt Concluded; Filings Met; Quotes/Trades To Resume"
}
Self::TradeHaltConcludedByOtherRegulatoryAuth => {
"C11 – Trade Halt Concluded By Other Regulatory Auth.; Quotes/Trades Resume"
}
Self::MarketWideCircuitBreakerResumption => {
"MWCQ – Market Wide Circuit Breaker Resumption"
}
Self::NewIssueAvailable => "R1 – New Issue Available",
Self::IssueAvailable => "R2 – Issue Available",
Self::IpoSecurityReleasedForQuotation => {
"IPOQ – IPO Security Released for Quotation (Nasdaq Securities Only)"
}
Self::IpoSecurityPositioningWindowExtension => {
"IPOE – IPO Security — Positioning Window Extension (Nasdaq Securities Only)"
}
Self::ReasonNotAvailable => " – Reason Not Available",
Self::Unknown(b) => {
return write!(f, "Unknown({:?})", core::str::from_utf8(b).unwrap_or("?"));
}
};
f.write_str(s)
}
}
/// Codes for Issue Classification Values
#[derive(Debug)]
#[repr(u8)]
pub enum IssueClassificationCode {
AmericanDepositaryShare = b'A',
Bond = b'B',
CommonStock = b'C',
DepositoryReceipt = b'F',
OneFourFourA = b'I',
LimitedPartnership = b'L',
Notes = b'N',
OrdinaryShare = b'O',
PreferredStock = b'P',
OtherSecurities = b'Q',
Right = b'R',
SharesofBeneficialInterest = b'S',
ConvertibleDebenture = b'T',
Unit = b'U',
UnitsBenifInt = b'V',
Warrant = b'W',
Unknown(u8),
}
impl IssueClassificationCode {
#[inline]
pub const fn from_byte(value: u8) -> Self {
match value {
b'A' => Self::AmericanDepositaryShare,
b'B' => Self::Bond,
b'C' => Self::CommonStock,
b'F' => Self::DepositoryReceipt,
b'I' => Self::OneFourFourA,
b'L' => Self::LimitedPartnership,
b'N' => Self::Notes,
b'O' => Self::OrdinaryShare,
b'P' => Self::PreferredStock,
b'Q' => Self::OtherSecurities,
b'R' => Self::Right,
b'S' => Self::SharesofBeneficialInterest,
b'T' => Self::ConvertibleDebenture,
b'U' => Self::Unit,
b'V' => Self::UnitsBenifInt,
b'W' => Self::Warrant,
_ => Self::Unknown(value),
}
}
}
impl From<u8> for IssueClassificationCode {
#[inline]
fn from(value: u8) -> Self {
Self::from_byte(value)
}
}
/// Codes for Issue Sub Type Values
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IssueSubTypeCode {
PreferredTrustSecurities,
AlphaIndexETNs,
IndexBasedDerivative,
CommonShares,
CommodityBasedTrustShares,
CommodityFuturesTrustShares,
CommodityLinkedSecurities,
CommodityIndexTrustShares,
CollateralizedMortgageObligation,
CurrencyTrustShares,
CommodityCurrencyLinkedSecurities,
CurrencyWarrants,
GlobalDepositaryShares,
ETFPortfolioDepositaryReceipt,
EquityGoldShares,
ETNEquityIndexLinkedSecurities,
NextSharesExchangeTradedManagedFund,
ExchangeTradedNotes,
EquityUnits,
HOLDRS,
ETNFixedIncomeLinkedSecurities,
ETNFuturesLinkedSecurities,
GlobalShares,
ETFIndexFundShares,
InterestRate,
IndexWarrant,
IndexLinkedExchangeableNotes,
CorporateBackedTrustSecurity,
ContingentLitigationRight,
LimitedLiabilityCompany,
EquityBasedDerivative,
ManagedFundShares,
ETNMultiFactorIndexLinkedSecurities,
ManagedTrustSecurities,
NYRegistryShares,
OpenEndedMutualFund,
PrivatelyHeldSecurity,
PoisonPill,
PartnershipUnits,
ClosedEndFunds,
RegS,
CommodityRedeemableCommodityLinkedSecurities,
ETNRedeemableFuturesLinkedSecurities,
REIT,
CommodityRedeemableCurrencyLinkedSecurities,
SEED,
SpotRateClosing,
SpotRateIntraday,
TrackingStock,
TrustCertificates,
TrustUnits,
Portal,
ContingentValueRight,
TrustIssuedReceipts,
WorldCurrencyOption,
Trust,
Other,
NotApplicable,
Unknown([u8; 2]),
}
impl IssueSubTypeCode {
#[inline]
pub const fn from_bytes(value: [u8; 2]) -> Self {
// ITCH 2-byte alphanumeric fields are left-justified and space-padded on the right.
match &value {
b"A " => Self::PreferredTrustSecurities,
b"AI" => Self::AlphaIndexETNs,
b"B " => Self::IndexBasedDerivative,
b"C " => Self::CommonShares,
b"CB" => Self::CommodityBasedTrustShares,
b"CF" => Self::CommodityFuturesTrustShares,
b"CL" => Self::CommodityLinkedSecurities,
b"CM" => Self::CommodityIndexTrustShares,
b"CO" => Self::CollateralizedMortgageObligation,
b"CT" => Self::CurrencyTrustShares,
b"CU" => Self::CommodityCurrencyLinkedSecurities,
b"CW" => Self::CurrencyWarrants,
b"D " => Self::GlobalDepositaryShares,
b"E " => Self::ETFPortfolioDepositaryReceipt,
b"EG" => Self::EquityGoldShares,
b"EI" => Self::ETNEquityIndexLinkedSecurities,
b"EM" => Self::NextSharesExchangeTradedManagedFund,
b"EN" => Self::ExchangeTradedNotes,
b"EU" => Self::EquityUnits,
b"F " => Self::HOLDRS,
b"FI" => Self::ETNFixedIncomeLinkedSecurities,
b"FL" => Self::ETNFuturesLinkedSecurities,
b"G " => Self::GlobalShares,
b"I " => Self::ETFIndexFundShares,
b"IR" => Self::InterestRate,
b"IW" => Self::IndexWarrant,
b"IX" => Self::IndexLinkedExchangeableNotes,
b"J " => Self::CorporateBackedTrustSecurity,
b"L " => Self::ContingentLitigationRight,
b"LL" => Self::LimitedLiabilityCompany,
b"M " => Self::EquityBasedDerivative,
b"MF" => Self::ManagedFundShares,
b"ML" => Self::ETNMultiFactorIndexLinkedSecurities,
b"MT" => Self::ManagedTrustSecurities,
b"N " => Self::NYRegistryShares,
b"O " => Self::OpenEndedMutualFund,
b"P " => Self::PrivatelyHeldSecurity,
b"PP" => Self::PoisonPill,
b"PU" => Self::PartnershipUnits,
b"Q " => Self::ClosedEndFunds,
b"R " => Self::RegS,
b"RC" => Self::CommodityRedeemableCommodityLinkedSecurities,
b"RF" => Self::ETNRedeemableFuturesLinkedSecurities,
b"RT" => Self::REIT,
b"RU" => Self::CommodityRedeemableCurrencyLinkedSecurities,
b"S " => Self::SEED,
b"SC" => Self::SpotRateClosing,
b"SI" => Self::SpotRateIntraday,
b"T " => Self::TrackingStock,
b"TC" => Self::TrustCertificates,
b"TU" => Self::TrustUnits,
b"U " => Self::Portal,
b"V " => Self::ContingentValueRight,
b"W " => Self::TrustIssuedReceipts,
b"WC" => Self::WorldCurrencyOption,
b"X " => Self::Trust,
b"Y " => Self::Other,
b"Z " => Self::NotApplicable,
_ => Self::Unknown(value),
}
}
}
impl From<[u8; 2]> for IssueSubTypeCode {
#[inline]
fn from(value: [u8; 2]) -> Self {
Self::from_bytes(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash_symbol_eq_unhash() {
let input = "aapl";
let s: Symbol = input.parse().expect("should not panic");
assert_eq!(Symbol::from_u64(s.to_u64()), s)
}
/// ITCH5 wire format right-pads symbols with ASCII spaces. `from_str` must
/// produce the same byte pattern so a user-supplied watch symbol hashes to
/// the same value as the symbol carried in a StockDirectory message.
#[test]
fn from_str_pads_with_spaces() {
let s: Symbol = "AAPL".parse().unwrap();
assert_eq!(&s.0, b"AAPL ");
}
#[test]
fn from_str_hash_matches_wire_symbol() {
let from_user: Symbol = "AAPL".parse().unwrap();
let from_wire = Symbol(*b"AAPL ");
assert_eq!(from_user.to_u64(), from_wire.to_u64());
assert_eq!(from_user, from_wire);
}
#[test]
fn from_str_uppercases() {
let lower: Symbol = "aapl".parse().unwrap();
let upper: Symbol = "AAPL".parse().unwrap();
assert_eq!(lower.to_u64(), upper.to_u64());
}
#[test]
fn from_str_full_eight_chars_not_padded() {
let s: Symbol = "ABCDEFGH".parse().unwrap();
assert_eq!(&s.0, b"ABCDEFGH");
}
#[test]
fn from_str_truncates_overlong_input() {
let s: Symbol = "ABCDEFGHIJ".parse().unwrap();
assert_eq!(&s.0, b"ABCDEFGH");
}
#[test]
fn from_str_empty_is_err() {
assert!("".parse::<Symbol>().is_err());
}
}