nautilus_model/orderbook/
level.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 discrete price level in an order book.
17
18use std::cmp::Ordering;
19
20use indexmap::IndexMap;
21use nautilus_core::UnixNanos;
22use rust_decimal::Decimal;
23
24use crate::{
25    data::order::{BookOrder, OrderId},
26    enums::OrderSideSpecified,
27    orderbook::{BookIntegrityError, BookPrice},
28    types::{fixed::FIXED_SCALAR, quantity::QuantityRaw},
29};
30
31/// Represents a discrete price level in an order book.
32///
33/// Orders are stored in an [`IndexMap`] which preserves FIFO (insertion) order.
34#[derive(Clone, Debug, Eq)]
35#[cfg_attr(
36    feature = "python",
37    pyo3::pyclass(module = "posei_trader.core.nautilus_pyo3.model")
38)]
39pub struct BookLevel {
40    pub price: BookPrice,
41    pub(crate) orders: IndexMap<OrderId, BookOrder>,
42}
43
44impl BookLevel {
45    /// Creates a new [`BookLevel`] instance.
46    #[must_use]
47    pub fn new(price: BookPrice) -> Self {
48        Self {
49            price,
50            orders: IndexMap::new(),
51        }
52    }
53
54    /// Creates a new [`BookLevel`] from an order, using the order's price and side.
55    #[must_use]
56    pub fn from_order(order: BookOrder) -> Self {
57        let mut level = Self {
58            price: order.to_book_price(),
59            orders: IndexMap::new(),
60        };
61        level.add(order);
62        level
63    }
64
65    pub fn side(&self) -> OrderSideSpecified {
66        self.price.side
67    }
68
69    /// Returns the number of orders at this price level.
70    #[must_use]
71    pub fn len(&self) -> usize {
72        self.orders.len()
73    }
74
75    /// Returns true if this price level has no orders.
76    #[must_use]
77    pub fn is_empty(&self) -> bool {
78        self.orders.is_empty()
79    }
80
81    /// Returns a reference to the first order at this price level in FIFO order.
82    #[inline]
83    #[must_use]
84    pub fn first(&self) -> Option<&BookOrder> {
85        self.orders.get_index(0).map(|(_key, order)| order)
86    }
87
88    /// Returns an iterator over the orders at this price level in FIFO order.
89    pub fn iter(&self) -> impl Iterator<Item = &BookOrder> {
90        self.orders.values()
91    }
92
93    /// Returns all orders at this price level in FIFO insertion order.
94    #[must_use]
95    pub fn get_orders(&self) -> Vec<BookOrder> {
96        self.orders.values().copied().collect()
97    }
98
99    /// Returns the total size of all orders at this price level as a float.
100    #[must_use]
101    pub fn size(&self) -> f64 {
102        self.orders.values().map(|o| o.size.as_f64()).sum()
103    }
104
105    /// Returns the total size of all orders at this price level as raw integer units.
106    #[must_use]
107    pub fn size_raw(&self) -> QuantityRaw {
108        self.orders.values().map(|o| o.size.raw).sum()
109    }
110
111    /// Returns the total size of all orders at this price level as a decimal.
112    #[must_use]
113    pub fn size_decimal(&self) -> Decimal {
114        self.orders.values().map(|o| o.size.as_decimal()).sum()
115    }
116
117    /// Returns the total exposure (price * size) of all orders at this price level as a float.
118    #[must_use]
119    pub fn exposure(&self) -> f64 {
120        self.orders
121            .values()
122            .map(|o| o.price.as_f64() * o.size.as_f64())
123            .sum()
124    }
125
126    /// Returns the total exposure (price * size) of all orders at this price level as raw integer units.
127    #[must_use]
128    pub fn exposure_raw(&self) -> QuantityRaw {
129        self.orders
130            .values()
131            .map(|o| {
132                let exposure_f64 = o.price.as_f64() * o.size.as_f64();
133                debug_assert!(
134                    exposure_f64.is_finite(),
135                    "Exposure calculation resulted in non-finite value for order {}: price={}, size={}",
136                    o.order_id,
137                    o.price,
138                    o.size
139                );
140                (exposure_f64 * FIXED_SCALAR) as QuantityRaw
141            })
142            .sum()
143    }
144
145    /// Adds multiple orders to this price level in FIFO order. Orders must match the level's price.
146    pub fn add_bulk(&mut self, orders: Vec<BookOrder>) {
147        for order in orders {
148            self.add(order);
149        }
150    }
151
152    /// Adds an order to this price level. Order must match the level's price.
153    pub fn add(&mut self, order: BookOrder) {
154        debug_assert_eq!(order.price, self.price.value);
155        debug_assert!(
156            order.size.is_positive(),
157            "Order size must be positive: {}",
158            order.size
159        );
160
161        self.orders.insert(order.order_id, order);
162    }
163
164    /// Updates an existing order at this price level. Updated order must match the level's price.
165    /// Removes the order if size becomes zero.
166    pub fn update(&mut self, order: BookOrder) {
167        debug_assert_eq!(order.price, self.price.value);
168
169        if order.size.raw == 0 {
170            // Updating non-existent order to zero size is a no-op, which is valid
171            self.orders.shift_remove(&order.order_id);
172        } else {
173            debug_assert!(
174                order.size.is_positive(),
175                "Order size must be positive: {}",
176                order.size
177            );
178            self.orders.insert(order.order_id, order);
179        }
180    }
181
182    /// Deletes an order from this price level.
183    pub fn delete(&mut self, order: &BookOrder) {
184        self.orders.shift_remove(&order.order_id);
185    }
186
187    /// Removes an order by its ID.
188    ///
189    /// # Panics
190    ///
191    /// Panics if no order with the given `order_id` exists at this level.
192    pub fn remove_by_id(&mut self, order_id: OrderId, sequence: u64, ts_event: UnixNanos) {
193        assert!(
194            self.orders.shift_remove(&order_id).is_some(),
195            "{}",
196            &BookIntegrityError::OrderNotFound(order_id, sequence, ts_event)
197        );
198    }
199}
200
201impl PartialEq for BookLevel {
202    fn eq(&self, other: &Self) -> bool {
203        self.price == other.price
204    }
205}
206
207impl PartialOrd for BookLevel {
208    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
209        Some(self.cmp(other))
210    }
211}
212
213impl Ord for BookLevel {
214    fn cmp(&self, other: &Self) -> Ordering {
215        self.price.cmp(&other.price)
216    }
217}
218
219////////////////////////////////////////////////////////////////////////////////
220// Tests
221////////////////////////////////////////////////////////////////////////////////
222#[cfg(test)]
223mod tests {
224    use rstest::rstest;
225    use rust_decimal_macros::dec;
226
227    use crate::{
228        data::order::BookOrder,
229        enums::{OrderSide, OrderSideSpecified},
230        orderbook::{BookLevel, BookPrice},
231        types::{Price, Quantity, fixed::FIXED_SCALAR, quantity::QuantityRaw},
232    };
233
234    #[rstest]
235    fn test_empty_level() {
236        let level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
237        assert!(level.first().is_none());
238        assert_eq!(level.side(), OrderSideSpecified::Buy);
239    }
240
241    #[rstest]
242    fn test_level_from_order() {
243        let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
244        let level = BookLevel::from_order(order);
245
246        assert_eq!(level.price.value, Price::from("1.00"));
247        assert_eq!(level.price.side, OrderSideSpecified::Buy);
248        assert_eq!(level.len(), 1);
249        assert_eq!(level.first().unwrap(), &order);
250        assert_eq!(level.size(), 10.0);
251    }
252
253    #[rstest]
254    #[should_panic(expected = "assertion `left == right` failed")]
255    fn test_add_order_incorrect_price_level() {
256        let mut level =
257            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
258        let incorrect_price_order =
259            BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 1);
260        level.add(incorrect_price_order);
261    }
262
263    #[rstest]
264    #[should_panic(expected = "assertion `left == right` failed")]
265    fn test_add_bulk_orders_incorrect_price() {
266        let mut level =
267            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
268        let orders = vec![
269            BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1),
270            BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 2), // Incorrect price
271        ];
272        level.add_bulk(orders);
273    }
274
275    #[rstest]
276    fn test_add_bulk_empty() {
277        let mut level =
278            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
279        level.add_bulk(vec![]);
280        assert!(level.is_empty());
281    }
282
283    #[rstest]
284    fn test_comparisons_bid_side() {
285        let level0 = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
286        let level1 = BookLevel::new(BookPrice::new(Price::from("1.01"), OrderSideSpecified::Buy));
287        assert_eq!(level0, level0);
288        assert!(level0 > level1);
289    }
290
291    #[rstest]
292    fn test_comparisons_ask_side() {
293        let level0 = BookLevel::new(BookPrice::new(
294            Price::from("1.00"),
295            OrderSideSpecified::Sell,
296        ));
297        let level1 = BookLevel::new(BookPrice::new(
298            Price::from("1.01"),
299            OrderSideSpecified::Sell,
300        ));
301        assert_eq!(level0, level0);
302        assert!(level0 < level1);
303    }
304
305    #[rstest]
306    fn test_book_level_sorting() {
307        let mut levels = vec![
308            BookLevel::new(BookPrice::new(
309                Price::from("1.00"),
310                OrderSideSpecified::Sell,
311            )),
312            BookLevel::new(BookPrice::new(
313                Price::from("1.02"),
314                OrderSideSpecified::Sell,
315            )),
316            BookLevel::new(BookPrice::new(
317                Price::from("1.01"),
318                OrderSideSpecified::Sell,
319            )),
320        ];
321        levels.sort();
322        assert_eq!(levels[0].price.value, Price::from("1.00"));
323        assert_eq!(levels[1].price.value, Price::from("1.01"));
324        assert_eq!(levels[2].price.value, Price::from("1.02"));
325    }
326
327    #[rstest]
328    fn test_add_single_order() {
329        let mut level =
330            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
331        let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
332
333        level.add(order);
334        assert!(!level.is_empty());
335        assert_eq!(level.len(), 1);
336        assert_eq!(level.size(), 10.0);
337        assert_eq!(level.first().unwrap(), &order);
338    }
339
340    #[rstest]
341    fn test_add_multiple_orders() {
342        let mut level =
343            BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
344        let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
345        let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
346
347        level.add(order1);
348        level.add(order2);
349        assert_eq!(level.len(), 2);
350        assert_eq!(level.size(), 30.0);
351        assert_eq!(level.exposure(), 60.0);
352        assert_eq!(level.first().unwrap(), &order1);
353    }
354
355    #[rstest]
356    fn test_get_orders() {
357        let mut level =
358            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
359        let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
360        let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
361
362        level.add(order1);
363        level.add(order2);
364
365        let orders = level.get_orders();
366        assert_eq!(orders.len(), 2);
367        assert_eq!(orders[0], order1); // Checks FIFO order maintained
368        assert_eq!(orders[1], order2);
369    }
370
371    #[rstest]
372    fn test_iter_returns_fifo() {
373        let mut level =
374            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
375        let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
376        let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
377        level.add(order1);
378        level.add(order2);
379
380        let orders: Vec<_> = level.iter().copied().collect();
381        assert_eq!(orders, vec![order1, order2]);
382    }
383
384    #[rstest]
385    fn test_update_order() {
386        let mut level =
387            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
388        let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
389        let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 0);
390
391        level.add(order1);
392        level.update(order2);
393        assert_eq!(level.len(), 1);
394        assert_eq!(level.size(), 20.0);
395        assert_eq!(level.exposure(), 20.0);
396    }
397
398    #[rstest]
399    fn test_update_inserts_if_missing() {
400        let mut level =
401            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
402        let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
403        level.update(order);
404        assert_eq!(level.len(), 1);
405        assert_eq!(level.first().unwrap(), &order);
406    }
407
408    #[rstest]
409    fn test_update_zero_size_nonexistent() {
410        let mut level =
411            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
412        let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::zero(0), 1);
413        level.update(order);
414        assert_eq!(level.len(), 0);
415    }
416
417    #[rstest]
418    fn test_fifo_order_after_updates() {
419        let mut level =
420            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
421
422        let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
423        let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
424
425        level.add(order1);
426        level.add(order2);
427
428        // Update order1 size
429        let updated_order1 =
430            BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(15), 1);
431        level.update(updated_order1);
432
433        let orders = level.get_orders();
434        assert_eq!(orders.len(), 2);
435        assert_eq!(orders[0], updated_order1); // First order still first
436        assert_eq!(orders[1], order2); // Second order still second
437    }
438
439    #[rstest]
440    fn test_insertion_order_after_mixed_operations() {
441        let mut level =
442            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
443        let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
444        let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
445        let order3 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(30), 3);
446
447        level.add(order1);
448        level.add(order2);
449        level.add(order3);
450
451        // Update order2 (should keep its position)
452        let updated_order2 =
453            BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(25), 2);
454        level.update(updated_order2);
455
456        // Remove order1; order2 (updated) should now be first
457        level.delete(&order1);
458
459        let orders = level.get_orders();
460        assert_eq!(orders, vec![updated_order2, order3]);
461    }
462
463    #[rstest]
464    #[should_panic(expected = "assertion `left == right` failed")]
465    fn test_update_order_incorrect_price() {
466        let mut level =
467            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
468
469        // Add initial order at correct price level
470        let initial_order =
471            BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
472        level.add(initial_order);
473
474        // Attempt to update with order at incorrect price level
475        let updated_order =
476            BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
477        level.update(updated_order);
478    }
479
480    #[rstest]
481    fn test_update_order_with_zero_size() {
482        let mut level =
483            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
484        let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
485        let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::zero(0), 0);
486
487        level.add(order1);
488        level.update(order2);
489        assert_eq!(level.len(), 0);
490        assert_eq!(level.size(), 0.0);
491        assert_eq!(level.exposure(), 0.0);
492    }
493
494    #[rstest]
495    fn test_delete_nonexistent_order() {
496        let mut level =
497            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
498        let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
499        level.delete(&order);
500        assert_eq!(level.len(), 0);
501    }
502
503    #[rstest]
504    fn test_delete_order() {
505        let mut level =
506            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
507        let order1_id = 0;
508        let order1 = BookOrder::new(
509            OrderSide::Buy,
510            Price::from("1.00"),
511            Quantity::from(10),
512            order1_id,
513        );
514        let order2_id = 1;
515        let order2 = BookOrder::new(
516            OrderSide::Buy,
517            Price::from("1.00"),
518            Quantity::from(20),
519            order2_id,
520        );
521
522        level.add(order1);
523        level.add(order2);
524        level.delete(&order1);
525        assert_eq!(level.len(), 1);
526        assert_eq!(level.size(), 20.0);
527        assert!(level.orders.contains_key(&order2_id));
528        assert_eq!(level.exposure(), 20.0);
529    }
530
531    #[rstest]
532    fn test_remove_order_by_id() {
533        let mut level =
534            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
535        let order1_id = 0;
536        let order1 = BookOrder::new(
537            OrderSide::Buy,
538            Price::from("1.00"),
539            Quantity::from(10),
540            order1_id,
541        );
542        let order2_id = 1;
543        let order2 = BookOrder::new(
544            OrderSide::Buy,
545            Price::from("1.00"),
546            Quantity::from(20),
547            order2_id,
548        );
549
550        level.add(order1);
551        level.add(order2);
552        level.remove_by_id(order2_id, 0, 0.into());
553        assert_eq!(level.len(), 1);
554        assert!(level.orders.contains_key(&order1_id));
555        assert_eq!(level.size(), 10.0);
556        assert_eq!(level.exposure(), 10.0);
557    }
558
559    #[rstest]
560    fn test_add_bulk_orders() {
561        let mut level =
562            BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
563        let order1_id = 0;
564        let order1 = BookOrder::new(
565            OrderSide::Buy,
566            Price::from("2.00"),
567            Quantity::from(10),
568            order1_id,
569        );
570        let order2_id = 1;
571        let order2 = BookOrder::new(
572            OrderSide::Buy,
573            Price::from("2.00"),
574            Quantity::from(20),
575            order2_id,
576        );
577
578        let orders = vec![order1, order2];
579        level.add_bulk(orders);
580        assert_eq!(level.len(), 2);
581        assert_eq!(level.size(), 30.0);
582        assert_eq!(level.exposure(), 60.0);
583    }
584
585    #[rstest]
586    fn test_maximum_order_id() {
587        let mut level =
588            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
589
590        let order = BookOrder::new(
591            OrderSide::Buy,
592            Price::from("1.00"),
593            Quantity::from(10),
594            u64::MAX,
595        );
596        level.add(order);
597
598        assert_eq!(level.len(), 1);
599        assert_eq!(level.first().unwrap(), &order);
600    }
601
602    #[rstest]
603    #[should_panic(
604        expected = "Integrity error: order not found: order_id=1, sequence=2, ts_event=3"
605    )]
606    fn test_remove_nonexistent_order() {
607        let mut level =
608            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
609        level.remove_by_id(1, 2, 3.into());
610    }
611
612    #[rstest]
613    fn test_size() {
614        let mut level =
615            BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
616        let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
617        let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(15), 1);
618
619        level.add(order1);
620        level.add(order2);
621        assert_eq!(level.size(), 25.0);
622    }
623
624    #[rstest]
625    fn test_size_raw() {
626        let mut level =
627            BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
628        let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
629        let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
630
631        level.add(order1);
632        level.add(order2);
633        assert_eq!(
634            level.size_raw(),
635            (30.0 * FIXED_SCALAR).round() as QuantityRaw
636        );
637    }
638
639    #[rstest]
640    fn test_size_decimal() {
641        let mut level =
642            BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
643        let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
644        let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
645
646        level.add(order1);
647        level.add(order2);
648        assert_eq!(level.size_decimal(), dec!(30.0));
649    }
650
651    #[rstest]
652    fn test_exposure() {
653        let mut level =
654            BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
655        let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
656        let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
657
658        level.add(order1);
659        level.add(order2);
660        assert_eq!(level.exposure(), 60.0);
661    }
662
663    #[rstest]
664    fn test_exposure_raw() {
665        let mut level =
666            BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
667        let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
668        let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
669
670        level.add(order1);
671        level.add(order2);
672        assert_eq!(
673            level.exposure_raw(),
674            (60.0 * FIXED_SCALAR).round() as QuantityRaw
675        );
676    }
677}