use super::{LevelEntries, QueueEntry};
use crate::{OrderId, Price, PriceConditionalOrder, RestingPriceConditionalOrder};
use std::ops::{Deref, DerefMut};
use rustc_hash::FxHashMap;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct TriggerPriceLevel {
level_entries: LevelEntries,
}
impl TriggerPriceLevel {
pub fn new() -> Self {
Self::default()
}
pub fn level_entries(&self) -> &LevelEntries {
&self.level_entries
}
pub(crate) fn add_order_entry(&mut self, queue_entry: QueueEntry) {
self.push(queue_entry);
self.increment_order_count();
}
pub(crate) fn mark_order_removed(&mut self) {
self.decrement_order_count();
}
pub(crate) fn drain_orders(
&mut self,
orders: &mut FxHashMap<OrderId, RestingPriceConditionalOrder>,
) -> Vec<(OrderId, PriceConditionalOrder)> {
let mut orders_vec = Vec::with_capacity(self.order_count() as usize);
while !self.is_empty() {
let queue_entry = self.pop().unwrap();
let order_id = queue_entry.order_id();
let Some(order) = orders.get(&order_id) else {
continue; };
if queue_entry.time_priority() != order.time_priority() {
continue; }
orders_vec.push((order_id, orders.remove(&order_id).unwrap().into_inner()));
self.decrement_order_count();
}
orders_vec
}
pub(crate) fn drain_ready_orders_at_price(
&mut self,
orders: &mut FxHashMap<OrderId, RestingPriceConditionalOrder>,
price: Price,
) -> Vec<(OrderId, PriceConditionalOrder)> {
let mut triggered_orders = Vec::new();
while !self.is_empty() {
let queue_entry = self.pop().unwrap();
let order_id = queue_entry.order_id();
let Some(order) = orders.get(&order_id) else {
continue; };
if queue_entry.time_priority() != order.time_priority() {
continue; }
self.decrement_order_count();
if !order.is_ready(price) {
continue;
}
let order = orders.remove(&order_id).unwrap();
triggered_orders.push((order_id, order.into_inner()));
}
triggered_orders
}
}
impl Deref for TriggerPriceLevel {
type Target = LevelEntries;
fn deref(&self) -> &Self::Target {
&self.level_entries
}
}
impl DerefMut for TriggerPriceLevel {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.level_entries
}
}
#[cfg(test)]
mod tests {
use crate::*;
use rustc_hash::FxHashMap;
fn make_resting_pco(
time_priority: SequenceNumber,
trigger_price: Price,
direction: TriggerDirection,
) -> RestingPriceConditionalOrder {
RestingPriceConditionalOrder::new(
time_priority,
0, PriceConditionalOrder::new(
PriceCondition::new(trigger_price, direction),
TriggerOrder::Market(MarketOrder::new(Quantity(1), Side::Buy, false)),
),
)
}
#[test]
fn test_order_count() {
let mut trigger_price_level = TriggerPriceLevel::new();
assert_eq!(trigger_price_level.order_count(), 0);
assert!(trigger_price_level.is_empty());
trigger_price_level.increment_order_count();
assert_eq!(trigger_price_level.order_count(), 1);
assert!(!trigger_price_level.is_empty());
trigger_price_level.decrement_order_count();
assert_eq!(trigger_price_level.order_count(), 0);
assert!(trigger_price_level.is_empty());
}
#[test]
fn test_add_order_entry_and_mark_order_removed() {
let mut trigger_price_level = TriggerPriceLevel::new();
assert_eq!(trigger_price_level.order_count(), 0);
trigger_price_level.add_order_entry(QueueEntry::new(SequenceNumber(0), OrderId(0)));
assert_eq!(trigger_price_level.order_count(), 1);
trigger_price_level.add_order_entry(QueueEntry::new(SequenceNumber(1), OrderId(1)));
assert_eq!(trigger_price_level.order_count(), 2);
trigger_price_level.add_order_entry(QueueEntry::new(SequenceNumber(2), OrderId(2)));
assert_eq!(trigger_price_level.order_count(), 3);
trigger_price_level.add_order_entry(QueueEntry::new(SequenceNumber(3), OrderId(3)));
assert_eq!(trigger_price_level.order_count(), 4);
trigger_price_level.add_order_entry(QueueEntry::new(SequenceNumber(4), OrderId(4)));
assert_eq!(trigger_price_level.order_count(), 5);
trigger_price_level.mark_order_removed();
assert_eq!(trigger_price_level.order_count(), 4);
trigger_price_level.mark_order_removed();
assert_eq!(trigger_price_level.order_count(), 3);
trigger_price_level.mark_order_removed();
assert_eq!(trigger_price_level.order_count(), 2);
trigger_price_level.mark_order_removed();
assert_eq!(trigger_price_level.order_count(), 1);
trigger_price_level.mark_order_removed();
assert_eq!(trigger_price_level.order_count(), 0);
}
#[test]
fn drain_orders_drains_valid_orders_and_decrements_order_count() {
let mut orders: FxHashMap<OrderId, RestingPriceConditionalOrder> = FxHashMap::default();
let mut level = TriggerPriceLevel::new();
let o0 = make_resting_pco(SequenceNumber(0), Price(100), TriggerDirection::AtOrAbove);
let o1 = make_resting_pco(SequenceNumber(1), Price(100), TriggerDirection::AtOrAbove);
orders.insert(OrderId(0), o0.clone());
orders.insert(OrderId(1), o1.clone());
level.add_order_entry(QueueEntry::new(SequenceNumber(0), OrderId(0)));
level.add_order_entry(QueueEntry::new(SequenceNumber(1), OrderId(1)));
assert_eq!(level.order_count(), 2);
let drained = level.drain_orders(&mut orders);
assert_eq!(
drained,
vec![(OrderId(0), o0.into_inner()), (OrderId(1), o1.into_inner())]
);
assert!(orders.is_empty());
assert_eq!(level.order_count(), 0);
assert!(level.is_empty());
assert!(level.queue().is_empty());
}
#[test]
fn drain_orders_skips_stale_missing_order_entries_before_valid_orders() {
let mut orders: FxHashMap<OrderId, RestingPriceConditionalOrder> = FxHashMap::default();
let mut level = TriggerPriceLevel::new();
level.push(QueueEntry::new(SequenceNumber(0), OrderId(999)));
let o1 = make_resting_pco(SequenceNumber(1), Price(123), TriggerDirection::AtOrAbove);
orders.insert(OrderId(1), o1.clone());
level.add_order_entry(QueueEntry::new(SequenceNumber(1), OrderId(1)));
assert_eq!(level.order_count(), 1);
let drained = level.drain_orders(&mut orders);
assert_eq!(drained, vec![(OrderId(1), o1.into_inner())]);
assert!(orders.is_empty());
assert_eq!(level.order_count(), 0);
assert!(level.queue().is_empty());
}
#[test]
fn drain_ready_orders_at_price_triggers_at_or_above_when_price_meets_threshold() {
let mut orders: FxHashMap<OrderId, RestingPriceConditionalOrder> = FxHashMap::default();
let mut level = TriggerPriceLevel::new();
let o0 = make_resting_pco(SequenceNumber(0), Price(100), TriggerDirection::AtOrAbove);
let o1 = make_resting_pco(SequenceNumber(1), Price(101), TriggerDirection::AtOrAbove);
orders.insert(OrderId(0), o0.clone());
orders.insert(OrderId(1), o1.clone());
level.add_order_entry(QueueEntry::new(SequenceNumber(0), OrderId(0)));
level.add_order_entry(QueueEntry::new(SequenceNumber(1), OrderId(1)));
assert_eq!(level.order_count(), 2);
let ready = level.drain_ready_orders_at_price(&mut orders, Price(101));
assert_eq!(
ready,
vec![(OrderId(0), o0.into_inner()), (OrderId(1), o1.into_inner())]
);
assert!(orders.is_empty());
assert_eq!(level.order_count(), 0);
assert!(level.queue().is_empty());
}
#[test]
fn drain_ready_orders_at_price_triggers_at_or_below_when_price_meets_threshold() {
let mut orders: FxHashMap<OrderId, RestingPriceConditionalOrder> = FxHashMap::default();
let mut level = TriggerPriceLevel::new();
let o0 = make_resting_pco(SequenceNumber(0), Price(100), TriggerDirection::AtOrBelow);
let o1 = make_resting_pco(SequenceNumber(1), Price(99), TriggerDirection::AtOrBelow);
orders.insert(OrderId(0), o0.clone());
orders.insert(OrderId(1), o1.clone());
level.add_order_entry(QueueEntry::new(SequenceNumber(0), OrderId(0)));
level.add_order_entry(QueueEntry::new(SequenceNumber(1), OrderId(1)));
assert_eq!(level.order_count(), 2);
let ready = level.drain_ready_orders_at_price(&mut orders, Price(99));
assert_eq!(
ready,
vec![(OrderId(0), o0.into_inner()), (OrderId(1), o1.into_inner())]
);
assert!(orders.is_empty());
assert_eq!(level.order_count(), 0);
assert!(level.queue().is_empty());
}
#[test]
fn drain_ready_orders_at_price_skips_stale_missing_order_entries_before_valid_orders() {
let mut orders: FxHashMap<OrderId, RestingPriceConditionalOrder> = FxHashMap::default();
let mut level = TriggerPriceLevel::new();
level.push(QueueEntry::new(SequenceNumber(0), OrderId(999)));
let o1 = make_resting_pco(SequenceNumber(1), Price(100), TriggerDirection::AtOrAbove);
orders.insert(OrderId(1), o1.clone());
level.add_order_entry(QueueEntry::new(SequenceNumber(1), OrderId(1)));
assert_eq!(level.order_count(), 1);
let ready = level.drain_ready_orders_at_price(&mut orders, Price(100));
assert_eq!(ready, vec![(OrderId(1), o1.into_inner())]);
assert!(orders.is_empty());
assert_eq!(level.order_count(), 0);
assert!(level.queue().is_empty());
}
#[test]
fn drain_ready_orders_at_price_skips_stale_order_on_time_priority_mismatch() {
let mut orders: FxHashMap<OrderId, RestingPriceConditionalOrder> = FxHashMap::default();
let mut level = TriggerPriceLevel::new();
let o0 = make_resting_pco(SequenceNumber(0), Price(100), TriggerDirection::AtOrAbove);
let o1 = make_resting_pco(SequenceNumber(1), Price(100), TriggerDirection::AtOrAbove);
orders.insert(OrderId(0), o0);
orders.insert(OrderId(1), o1.clone());
level.add_order_entry(QueueEntry::new(SequenceNumber(999), OrderId(0)));
level.add_order_entry(QueueEntry::new(SequenceNumber(1), OrderId(1)));
level.mark_order_removed();
let ready = level.drain_ready_orders_at_price(&mut orders, Price(100));
assert_eq!(ready, vec![(OrderId(1), o1.into_inner())]);
assert!(orders.contains_key(&OrderId(0)));
assert!(!orders.contains_key(&OrderId(1)));
assert!(level.queue().is_empty());
}
#[test]
fn drain_ready_orders_at_price_dequeues_non_triggered_orders_but_leaves_them_in_orders_map() {
let mut orders: FxHashMap<OrderId, RestingPriceConditionalOrder> = FxHashMap::default();
let mut level = TriggerPriceLevel::new();
let o0 = make_resting_pco(SequenceNumber(0), Price(101), TriggerDirection::AtOrAbove);
orders.insert(OrderId(0), o0.clone());
level.add_order_entry(QueueEntry::new(SequenceNumber(0), OrderId(0)));
assert_eq!(level.order_count(), 1);
let ready = level.drain_ready_orders_at_price(&mut orders, Price(100));
assert!(ready.is_empty());
assert!(orders.contains_key(&OrderId(0)));
assert_eq!(level.order_count(), 0);
assert!(level.queue().is_empty());
}
}