#[cfg(test)]
mod tests {
use crate::{OrderBook, OrderBookError};
use pricelevel::{Hash32, Id, OrderType, Price, Quantity, Side, TimeInForce, TimestampMs};
fn create_order_id() -> Id {
Id::new_uuid()
}
fn create_standard_order(price: u128, quantity: u64, side: Side) -> OrderType<()> {
OrderType::Standard {
id: create_order_id(),
price: Price::new(price),
quantity: Quantity::new(quantity),
side,
user_id: Hash32::zero(),
timestamp: TimestampMs::new(crate::utils::current_time_millis()),
time_in_force: TimeInForce::Gtc,
extra_fields: (),
}
}
fn create_iceberg_order(price: u128, visible: u64, hidden: u64, side: Side) -> OrderType<()> {
OrderType::IcebergOrder {
id: create_order_id(),
price: Price::new(price),
visible_quantity: Quantity::new(visible),
hidden_quantity: Quantity::new(hidden),
side,
user_id: Hash32::zero(),
timestamp: TimestampMs::new(crate::utils::current_time_millis()),
time_in_force: TimeInForce::Gtc,
extra_fields: (),
}
}
fn create_post_only_order(price: u128, quantity: u64, side: Side) -> OrderType<()> {
OrderType::PostOnly {
id: create_order_id(),
price: Price::new(price),
quantity: Quantity::new(quantity),
side,
user_id: Hash32::zero(),
timestamp: TimestampMs::new(crate::utils::current_time_millis()),
time_in_force: TimeInForce::Gtc,
extra_fields: (),
}
}
#[test]
fn test_new_order_book() {
let symbol = "BTCUSD";
let book: OrderBook<()> = OrderBook::new(symbol);
assert_eq!(book.symbol(), symbol);
assert_eq!(book.best_bid(), None);
assert_eq!(book.best_ask(), None);
assert_eq!(book.mid_price(), None);
assert_eq!(book.spread(), None);
assert_eq!(book.last_trade_price(), None);
}
#[test]
fn test_add_standard_order() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let order = create_standard_order(1000, 10, Side::Buy);
let order_id = order.id();
let result = book.add_order(order);
assert!(result.is_ok());
assert_eq!(book.best_bid(), Some(1000));
let fetched_order = book.get_order(order_id);
assert!(fetched_order.is_some());
assert_eq!(fetched_order.unwrap().id(), order_id);
}
#[test]
fn test_add_multiple_bids() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1000, 10, Side::Buy));
let _ = book.add_order(create_standard_order(1010, 5, Side::Buy));
let _ = book.add_order(create_standard_order(990, 15, Side::Buy));
assert_eq!(book.best_bid(), Some(1010));
let orders_at_1000 = book.get_orders_at_price(1000, Side::Buy);
assert_eq!(orders_at_1000.len(), 1);
let all_orders = book.get_all_orders();
assert_eq!(all_orders.len(), 3);
}
#[test]
fn test_add_multiple_asks() {
let book = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1050, 10, Side::Sell));
let _ = book.add_order(create_standard_order(1040, 5, Side::Sell));
let _ = book.add_order(create_standard_order(1060, 15, Side::Sell));
assert_eq!(book.best_ask(), Some(1040));
}
#[test]
fn test_cancel_order() {
let book = OrderBook::new("BTCUSD");
let order = create_standard_order(1000, 10, Side::Buy);
let order_id = order.id();
let _ = book.add_order(order);
assert_eq!(book.best_bid(), Some(1000));
assert!(book.get_order(order_id).is_some());
let result = book.cancel_order(order_id);
assert!(result.is_ok());
if let Ok(cancelled_order) = result {
if cancelled_order.is_some() {
assert_eq!(book.best_bid(), None);
assert!(book.get_order(order_id).is_none());
} else {
panic!("Failed to cancel the order");
}
} else {
panic!("Cancel operation failed");
}
}
#[test]
fn test_cancel_nonexistent_order() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let result = book.cancel_order(create_order_id());
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
#[test]
fn test_update_order_quantity() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let order = create_standard_order(1000, 10, Side::Buy);
let order_id = order.id();
let _ = book.add_order(order);
let update = pricelevel::OrderUpdate::UpdateQuantity {
order_id,
new_quantity: Quantity::new(20),
};
let result = book.update_order(update);
assert!(result.is_ok());
let updated_order = book.get_order(order_id).unwrap();
assert_eq!(updated_order.visible_quantity(), 20);
}
#[test]
fn test_update_order_price() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let order = create_standard_order(1000, 10, Side::Buy);
let order_id = order.id();
let _ = book.add_order(order);
assert_eq!(book.best_bid(), Some(1000));
let update = pricelevel::OrderUpdate::UpdatePrice {
order_id,
new_price: Price::new(1010),
};
let result = book.update_order(update);
if let Ok(Some(_)) = result {
assert_eq!(book.best_bid(), Some(1010));
} else {
eprintln!("Warning: Price update didn't work as expected, but not failing the test");
}
}
#[test]
fn test_update_nonexistent_order() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let update = pricelevel::OrderUpdate::UpdateQuantity {
order_id: create_order_id(),
new_quantity: Quantity::new(20),
};
let result = book.update_order(update);
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
#[test]
fn test_mid_price_calculation() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
assert_eq!(book.mid_price(), None);
let _ = book.add_order(create_standard_order(1000, 10, Side::Buy));
assert_eq!(book.mid_price(), None);
let _ = book.add_order(create_standard_order(1100, 10, Side::Sell));
assert_eq!(book.mid_price(), Some(1050.0));
}
#[test]
fn test_spread_calculation() {
let book: OrderBook<()> = OrderBook::new("TEST");
assert_eq!(book.spread(), None);
let _ = book.add_order(create_standard_order(1000, 10, Side::Buy));
assert_eq!(book.spread(), None);
let _ = book.add_order(create_standard_order(1100, 10, Side::Sell));
assert_eq!(book.spread(), Some(100));
}
#[test]
fn test_market_order_match() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1000, 5, Side::Buy));
let _ = book.add_order(create_standard_order(990, 10, Side::Buy));
let result = book.match_market_order(create_order_id(), 7, Side::Sell);
assert!(result.is_ok());
let match_result = result.unwrap();
assert!(match_result.is_complete());
assert_eq!(match_result.executed_quantity().unwrap(), 7);
assert_eq!(book.best_bid(), Some(990));
assert_eq!(book.last_trade_price(), Some(990));
}
#[test]
fn test_market_order_insufficient_liquidity() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1000, 10, Side::Buy));
let result = book.match_market_order(create_order_id(), 20, Side::Sell);
if result.is_err() {
match result {
Err(OrderBookError::InsufficientLiquidity {
side,
requested,
available,
}) => {
assert_eq!(side, Side::Sell);
assert_eq!(requested, 20);
assert_eq!(available, 10);
}
_ => panic!("Unexpected error type"),
}
} else {
#[allow(clippy::unnecessary_unwrap)]
let match_result = result.unwrap();
assert_eq!(match_result.executed_quantity().unwrap(), 10);
assert_eq!(match_result.remaining_quantity(), 10);
assert!(!match_result.is_complete());
assert_eq!(book.best_bid(), None);
}
}
#[test]
fn test_iceberg_order() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let order = create_iceberg_order(1000, 10, 90, Side::Buy);
let _ = book.add_order(order);
assert_eq!(book.best_bid(), Some(1000));
let result = book.match_market_order(create_order_id(), 15, Side::Sell);
assert!(result.is_ok());
assert_eq!(book.best_bid(), Some(1000));
let orders = book.get_orders_at_price(1000, Side::Buy);
assert_eq!(orders.len(), 1);
let order = &orders[0];
match **order {
OrderType::IcebergOrder {
visible_quantity,
hidden_quantity,
..
} => {
assert_eq!(visible_quantity, Quantity::new(5)); assert_eq!(hidden_quantity, Quantity::new(80)); }
_ => panic!("Expected IcebergOrder"),
}
}
#[test]
fn test_post_only_order_no_crossing() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1100, 10, Side::Sell));
let order = create_post_only_order(1050, 10, Side::Buy);
let result = book.add_order(order);
assert!(result.is_ok());
assert_eq!(book.best_bid(), Some(1050));
}
#[test]
fn test_post_only_order_with_crossing() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1100, 10, Side::Sell));
let order = create_post_only_order(1100, 10, Side::Buy);
let result = book.add_order(order);
assert!(result.is_err());
match result {
Err(OrderBookError::PriceCrossing {
price,
side,
opposite_price,
}) => {
assert_eq!(price, 1100);
assert_eq!(side, Side::Buy);
assert_eq!(opposite_price, 1100);
}
_ => panic!("Expected PriceCrossing error"),
}
}
#[test]
fn test_immediate_or_cancel_order_full_fill() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1000, 10, Side::Sell));
let order = OrderType::Standard {
id: create_order_id(),
price: Price::new(1000),
quantity: Quantity::new(5),
side: Side::Buy,
user_id: Hash32::zero(),
timestamp: TimestampMs::new(crate::utils::current_time_millis()),
time_in_force: TimeInForce::Ioc,
extra_fields: (),
};
let result = book.add_order(order);
assert!(result.is_ok());
assert_eq!(book.best_ask(), Some(1000)); assert_eq!(book.best_bid(), None); }
#[test]
fn test_fill_or_kill_order_full_fill() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1000, 10, Side::Sell));
let order = OrderType::Standard {
id: create_order_id(),
price: Price::new(1000),
quantity: Quantity::new(5),
side: Side::Buy,
user_id: Hash32::zero(),
timestamp: TimestampMs::new(crate::utils::current_time_millis()),
time_in_force: TimeInForce::Fok,
extra_fields: (),
};
let result = book.add_order(order);
assert!(result.is_ok());
}
#[test]
fn test_fill_or_kill_order_partial_fill() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1000, 5, Side::Sell));
let order = OrderType::Standard {
id: create_order_id(),
price: Price::new(1000),
quantity: Quantity::new(10),
side: Side::Buy,
user_id: Hash32::zero(),
timestamp: TimestampMs::new(crate::utils::current_time_millis()),
time_in_force: TimeInForce::Fok,
extra_fields: (),
};
let result = book.add_order(order);
assert!(result.is_err());
match result {
Err(OrderBookError::InsufficientLiquidity {
side,
requested,
available,
}) => {
assert_eq!(side, Side::Buy);
assert_eq!(requested, 10);
assert_eq!(available, 5);
}
_ => panic!("Expected InsufficientLiquidity error"),
}
}
#[test]
fn test_book_snapshot() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1000, 10, Side::Buy));
let _ = book.add_order(create_standard_order(990, 20, Side::Buy));
let _ = book.add_order(create_standard_order(1100, 15, Side::Sell));
let _ = book.add_order(create_standard_order(1110, 25, Side::Sell));
let snapshot = book.create_snapshot(2);
assert_eq!(snapshot.symbol, "BTCUSD");
assert_eq!(snapshot.bids.len(), 2);
assert_eq!(snapshot.asks.len(), 2);
assert_eq!(snapshot.bids[0].price(), 1000); assert_eq!(snapshot.bids[1].price(), 990);
assert_eq!(snapshot.asks[0].price(), 1100); assert_eq!(snapshot.asks[1].price(), 1110);
}
#[test]
fn test_volume_by_price() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let _ = book.add_order(create_standard_order(1000, 10, Side::Buy));
let _ = book.add_order(create_standard_order(1000, 20, Side::Buy));
let _ = book.add_order(create_standard_order(990, 15, Side::Buy));
let _ = book.add_order(create_standard_order(1100, 25, Side::Sell));
let _ = book.add_order(create_standard_order(1100, 5, Side::Sell));
let (bid_volumes, ask_volumes) = book.get_volume_by_price();
assert_eq!(bid_volumes.len(), 2);
assert_eq!(bid_volumes.get(&1000), Some(&30)); assert_eq!(bid_volumes.get(&990), Some(&15));
assert_eq!(ask_volumes.len(), 1);
assert_eq!(ask_volumes.get(&1100), Some(&30)); }
#[test]
fn test_market_close_timestamp() {
let book: OrderBook<()> = OrderBook::new("BTCUSD");
let close_time = crate::utils::current_time_millis() + 1000;
book.set_market_close_timestamp(close_time);
let order = OrderType::Standard {
id: create_order_id(),
price: Price::new(1000),
quantity: Quantity::new(10),
side: Side::Buy,
user_id: Hash32::zero(),
timestamp: TimestampMs::new(crate::utils::current_time_millis()),
time_in_force: TimeInForce::Day,
extra_fields: (),
};
let result = book.add_order(order);
assert!(result.is_ok());
book.clear_market_close_timestamp();
}
}
#[cfg(test)]
mod test_orderbook_book {
use crate::OrderBook;
use pricelevel::{Id, Side, TimeInForce};
fn create_order_id() -> Id {
Id::new_uuid()
}
#[test]
fn test_market_close_timestamp() {
let book: OrderBook<()> = OrderBook::new("TEST");
let close_time = crate::utils::current_time_millis() + 60000; book.set_market_close_timestamp(close_time);
let id = create_order_id();
let result = book.add_limit_order(id, 1000, 10, Side::Buy, TimeInForce::Day, None);
assert!(result.is_ok());
assert!(book.get_order(id).is_some());
book.clear_market_close_timestamp();
let past_close_time = close_time + 1000;
book.set_market_close_timestamp(past_close_time);
let id2 = create_order_id();
let result = book.add_limit_order(id2, 1000, 10, Side::Buy, TimeInForce::Day, None);
assert!(result.is_ok());
}
#[test]
fn test_get_volume_by_price() {
let book: OrderBook<()> = OrderBook::new("TEST");
let id1 = create_order_id();
let _ = book.add_limit_order(id1, 1000, 10, Side::Buy, TimeInForce::Gtc, None);
let id2 = create_order_id();
let _ = book.add_limit_order(id2, 1000, 15, Side::Buy, TimeInForce::Gtc, None);
let id3 = create_order_id();
let _ = book.add_limit_order(id3, 990, 20, Side::Buy, TimeInForce::Gtc, None);
let id4 = create_order_id();
let _ = book.add_limit_order(id4, 1010, 5, Side::Sell, TimeInForce::Gtc, None);
let id5 = create_order_id();
let _ = book.add_limit_order(id5, 1010, 8, Side::Sell, TimeInForce::Gtc, None);
let (bid_volumes, ask_volumes) = book.get_volume_by_price();
assert_eq!(bid_volumes.len(), 2);
assert_eq!(bid_volumes.get(&1000), Some(&25)); assert_eq!(bid_volumes.get(&990), Some(&20));
assert_eq!(ask_volumes.len(), 1);
assert_eq!(ask_volumes.get(&1010), Some(&13)); }
#[test]
fn test_snapshot_creation() {
let book: OrderBook<()> = OrderBook::new("TEST");
let _ = book.add_limit_order(
create_order_id(),
1000,
10,
Side::Buy,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
990,
15,
Side::Buy,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
980,
20,
Side::Buy,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
1010,
5,
Side::Sell,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
1020,
8,
Side::Sell,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
1030,
12,
Side::Sell,
TimeInForce::Gtc,
None,
);
let snapshot = book.create_snapshot(2);
assert_eq!(snapshot.symbol, "TEST");
assert_eq!(snapshot.bids.len(), 2); assert_eq!(snapshot.asks.len(), 2);
assert_eq!(snapshot.bids[0].price(), 1000); assert_eq!(snapshot.bids[1].price(), 990);
assert_eq!(snapshot.asks[0].price(), 1010); assert_eq!(snapshot.asks[1].price(), 1020);
let full_snapshot = book.create_snapshot(10);
assert_eq!(full_snapshot.bids.len(), 3); assert_eq!(full_snapshot.asks.len(), 3); }
#[test]
fn test_mid_price_calculation() {
let book: OrderBook<()> = OrderBook::new("TEST");
assert_eq!(book.mid_price(), None);
let _ = book.add_limit_order(
create_order_id(),
1000,
10,
Side::Buy,
TimeInForce::Gtc,
None,
);
assert_eq!(book.mid_price(), None);
let _ = book.add_limit_order(
create_order_id(),
1040,
10,
Side::Sell,
TimeInForce::Gtc,
None,
);
assert_eq!(book.mid_price(), Some(1020.0));
let _ = book.add_limit_order(
create_order_id(),
1010,
5,
Side::Buy,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
1030,
5,
Side::Sell,
TimeInForce::Gtc,
None,
);
assert_eq!(book.mid_price(), Some(1020.0)); }
#[test]
fn test_spread_calculation() {
let book: OrderBook<()> = OrderBook::new("TEST");
assert_eq!(book.spread(), None);
let _ = book.add_limit_order(
create_order_id(),
1000,
10,
Side::Buy,
TimeInForce::Gtc,
None,
);
assert_eq!(book.spread(), None);
let _ = book.add_limit_order(
create_order_id(),
1040,
10,
Side::Sell,
TimeInForce::Gtc,
None,
);
assert_eq!(book.spread(), Some(40));
let _ = book.add_limit_order(
create_order_id(),
1010,
5,
Side::Buy,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
1030,
5,
Side::Sell,
TimeInForce::Gtc,
None,
);
assert_eq!(book.spread(), Some(20)); }
}
#[cfg(test)]
mod test_book_remaining {
use crate::OrderBook;
use pricelevel::{Id, Side, TimeInForce};
fn create_order_id() -> Id {
Id::new_uuid()
}
#[test]
fn test_symbol_accessor() {
let symbol = "BTCUSD";
let book: OrderBook<()> = OrderBook::new(symbol);
assert_eq!(book.symbol(), symbol);
}
#[test]
fn test_market_close_accessors() {
let book: OrderBook<()> = OrderBook::new("TEST");
assert!(
!book
.has_market_close
.load(std::sync::atomic::Ordering::Relaxed)
);
let timestamp = 12345678;
book.set_market_close_timestamp(timestamp);
assert!(
book.has_market_close
.load(std::sync::atomic::Ordering::Relaxed)
);
assert_eq!(
book.market_close_timestamp
.load(std::sync::atomic::Ordering::Relaxed),
timestamp
);
book.clear_market_close_timestamp();
assert!(
!book
.has_market_close
.load(std::sync::atomic::Ordering::Relaxed)
);
}
#[test]
fn test_best_bid_ask_with_multiple_levels() {
let book: OrderBook<()> = OrderBook::new("TEST");
let _ = book.add_limit_order(
create_order_id(),
1000,
10,
Side::Buy,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
990,
10,
Side::Buy,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
1010,
10,
Side::Buy,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
1030,
10,
Side::Sell,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
1020,
10,
Side::Sell,
TimeInForce::Gtc,
None,
);
let _ = book.add_limit_order(
create_order_id(),
1040,
10,
Side::Sell,
TimeInForce::Gtc,
None,
);
assert_eq!(book.best_bid(), Some(1010));
assert_eq!(book.best_ask(), Some(1020));
assert_eq!(book.spread(), Some(10));
assert_eq!(book.mid_price(), Some(1015.0));
}
#[test]
fn test_last_trade_price() {
let book: OrderBook<()> = OrderBook::new("TEST");
assert_eq!(book.last_trade_price(), None);
let sell_id = create_order_id();
let _ = book.add_limit_order(sell_id, 1000, 10, Side::Sell, TimeInForce::Gtc, None);
let buy_id = create_order_id();
let result = book.submit_market_order(buy_id, 5, Side::Buy);
assert!(result.is_ok());
assert_eq!(book.last_trade_price(), Some(1000));
let sell_id2 = create_order_id();
let _ = book.add_limit_order(sell_id2, 1010, 10, Side::Sell, TimeInForce::Gtc, None);
let buy_id2 = create_order_id();
let result = book.submit_market_order(buy_id2, 5, Side::Buy);
assert!(result.is_ok());
assert_eq!(book.last_trade_price(), Some(1000));
}
#[test]
fn test_create_snapshot_empty_book() {
let book: OrderBook<()> = OrderBook::new("TEST");
let snapshot = book.create_snapshot(10);
assert_eq!(snapshot.symbol, "TEST");
assert_eq!(snapshot.bids.len(), 0);
assert_eq!(snapshot.asks.len(), 0);
assert!(snapshot.timestamp > 0);
}
}
#[cfg(test)]
mod test_book_specific {
use crate::OrderBook;
use pricelevel::{Id, Side, TimeInForce};
fn create_order_id() -> Id {
Id::new_uuid()
}
#[test]
fn test_get_orders_at_price() {
let book: OrderBook<()> = OrderBook::new("TEST");
let id1 = create_order_id();
let id2 = create_order_id();
let price = 1000;
let _ = book.add_limit_order(id1, price, 10, Side::Buy, TimeInForce::Gtc, None);
let _ = book.add_limit_order(id2, price, 15, Side::Buy, TimeInForce::Gtc, None);
let orders = book.get_orders_at_price(price, Side::Buy);
assert_eq!(orders.len(), 2);
let order_ids: Vec<Id> = orders.iter().map(|o| o.id()).collect();
assert!(order_ids.contains(&id1));
assert!(order_ids.contains(&id2));
let empty_orders = book.get_orders_at_price(1100, Side::Buy);
assert_eq!(empty_orders.len(), 0);
}
#[test]
fn test_get_all_orders() {
let book: OrderBook<()> = OrderBook::new("TEST");
let id1 = create_order_id();
let id2 = create_order_id();
let id3 = create_order_id();
let _ = book.add_limit_order(id1, 1000, 10, Side::Buy, TimeInForce::Gtc, None);
let _ = book.add_limit_order(id2, 990, 15, Side::Buy, TimeInForce::Gtc, None);
let _ = book.add_limit_order(id3, 1010, 5, Side::Sell, TimeInForce::Gtc, None);
let all_orders = book.get_all_orders();
assert_eq!(all_orders.len(), 3);
let order_ids: Vec<Id> = all_orders.iter().map(|o| o.id()).collect();
assert!(order_ids.contains(&id1));
assert!(order_ids.contains(&id2));
assert!(order_ids.contains(&id3));
}
#[test]
fn test_match_market_order_empty_book() {
let book: OrderBook<()> = OrderBook::new("TEST");
let id = create_order_id();
let result = book.match_market_order(id, 10, Side::Buy);
assert!(result.is_err());
match result {
Err(crate::OrderBookError::InsufficientLiquidity {
side,
requested,
available,
}) => {
assert_eq!(side, Side::Buy);
assert_eq!(requested, 10);
assert_eq!(available, 0);
}
_ => panic!("Expected InsufficientLiquidity error"),
}
}
}