1use std::{cmp::Ordering, collections::BTreeMap};
19
20use nautilus_core::UnixNanos;
21use rust_decimal::Decimal;
22
23use crate::{
24 data::order::{BookOrder, OrderId},
25 orderbook::{BookIntegrityError, BookPrice},
26 types::{fixed::FIXED_SCALAR, quantity::QuantityRaw},
27};
28
29#[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 orders: BTreeMap<OrderId, BookOrder>,
41 insertion_order: Vec<OrderId>,
42}
43
44impl BookLevel {
45 #[must_use]
47 pub fn new(price: BookPrice) -> Self {
48 Self {
49 price,
50 orders: BTreeMap::new(),
51 insertion_order: Vec::new(),
52 }
53 }
54
55 #[must_use]
57 pub fn from_order(order: BookOrder) -> Self {
58 let mut level = Self {
59 price: order.to_book_price(),
60 orders: BTreeMap::new(),
61 insertion_order: Vec::new(),
62 };
63 level.add(order);
64 level
65 }
66
67 #[must_use]
69 pub fn len(&self) -> usize {
70 self.orders.len()
71 }
72
73 #[must_use]
75 pub fn is_empty(&self) -> bool {
76 self.orders.is_empty()
77 }
78
79 #[must_use]
81 pub fn first(&self) -> Option<&BookOrder> {
82 self.insertion_order
83 .first()
84 .and_then(|&id| self.orders.get(&id))
85 }
86
87 #[must_use]
89 pub fn get_orders(&self) -> Vec<BookOrder> {
90 self.insertion_order
91 .iter()
92 .filter_map(|id| self.orders.get(id))
93 .copied()
94 .collect()
95 }
96
97 #[must_use]
99 pub fn size(&self) -> f64 {
100 self.orders.values().map(|o| o.size.as_f64()).sum()
101 }
102
103 #[must_use]
105 pub fn size_raw(&self) -> QuantityRaw {
106 self.orders.values().map(|o| o.size.raw).sum()
107 }
108
109 #[must_use]
111 pub fn size_decimal(&self) -> Decimal {
112 self.orders.values().map(|o| o.size.as_decimal()).sum()
113 }
114
115 #[must_use]
117 pub fn exposure(&self) -> f64 {
118 self.orders
119 .values()
120 .map(|o| o.price.as_f64() * o.size.as_f64())
121 .sum()
122 }
123
124 #[must_use]
126 pub fn exposure_raw(&self) -> QuantityRaw {
127 self.orders
128 .values()
129 .map(|o| ((o.price.as_f64() * o.size.as_f64()) * FIXED_SCALAR) as QuantityRaw)
130 .sum()
131 }
132
133 pub fn add_bulk(&mut self, orders: Vec<BookOrder>) {
135 self.insertion_order
136 .extend(orders.iter().map(|o| o.order_id));
137
138 for order in orders {
139 self.check_order_for_this_level(&order);
140 self.orders.insert(order.order_id, order);
141 }
142 }
143
144 pub fn add(&mut self, order: BookOrder) {
146 self.check_order_for_this_level(&order);
147
148 self.orders.insert(order.order_id, order);
149 self.insertion_order.push(order.order_id);
150 }
151
152 pub fn update(&mut self, order: BookOrder) {
155 self.check_order_for_this_level(&order);
156
157 if order.size.raw == 0 {
158 self.orders.remove(&order.order_id);
159 self.update_insertion_order();
160 } else {
161 self.orders.insert(order.order_id, order);
162 }
163 }
164
165 pub fn delete(&mut self, order: &BookOrder) {
167 self.orders.remove(&order.order_id);
168 self.update_insertion_order();
169 }
170
171 pub fn remove_by_id(&mut self, order_id: OrderId, sequence: u64, ts_event: UnixNanos) {
173 assert!(
174 self.orders.remove(&order_id).is_some(),
175 "{}",
176 &BookIntegrityError::OrderNotFound(order_id, sequence, ts_event)
177 );
178 self.update_insertion_order();
179 }
180
181 fn check_order_for_this_level(&self, order: &BookOrder) {
182 assert_eq!(order.price, self.price.value);
183 }
184
185 fn update_insertion_order(&mut self) {
186 if self
187 .insertion_order
188 .iter()
189 .any(|id| !self.orders.contains_key(id))
190 {
191 self.insertion_order
192 .retain(|&id| self.orders.contains_key(&id));
193 }
194 }
195}
196
197impl PartialEq for BookLevel {
198 fn eq(&self, other: &Self) -> bool {
199 self.price == other.price
200 }
201}
202
203impl PartialOrd for BookLevel {
204 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
205 Some(self.cmp(other))
206 }
207
208 fn lt(&self, other: &Self) -> bool {
209 self.price.lt(&other.price)
210 }
211
212 fn le(&self, other: &Self) -> bool {
213 self.price.le(&other.price)
214 }
215
216 fn gt(&self, other: &Self) -> bool {
217 self.price.gt(&other.price)
218 }
219
220 fn ge(&self, other: &Self) -> bool {
221 self.price.ge(&other.price)
222 }
223}
224
225impl Ord for BookLevel {
226 fn cmp(&self, other: &Self) -> Ordering {
227 self.price.cmp(&other.price)
228 }
229}
230
231#[cfg(test)]
235mod tests {
236 use rstest::rstest;
237 use rust_decimal_macros::dec;
238
239 use crate::{
240 data::order::BookOrder,
241 enums::OrderSide,
242 orderbook::{BookLevel, BookPrice},
243 types::{fixed::FIXED_SCALAR, quantity::QuantityRaw, Price, Quantity},
244 };
245
246 #[rstest]
247 fn test_empty_level() {
248 let level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
249 assert!(level.first().is_none());
250 }
251
252 #[rstest]
253 fn test_level_from_order() {
254 let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
255 let level = BookLevel::from_order(order);
256
257 assert_eq!(level.price.value, Price::from("1.00"));
258 assert_eq!(level.price.side, OrderSide::Buy);
259 assert_eq!(level.len(), 1);
260 assert_eq!(level.first().unwrap(), &order);
261 assert_eq!(level.size(), 10.0);
262 }
263
264 #[rstest]
265 #[should_panic(expected = "assertion `left == right` failed")]
266 fn test_add_order_incorrect_price_level() {
267 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
268 let incorrect_price_order =
269 BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 1);
270 level.add(incorrect_price_order);
271 }
272
273 #[rstest]
274 #[should_panic(expected = "assertion `left == right` failed")]
275 fn test_add_bulk_orders_incorrect_price() {
276 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
277 let orders = vec![
278 BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1),
279 BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 2), ];
281 level.add_bulk(orders);
282 }
283
284 #[rstest]
285 fn test_comparisons_bid_side() {
286 let level0 = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
287 let level1 = BookLevel::new(BookPrice::new(Price::from("1.01"), OrderSide::Buy));
288 assert_eq!(level0, level0);
289 assert!(level0 > level1);
290 }
291
292 #[rstest]
293 fn test_comparisons_ask_side() {
294 let level0 = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Sell));
295 let level1 = BookLevel::new(BookPrice::new(Price::from("1.01"), OrderSide::Sell));
296 assert_eq!(level0, level0);
297 assert!(level0 < level1);
298 }
299
300 #[rstest]
301 fn test_add_single_order() {
302 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
303 let order = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
304
305 level.add(order);
306 assert!(!level.is_empty());
307 assert_eq!(level.len(), 1);
308 assert_eq!(level.size(), 10.0);
309 assert_eq!(level.first().unwrap(), &order);
310 }
311
312 #[rstest]
313 fn test_add_multiple_orders() {
314 let mut level = BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSide::Buy));
315 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
316 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
317
318 level.add(order1);
319 level.add(order2);
320 assert_eq!(level.len(), 2);
321 assert_eq!(level.size(), 30.0);
322 assert_eq!(level.exposure(), 60.0);
323 assert_eq!(level.first().unwrap(), &order1);
324 }
325
326 #[rstest]
327 fn test_get_orders() {
328 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
329 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
330 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
331
332 level.add(order1);
333 level.add(order2);
334
335 let orders = level.get_orders();
336 assert_eq!(orders.len(), 2);
337 assert_eq!(orders[0], order1); assert_eq!(orders[1], order2);
339 }
340
341 #[rstest]
342 fn test_update_order() {
343 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
344 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
345 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 0);
346
347 level.add(order1);
348 level.update(order2);
349 assert_eq!(level.len(), 1);
350 assert_eq!(level.size(), 20.0);
351 assert_eq!(level.exposure(), 20.0);
352 }
353
354 #[rstest]
355 fn test_fifo_order_after_updates() {
356 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
357
358 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
359 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
360
361 level.add(order1);
362 level.add(order2);
363
364 let updated_order1 =
366 BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(15), 1);
367 level.update(updated_order1);
368
369 let orders = level.get_orders();
370 assert_eq!(orders.len(), 2);
371 assert_eq!(orders[0], updated_order1); assert_eq!(orders[1], order2); }
374
375 #[rstest]
376 #[should_panic(expected = "assertion `left == right` failed")]
377 fn test_update_order_incorrect_price() {
378 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
379
380 let initial_order =
382 BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
383 level.add(initial_order);
384
385 let updated_order =
387 BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
388 level.update(updated_order);
389 }
390
391 #[rstest]
392 fn test_update_order_with_zero_size() {
393 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
394 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
395 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::zero(0), 0);
396
397 level.add(order1);
398 level.update(order2);
399 assert_eq!(level.len(), 0);
400 assert_eq!(level.size(), 0.0);
401 assert_eq!(level.exposure(), 0.0);
402 }
403
404 #[rstest]
405 fn test_update_insertion_order_optimization() {
406 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
407
408 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 1);
410 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(20), 2);
411 level.add(order1);
412 level.add(order2);
413
414 let initial_insertion_order = level.insertion_order.clone();
416 level.update_insertion_order();
417 assert_eq!(level.insertion_order, initial_insertion_order);
418
419 level.orders.remove(&1);
421 level.update_insertion_order();
422 assert_eq!(level.insertion_order, vec![2]);
423 }
424
425 #[rstest]
426 fn test_delete_order() {
427 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
428 let order1_id = 0;
429 let order1 = BookOrder::new(
430 OrderSide::Buy,
431 Price::from("1.00"),
432 Quantity::from(10),
433 order1_id,
434 );
435 let order2_id = 1;
436 let order2 = BookOrder::new(
437 OrderSide::Buy,
438 Price::from("1.00"),
439 Quantity::from(20),
440 order2_id,
441 );
442
443 level.add(order1);
444 level.add(order2);
445 level.delete(&order1);
446 assert_eq!(level.len(), 1);
447 assert_eq!(level.size(), 20.0);
448 assert!(level.orders.contains_key(&order2_id));
449 assert_eq!(level.exposure(), 20.0);
450 }
451
452 #[rstest]
453 fn test_remove_order_by_id() {
454 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
455 let order1_id = 0;
456 let order1 = BookOrder::new(
457 OrderSide::Buy,
458 Price::from("1.00"),
459 Quantity::from(10),
460 order1_id,
461 );
462 let order2_id = 1;
463 let order2 = BookOrder::new(
464 OrderSide::Buy,
465 Price::from("1.00"),
466 Quantity::from(20),
467 order2_id,
468 );
469
470 level.add(order1);
471 level.add(order2);
472 level.remove_by_id(order2_id, 0, 0.into());
473 assert_eq!(level.len(), 1);
474 assert!(level.orders.contains_key(&order1_id));
475 assert_eq!(level.size(), 10.0);
476 assert_eq!(level.exposure(), 10.0);
477 }
478
479 #[rstest]
480 fn test_add_bulk_orders() {
481 let mut level = BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSide::Buy));
482 let order1_id = 0;
483 let order1 = BookOrder::new(
484 OrderSide::Buy,
485 Price::from("2.00"),
486 Quantity::from(10),
487 order1_id,
488 );
489 let order2_id = 1;
490 let order2 = BookOrder::new(
491 OrderSide::Buy,
492 Price::from("2.00"),
493 Quantity::from(20),
494 order2_id,
495 );
496
497 let orders = vec![order1, order2];
498 level.add_bulk(orders);
499 assert_eq!(level.len(), 2);
500 assert_eq!(level.size(), 30.0);
501 assert_eq!(level.exposure(), 60.0);
502 }
503
504 #[rstest]
505 fn test_maximum_order_id() {
506 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
507
508 let order = BookOrder::new(
509 OrderSide::Buy,
510 Price::from("1.00"),
511 Quantity::from(10),
512 u64::MAX,
513 );
514 level.add(order);
515
516 assert_eq!(level.len(), 1);
517 assert_eq!(level.first().unwrap(), &order);
518 }
519
520 #[rstest]
521 #[should_panic(
522 expected = "Integrity error: order not found: order_id=1, sequence=2, ts_event=3"
523 )]
524 fn test_remove_nonexistent_order() {
525 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
526 level.remove_by_id(1, 2, 3.into());
527 }
528
529 #[rstest]
530 fn test_size() {
531 let mut level = BookLevel::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
532 let order1 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(10), 0);
533 let order2 = BookOrder::new(OrderSide::Buy, Price::from("1.00"), Quantity::from(15), 1);
534
535 level.add(order1);
536 level.add(order2);
537 assert_eq!(level.size(), 25.0);
538 }
539
540 #[rstest]
541 fn test_size_raw() {
542 let mut level = BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSide::Buy));
543 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
544 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
545
546 level.add(order1);
547 level.add(order2);
548 assert_eq!(
549 level.size_raw(),
550 (30.0 * FIXED_SCALAR).round() as QuantityRaw
551 );
552 }
553
554 #[rstest]
555 fn test_size_decimal() {
556 let mut level = BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSide::Buy));
557 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
558 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
559
560 level.add(order1);
561 level.add(order2);
562 assert_eq!(level.size_decimal(), dec!(30.0));
563 }
564
565 #[rstest]
566 fn test_exposure() {
567 let mut level = BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSide::Buy));
568 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
569 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
570
571 level.add(order1);
572 level.add(order2);
573 assert_eq!(level.exposure(), 60.0);
574 }
575
576 #[rstest]
577 fn test_exposure_raw() {
578 let mut level = BookLevel::new(BookPrice::new(Price::from("2.00"), OrderSide::Buy));
579 let order1 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(10), 0);
580 let order2 = BookOrder::new(OrderSide::Buy, Price::from("2.00"), Quantity::from(20), 1);
581
582 level.add(order1);
583 level.add(order2);
584 assert_eq!(
585 level.exposure_raw(),
586 (60.0 * FIXED_SCALAR).round() as QuantityRaw
587 );
588 }
589}