#[cfg(test)]
mod tests {
use crate::orderbook::book::OrderBook;
use pricelevel::{Hash32, Id, OrderType, Price, Quantity, Side, TimeInForce, TimestampMs};
use std::num::NonZeroU64;
use std::sync::atomic::Ordering;
const PRICE: u128 = 100;
const EXPIRY_DEADLINE_MS: u64 = 4_000_000_000_000;
fn count(book: &OrderBook<()>) -> usize {
book.strandable_makers_resting.load(Ordering::Relaxed)
}
fn armed(book: &OrderBook<()>) -> bool {
count(book) > 0
}
fn reserve_buy(
id: Id,
visible: u64,
hidden: u64,
replenish_amount: Option<u64>,
auto_replenish: bool,
) -> OrderType<()> {
OrderType::ReserveOrder {
id,
price: Price::new(PRICE),
visible_quantity: Quantity::new(visible),
hidden_quantity: Quantity::new(hidden),
side: Side::Buy,
user_id: Hash32::zero(),
timestamp: TimestampMs::new(0),
time_in_force: TimeInForce::Gtc,
replenish_threshold: Quantity::new(0),
replenish_amount: replenish_amount.and_then(NonZeroU64::new),
auto_replenish,
extra_fields: (),
}
}
fn iceberg_buy(id: Id, visible: u64, hidden: u64) -> OrderType<()> {
OrderType::IcebergOrder {
id,
price: Price::new(PRICE),
visible_quantity: Quantity::new(visible),
hidden_quantity: Quantity::new(hidden),
side: Side::Buy,
user_id: Hash32::zero(),
timestamp: TimestampMs::new(0),
time_in_force: TimeInForce::Gtc,
extra_fields: (),
}
}
#[test]
fn test_strandable_makers_resting_starts_false() {
let book: OrderBook<()> = OrderBook::new("FLAG-NEW");
assert!(!armed(&book), "a fresh book has rested nothing");
}
#[test]
fn test_strandable_makers_resting_stays_false_for_iceberg_and_auto_reserve() {
let book: OrderBook<()> = OrderBook::new("FLAG-SAFE");
let iceberg_id = Id::new();
let auto_id = Id::new();
assert!(
book.add_order(iceberg_buy(iceberg_id, 10, 20)).is_ok(),
"iceberg must rest"
);
assert!(
book.add_order(reserve_buy(auto_id, 10, 20, Some(10), true))
.is_ok(),
"auto-replenishing reserve must rest"
);
assert!(!armed(&book), "neither kind can strand hidden depth");
assert!(
book.add_limit_order(Id::new(), PRICE, 15, Side::Sell, TimeInForce::Gtc, None)
.is_ok(),
"the crossing sell must be accepted"
);
assert!(!armed(&book), "matching does not arm the gate by itself");
}
#[test]
fn test_strandable_makers_resting_set_by_resting_non_auto_reserve() {
let book: OrderBook<()> = OrderBook::new("FLAG-ARM");
assert!(
book.add_order(reserve_buy(Id::new(), 10, 20, None, false))
.is_ok(),
"the reserve must rest"
);
assert!(armed(&book), "a strandable maker is now on the book");
}
#[test]
fn test_strandable_makers_resting_stays_false_without_hidden_depth() {
let book: OrderBook<()> = OrderBook::new("FLAG-NO-HIDDEN");
assert!(
book.add_order(reserve_buy(Id::new(), 10, 0, None, false))
.is_ok(),
"the reserve must rest"
);
assert!(!armed(&book), "no hidden tranche, nothing to strand");
}
#[test]
fn test_strandable_makers_resting_set_by_rested_residual() {
let book: OrderBook<()> = OrderBook::new("FLAG-RESIDUAL");
assert!(
book.add_limit_order(Id::new(), PRICE, 5, Side::Sell, TimeInForce::Gtc, None)
.is_ok(),
"contra depth must rest"
);
let taker_id = Id::new();
assert!(
book.add_order(reserve_buy(taker_id, 10, 20, None, false))
.is_ok(),
"the aggressive reserve must rest its residual"
);
assert!(
book.get_order(taker_id).is_some(),
"5 of the visible tranche was taken, so the residual rests"
);
assert!(armed(&book), "the rested residual is strandable");
}
#[test]
fn test_strandable_makers_resting_rederived_by_snapshot_package_restore() {
let source: OrderBook<()> = OrderBook::new("FLAG-RESTORE");
let order_id = Id::new();
assert!(
source
.add_order(reserve_buy(order_id, 10, 20, None, false))
.is_ok(),
"the reserve must rest on the source book"
);
assert!(armed(&source), "the source book is armed");
let package = match source.create_snapshot_package(usize::MAX) {
Ok(package) => package,
Err(error) => panic!("snapshot package must build: {error}"),
};
let mut restored: OrderBook<()> = OrderBook::new("FLAG-RESTORE");
assert!(!armed(&restored), "the destination starts closed");
assert!(
restored.restore_from_snapshot_package(package).is_ok(),
"the package must restore"
);
assert!(
restored.get_order(order_id).is_some(),
"the strandable maker is on the restored book"
);
assert!(
armed(&restored),
"restore must re-derive the gate from the installed orders"
);
}
#[test]
fn test_strandable_makers_resting_stays_false_restoring_a_safe_package() {
let source: OrderBook<()> = OrderBook::new("FLAG-RESTORE-SAFE");
assert!(
source.add_order(iceberg_buy(Id::new(), 10, 20)).is_ok(),
"the iceberg must rest on the source book"
);
let package = match source.create_snapshot_package(usize::MAX) {
Ok(package) => package,
Err(error) => panic!("snapshot package must build: {error}"),
};
let mut restored: OrderBook<()> = OrderBook::new("FLAG-RESTORE-SAFE");
assert!(
restored.restore_from_snapshot_package(package).is_ok(),
"the package must restore"
);
assert!(!armed(&restored), "nothing installed can strand anything");
}
#[test]
fn test_strandable_makers_resting_returns_to_zero_on_cancel() {
let book: OrderBook<()> = OrderBook::new("COUNT-CANCEL");
let first = Id::new();
let second = Id::new();
assert!(
book.add_order(reserve_buy(first, 10, 20, None, false))
.is_ok()
);
assert!(
book.add_order(reserve_buy(second, 10, 20, None, false))
.is_ok()
);
assert_eq!(count(&book), 2, "both strandable makers are counted");
assert!(book.cancel_order(first).is_ok(), "first cancel");
assert_eq!(count(&book), 1, "one decrement per removal");
assert!(book.cancel_order(second).is_ok(), "second cancel");
assert_eq!(count(&book), 0, "the gate closes again");
assert!(!armed(&book), "no scan is armed once none rest");
}
#[test]
fn test_strandable_makers_resting_returns_to_zero_on_mass_cancel() {
let book: OrderBook<()> = OrderBook::new("COUNT-MASS");
assert!(
book.add_order(reserve_buy(Id::new(), 10, 20, None, false))
.is_ok()
);
assert!(
book.add_order(reserve_buy(Id::new(), 10, 20, None, false))
.is_ok()
);
assert!(book.add_order(iceberg_buy(Id::new(), 10, 20)).is_ok());
assert_eq!(count(&book), 2, "only the reserves are counted");
let cancelled = book.cancel_all_orders();
assert_eq!(cancelled.cancelled_count(), 3, "every maker is cancelled");
assert_eq!(
count(&book),
0,
"mass cancel decrements through the same funnel"
);
}
#[test]
fn test_strandable_makers_resting_returns_to_zero_when_consumed() {
let book: OrderBook<()> = OrderBook::new("COUNT-CONSUMED");
let maker_id = Id::new();
assert!(
book.add_order(reserve_buy(maker_id, 10, 20, None, false))
.is_ok()
);
assert_eq!(count(&book), 1, "the maker is counted");
assert!(
book.add_limit_order(Id::new(), PRICE, 10, Side::Sell, TimeInForce::Gtc, None)
.is_ok(),
"the sweep takes the whole visible tranche"
);
assert!(book.get_order(maker_id).is_none(), "the maker is removed");
assert_eq!(count(&book), 0, "the fill drain decrements the count");
assert!(
!armed(&book),
"the gate closes after the last one is consumed"
);
}
#[test]
fn test_strandable_makers_resting_decrements_exactly_once_per_maker() {
let cancelled: OrderBook<()> = OrderBook::new("COUNT-ONCE-CANCEL");
let maker = Id::new();
assert!(
cancelled
.add_order(reserve_buy(maker, 10, 20, None, false))
.is_ok()
);
assert_eq!(count(&cancelled), 1);
assert!(cancelled.cancel_order(maker).is_ok(), "cancel removes it");
assert_eq!(count(&cancelled), 0, "one decrement");
assert!(
cancelled
.add_limit_order(Id::new(), PRICE, 10, Side::Sell, TimeInForce::Gtc, None)
.is_ok(),
"a later sweep finds nothing to consume"
);
assert_eq!(count(&cancelled), 0, "no second decrement");
let matched: OrderBook<()> = OrderBook::new("COUNT-ONCE-MATCH");
let consumed = Id::new();
assert!(
matched
.add_order(reserve_buy(consumed, 10, 20, None, false))
.is_ok()
);
assert_eq!(count(&matched), 1);
assert!(
matched
.add_limit_order(Id::new(), PRICE, 10, Side::Sell, TimeInForce::Gtc, None)
.is_ok(),
"the sweep consumes it"
);
assert_eq!(count(&matched), 0, "one decrement");
assert!(
matches!(matched.cancel_order(consumed), Ok(None)),
"the order is already gone"
);
assert_eq!(count(&matched), 0, "no second decrement");
}
#[test]
fn test_strandable_makers_resting_ignores_a_reused_id() {
let book: OrderBook<()> = OrderBook::new("COUNT-ID-REUSE");
let shared_id = Id::new();
assert!(
book.add_order(reserve_buy(shared_id, 10, 20, None, false))
.is_ok()
);
assert_eq!(count(&book), 1);
assert!(book.cancel_order(shared_id).is_ok());
assert_eq!(count(&book), 0, "the reserve is gone");
assert!(
book.add_limit_order(shared_id, PRICE, 10, Side::Buy, TimeInForce::Gtc, None)
.is_ok(),
"the freed id is reusable"
);
assert_eq!(count(&book), 0, "a Standard order is not strandable");
assert!(
book.add_limit_order(Id::new(), PRICE, 10, Side::Sell, TimeInForce::Gtc, None)
.is_ok(),
"a sweep consumes the plain order"
);
assert_eq!(count(&book), 0, "consuming it decrements nothing");
}
#[test]
fn test_strandable_makers_resting_follows_every_mass_cancel_entry_point() {
for label in ["by_side", "by_user", "by_price_range", "expiry"] {
let book: OrderBook<()> = OrderBook::new("COUNT-MASS-PATHS");
let user = pricelevel::Hash32::from([5u8; 32]);
let mut order = reserve_buy(Id::new(), 10, 20, None, false);
if let OrderType::ReserveOrder {
user_id,
time_in_force,
..
} = &mut order
{
*user_id = user;
if label == "expiry" {
*time_in_force = TimeInForce::Gtd(EXPIRY_DEADLINE_MS);
}
}
assert!(book.add_order(order).is_ok(), "{label}: the reserve rests");
assert_eq!(count(&book), 1, "{label}: counted");
match label {
"by_side" => {
let removed = book.cancel_orders_by_side(Side::Buy);
assert_eq!(removed.cancelled_count(), 1, "{label}: one order cancelled");
}
"by_user" => {
let removed = book.cancel_orders_by_user(user);
assert_eq!(removed.cancelled_count(), 1, "{label}: one order cancelled");
}
"by_price_range" => {
let removed =
book.cancel_orders_by_price_range(Side::Buy, PRICE - 1, PRICE + 1);
assert_eq!(removed.cancelled_count(), 1, "{label}: one order cancelled");
}
_ => {
book.evict_expired_orders(TimestampMs::new(EXPIRY_DEADLINE_MS + 1));
}
}
assert_eq!(count(&book), 0, "{label}: the count returns to zero");
}
}
#[test]
fn test_strandable_makers_resting_survives_a_cancel_then_add_modify() {
let book: OrderBook<()> = OrderBook::new("COUNT-MODIFY");
let maker = Id::new();
assert!(
book.add_order(reserve_buy(maker, 10, 20, None, false))
.is_ok()
);
assert_eq!(count(&book), 1);
let repriced = book.update_order(pricelevel::OrderUpdate::UpdatePrice {
order_id: maker,
new_price: Price::new(PRICE - 1),
});
assert!(repriced.is_ok(), "the re-price succeeds: {repriced:?}");
assert_eq!(
count(&book),
1,
"the cancel decremented and the re-add incremented"
);
match book.get_order(maker) {
Some(order) => assert_eq!(order.price().as_u128(), PRICE - 1, "re-priced"),
None => panic!("the re-priced maker must rest"),
}
}
fn book_holding_a_ghost(symbol: &str, ghost_id: Id) -> OrderBook<()> {
let source: OrderBook<()> = OrderBook::new(symbol);
assert!(
source
.add_order(reserve_buy(ghost_id, 10, 20, None, false))
.is_ok(),
"the reserve rests with a visible tranche"
);
{
let level = source.bids.get(&PRICE).expect("the bid level exists");
let emptied = level
.value()
.update_order(pricelevel::OrderUpdate::UpdateQuantity {
order_id: ghost_id,
new_quantity: Quantity::new(0),
});
assert!(
emptied.is_ok(),
"the level accepts the zero update: {emptied:?}"
);
}
source
}
fn destination_with_state(symbol: &str, survivor: Id) -> OrderBook<()> {
let mut destination: OrderBook<()> = OrderBook::new(symbol);
destination.set_lot_size(5);
destination.set_min_order_size(5);
assert!(
destination
.add_limit_order(survivor, PRICE, 5, Side::Buy, TimeInForce::Gtc, None)
.is_ok(),
"the destination has state to protect"
);
destination
}
fn assert_destination_intact(book: &OrderBook<()>, survivor: Id, ghost_id: Id) {
assert!(
book.get_order(survivor).is_some(),
"a rejected restore must leave the live book untouched"
);
assert!(
book.get_order(ghost_id).is_none(),
"nothing from the rejected package may land"
);
assert_eq!(
book.best_bid(),
Some(PRICE),
"the destination's level survives"
);
assert_eq!(book.lot_size(), Some(5), "configuration survives");
assert_eq!(book.min_order_size(), Some(5), "configuration survives");
}
#[test]
fn test_direct_restore_rejects_a_zero_visible_non_auto_reserve() {
let ghost_id = Id::new();
let source = book_holding_a_ghost("GHOST-DIRECT", ghost_id);
let snapshot = source.create_snapshot(usize::MAX);
let survivor = Id::new();
let destination = destination_with_state("GHOST-DIRECT", survivor);
match destination.restore_from_snapshot(snapshot) {
Err(crate::orderbook::error::OrderBookError::ZeroVisibleTranche {
order_id,
hidden_quantity,
}) => {
assert_eq!(order_id, ghost_id);
assert_eq!(hidden_quantity, 20);
}
other => panic!("expected ZeroVisibleTranche from the direct restore, got {other:?}"),
}
assert_destination_intact(&destination, survivor, ghost_id);
}
#[test]
fn test_json_restore_rejects_a_zero_visible_non_auto_reserve() {
let ghost_id = Id::new();
let source = book_holding_a_ghost("GHOST-JSON", ghost_id);
let json = match source.snapshot_to_json(usize::MAX) {
Ok(json) => json,
Err(error) => panic!("snapshot json must build: {error}"),
};
let survivor = Id::new();
let mut destination = destination_with_state("GHOST-JSON", survivor);
match destination.restore_from_snapshot_json(&json) {
Err(crate::orderbook::error::OrderBookError::ZeroVisibleTranche {
order_id,
hidden_quantity,
}) => {
assert_eq!(order_id, ghost_id);
assert_eq!(hidden_quantity, 20);
}
other => panic!("expected ZeroVisibleTranche from the json restore, got {other:?}"),
}
assert_destination_intact(&destination, survivor, ghost_id);
}
#[test]
fn test_restore_rejects_a_package_holding_a_zero_visible_non_auto_reserve() {
let source: OrderBook<()> = OrderBook::new("GHOST-RESTORE");
let ghost_id = Id::new();
assert!(
source
.add_order(reserve_buy(ghost_id, 10, 20, None, false))
.is_ok(),
"the reserve rests with a visible tranche"
);
let level = source.bids.get(&PRICE).expect("the bid level exists");
let emptied = level
.value()
.update_order(pricelevel::OrderUpdate::UpdateQuantity {
order_id: ghost_id,
new_quantity: Quantity::new(0),
});
assert!(
emptied.is_ok(),
"the level accepts the zero update: {emptied:?}"
);
let package = match source.create_snapshot_package(usize::MAX) {
Ok(package) => package,
Err(error) => panic!("snapshot package must build: {error}"),
};
let mut destination: OrderBook<()> = OrderBook::new("GHOST-RESTORE");
let survivor = Id::new();
assert!(
destination
.add_limit_order(survivor, PRICE, 5, Side::Buy, TimeInForce::Gtc, None)
.is_ok(),
"the destination has state to protect"
);
match destination.restore_from_snapshot_package(package) {
Err(crate::orderbook::error::OrderBookError::ZeroVisibleTranche {
order_id,
hidden_quantity,
}) => {
assert_eq!(order_id, ghost_id, "the offending order is named");
assert_eq!(hidden_quantity, 20, "the stranded tranche is reported");
}
other => panic!("expected ZeroVisibleTranche from the restore, got {other:?}"),
}
assert!(
destination.get_order(survivor).is_some(),
"a rejected restore must leave the live book untouched"
);
assert!(
destination.get_order(ghost_id).is_none(),
"nothing from the rejected package may land"
);
assert_eq!(
destination.best_bid(),
Some(PRICE),
"the destination's own level survives"
);
}
#[test]
fn test_stp_cancel_taker_arm_captures_without_removing_the_maker() {
use crate::orderbook::stp::STPMode;
let user = pricelevel::Hash32::from([7u8; 32]);
let book: OrderBook<()> = OrderBook::with_stp_mode("STP-CAPTURE", STPMode::CancelTaker);
let foreign = Id::new();
assert!(
book.add_limit_order_with_user(
foreign,
PRICE,
3,
Side::Buy,
TimeInForce::Gtc,
pricelevel::Hash32::from([9u8; 32]),
None,
)
.is_ok(),
"the foreign maker rests first"
);
let mut strandable = reserve_buy(Id::new(), 10, 20, None, false);
let strandable_id = strandable.id();
if let OrderType::ReserveOrder { user_id, .. } = &mut strandable {
*user_id = user;
}
assert!(book.add_order(strandable).is_ok(), "the reserve rests");
assert_eq!(count(&book), 1, "the strandable maker is counted");
let taker = book.add_limit_order_with_user(
Id::new(),
PRICE,
8,
Side::Sell,
TimeInForce::Gtc,
user,
None,
);
assert!(
taker.is_err(),
"CancelTaker must cancel the self-crossing taker: {taker:?}"
);
match book.get_order(strandable_id) {
Some(order) => assert_eq!(
(
order.visible_quantity().as_u64(),
order.hidden_quantity().as_u64()
),
(10, 20),
"the same-user maker is untouched by its own taker"
),
None => panic!("the strandable maker must survive the STP cancel"),
}
assert_eq!(
count(&book),
1,
"nothing was discarded, so the count does not move"
);
}
#[test]
fn test_strandable_scan_gate_does_not_change_matching_outcome() {
let book: OrderBook<()> = OrderBook::new("FLAG-SEMANTICS");
let maker_id = Id::new();
assert!(
book.add_order(reserve_buy(maker_id, 10, 20, None, false))
.is_ok(),
"the maker must rest"
);
book.strandable_makers_resting.store(0, Ordering::Relaxed);
assert!(
book.add_limit_order(Id::new(), PRICE, 10, Side::Sell, TimeInForce::Gtc, None)
.is_ok(),
"the crossing sell must be accepted"
);
assert!(
book.get_order(maker_id).is_none(),
"the depleted non-replenishing maker leaves the book either way"
);
assert!(
book.best_bid().is_none(),
"the emptied level is removed either way"
);
}
#[test]
fn test_strandable_makers_resting_returns_to_zero_on_a_zero_quantity_update() {
let book: OrderBook<()> = OrderBook::new("COUNT-ZERO-UPDATE");
let first = Id::new();
let second = Id::new();
assert!(
book.add_order(reserve_buy(first, 10, 20, None, false))
.is_ok()
);
assert!(
book.add_order(reserve_buy(second, 10, 20, None, false))
.is_ok()
);
assert_eq!(count(&book), 2, "both strandable makers are counted");
let zero_update = |order_id: Id| {
book.update_order(pricelevel::OrderUpdate::UpdateQuantity {
order_id,
new_quantity: Quantity::new(0),
})
};
assert!(
matches!(zero_update(first), Ok(Some(_))),
"the zero update removes the first maker"
);
assert_eq!(count(&book), 1, "exactly one decrement");
assert!(
armed(&book),
"the second maker still rests, so the scan stays armed"
);
assert!(
matches!(zero_update(second), Ok(Some(_))),
"the zero update removes the second maker"
);
assert_eq!(count(&book), 0, "the gate closes again");
assert!(!armed(&book), "no scan is armed once none rest");
assert!(
book.add_limit_order(Id::new(), PRICE, 10, Side::Sell, TimeInForce::Gtc, None)
.is_ok(),
"a later sweep finds nothing to consume"
);
assert_eq!(count(&book), 0, "no second decrement");
}
}