nautilus_model/orderbook/
ladder.rs

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