nautilus_model/orderbook/
ladder.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Represents a ladder of price levels for one side of an order book.
17
18use 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/// Represents a price level with a specified side in an order books ladder.
34#[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    /// Creates a new [`BookPrice`] instance.
46    #[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/// Represents a ladder of price levels for one side of an order book.
80#[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    /// Creates a new [`Ladder`] instance.
89    #[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    /// Returns the number of price levels in the ladder.
99    #[must_use]
100    pub fn len(&self) -> usize {
101        self.levels.len()
102    }
103
104    /// Returns true if the ladder has no price levels.
105    #[must_use]
106    #[allow(dead_code)] // Used in tests
107    pub fn is_empty(&self) -> bool {
108        self.levels.is_empty()
109    }
110
111    #[allow(dead_code)] // Used in tests
112    /// Adds multiple orders to the ladder.
113    pub fn add_bulk(&mut self, orders: Vec<BookOrder>) {
114        for order in orders {
115            self.add(order);
116        }
117    }
118
119    /// Removes all orders and price levels from the ladder.
120    pub fn clear(&mut self) {
121        self.levels.clear();
122        self.cache.clear();
123    }
124
125    /// Adds an order to the ladder at its price level.
126    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    /// Updates an existing order in the ladder, moving it to a new price level if needed.
142    pub fn update(&mut self, order: BookOrder) {
143        let price = self.cache.get(&order.order_id).copied();
144        if let Some(price) = price {
145            if let Some(level) = self.levels.get_mut(&price) {
146                if order.price == level.price.value {
147                    // Update at current price level
148                    level.update(order);
149                    return;
150                }
151
152                // Price update: delete and insert at new level
153                self.cache.remove(&order.order_id);
154                level.delete(&order);
155                if level.is_empty() {
156                    self.levels.remove(&price);
157                }
158            }
159        }
160
161        self.add(order);
162    }
163
164    /// Deletes an order from the ladder.
165    pub fn delete(&mut self, order: BookOrder, sequence: u64, ts_event: UnixNanos) {
166        self.remove(order.order_id, sequence, ts_event);
167    }
168
169    /// Removes an order by its ID from the ladder.
170    pub fn remove(&mut self, order_id: OrderId, sequence: u64, ts_event: UnixNanos) {
171        if let Some(price) = self.cache.remove(&order_id) {
172            if let Some(level) = self.levels.get_mut(&price) {
173                level.remove_by_id(order_id, sequence, ts_event);
174                if level.is_empty() {
175                    self.levels.remove(&price);
176                }
177            }
178        }
179    }
180
181    /// Returns the total size of all orders in the ladder.
182    #[must_use]
183    #[allow(dead_code)] // Used in tests
184    pub fn sizes(&self) -> f64 {
185        self.levels.values().map(BookLevel::size).sum()
186    }
187
188    /// Returns the total value exposure (price * size) of all orders in the ladder.
189    #[must_use]
190    #[allow(dead_code)] // Used in tests
191    pub fn exposures(&self) -> f64 {
192        self.levels.values().map(BookLevel::exposure).sum()
193    }
194
195    /// Returns the best price level in the ladder.
196    #[must_use]
197    pub fn top(&self) -> Option<&BookLevel> {
198        match self.levels.iter().next() {
199            Some((_, l)) => Option::Some(l),
200            None => Option::None,
201        }
202    }
203
204    /// Simulates fills for an order against this ladder's liquidity.
205    /// Returns a list of (price, size) tuples representing the simulated fills.
206    #[must_use]
207    pub fn simulate_fills(&self, order: &BookOrder) -> Vec<(Price, Quantity)> {
208        let is_reversed = self.side == OrderSideSpecified::Buy;
209        let mut fills = Vec::new();
210        let mut cumulative_denominator = Quantity::zero(order.size.precision);
211        let target = order.size;
212
213        for level in self.levels.values() {
214            if (is_reversed && level.price.value < order.price)
215                || (!is_reversed && level.price.value > order.price)
216            {
217                break;
218            }
219
220            for book_order in level.orders.values() {
221                let current = book_order.size;
222                if cumulative_denominator + current >= target {
223                    // This order has filled us, add fill and return
224                    let remainder = target - cumulative_denominator;
225                    if remainder.is_positive() {
226                        fills.push((book_order.price, remainder));
227                    }
228                    return fills;
229                }
230
231                // Add this fill and continue
232                fills.push((book_order.price, current));
233                cumulative_denominator += current;
234            }
235        }
236
237        fills
238    }
239}
240
241impl Display for BookLadder {
242    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243        writeln!(f, "{}(side={})", stringify!(BookLadder), self.side)?;
244        for (price, level) in &self.levels {
245            writeln!(f, "  {} -> {} orders", price, level.len())?;
246        }
247        Ok(())
248    }
249}
250
251////////////////////////////////////////////////////////////////////////////////
252// Tests
253////////////////////////////////////////////////////////////////////////////////
254#[cfg(test)]
255mod tests {
256    use rstest::rstest;
257
258    use crate::{
259        data::order::BookOrder,
260        enums::{OrderSide, OrderSideSpecified},
261        orderbook::ladder::{BookLadder, BookPrice},
262        types::{Price, Quantity},
263    };
264
265    #[rstest]
266    fn test_is_empty() {
267        let ladder = BookLadder::new(OrderSideSpecified::Buy);
268        assert!(ladder.is_empty(), "A new ladder should be empty");
269    }
270
271    #[rstest]
272    fn test_is_empty_after_add() {
273        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
274        assert!(ladder.is_empty(), "Ladder should start empty");
275        let order = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(100), 1);
276        ladder.add(order);
277        assert!(
278            !ladder.is_empty(),
279            "Ladder should not be empty after adding an order"
280        );
281    }
282
283    #[rstest]
284    fn test_add_bulk_empty() {
285        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
286        ladder.add_bulk(vec![]);
287        assert!(
288            ladder.is_empty(),
289            "Adding an empty vector should leave the ladder empty"
290        );
291    }
292
293    #[rstest]
294    fn test_add_bulk_orders() {
295        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
296        let orders = vec![
297            BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1),
298            BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(30), 2),
299            BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(50), 3),
300        ];
301        ladder.add_bulk(orders);
302        // All orders share the same price, so there should be one price level.
303        assert_eq!(ladder.len(), 1, "Ladder should have one price level");
304        let orders_in_level = ladder.top().unwrap().get_orders();
305        assert_eq!(
306            orders_in_level.len(),
307            3,
308            "Price level should contain all bulk orders"
309        );
310    }
311
312    #[rstest]
313    fn test_book_price_bid_sorting() {
314        let mut bid_prices = [
315            BookPrice::new(Price::from("2.0"), OrderSideSpecified::Buy),
316            BookPrice::new(Price::from("4.0"), OrderSideSpecified::Buy),
317            BookPrice::new(Price::from("1.0"), OrderSideSpecified::Buy),
318            BookPrice::new(Price::from("3.0"), OrderSideSpecified::Buy),
319        ];
320        bid_prices.sort();
321        assert_eq!(bid_prices[0].value.as_f64(), 4.0);
322    }
323
324    #[rstest]
325    fn test_book_price_ask_sorting() {
326        let mut ask_prices = [
327            BookPrice::new(Price::from("2.0"), OrderSideSpecified::Sell),
328            BookPrice::new(Price::from("4.0"), OrderSideSpecified::Sell),
329            BookPrice::new(Price::from("1.0"), OrderSideSpecified::Sell),
330            BookPrice::new(Price::from("3.0"), OrderSideSpecified::Sell),
331        ];
332
333        ask_prices.sort();
334        assert_eq!(ask_prices[0].value.as_f64(), 1.0);
335    }
336
337    #[rstest]
338    fn test_add_single_order() {
339        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
340        let order = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 0);
341
342        ladder.add(order);
343        assert_eq!(ladder.len(), 1);
344        assert_eq!(ladder.sizes(), 20.0);
345        assert_eq!(ladder.exposures(), 200.0);
346        assert_eq!(ladder.top().unwrap().price.value.as_f64(), 10.0);
347    }
348
349    #[rstest]
350    fn test_add_multiple_buy_orders() {
351        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
352        let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 0);
353        let order2 = BookOrder::new(OrderSide::Buy, Price::from("9.00"), Quantity::from(30), 1);
354        let order3 = BookOrder::new(OrderSide::Buy, Price::from("9.00"), Quantity::from(50), 2);
355        let order4 = BookOrder::new(OrderSide::Buy, Price::from("8.00"), Quantity::from(200), 3);
356
357        ladder.add_bulk(vec![order1, order2, order3, order4]);
358        assert_eq!(ladder.len(), 3);
359        assert_eq!(ladder.sizes(), 300.0);
360        assert_eq!(ladder.exposures(), 2520.0);
361        assert_eq!(ladder.top().unwrap().price.value.as_f64(), 10.0);
362    }
363
364    #[rstest]
365    fn test_add_multiple_sell_orders() {
366        let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
367        let order1 = BookOrder::new(OrderSide::Sell, Price::from("11.00"), Quantity::from(20), 0);
368        let order2 = BookOrder::new(OrderSide::Sell, Price::from("12.00"), Quantity::from(30), 1);
369        let order3 = BookOrder::new(OrderSide::Sell, Price::from("12.00"), Quantity::from(50), 2);
370        let order4 = BookOrder::new(
371            OrderSide::Sell,
372            Price::from("13.00"),
373            Quantity::from(200),
374            0,
375        );
376
377        ladder.add_bulk(vec![order1, order2, order3, order4]);
378        assert_eq!(ladder.len(), 3);
379        assert_eq!(ladder.sizes(), 300.0);
380        assert_eq!(ladder.exposures(), 3780.0);
381        assert_eq!(ladder.top().unwrap().price.value.as_f64(), 11.0);
382    }
383
384    #[rstest]
385    fn test_add_to_same_price_level() {
386        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
387        let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
388        let order2 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(30), 2);
389
390        ladder.add(order1);
391        ladder.add(order2);
392
393        assert_eq!(ladder.len(), 1);
394        assert_eq!(ladder.sizes(), 50.0);
395        assert_eq!(ladder.exposures(), 500.0);
396    }
397
398    #[rstest]
399    fn test_add_descending_buy_orders() {
400        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
401        let order1 = BookOrder::new(OrderSide::Buy, Price::from("9.00"), Quantity::from(20), 1);
402        let order2 = BookOrder::new(OrderSide::Buy, Price::from("8.00"), Quantity::from(30), 2);
403
404        ladder.add(order1);
405        ladder.add(order2);
406
407        assert_eq!(ladder.top().unwrap().price.value, Price::from("9.00"));
408    }
409
410    #[rstest]
411    fn test_add_ascending_sell_orders() {
412        let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
413        let order1 = BookOrder::new(OrderSide::Sell, Price::from("8.00"), Quantity::from(20), 1);
414        let order2 = BookOrder::new(OrderSide::Sell, Price::from("9.00"), Quantity::from(30), 2);
415
416        ladder.add(order1);
417        ladder.add(order2);
418
419        assert_eq!(ladder.top().unwrap().price.value, Price::from("8.00"));
420    }
421
422    #[rstest]
423    fn test_update_buy_order_price() {
424        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
425        let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(20), 1);
426
427        ladder.add(order);
428        let order = BookOrder::new(OrderSide::Buy, Price::from("11.10"), Quantity::from(20), 1);
429
430        ladder.update(order);
431        assert_eq!(ladder.len(), 1);
432        assert_eq!(ladder.sizes(), 20.0);
433        assert_eq!(ladder.exposures(), 222.0);
434        assert_eq!(ladder.top().unwrap().price.value.as_f64(), 11.1);
435    }
436
437    #[rstest]
438    fn test_update_sell_order_price() {
439        let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
440        let order = BookOrder::new(OrderSide::Sell, Price::from("11.00"), Quantity::from(20), 1);
441
442        ladder.add(order);
443
444        let order = BookOrder::new(OrderSide::Sell, Price::from("11.10"), Quantity::from(20), 1);
445
446        ladder.update(order);
447        assert_eq!(ladder.len(), 1);
448        assert_eq!(ladder.sizes(), 20.0);
449        assert_eq!(ladder.exposures(), 222.0);
450        assert_eq!(ladder.top().unwrap().price.value.as_f64(), 11.1);
451    }
452
453    #[rstest]
454    fn test_update_buy_order_size() {
455        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
456        let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(20), 1);
457
458        ladder.add(order);
459
460        let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(10), 1);
461
462        ladder.update(order);
463        assert_eq!(ladder.len(), 1);
464        assert_eq!(ladder.sizes(), 10.0);
465        assert_eq!(ladder.exposures(), 110.0);
466        assert_eq!(ladder.top().unwrap().price.value.as_f64(), 11.0);
467    }
468
469    #[rstest]
470    fn test_update_sell_order_size() {
471        let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
472        let order = BookOrder::new(OrderSide::Sell, Price::from("11.00"), Quantity::from(20), 1);
473
474        ladder.add(order);
475
476        let order = BookOrder::new(OrderSide::Sell, Price::from("11.00"), Quantity::from(10), 1);
477
478        ladder.update(order);
479        assert_eq!(ladder.len(), 1);
480        assert_eq!(ladder.sizes(), 10.0);
481        assert_eq!(ladder.exposures(), 110.0);
482        assert_eq!(ladder.top().unwrap().price.value.as_f64(), 11.0);
483    }
484
485    #[rstest]
486    fn test_delete_non_existing_order() {
487        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
488        let order = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
489
490        ladder.delete(order, 0, 0.into());
491
492        assert_eq!(ladder.len(), 0);
493    }
494
495    #[rstest]
496    fn test_delete_buy_order() {
497        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
498        let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(20), 1);
499
500        ladder.add(order);
501
502        let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(10), 1);
503
504        ladder.delete(order, 0, 0.into());
505        assert_eq!(ladder.len(), 0);
506        assert_eq!(ladder.sizes(), 0.0);
507        assert_eq!(ladder.exposures(), 0.0);
508        assert_eq!(ladder.top(), None);
509    }
510
511    #[rstest]
512    fn test_delete_sell_order() {
513        let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
514        let order = BookOrder::new(OrderSide::Sell, Price::from("10.00"), Quantity::from(10), 1);
515
516        ladder.add(order);
517
518        let order = BookOrder::new(OrderSide::Sell, Price::from("10.00"), Quantity::from(10), 1);
519
520        ladder.delete(order, 0, 0.into());
521        assert_eq!(ladder.len(), 0);
522        assert_eq!(ladder.sizes(), 0.0);
523        assert_eq!(ladder.exposures(), 0.0);
524        assert_eq!(ladder.top(), None);
525    }
526
527    #[rstest]
528    fn test_ladder_sizes_empty() {
529        let ladder = BookLadder::new(OrderSideSpecified::Buy);
530        assert_eq!(
531            ladder.sizes(),
532            0.0,
533            "An empty ladder should have total size 0.0"
534        );
535    }
536
537    #[rstest]
538    fn test_ladder_exposures_empty() {
539        let ladder = BookLadder::new(OrderSideSpecified::Buy);
540        assert_eq!(
541            ladder.exposures(),
542            0.0,
543            "An empty ladder should have total exposure 0.0"
544        );
545    }
546
547    #[rstest]
548    fn test_ladder_sizes() {
549        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
550        let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
551        let order2 = BookOrder::new(OrderSide::Buy, Price::from("9.50"), Quantity::from(30), 2);
552        ladder.add(order1);
553        ladder.add(order2);
554
555        let expected_size = 20.0 + 30.0;
556        assert_eq!(
557            ladder.sizes(),
558            expected_size,
559            "Ladder total size should match the sum of order sizes"
560        );
561    }
562
563    #[rstest]
564    fn test_ladder_exposures() {
565        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
566        let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
567        let order2 = BookOrder::new(OrderSide::Buy, Price::from("9.50"), Quantity::from(30), 2);
568        ladder.add(order1);
569        ladder.add(order2);
570
571        let expected_exposure = 10.00 * 20.0 + 9.50 * 30.0;
572        assert_eq!(
573            ladder.exposures(),
574            expected_exposure,
575            "Ladder total exposure should match the sum of individual exposures"
576        );
577    }
578
579    #[rstest]
580    fn test_iter_returns_fifo() {
581        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
582        let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
583        let order2 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(30), 2);
584        ladder.add(order1);
585        ladder.add(order2);
586        let orders: Vec<BookOrder> = ladder.top().unwrap().iter().copied().collect();
587        assert_eq!(
588            orders,
589            vec![order1, order2],
590            "Iterator should return orders in FIFO order"
591        );
592    }
593
594    #[rstest]
595    fn test_update_missing_order_inserts() {
596        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
597        let order = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
598        // Call update on an order that hasn't been added yet (upsert behavior)
599        ladder.update(order);
600        assert_eq!(
601            ladder.len(),
602            1,
603            "Ladder should have one level after upsert update"
604        );
605        let orders = ladder.top().unwrap().get_orders();
606        assert_eq!(
607            orders.len(),
608            1,
609            "Price level should contain the inserted order"
610        );
611        assert_eq!(orders[0], order, "The inserted order should match");
612    }
613
614    #[rstest]
615    fn test_cache_consistency_after_operations() {
616        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
617        let order1 = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
618        let order2 = BookOrder::new(OrderSide::Buy, Price::from("9.00"), Quantity::from(30), 2);
619        ladder.add(order1);
620        ladder.add(order2);
621
622        // Ensure that each order in the cache is present in the corresponding price level.
623        for (order_id, price) in &ladder.cache {
624            let level = ladder
625                .levels
626                .get(price)
627                .expect("Every price in the cache should have a corresponding level");
628            assert!(
629                level.orders.contains_key(order_id),
630                "Order id {order_id} should be present in the level for price {price}",
631            );
632        }
633    }
634
635    #[rstest]
636    fn test_simulate_fills_with_empty_book() {
637        let ladder = BookLadder::new(OrderSideSpecified::Buy);
638        let order = BookOrder::new(OrderSide::Buy, Price::max(2), Quantity::from(500), 1);
639
640        let fills = ladder.simulate_fills(&order);
641
642        assert!(fills.is_empty());
643    }
644
645    #[rstest]
646    #[case(OrderSide::Buy, Price::max(2), OrderSideSpecified::Sell)]
647    #[case(OrderSide::Sell, Price::min(2), OrderSideSpecified::Buy)]
648    fn test_simulate_order_fills_with_no_size(
649        #[case] side: OrderSide,
650        #[case] price: Price,
651        #[case] ladder_side: OrderSideSpecified,
652    ) {
653        let ladder = BookLadder::new(ladder_side);
654        let order = BookOrder {
655            price, // <-- Simulate a MARKET order
656            size: Quantity::from(500),
657            side,
658            order_id: 2,
659        };
660
661        let fills = ladder.simulate_fills(&order);
662
663        assert!(fills.is_empty());
664    }
665
666    #[rstest]
667    #[case(OrderSide::Buy, OrderSideSpecified::Sell, Price::from("60.0"))]
668    #[case(OrderSide::Sell, OrderSideSpecified::Buy, Price::from("40.0"))]
669    fn test_simulate_order_fills_buy_when_far_from_market(
670        #[case] order_side: OrderSide,
671        #[case] ladder_side: OrderSideSpecified,
672        #[case] ladder_price: Price,
673    ) {
674        let mut ladder = BookLadder::new(ladder_side);
675
676        ladder.add(BookOrder {
677            price: ladder_price,
678            size: Quantity::from(100),
679            side: ladder_side.as_order_side(),
680            order_id: 1,
681        });
682
683        let order = BookOrder {
684            price: Price::from("50.00"),
685            size: Quantity::from(500),
686            side: order_side,
687            order_id: 2,
688        };
689
690        let fills = ladder.simulate_fills(&order);
691
692        assert!(fills.is_empty());
693    }
694
695    #[rstest]
696    fn test_simulate_order_fills_sell_when_far_from_market() {
697        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
698
699        ladder.add(BookOrder {
700            price: Price::from("100.00"),
701            size: Quantity::from(100),
702            side: OrderSide::Buy,
703            order_id: 1,
704        });
705
706        let order = BookOrder {
707            price: Price::from("150.00"), // <-- Simulate a MARKET order
708            size: Quantity::from(500),
709            side: OrderSide::Buy,
710            order_id: 2,
711        };
712
713        let fills = ladder.simulate_fills(&order);
714
715        assert!(fills.is_empty());
716    }
717
718    #[rstest]
719    fn test_simulate_order_fills_buy() {
720        let mut ladder = BookLadder::new(OrderSideSpecified::Sell);
721
722        ladder.add_bulk(vec![
723            BookOrder {
724                price: Price::from("100.00"),
725                size: Quantity::from(100),
726                side: OrderSide::Sell,
727                order_id: 1,
728            },
729            BookOrder {
730                price: Price::from("101.00"),
731                size: Quantity::from(200),
732                side: OrderSide::Sell,
733                order_id: 2,
734            },
735            BookOrder {
736                price: Price::from("102.00"),
737                size: Quantity::from(400),
738                side: OrderSide::Sell,
739                order_id: 3,
740            },
741        ]);
742
743        let order = BookOrder {
744            price: Price::max(2), // <-- Simulate a MARKET order
745            size: Quantity::from(500),
746            side: OrderSide::Buy,
747            order_id: 4,
748        };
749
750        let fills = ladder.simulate_fills(&order);
751
752        assert_eq!(fills.len(), 3);
753
754        let (price1, size1) = fills[0];
755        assert_eq!(price1, Price::from("100.00"));
756        assert_eq!(size1, Quantity::from(100));
757
758        let (price2, size2) = fills[1];
759        assert_eq!(price2, Price::from("101.00"));
760        assert_eq!(size2, Quantity::from(200));
761
762        let (price3, size3) = fills[2];
763        assert_eq!(price3, Price::from("102.00"));
764        assert_eq!(size3, Quantity::from(200));
765    }
766
767    #[rstest]
768    fn test_simulate_order_fills_sell() {
769        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
770
771        ladder.add_bulk(vec![
772            BookOrder {
773                price: Price::from("102.00"),
774                size: Quantity::from(100),
775                side: OrderSide::Buy,
776                order_id: 1,
777            },
778            BookOrder {
779                price: Price::from("101.00"),
780                size: Quantity::from(200),
781                side: OrderSide::Buy,
782                order_id: 2,
783            },
784            BookOrder {
785                price: Price::from("100.00"),
786                size: Quantity::from(400),
787                side: OrderSide::Buy,
788                order_id: 3,
789            },
790        ]);
791
792        let order = BookOrder {
793            price: Price::min(2), // <-- Simulate a MARKET order
794            size: Quantity::from(500),
795            side: OrderSide::Sell,
796            order_id: 4,
797        };
798
799        let fills = ladder.simulate_fills(&order);
800
801        assert_eq!(fills.len(), 3);
802
803        let (price1, size1) = fills[0];
804        assert_eq!(price1, Price::from("102.00"));
805        assert_eq!(size1, Quantity::from(100));
806
807        let (price2, size2) = fills[1];
808        assert_eq!(price2, Price::from("101.00"));
809        assert_eq!(size2, Quantity::from(200));
810
811        let (price3, size3) = fills[2];
812        assert_eq!(price3, Price::from("100.00"));
813        assert_eq!(size3, Quantity::from(200));
814    }
815
816    #[rstest]
817    fn test_simulate_order_fills_sell_with_size_at_limit_of_precision() {
818        let mut ladder = BookLadder::new(OrderSideSpecified::Buy);
819
820        ladder.add_bulk(vec![
821            BookOrder {
822                price: Price::from("102.00"),
823                size: Quantity::from("100.000000000"),
824                side: OrderSide::Buy,
825                order_id: 1,
826            },
827            BookOrder {
828                price: Price::from("101.00"),
829                size: Quantity::from("200.000000000"),
830                side: OrderSide::Buy,
831                order_id: 2,
832            },
833            BookOrder {
834                price: Price::from("100.00"),
835                size: Quantity::from("400.000000000"),
836                side: OrderSide::Buy,
837                order_id: 3,
838            },
839        ]);
840
841        let order = BookOrder {
842            price: Price::min(2),                  // <-- Simulate a MARKET order
843            size: Quantity::from("699.999999999"), // <-- Size slightly less than total size in ladder
844            side: OrderSide::Sell,
845            order_id: 4,
846        };
847
848        let fills = ladder.simulate_fills(&order);
849
850        assert_eq!(fills.len(), 3);
851
852        let (price1, size1) = fills[0];
853        assert_eq!(price1, Price::from("102.00"));
854        assert_eq!(size1, Quantity::from("100.000000000"));
855
856        let (price2, size2) = fills[1];
857        assert_eq!(price2, Price::from("101.00"));
858        assert_eq!(size2, Quantity::from("200.000000000"));
859
860        let (price3, size3) = fills[2];
861        assert_eq!(price3, Price::from("100.00"));
862        assert_eq!(size3, Quantity::from("399.999999999"));
863    }
864
865    #[rstest]
866    fn test_boundary_prices() {
867        let max_price = Price::max(1);
868        let min_price = Price::min(1);
869
870        let mut ladder_buy = BookLadder::new(OrderSideSpecified::Buy);
871        let mut ladder_sell = BookLadder::new(OrderSideSpecified::Sell);
872
873        let order_buy = BookOrder::new(OrderSide::Buy, min_price, Quantity::from(1), 1);
874        let order_sell = BookOrder::new(OrderSide::Sell, max_price, Quantity::from(1), 1);
875
876        ladder_buy.add(order_buy);
877        ladder_sell.add(order_sell);
878
879        assert_eq!(ladder_buy.top().unwrap().price.value, min_price);
880        assert_eq!(ladder_sell.top().unwrap().price.value, max_price);
881    }
882}