1use 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#[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 #[must_use]
47 pub fn new(price: BookPrice) -> Self {
48 Self {
49 price,
50 orders: IndexMap::new(),
51 }
52 }
53
54 #[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 #[must_use]
71 pub fn len(&self) -> usize {
72 self.orders.len()
73 }
74
75 #[must_use]
77 pub fn is_empty(&self) -> bool {
78 self.orders.is_empty()
79 }
80
81 #[inline]
83 #[must_use]
84 pub fn first(&self) -> Option<&BookOrder> {
85 self.orders.get_index(0).map(|(_key, order)| order)
86 }
87
88 pub fn iter(&self) -> impl Iterator<Item = &BookOrder> {
90 self.orders.values()
91 }
92
93 #[must_use]
95 pub fn get_orders(&self) -> Vec<BookOrder> {
96 self.orders.values().copied().collect()
97 }
98
99 #[must_use]
101 pub fn size(&self) -> f64 {
102 self.orders.values().map(|o| o.size.as_f64()).sum()
103 }
104
105 #[must_use]
107 pub fn size_raw(&self) -> QuantityRaw {
108 self.orders.values().map(|o| o.size.raw).sum()
109 }
110
111 #[must_use]
113 pub fn size_decimal(&self) -> Decimal {
114 self.orders.values().map(|o| o.size.as_decimal()).sum()
115 }
116
117 #[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 #[must_use]
128 pub fn exposure_raw(&self) -> QuantityRaw {
129 self.orders
130 .values()
131 .map(|o| ((o.price.as_f64() * o.size.as_f64()) * FIXED_SCALAR) as QuantityRaw)
132 .sum()
133 }
134
135 pub fn add_bulk(&mut self, orders: Vec<BookOrder>) {
137 for order in orders {
138 self.add(order);
139 }
140 }
141
142 pub fn add(&mut self, order: BookOrder) {
144 debug_assert_eq!(order.price, self.price.value);
145
146 self.orders.insert(order.order_id, order);
147 }
148
149 pub fn update(&mut self, order: BookOrder) {
152 debug_assert_eq!(order.price, self.price.value);
153
154 if order.size.raw == 0 {
155 self.orders.shift_remove(&order.order_id);
156 } else {
157 self.orders.insert(order.order_id, order);
158 }
159 }
160
161 pub fn delete(&mut self, order: &BookOrder) {
163 self.orders.shift_remove(&order.order_id);
164 }
165
166 pub fn remove_by_id(&mut self, order_id: OrderId, sequence: u64, ts_event: UnixNanos) {
172 assert!(
173 self.orders.shift_remove(&order_id).is_some(),
174 "{}",
175 &BookIntegrityError::OrderNotFound(order_id, sequence, ts_event)
176 );
177 }
178}
179
180impl PartialEq for BookLevel {
181 fn eq(&self, other: &Self) -> bool {
182 self.price == other.price
183 }
184}
185
186impl PartialOrd for BookLevel {
187 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
188 Some(self.cmp(other))
189 }
190}
191
192impl Ord for BookLevel {
193 fn cmp(&self, other: &Self) -> Ordering {
194 self.price.cmp(&other.price)
195 }
196}
197
198#[cfg(test)]
202mod tests {
203 use rstest::rstest;
204 use rust_decimal_macros::dec;
205
206 use crate::{
207 data::order::BookOrder,
208 enums::{OrderSide, OrderSideSpecified},
209 orderbook::{BookLevel, BookPrice},
210 types::{Price, Quantity, fixed::FIXED_SCALAR, quantity::QuantityRaw},
211 };
212
213 #[rstest]
214 fn test_empty_level() {
215 let level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
216 assert!(level.first().is_none());
217 assert_eq!(level.side(), OrderSideSpecified::Buy);
218 }
219
220 #[rstest]
221 fn test_level_from_order() {
222 let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
223 let level = BookLevel::from_order(order);
224
225 assert_eq!(level.price.value, Price::from("1.00"));
226 assert_eq!(level.price.side, OrderSideSpecified::Buy);
227 assert_eq!(level.len(), 1);
228 assert_eq!(level.first().unwrap(), &order);
229 assert_eq!(level.size(), 10.0);
230 }
231
232 #[rstest]
233 #[should_panic(expected = "assertion `left == right` failed")]
234 fn test_add_order_incorrect_price_level() {
235 let mut level =
236 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
237 let incorrect_price_order =
238 BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 1);
239 level.add(incorrect_price_order);
240 }
241
242 #[rstest]
243 #[should_panic(expected = "assertion `left == right` failed")]
244 fn test_add_bulk_orders_incorrect_price() {
245 let mut level =
246 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
247 let orders = vec![
248 BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1),
249 BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 2), ];
251 level.add_bulk(orders);
252 }
253
254 #[rstest]
255 fn test_add_bulk_empty() {
256 let mut level =
257 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
258 level.add_bulk(vec![]);
259 assert!(level.is_empty());
260 }
261
262 #[rstest]
263 fn test_comparisons_bid_side() {
264 let level0 = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
265 let level1 = BookLevel::new(BookPrice::new(Price::from("1.01"), OrderSideSpecified::Buy));
266 assert_eq!(level0, level0);
267 assert!(level0 > level1);
268 }
269
270 #[rstest]
271 fn test_comparisons_ask_side() {
272 let level0 = BookLevel::new(BookPrice::new(
273 Price::from("1.00"),
274 OrderSideSpecified::Sell,
275 ));
276 let level1 = BookLevel::new(BookPrice::new(
277 Price::from("1.01"),
278 OrderSideSpecified::Sell,
279 ));
280 assert_eq!(level0, level0);
281 assert!(level0 < level1);
282 }
283
284 #[rstest]
285 fn test_book_level_sorting() {
286 let mut levels = vec![
287 BookLevel::new(BookPrice::new(
288 Price::from("1.00"),
289 OrderSideSpecified::Sell,
290 )),
291 BookLevel::new(BookPrice::new(
292 Price::from("1.02"),
293 OrderSideSpecified::Sell,
294 )),
295 BookLevel::new(BookPrice::new(
296 Price::from("1.01"),
297 OrderSideSpecified::Sell,
298 )),
299 ];
300 levels.sort();
301 assert_eq!(levels[0].price.value, Price::from("1.00"));
302 assert_eq!(levels[1].price.value, Price::from("1.01"));
303 assert_eq!(levels[2].price.value, Price::from("1.02"));
304 }
305
306 #[rstest]
307 fn test_add_single_order() {
308 let mut level =
309 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
310 let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
311
312 level.add(order);
313 assert!(!level.is_empty());
314 assert_eq!(level.len(), 1);
315 assert_eq!(level.size(), 10.0);
316 assert_eq!(level.first().unwrap(), &order);
317 }
318
319 #[rstest]
320 fn test_add_multiple_orders() {
321 let mut level =
322 BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
323 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
324 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
325
326 level.add(order1);
327 level.add(order2);
328 assert_eq!(level.len(), 2);
329 assert_eq!(level.size(), 30.0);
330 assert_eq!(level.exposure(), 60.0);
331 assert_eq!(level.first().unwrap(), &order1);
332 }
333
334 #[rstest]
335 fn test_get_orders() {
336 let mut level =
337 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
338 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
339 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
340
341 level.add(order1);
342 level.add(order2);
343
344 let orders = level.get_orders();
345 assert_eq!(orders.len(), 2);
346 assert_eq!(orders[0], order1); assert_eq!(orders[1], order2);
348 }
349
350 #[rstest]
351 fn test_iter_returns_fifo() {
352 let mut level =
353 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
354 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
355 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
356 level.add(order1);
357 level.add(order2);
358
359 let orders: Vec<_> = level.iter().copied().collect();
360 assert_eq!(orders, vec![order1, order2]);
361 }
362
363 #[rstest]
364 fn test_update_order() {
365 let mut level =
366 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
367 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
368 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 0);
369
370 level.add(order1);
371 level.update(order2);
372 assert_eq!(level.len(), 1);
373 assert_eq!(level.size(), 20.0);
374 assert_eq!(level.exposure(), 20.0);
375 }
376
377 #[rstest]
378 fn test_update_inserts_if_missing() {
379 let mut level =
380 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
381 let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
382 level.update(order);
383 assert_eq!(level.len(), 1);
384 assert_eq!(level.first().unwrap(), &order);
385 }
386
387 #[rstest]
388 fn test_update_zero_size_nonexistent() {
389 let mut level =
390 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
391 let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::zero(0), 1);
392 level.update(order);
393 assert_eq!(level.len(), 0);
394 }
395
396 #[rstest]
397 fn test_fifo_order_after_updates() {
398 let mut level =
399 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
400
401 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
402 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
403
404 level.add(order1);
405 level.add(order2);
406
407 let updated_order1 =
409 BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(15), 1);
410 level.update(updated_order1);
411
412 let orders = level.get_orders();
413 assert_eq!(orders.len(), 2);
414 assert_eq!(orders[0], updated_order1); assert_eq!(orders[1], order2); }
417
418 #[rstest]
419 fn test_insertion_order_after_mixed_operations() {
420 let mut level =
421 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
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 let order3 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(30), 3);
425
426 level.add(order1);
427 level.add(order2);
428 level.add(order3);
429
430 let updated_order2 =
432 BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(25), 2);
433 level.update(updated_order2);
434
435 level.delete(&order1);
437
438 let orders = level.get_orders();
439 assert_eq!(orders, vec![updated_order2, order3]);
440 }
441
442 #[rstest]
443 #[should_panic(expected = "assertion `left == right` failed")]
444 fn test_update_order_incorrect_price() {
445 let mut level =
446 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
447
448 let initial_order =
450 BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
451 level.add(initial_order);
452
453 let updated_order =
455 BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
456 level.update(updated_order);
457 }
458
459 #[rstest]
460 fn test_update_order_with_zero_size() {
461 let mut level =
462 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
463 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
464 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::zero(0), 0);
465
466 level.add(order1);
467 level.update(order2);
468 assert_eq!(level.len(), 0);
469 assert_eq!(level.size(), 0.0);
470 assert_eq!(level.exposure(), 0.0);
471 }
472
473 #[rstest]
474 fn test_delete_nonexistent_order() {
475 let mut level =
476 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
477 let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
478 level.delete(&order);
479 assert_eq!(level.len(), 0);
480 }
481
482 #[rstest]
483 fn test_delete_order() {
484 let mut level =
485 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
486 let order1_id = 0;
487 let order1 = BookOrder::new(
488 OrderSide::Buy,
489 Price::from("1.00"),
490 Quantity::from(10),
491 order1_id,
492 );
493 let order2_id = 1;
494 let order2 = BookOrder::new(
495 OrderSide::Buy,
496 Price::from("1.00"),
497 Quantity::from(20),
498 order2_id,
499 );
500
501 level.add(order1);
502 level.add(order2);
503 level.delete(&order1);
504 assert_eq!(level.len(), 1);
505 assert_eq!(level.size(), 20.0);
506 assert!(level.orders.contains_key(&order2_id));
507 assert_eq!(level.exposure(), 20.0);
508 }
509
510 #[rstest]
511 fn test_remove_order_by_id() {
512 let mut level =
513 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
514 let order1_id = 0;
515 let order1 = BookOrder::new(
516 OrderSide::Buy,
517 Price::from("1.00"),
518 Quantity::from(10),
519 order1_id,
520 );
521 let order2_id = 1;
522 let order2 = BookOrder::new(
523 OrderSide::Buy,
524 Price::from("1.00"),
525 Quantity::from(20),
526 order2_id,
527 );
528
529 level.add(order1);
530 level.add(order2);
531 level.remove_by_id(order2_id, 0, 0.into());
532 assert_eq!(level.len(), 1);
533 assert!(level.orders.contains_key(&order1_id));
534 assert_eq!(level.size(), 10.0);
535 assert_eq!(level.exposure(), 10.0);
536 }
537
538 #[rstest]
539 fn test_add_bulk_orders() {
540 let mut level =
541 BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
542 let order1_id = 0;
543 let order1 = BookOrder::new(
544 OrderSide::Buy,
545 Price::from("2.00"),
546 Quantity::from(10),
547 order1_id,
548 );
549 let order2_id = 1;
550 let order2 = BookOrder::new(
551 OrderSide::Buy,
552 Price::from("2.00"),
553 Quantity::from(20),
554 order2_id,
555 );
556
557 let orders = vec![order1, order2];
558 level.add_bulk(orders);
559 assert_eq!(level.len(), 2);
560 assert_eq!(level.size(), 30.0);
561 assert_eq!(level.exposure(), 60.0);
562 }
563
564 #[rstest]
565 fn test_maximum_order_id() {
566 let mut level =
567 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
568
569 let order = BookOrder::new(
570 OrderSide::Buy,
571 Price::from("1.00"),
572 Quantity::from(10),
573 u64::MAX,
574 );
575 level.add(order);
576
577 assert_eq!(level.len(), 1);
578 assert_eq!(level.first().unwrap(), &order);
579 }
580
581 #[rstest]
582 #[should_panic(
583 expected = "Integrity error: order not found: order_id=1, sequence=2, ts_event=3"
584 )]
585 fn test_remove_nonexistent_order() {
586 let mut level =
587 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
588 level.remove_by_id(1, 2, 3.into());
589 }
590
591 #[rstest]
592 fn test_size() {
593 let mut level =
594 BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSideSpecified::Buy));
595 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
596 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(15), 1);
597
598 level.add(order1);
599 level.add(order2);
600 assert_eq!(level.size(), 25.0);
601 }
602
603 #[rstest]
604 fn test_size_raw() {
605 let mut level =
606 BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
607 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
608 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
609
610 level.add(order1);
611 level.add(order2);
612 assert_eq!(
613 level.size_raw(),
614 (30.0 * FIXED_SCALAR).round() as QuantityRaw
615 );
616 }
617
618 #[rstest]
619 fn test_size_decimal() {
620 let mut level =
621 BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
622 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
623 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
624
625 level.add(order1);
626 level.add(order2);
627 assert_eq!(level.size_decimal(), dec!(30.0));
628 }
629
630 #[rstest]
631 fn test_exposure() {
632 let mut level =
633 BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
634 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
635 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
636
637 level.add(order1);
638 level.add(order2);
639 assert_eq!(level.exposure(), 60.0);
640 }
641
642 #[rstest]
643 fn test_exposure_raw() {
644 let mut level =
645 BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSideSpecified::Buy));
646 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
647 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
648
649 level.add(order1);
650 level.add(order2);
651 assert_eq!(
652 level.exposure_raw(),
653 (60.0 * FIXED_SCALAR).round() as QuantityRaw
654 );
655 }
656}