use nautilus_model::{
data::{
FundingRateUpdate, IndexPriceUpdate, InstrumentClose, InstrumentStatus, MarkPriceUpdate,
OptionGreekValues, OptionGreeks, QuoteTick, TradeTick,
},
identifiers::TradeId,
};
use super::{
super::{SbeCursor, SbeDecodeError, SbeEncodeError, SbeWriter},
MarketSbeMessage,
common::{
DECIMAL_BLOCK_LENGTH, PRICE_BLOCK_LENGTH, QUANTITY_BLOCK_LENGTH, decode_aggressor_side,
decode_decimal, decode_greeks_convention, decode_instrument_close_type,
decode_instrument_id, decode_market_status_action, decode_optional_bool,
decode_optional_ustr, decode_price, decode_quantity, decode_unix_nanos, encode_decimal,
encode_instrument_id, encode_optional_bool, encode_optional_ustr, encode_price,
encode_quantity, encode_unix_nanos, encode_var_string16, encoded_instrument_id_size,
encoded_optional_ustr_size, encoded_var_string16_size,
},
template_id,
};
impl MarketSbeMessage for QuoteTick {
const TEMPLATE_ID: u16 = template_id::QUOTE_TICK;
const BLOCK_LENGTH: u16 = (PRICE_BLOCK_LENGTH * 2) + (QUANTITY_BLOCK_LENGTH * 2) + 16;
fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError> {
encode_price(writer, &self.bid_price);
encode_price(writer, &self.ask_price);
encode_quantity(writer, &self.bid_size);
encode_quantity(writer, &self.ask_size);
encode_unix_nanos(writer, self.ts_event);
encode_unix_nanos(writer, self.ts_init);
encode_instrument_id(writer, &self.instrument_id)
}
fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError> {
let bid_price = decode_price(cursor)?;
let ask_price = decode_price(cursor)?;
let bid_size = decode_quantity(cursor)?;
let ask_size = decode_quantity(cursor)?;
let ts_event = decode_unix_nanos(cursor)?;
let ts_init = decode_unix_nanos(cursor)?;
let instrument_id = decode_instrument_id(cursor)?;
Ok(Self {
instrument_id,
bid_price,
ask_price,
bid_size,
ask_size,
ts_event,
ts_init,
})
}
fn encoded_body_size(&self) -> usize {
usize::from(Self::BLOCK_LENGTH) + encoded_instrument_id_size(&self.instrument_id)
}
}
impl MarketSbeMessage for TradeTick {
const TEMPLATE_ID: u16 = template_id::TRADE_TICK;
const BLOCK_LENGTH: u16 = PRICE_BLOCK_LENGTH + QUANTITY_BLOCK_LENGTH + 17;
fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError> {
encode_price(writer, &self.price);
encode_quantity(writer, &self.size);
writer.write_u8(self.aggressor_side as u8);
encode_unix_nanos(writer, self.ts_event);
encode_unix_nanos(writer, self.ts_init);
encode_instrument_id(writer, &self.instrument_id)?;
encode_var_string16(writer, "TradeTick.trade_id", self.trade_id.as_str())
}
fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError> {
let price = decode_price(cursor)?;
let size = decode_quantity(cursor)?;
let aggressor_side = decode_aggressor_side(cursor)?;
let ts_event = decode_unix_nanos(cursor)?;
let ts_init = decode_unix_nanos(cursor)?;
let instrument_id = decode_instrument_id(cursor)?;
let trade_id = TradeId::new(cursor.read_var_string16_ref()?);
Ok(Self {
instrument_id,
price,
size,
aggressor_side,
trade_id,
ts_event,
ts_init,
})
}
fn encoded_body_size(&self) -> usize {
usize::from(Self::BLOCK_LENGTH)
+ encoded_instrument_id_size(&self.instrument_id)
+ encoded_var_string16_size(self.trade_id.as_str())
}
}
impl MarketSbeMessage for MarkPriceUpdate {
const TEMPLATE_ID: u16 = template_id::MARK_PRICE_UPDATE;
const BLOCK_LENGTH: u16 = PRICE_BLOCK_LENGTH + 16;
fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError> {
encode_price(writer, &self.value);
encode_unix_nanos(writer, self.ts_event);
encode_unix_nanos(writer, self.ts_init);
encode_instrument_id(writer, &self.instrument_id)
}
fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError> {
let value = decode_price(cursor)?;
let ts_event = decode_unix_nanos(cursor)?;
let ts_init = decode_unix_nanos(cursor)?;
let instrument_id = decode_instrument_id(cursor)?;
Ok(Self {
instrument_id,
value,
ts_event,
ts_init,
})
}
fn encoded_body_size(&self) -> usize {
usize::from(Self::BLOCK_LENGTH) + encoded_instrument_id_size(&self.instrument_id)
}
}
impl MarketSbeMessage for IndexPriceUpdate {
const TEMPLATE_ID: u16 = template_id::INDEX_PRICE_UPDATE;
const BLOCK_LENGTH: u16 = PRICE_BLOCK_LENGTH + 16;
fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError> {
encode_price(writer, &self.value);
encode_unix_nanos(writer, self.ts_event);
encode_unix_nanos(writer, self.ts_init);
encode_instrument_id(writer, &self.instrument_id)
}
fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError> {
let value = decode_price(cursor)?;
let ts_event = decode_unix_nanos(cursor)?;
let ts_init = decode_unix_nanos(cursor)?;
let instrument_id = decode_instrument_id(cursor)?;
Ok(Self {
instrument_id,
value,
ts_event,
ts_init,
})
}
fn encoded_body_size(&self) -> usize {
usize::from(Self::BLOCK_LENGTH) + encoded_instrument_id_size(&self.instrument_id)
}
}
impl MarketSbeMessage for FundingRateUpdate {
const TEMPLATE_ID: u16 = template_id::FUNDING_RATE_UPDATE;
const BLOCK_LENGTH: u16 = DECIMAL_BLOCK_LENGTH + 26;
fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError> {
encode_decimal(writer, &self.rate);
writer.write_u16_le(self.interval.unwrap_or(u16::MAX));
writer.write_u64_le(self.next_funding_ns.map_or(u64::MAX, |value| *value));
encode_unix_nanos(writer, self.ts_event);
encode_unix_nanos(writer, self.ts_init);
encode_instrument_id(writer, &self.instrument_id)
}
fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError> {
let rate = decode_decimal(cursor)?;
let interval_raw = cursor.read_u16_le()?;
let next_funding_raw = cursor.read_u64_le()?;
let ts_event = decode_unix_nanos(cursor)?;
let ts_init = decode_unix_nanos(cursor)?;
let instrument_id = decode_instrument_id(cursor)?;
Ok(Self {
instrument_id,
rate,
interval: (interval_raw != u16::MAX).then_some(interval_raw),
next_funding_ns: (next_funding_raw != u64::MAX).then_some(next_funding_raw.into()),
ts_event,
ts_init,
})
}
fn encoded_body_size(&self) -> usize {
usize::from(Self::BLOCK_LENGTH) + encoded_instrument_id_size(&self.instrument_id)
}
}
impl MarketSbeMessage for OptionGreeks {
const TEMPLATE_ID: u16 = template_id::OPTION_GREEKS;
const BLOCK_LENGTH: u16 = 98;
fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError> {
writer.write_u8(self.convention as u8);
writer.write_u8(option_greeks_optional_mask(self));
encode_float64(writer, self.greeks.delta);
encode_float64(writer, self.greeks.gamma);
encode_float64(writer, self.greeks.vega);
encode_float64(writer, self.greeks.theta);
encode_float64(writer, self.greeks.rho);
encode_optional_float64(writer, self.mark_iv);
encode_optional_float64(writer, self.bid_iv);
encode_optional_float64(writer, self.ask_iv);
encode_optional_float64(writer, self.underlying_price);
encode_optional_float64(writer, self.open_interest);
encode_unix_nanos(writer, self.ts_event);
encode_unix_nanos(writer, self.ts_init);
encode_instrument_id(writer, &self.instrument_id)
}
fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError> {
let convention = decode_greeks_convention(cursor)?;
let optional_mask = cursor.read_u8()?;
validate_option_greeks_optional_mask(optional_mask)?;
let greeks = OptionGreekValues {
delta: decode_float64(cursor)?,
gamma: decode_float64(cursor)?,
vega: decode_float64(cursor)?,
theta: decode_float64(cursor)?,
rho: decode_float64(cursor)?,
};
let mark_iv = decode_optional_float64(cursor, optional_mask, OPTION_GREEKS_HAS_MARK_IV)?;
let bid_iv = decode_optional_float64(cursor, optional_mask, OPTION_GREEKS_HAS_BID_IV)?;
let ask_iv = decode_optional_float64(cursor, optional_mask, OPTION_GREEKS_HAS_ASK_IV)?;
let underlying_price =
decode_optional_float64(cursor, optional_mask, OPTION_GREEKS_HAS_UNDERLYING_PRICE)?;
let open_interest =
decode_optional_float64(cursor, optional_mask, OPTION_GREEKS_HAS_OPEN_INTEREST)?;
let ts_event = decode_unix_nanos(cursor)?;
let ts_init = decode_unix_nanos(cursor)?;
let instrument_id = decode_instrument_id(cursor)?;
Ok(Self {
instrument_id,
convention,
greeks,
mark_iv,
bid_iv,
ask_iv,
underlying_price,
open_interest,
ts_event,
ts_init,
})
}
fn encoded_body_size(&self) -> usize {
usize::from(Self::BLOCK_LENGTH) + encoded_instrument_id_size(&self.instrument_id)
}
}
impl MarketSbeMessage for InstrumentStatus {
const TEMPLATE_ID: u16 = template_id::INSTRUMENT_STATUS;
const BLOCK_LENGTH: u16 = 21;
fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError> {
writer.write_u16_le(self.action as u16);
writer.write_u8(encode_optional_bool(self.is_trading));
writer.write_u8(encode_optional_bool(self.is_quoting));
writer.write_u8(encode_optional_bool(self.is_short_sell_restricted));
encode_unix_nanos(writer, self.ts_event);
encode_unix_nanos(writer, self.ts_init);
encode_instrument_id(writer, &self.instrument_id)?;
encode_optional_ustr(writer, "InstrumentStatus.reason", self.reason)?;
encode_optional_ustr(writer, "InstrumentStatus.trading_event", self.trading_event)
}
fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError> {
let action = decode_market_status_action(cursor)?;
let is_trading = decode_optional_bool(cursor, "InstrumentStatus.is_trading")?;
let is_quoting = decode_optional_bool(cursor, "InstrumentStatus.is_quoting")?;
let is_short_sell_restricted =
decode_optional_bool(cursor, "InstrumentStatus.is_short_sell_restricted")?;
let ts_event = decode_unix_nanos(cursor)?;
let ts_init = decode_unix_nanos(cursor)?;
let instrument_id = decode_instrument_id(cursor)?;
let reason = decode_optional_ustr(cursor)?;
let trading_event = decode_optional_ustr(cursor)?;
Ok(Self {
instrument_id,
action,
ts_event,
ts_init,
reason,
trading_event,
is_trading,
is_quoting,
is_short_sell_restricted,
})
}
fn encoded_body_size(&self) -> usize {
usize::from(Self::BLOCK_LENGTH)
+ encoded_instrument_id_size(&self.instrument_id)
+ encoded_optional_ustr_size(self.reason)
+ encoded_optional_ustr_size(self.trading_event)
}
}
impl MarketSbeMessage for InstrumentClose {
const TEMPLATE_ID: u16 = template_id::INSTRUMENT_CLOSE;
const BLOCK_LENGTH: u16 = PRICE_BLOCK_LENGTH + 17;
fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError> {
encode_price(writer, &self.close_price);
writer.write_u8(self.close_type as u8);
encode_unix_nanos(writer, self.ts_event);
encode_unix_nanos(writer, self.ts_init);
encode_instrument_id(writer, &self.instrument_id)
}
fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError> {
let close_price = decode_price(cursor)?;
let close_type = decode_instrument_close_type(cursor)?;
let ts_event = decode_unix_nanos(cursor)?;
let ts_init = decode_unix_nanos(cursor)?;
let instrument_id = decode_instrument_id(cursor)?;
Ok(Self {
instrument_id,
close_price,
close_type,
ts_event,
ts_init,
})
}
fn encoded_body_size(&self) -> usize {
usize::from(Self::BLOCK_LENGTH) + encoded_instrument_id_size(&self.instrument_id)
}
}
const OPTION_GREEKS_HAS_MARK_IV: u8 = 1 << 0;
const OPTION_GREEKS_HAS_BID_IV: u8 = 1 << 1;
const OPTION_GREEKS_HAS_ASK_IV: u8 = 1 << 2;
const OPTION_GREEKS_HAS_UNDERLYING_PRICE: u8 = 1 << 3;
const OPTION_GREEKS_HAS_OPEN_INTEREST: u8 = 1 << 4;
const OPTION_GREEKS_KNOWN_OPTIONAL_MASK: u8 = OPTION_GREEKS_HAS_MARK_IV
| OPTION_GREEKS_HAS_BID_IV
| OPTION_GREEKS_HAS_ASK_IV
| OPTION_GREEKS_HAS_UNDERLYING_PRICE
| OPTION_GREEKS_HAS_OPEN_INTEREST;
#[inline]
fn option_greeks_optional_mask(value: &OptionGreeks) -> u8 {
let mut mask = 0;
if value.mark_iv.is_some() {
mask |= OPTION_GREEKS_HAS_MARK_IV;
}
if value.bid_iv.is_some() {
mask |= OPTION_GREEKS_HAS_BID_IV;
}
if value.ask_iv.is_some() {
mask |= OPTION_GREEKS_HAS_ASK_IV;
}
if value.underlying_price.is_some() {
mask |= OPTION_GREEKS_HAS_UNDERLYING_PRICE;
}
if value.open_interest.is_some() {
mask |= OPTION_GREEKS_HAS_OPEN_INTEREST;
}
mask
}
fn validate_option_greeks_optional_mask(mask: u8) -> Result<(), SbeDecodeError> {
if mask & !OPTION_GREEKS_KNOWN_OPTIONAL_MASK != 0 {
return Err(SbeDecodeError::InvalidValue {
field: "OptionGreeks.optional_mask",
});
}
Ok(())
}
#[inline]
fn encode_float64(writer: &mut SbeWriter<'_>, value: f64) {
writer.write_u64_le(value.to_bits());
}
#[inline]
fn encode_optional_float64(writer: &mut SbeWriter<'_>, value: Option<f64>) {
encode_float64(writer, value.unwrap_or(0.0));
}
#[inline]
fn decode_float64(cursor: &mut SbeCursor<'_>) -> Result<f64, SbeDecodeError> {
Ok(f64::from_bits(cursor.read_u64_le()?))
}
fn decode_optional_float64(
cursor: &mut SbeCursor<'_>,
mask: u8,
flag: u8,
) -> Result<Option<f64>, SbeDecodeError> {
let value = decode_float64(cursor)?;
Ok((mask & flag != 0).then_some(value))
}