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