use std::{
collections::HashMap,
fmt::{Display, Formatter},
hash::Hash,
};
use indexmap::IndexMap;
use nautilus_core::{nanos::UnixNanos, serialization::Serializable};
use serde::{Deserialize, Serialize};
use super::{
order::{BookOrder, NULL_ORDER},
GetTsInit,
};
use crate::{
enums::{BookAction, RecordFlag},
identifiers::InstrumentId,
};
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "type")]
#[cfg_attr(
feature = "python",
pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
)]
#[cfg_attr(feature = "trivial_copy", derive(Copy))]
pub struct OrderBookDelta {
pub instrument_id: InstrumentId,
pub action: BookAction,
pub order: BookOrder,
pub flags: u8,
pub sequence: u64,
pub ts_event: UnixNanos,
pub ts_init: UnixNanos,
}
impl OrderBookDelta {
#[allow(clippy::too_many_arguments)]
#[must_use]
pub fn new(
instrument_id: InstrumentId,
action: BookAction,
order: BookOrder,
flags: u8,
sequence: u64,
ts_event: UnixNanos,
ts_init: UnixNanos,
) -> Self {
Self {
instrument_id,
action,
order,
flags,
sequence,
ts_event,
ts_init,
}
}
#[must_use]
pub fn clear(
instrument_id: InstrumentId,
sequence: u64,
ts_event: UnixNanos,
ts_init: UnixNanos,
) -> Self {
Self {
instrument_id,
action: BookAction::Clear,
order: NULL_ORDER,
flags: RecordFlag::F_SNAPSHOT as u8,
sequence,
ts_event,
ts_init,
}
}
#[must_use]
pub fn get_metadata(
instrument_id: &InstrumentId,
price_precision: u8,
size_precision: u8,
) -> HashMap<String, String> {
let mut metadata = HashMap::new();
metadata.insert("instrument_id".to_string(), instrument_id.to_string());
metadata.insert("price_precision".to_string(), price_precision.to_string());
metadata.insert("size_precision".to_string(), size_precision.to_string());
metadata
}
#[must_use]
pub fn get_fields() -> IndexMap<String, String> {
let mut metadata = IndexMap::new();
metadata.insert("action".to_string(), "UInt8".to_string());
metadata.insert("side".to_string(), "UInt8".to_string());
metadata.insert("price".to_string(), "Int64".to_string());
metadata.insert("size".to_string(), "UInt64".to_string());
metadata.insert("order_id".to_string(), "UInt64".to_string());
metadata.insert("flags".to_string(), "UInt8".to_string());
metadata.insert("sequence".to_string(), "UInt64".to_string());
metadata.insert("ts_event".to_string(), "UInt64".to_string());
metadata.insert("ts_init".to_string(), "UInt64".to_string());
metadata
}
}
impl Display for OrderBookDelta {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{},{},{},{},{},{},{}",
self.instrument_id,
self.action,
self.order,
self.flags,
self.sequence,
self.ts_event,
self.ts_init
)
}
}
impl Serializable for OrderBookDelta {}
impl GetTsInit for OrderBookDelta {
fn ts_init(&self) -> UnixNanos {
self.ts_init
}
}
#[cfg(test)]
mod tests {
use nautilus_core::serialization::Serializable;
use rstest::rstest;
use crate::{
data::{delta::OrderBookDelta, order::BookOrder, stubs::*},
enums::{BookAction, OrderSide},
identifiers::InstrumentId,
types::{price::Price, quantity::Quantity},
};
#[rstest]
fn test_new() {
let instrument_id = InstrumentId::from("AAPL.XNAS");
let action = BookAction::Add;
let price = Price::from("100.00");
let size = Quantity::from("10");
let side = OrderSide::Buy;
let order_id = 123_456;
let flags = 0;
let sequence = 1;
let ts_event = 1;
let ts_init = 2;
let order = BookOrder::new(side, price, size, order_id);
let delta = OrderBookDelta::new(
instrument_id,
action,
order,
flags,
sequence,
ts_event.into(),
ts_init.into(),
);
assert_eq!(delta.instrument_id, instrument_id);
assert_eq!(delta.action, action);
assert_eq!(delta.order.price, price);
assert_eq!(delta.order.size, size);
assert_eq!(delta.order.side, side);
assert_eq!(delta.order.order_id, order_id);
assert_eq!(delta.flags, flags);
assert_eq!(delta.sequence, sequence);
assert_eq!(delta.ts_event, ts_event);
assert_eq!(delta.ts_init, ts_init);
}
#[rstest]
fn test_clear() {
let instrument_id = InstrumentId::from("AAPL.XNAS");
let sequence = 1;
let ts_event = 2;
let ts_init = 3;
let delta = OrderBookDelta::clear(instrument_id, sequence, ts_event.into(), ts_init.into());
assert_eq!(delta.instrument_id, instrument_id);
assert_eq!(delta.action, BookAction::Clear);
assert!(delta.order.price.is_zero());
assert!(delta.order.size.is_zero());
assert_eq!(delta.order.side, OrderSide::NoOrderSide);
assert_eq!(delta.order.order_id, 0);
assert_eq!(delta.flags, 32);
assert_eq!(delta.sequence, sequence);
assert_eq!(delta.ts_event, ts_event);
assert_eq!(delta.ts_init, ts_init);
}
#[rstest]
fn test_display(stub_delta: OrderBookDelta) {
let delta = stub_delta;
assert_eq!(
format!("{delta}"),
"AAPL.XNAS,ADD,BUY,100.00,10,123456,0,1,1,2".to_string()
);
}
#[rstest]
fn test_json_serialization(stub_delta: OrderBookDelta) {
let delta = stub_delta;
let serialized = delta.as_json_bytes().unwrap();
let deserialized = OrderBookDelta::from_json_bytes(serialized.as_ref()).unwrap();
assert_eq!(deserialized, delta);
}
#[rstest]
fn test_msgpack_serialization(stub_delta: OrderBookDelta) {
let delta = stub_delta;
let serialized = delta.as_msgpack_bytes().unwrap();
let deserialized = OrderBookDelta::from_msgpack_bytes(serialized.as_ref()).unwrap();
assert_eq!(deserialized, delta);
}
}