1use std::{
19 cmp::Ordering,
20 collections::{BTreeMap, HashMap},
21 fmt::{Debug, Display, Formatter},
22};
23
24use nautilus_core::UnixNanos;
25
26use crate::{
27 data::order::{BookOrder, OrderId},
28 enums::OrderSideSpecified,
29 orderbook::BookLevel,
30 types::{Price, Quantity},
31};
32
33#[derive(Clone, Copy, Debug, Eq)]
35#[cfg_attr(
36 feature = "python",
37 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
38)]
39pub struct BookPrice {
40 pub value: Price,
41 pub side: OrderSideSpecified,
42}
43
44impl BookPrice {
45 #[must_use]
47 pub fn new(value: Price, side: OrderSideSpecified) -> Self {
48 Self { value, side }
49 }
50}
51
52impl PartialOrd for BookPrice {
53 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
54 Some(self.cmp(other))
55 }
56}
57
58impl PartialEq for BookPrice {
59 fn eq(&self, other: &Self) -> bool {
60 self.value == other.value
61 }
62}
63
64impl Ord for BookPrice {
65 fn cmp(&self, other: &Self) -> Ordering {
66 match self.side {
67 OrderSideSpecified::Buy => other.value.cmp(&self.value),
68 OrderSideSpecified::Sell => self.value.cmp(&other.value),
69 }
70 }
71}
72
73impl Display for BookPrice {
74 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}", self.value)
76 }
77}
78
79#[derive(Clone, Debug)]
81pub(crate) struct BookLadder {
82 pub side: OrderSideSpecified,
83 pub levels: BTreeMap<BookPrice, BookLevel>,
84 pub cache: HashMap<u64, BookPrice>,
85}
86
87impl BookLadder {
88 #[must_use]
90 pub fn new(side: OrderSideSpecified) -> Self {
91 Self {
92 side,
93 levels: BTreeMap::new(),
94 cache: HashMap::new(),
95 }
96 }
97
98 #[must_use]
100 pub fn len(&self) -> usize {
101 self.levels.len()
102 }
103
104 #[must_use]
106 #[allow(dead_code)] pub fn is_empty(&self) -> bool {
108 self.levels.is_empty()
109 }
110
111 #[allow(dead_code)] pub fn add_bulk(&mut self, orders: Vec<BookOrder>) {
114 for order in orders {
115 self.add(order);
116 }
117 }
118
119 pub fn clear(&mut self) {
121 self.levels.clear();
122 self.cache.clear();
123 }
124
125 pub fn add(&mut self, order: BookOrder) {
127 let book_price = order.to_book_price();
128 self.cache.insert(order.order_id, book_price);
129
130 match self.levels.get_mut(&book_price) {
131 Some(level) => {
132 level.add(order);
133 }
134 None => {
135 let level = BookLevel::from_order(order);
136 self.levels.insert(book_price, level);
137 }
138 }
139 }
140
141 pub fn update(&mut self, order: BookOrder) {
143 let price = self.cache.get(&order.order_id).copied();
144 if let Some(price) = price
145 && let Some(level) = self.levels.get_mut(&price)
146 {
147 if order.price == level.price.value {
148 let level_len_before = level.len();
150 level.update(order);
151
152 if order.size.raw == 0 {
154 self.cache.remove(&order.order_id);
155 debug_assert_eq!(
156 level.len(),
157 level_len_before - 1,
158 "Level should have one less order after zero-size update"
159 );
160 } else {
161 debug_assert!(
162 self.cache.contains_key(&order.order_id),
163 "Cache should still contain order {0} after update",
164 order.order_id
165 );
166 }
167
168 if level.is_empty() {
170 self.levels.remove(&price);
171 debug_assert!(
172 !self.cache.values().any(|p| *p == price),
173 "Cache should not contain removed price level {price:?}"
174 );
175 }
176
177 debug_assert_eq!(
179 self.cache.len(),
180 self.levels.values().map(|level| level.len()).sum::<usize>(),
181 "Cache size should equal total orders across all levels"
182 );
183 return;
184 }
185
186 self.cache.remove(&order.order_id);
188 level.delete(&order);
189 if level.is_empty() {
190 self.levels.remove(&price);
191 debug_assert!(
192 !self.cache.values().any(|p| *p == price),
193 "Cache should not contain removed price level {price:?}"
194 );
195 }
196 }
197
198 if order.size.is_positive() {
200 self.add(order);
201 }
202
203 debug_assert_eq!(
205 self.cache.len(),
206 self.levels.values().map(|level| level.len()).sum::<usize>(),
207 "Cache size should equal total orders across all levels"
208 );
209 }
210
211 pub fn delete(&mut self, order: BookOrder, sequence: u64, ts_event: UnixNanos) {
213 self.remove_order(order.order_id, sequence, ts_event);
214 }
215
216 pub fn remove_order(&mut self, order_id: OrderId, sequence: u64, ts_event: UnixNanos) {
218 if let Some(price) = self.cache.get(&order_id).copied()
219 && let Some(level) = self.levels.get_mut(&price)
220 {
221 if level.orders.contains_key(&order_id) {
223 let level_len_before = level.len();
224
225 self.cache.remove(&order_id);
227 level.remove_by_id(order_id, sequence, ts_event);
228
229 debug_assert_eq!(
230 level.len(),
231 level_len_before - 1,
232 "Level should have exactly one less order after removal"
233 );
234
235 if level.is_empty() {
236 self.levels.remove(&price);
237 debug_assert!(
238 !self.cache.values().any(|p| *p == price),
239 "Cache should not contain removed price level {price:?}"
240 );
241 }
242 }
243 }
244
245 debug_assert_eq!(
247 self.cache.len(),
248 self.levels.values().map(|level| level.len()).sum::<usize>(),
249 "Cache size should equal total orders across all levels"
250 );
251 }
252
253 pub fn remove_level(&mut self, price: BookPrice) -> Option<BookLevel> {
255 if let Some(level) = self.levels.remove(&price) {
256 for order_id in level.orders.keys() {
258 self.cache.remove(order_id);
259 }
260
261 debug_assert_eq!(
262 self.cache.len(),
263 self.levels.values().map(|level| level.len()).sum::<usize>(),
264 "Cache size should equal total orders across all levels"
265 );
266
267 Some(level)
268 } else {
269 None
270 }
271 }
272
273 #[must_use]
275 #[allow(dead_code)] pub fn sizes(&self) -> f64 {
277 self.levels.values().map(BookLevel::size).sum()
278 }
279
280 #[must_use]
282 #[allow(dead_code)] pub fn exposures(&self) -> f64 {
284 self.levels.values().map(BookLevel::exposure).sum()
285 }
286
287 #[must_use]
289 pub fn top(&self) -> Option<&BookLevel> {
290 match self.levels.iter().next() {
291 Some((_, l)) => Option::Some(l),
292 None => Option::None,
293 }
294 }
295
296 #[must_use]
299 pub fn simulate_fills(&self, order: &BookOrder) -> Vec<(Price, Quantity)> {
300 let is_reversed = self.side == OrderSideSpecified::Buy;
301 let mut fills = Vec::new();
302 let mut cumulative_denominator = Quantity::zero(order.size.precision);
303 let target = order.size;
304
305 for level in self.levels.values() {
306 if (is_reversed && level.price.value < order.price)
307 || (!is_reversed && level.price.value > order.price)
308 {
309 break;
310 }
311
312 for book_order in level.orders.values() {
313 let current = book_order.size;
314 if cumulative_denominator + current >= target {
315 let remainder = target - cumulative_denominator;
317 if remainder.is_positive() {
318 fills.push((book_order.price, remainder));
319 }
320 return fills;
321 }
322
323 fills.push((book_order.price, current));
325 cumulative_denominator += current;
326 }
327 }
328
329 fills
330 }
331}
332
333impl Display for BookLadder {
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 writeln!(f, "{}(side={})", stringify!(BookLadder), self.side)?;
336 for (price, level) in &self.levels {
337 writeln!(f, " {} -> {} orders", price, level.len())?;
338 }
339 Ok(())
340 }
341}
342
343#[cfg(test)]
347mod tests {
348 use rstest::rstest;
349
350 use crate::{
351 data::order::BookOrder,
352 enums::{OrderSide, OrderSideSpecified},
353 orderbook::ladder::{BookLadder, BookPrice},
354 types::{Price, Quantity},
355 };
356
357 #[rstest]
358 fn test_is_empty() {
359 let ladder = BookLadder::new(OrderSideSpecified::Buy);
360 assert!(ladder.is_empty(), "A new ladder should be empty");
361 }
362
363 #[rstest]
364 fn test_is_empty_after_add() {
365 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
366 assert!(ladder.is_empty(), "Ladder should start empty");
367 let order = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(100), 1);
368 ladder.add(order);
369 assert!(
370 !ladder.is_empty(),
371 "Ladder should not be empty after adding an order"
372 );
373 }
374
375 #[rstest]
376 fn test_add_bulk_empty() {
377 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
378 ladder.add_bulk(vec![]);
379 assert!(
380 ladder.is_empty(),
381 "Adding an empty vector should leave the ladder empty"
382 );
383 }
384
385 #[rstest]
386 fn test_add_bulk_orders() {
387 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
388 let orders = vec![
389 BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1),
390 BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(30), 2),
391 BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(50), 3),
392 ];
393 ladder.add_bulk(orders);
394 assert_eq!(ladder.len(), 1, "Ladder should have one price level");
396 let orders_in_level = ladder.top().unwrap().get_orders();
397 assert_eq!(
398 orders_in_level.len(),
399 3,
400 "Price level should contain all bulk orders"
401 );
402 }
403
404 #[rstest]
405 fn test_book_price_bid_sorting() {
406 let mut bid_prices = [
407 BookPrice::new(Price::from("2.0"), OrderSideSpecified::Buy),
408 BookPrice::new(Price::from("4.0"), OrderSideSpecified::Buy),
409 BookPrice::new(Price::from("1.0"), OrderSideSpecified::Buy),
410 BookPrice::new(Price::from("3.0"), OrderSideSpecified::Buy),
411 ];
412 bid_prices.sort();
413 assert_eq!(bid_prices[0].value, Price::from("4.0"));
414 }
415
416 #[rstest]
417 fn test_book_price_ask_sorting() {
418 let mut ask_prices = [
419 BookPrice::new(Price::from("2.0"), OrderSideSpecified::Sell),
420 BookPrice::new(Price::from("4.0"), OrderSideSpecified::Sell),
421 BookPrice::new(Price::from("1.0"), OrderSideSpecified::Sell),
422 BookPrice::new(Price::from("3.0"), OrderSideSpecified::Sell),
423 ];
424
425 ask_prices.sort();
426 assert_eq!(ask_prices[0].value, Price::from("1.0"));
427 }
428
429 #[rstest]
430 fn test_add_single_order() {
431 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
432 let order = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 0);
433
434 ladder.add(order);
435 assert_eq!(ladder.len(), 1);
436 assert_eq!(ladder.sizes(), 20.0);
437 assert_eq!(ladder.exposures(), 200.0);
438 assert_eq!(ladder.top().unwrap().price.value, Price::from("10.0"));
439 }
440
441 #[rstest]
442 fn test_add_multiple_buy_orders() {
443 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
444 let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 0);
445 let order2 = BookOrder::new(OrderSide::Buy, Price::from("9.00"), Quantity::from(30), 1);
446 let order3 = BookOrder::new(OrderSide::Buy, Price::from("9.00"), Quantity::from(50), 2);
447 let order4 = BookOrder::new(OrderSide::Buy, Price::from("8.00"), Quantity::from(200), 3);
448
449 ladder.add_bulk(vec![order1, order2, order3, order4]);
450 assert_eq!(ladder.len(), 3);
451 assert_eq!(ladder.sizes(), 300.0);
452 assert_eq!(ladder.exposures(), 2520.0);
453 assert_eq!(ladder.top().unwrap().price.value, Price::from("10.0"));
454 }
455
456 #[rstest]
457 fn test_add_multiple_sell_orders() {
458 let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
459 let order1 = BookOrder::new(OrderSide::Sell, Price::from("11.00"), Quantity::from(20), 0);
460 let order2 = BookOrder::new(OrderSide::Sell, Price::from("12.00"), Quantity::from(30), 1);
461 let order3 = BookOrder::new(OrderSide::Sell, Price::from("12.00"), Quantity::from(50), 2);
462 let order4 = BookOrder::new(
463 OrderSide::Sell,
464 Price::from("13.00"),
465 Quantity::from(200),
466 0,
467 );
468
469 ladder.add_bulk(vec![order1, order2, order3, order4]);
470 assert_eq!(ladder.len(), 3);
471 assert_eq!(ladder.sizes(), 300.0);
472 assert_eq!(ladder.exposures(), 3780.0);
473 assert_eq!(ladder.top().unwrap().price.value, Price::from("11.0"));
474 }
475
476 #[rstest]
477 fn test_add_to_same_price_level() {
478 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
479 let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
480 let order2 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(30), 2);
481
482 ladder.add(order1);
483 ladder.add(order2);
484
485 assert_eq!(ladder.len(), 1);
486 assert_eq!(ladder.sizes(), 50.0);
487 assert_eq!(ladder.exposures(), 500.0);
488 }
489
490 #[rstest]
491 fn test_add_descending_buy_orders() {
492 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
493 let order1 = BookOrder::new(OrderSide::Buy, Price::from("9.00"), Quantity::from(20), 1);
494 let order2 = BookOrder::new(OrderSide::Buy, Price::from("8.00"), Quantity::from(30), 2);
495
496 ladder.add(order1);
497 ladder.add(order2);
498
499 assert_eq!(ladder.top().unwrap().price.value, Price::from("9.00"));
500 }
501
502 #[rstest]
503 fn test_add_ascending_sell_orders() {
504 let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
505 let order1 = BookOrder::new(OrderSide::Sell, Price::from("8.00"), Quantity::from(20), 1);
506 let order2 = BookOrder::new(OrderSide::Sell, Price::from("9.00"), Quantity::from(30), 2);
507
508 ladder.add(order1);
509 ladder.add(order2);
510
511 assert_eq!(ladder.top().unwrap().price.value, Price::from("8.00"));
512 }
513
514 #[rstest]
515 fn test_update_buy_order_price() {
516 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
517 let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(20), 1);
518
519 ladder.add(order);
520 let order = BookOrder::new(OrderSide::Buy, Price::from("11.10"), Quantity::from(20), 1);
521
522 ladder.update(order);
523 assert_eq!(ladder.len(), 1);
524 assert_eq!(ladder.sizes(), 20.0);
525 assert_eq!(ladder.exposures(), 222.0);
526 assert_eq!(ladder.top().unwrap().price.value, Price::from("11.1"));
527 }
528
529 #[rstest]
530 fn test_update_sell_order_price() {
531 let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
532 let order = BookOrder::new(OrderSide::Sell, Price::from("11.00"), Quantity::from(20), 1);
533
534 ladder.add(order);
535
536 let order = BookOrder::new(OrderSide::Sell, Price::from("11.10"), Quantity::from(20), 1);
537
538 ladder.update(order);
539 assert_eq!(ladder.len(), 1);
540 assert_eq!(ladder.sizes(), 20.0);
541 assert_eq!(ladder.exposures(), 222.0);
542 assert_eq!(ladder.top().unwrap().price.value, Price::from("11.1"));
543 }
544
545 #[rstest]
546 fn test_update_buy_order_size() {
547 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
548 let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(20), 1);
549
550 ladder.add(order);
551
552 let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(10), 1);
553
554 ladder.update(order);
555 assert_eq!(ladder.len(), 1);
556 assert_eq!(ladder.sizes(), 10.0);
557 assert_eq!(ladder.exposures(), 110.0);
558 assert_eq!(ladder.top().unwrap().price.value, Price::from("11.0"));
559 }
560
561 #[rstest]
562 fn test_update_sell_order_size() {
563 let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
564 let order = BookOrder::new(OrderSide::Sell, Price::from("11.00"), Quantity::from(20), 1);
565
566 ladder.add(order);
567
568 let order = BookOrder::new(OrderSide::Sell, Price::from("11.00"), Quantity::from(10), 1);
569
570 ladder.update(order);
571 assert_eq!(ladder.len(), 1);
572 assert_eq!(ladder.sizes(), 10.0);
573 assert_eq!(ladder.exposures(), 110.0);
574 assert_eq!(ladder.top().unwrap().price.value, Price::from("11.0"));
575 }
576
577 #[rstest]
578 fn test_delete_non_existing_order() {
579 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
580 let order = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
581
582 ladder.delete(order, 0, 0.into());
583
584 assert_eq!(ladder.len(), 0);
585 }
586
587 #[rstest]
588 fn test_delete_buy_order() {
589 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
590 let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(20), 1);
591
592 ladder.add(order);
593
594 let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(10), 1);
595
596 ladder.delete(order, 0, 0.into());
597 assert_eq!(ladder.len(), 0);
598 assert_eq!(ladder.sizes(), 0.0);
599 assert_eq!(ladder.exposures(), 0.0);
600 assert_eq!(ladder.top(), None);
601 }
602
603 #[rstest]
604 fn test_delete_sell_order() {
605 let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
606 let order = BookOrder::new(OrderSide::Sell, Price::from("10.00"), Quantity::from(10), 1);
607
608 ladder.add(order);
609
610 let order = BookOrder::new(OrderSide::Sell, Price::from("10.00"), Quantity::from(10), 1);
611
612 ladder.delete(order, 0, 0.into());
613 assert_eq!(ladder.len(), 0);
614 assert_eq!(ladder.sizes(), 0.0);
615 assert_eq!(ladder.exposures(), 0.0);
616 assert_eq!(ladder.top(), None);
617 }
618
619 #[rstest]
620 fn test_ladder_sizes_empty() {
621 let ladder = BookLadder::new(OrderSideSpecified::Buy);
622 assert_eq!(
623 ladder.sizes(),
624 0.0,
625 "An empty ladder should have total size 0.0"
626 );
627 }
628
629 #[rstest]
630 fn test_ladder_exposures_empty() {
631 let ladder = BookLadder::new(OrderSideSpecified::Buy);
632 assert_eq!(
633 ladder.exposures(),
634 0.0,
635 "An empty ladder should have total exposure 0.0"
636 );
637 }
638
639 #[rstest]
640 fn test_ladder_sizes() {
641 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
642 let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
643 let order2 = BookOrder::new(OrderSide::Buy, Price::from("9.50"), Quantity::from(30), 2);
644 ladder.add(order1);
645 ladder.add(order2);
646
647 let expected_size = 20.0 + 30.0;
648 assert_eq!(
649 ladder.sizes(),
650 expected_size,
651 "Ladder total size should match the sum of order sizes"
652 );
653 }
654
655 #[rstest]
656 fn test_ladder_exposures() {
657 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
658 let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
659 let order2 = BookOrder::new(OrderSide::Buy, Price::from("9.50"), Quantity::from(30), 2);
660 ladder.add(order1);
661 ladder.add(order2);
662
663 let expected_exposure = 10.00 * 20.0 + 9.50 * 30.0;
664 assert_eq!(
665 ladder.exposures(),
666 expected_exposure,
667 "Ladder total exposure should match the sum of individual exposures"
668 );
669 }
670
671 #[rstest]
672 fn test_iter_returns_fifo() {
673 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
674 let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
675 let order2 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(30), 2);
676 ladder.add(order1);
677 ladder.add(order2);
678 let orders: Vec<BookOrder> = ladder.top().unwrap().iter().copied().collect();
679 assert_eq!(
680 orders,
681 vec![order1, order2],
682 "Iterator should return orders in FIFO order"
683 );
684 }
685
686 #[rstest]
687 fn test_update_missing_order_inserts() {
688 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
689 let order = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
690 ladder.update(order);
692 assert_eq!(
693 ladder.len(),
694 1,
695 "Ladder should have one level after upsert update"
696 );
697 let orders = ladder.top().unwrap().get_orders();
698 assert_eq!(
699 orders.len(),
700 1,
701 "Price level should contain the inserted order"
702 );
703 assert_eq!(orders[0], order, "The inserted order should match");
704 }
705
706 #[rstest]
707 fn test_cache_consistency_after_operations() {
708 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
709 let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
710 let order2 = BookOrder::new(OrderSide::Buy, Price::from("9.00"), Quantity::from(30), 2);
711 ladder.add(order1);
712 ladder.add(order2);
713
714 for (order_id, price) in &ladder.cache {
716 let level = ladder
717 .levels
718 .get(price)
719 .expect("Every price in the cache should have a corresponding level");
720 assert!(
721 level.orders.contains_key(order_id),
722 "Order id {order_id} should be present in the level for price {price}",
723 );
724 }
725 }
726
727 #[rstest]
728 fn test_simulate_fills_with_empty_book() {
729 let ladder = BookLadder::new(OrderSideSpecified::Buy);
730 let order = BookOrder::new(OrderSide::Buy, Price::max(2), Quantity::from(500), 1);
731
732 let fills = ladder.simulate_fills(&order);
733
734 assert!(fills.is_empty());
735 }
736
737 #[rstest]
738 #[case(OrderSide::Buy, Price::max(2), OrderSideSpecified::Sell)]
739 #[case(OrderSide::Sell, Price::min(2), OrderSideSpecified::Buy)]
740 fn test_simulate_order_fills_with_no_size(
741 #[case] side: OrderSide,
742 #[case] price: Price,
743 #[case] ladder_side: OrderSideSpecified,
744 ) {
745 let ladder = BookLadder::new(ladder_side);
746 let order = BookOrder {
747 price, size: Quantity::from(500),
749 side,
750 order_id: 2,
751 };
752
753 let fills = ladder.simulate_fills(&order);
754
755 assert!(fills.is_empty());
756 }
757
758 #[rstest]
759 #[case(OrderSide::Buy, OrderSideSpecified::Sell, Price::from("60.0"))]
760 #[case(OrderSide::Sell, OrderSideSpecified::Buy, Price::from("40.0"))]
761 fn test_simulate_order_fills_buy_when_far_from_market(
762 #[case] order_side: OrderSide,
763 #[case] ladder_side: OrderSideSpecified,
764 #[case] ladder_price: Price,
765 ) {
766 let mut ladder = BookLadder::new(ladder_side);
767
768 ladder.add(BookOrder {
769 price: ladder_price,
770 size: Quantity::from(100),
771 side: ladder_side.as_order_side(),
772 order_id: 1,
773 });
774
775 let order = BookOrder {
776 price: Price::from("50.00"),
777 size: Quantity::from(500),
778 side: order_side,
779 order_id: 2,
780 };
781
782 let fills = ladder.simulate_fills(&order);
783
784 assert!(fills.is_empty());
785 }
786
787 #[rstest]
788 fn test_simulate_order_fills_sell_when_far_from_market() {
789 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
790
791 ladder.add(BookOrder {
792 price: Price::from("100.00"),
793 size: Quantity::from(100),
794 side: OrderSide::Buy,
795 order_id: 1,
796 });
797
798 let order = BookOrder {
799 price: Price::from("150.00"), size: Quantity::from(500),
801 side: OrderSide::Buy,
802 order_id: 2,
803 };
804
805 let fills = ladder.simulate_fills(&order);
806
807 assert!(fills.is_empty());
808 }
809
810 #[rstest]
811 fn test_simulate_order_fills_buy() {
812 let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
813
814 ladder.add_bulk(vec![
815 BookOrder {
816 price: Price::from("100.00"),
817 size: Quantity::from(100),
818 side: OrderSide::Sell,
819 order_id: 1,
820 },
821 BookOrder {
822 price: Price::from("101.00"),
823 size: Quantity::from(200),
824 side: OrderSide::Sell,
825 order_id: 2,
826 },
827 BookOrder {
828 price: Price::from("102.00"),
829 size: Quantity::from(400),
830 side: OrderSide::Sell,
831 order_id: 3,
832 },
833 ]);
834
835 let order = BookOrder {
836 price: Price::max(2), size: Quantity::from(500),
838 side: OrderSide::Buy,
839 order_id: 4,
840 };
841
842 let fills = ladder.simulate_fills(&order);
843
844 assert_eq!(fills.len(), 3);
845
846 let (price1, size1) = fills[0];
847 assert_eq!(price1, Price::from("100.00"));
848 assert_eq!(size1, Quantity::from(100));
849
850 let (price2, size2) = fills[1];
851 assert_eq!(price2, Price::from("101.00"));
852 assert_eq!(size2, Quantity::from(200));
853
854 let (price3, size3) = fills[2];
855 assert_eq!(price3, Price::from("102.00"));
856 assert_eq!(size3, Quantity::from(200));
857 }
858
859 #[rstest]
860 fn test_simulate_order_fills_sell() {
861 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
862
863 ladder.add_bulk(vec![
864 BookOrder {
865 price: Price::from("102.00"),
866 size: Quantity::from(100),
867 side: OrderSide::Buy,
868 order_id: 1,
869 },
870 BookOrder {
871 price: Price::from("101.00"),
872 size: Quantity::from(200),
873 side: OrderSide::Buy,
874 order_id: 2,
875 },
876 BookOrder {
877 price: Price::from("100.00"),
878 size: Quantity::from(400),
879 side: OrderSide::Buy,
880 order_id: 3,
881 },
882 ]);
883
884 let order = BookOrder {
885 price: Price::min(2), size: Quantity::from(500),
887 side: OrderSide::Sell,
888 order_id: 4,
889 };
890
891 let fills = ladder.simulate_fills(&order);
892
893 assert_eq!(fills.len(), 3);
894
895 let (price1, size1) = fills[0];
896 assert_eq!(price1, Price::from("102.00"));
897 assert_eq!(size1, Quantity::from(100));
898
899 let (price2, size2) = fills[1];
900 assert_eq!(price2, Price::from("101.00"));
901 assert_eq!(size2, Quantity::from(200));
902
903 let (price3, size3) = fills[2];
904 assert_eq!(price3, Price::from("100.00"));
905 assert_eq!(size3, Quantity::from(200));
906 }
907
908 #[rstest]
909 fn test_simulate_order_fills_sell_with_size_at_limit_of_precision() {
910 let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
911
912 ladder.add_bulk(vec![
913 BookOrder {
914 price: Price::from("102.00"),
915 size: Quantity::from("100.000000000"),
916 side: OrderSide::Buy,
917 order_id: 1,
918 },
919 BookOrder {
920 price: Price::from("101.00"),
921 size: Quantity::from("200.000000000"),
922 side: OrderSide::Buy,
923 order_id: 2,
924 },
925 BookOrder {
926 price: Price::from("100.00"),
927 size: Quantity::from("400.000000000"),
928 side: OrderSide::Buy,
929 order_id: 3,
930 },
931 ]);
932
933 let order = BookOrder {
934 price: Price::min(2), size: Quantity::from("699.999999999"), side: OrderSide::Sell,
937 order_id: 4,
938 };
939
940 let fills = ladder.simulate_fills(&order);
941
942 assert_eq!(fills.len(), 3);
943
944 let (price1, size1) = fills[0];
945 assert_eq!(price1, Price::from("102.00"));
946 assert_eq!(size1, Quantity::from("100.000000000"));
947
948 let (price2, size2) = fills[1];
949 assert_eq!(price2, Price::from("101.00"));
950 assert_eq!(size2, Quantity::from("200.000000000"));
951
952 let (price3, size3) = fills[2];
953 assert_eq!(price3, Price::from("100.00"));
954 assert_eq!(size3, Quantity::from("399.999999999"));
955 }
956
957 #[rstest]
958 fn test_boundary_prices() {
959 let max_price = Price::max(1);
960 let min_price = Price::min(1);
961
962 let mut ladder_buy = BookLadder::new(OrderSideSpecified::Buy);
963 let mut ladder_sell = BookLadder::new(OrderSideSpecified::Sell);
964
965 let order_buy = BookOrder::new(OrderSide::Buy, min_price, Quantity::from(1), 1);
966 let order_sell = BookOrder::new(OrderSide::Sell, max_price, Quantity::from(1), 1);
967
968 ladder_buy.add(order_buy);
969 ladder_sell.add(order_sell);
970
971 assert_eq!(ladder_buy.top().unwrap().price.value, min_price);
972 assert_eq!(ladder_sell.top().unwrap().price.value, max_price);
973 }
974}