1mod error;
8mod level;
9mod order;
10#[cfg(feature = "display")]
11mod view;
12
13#[cfg(test)]
14mod tests;
15
16use std::{
17 cmp::Reverse,
18 collections::{BTreeMap, HashMap},
19};
20
21pub use error::{OrderBookError, OrderBookResult};
22use fastnum::{
23 UD64, UD128,
24 decimal::{Context, RoundingMode},
25};
26use itertools::{FoldWhile, Itertools};
27pub use level::BookLevel;
28pub use order::BookOrder;
29#[cfg(feature = "display")]
30pub use view::{OrderBookView, OrderHighlight};
31
32use crate::{state::Order, types};
33
34#[derive(Clone, Debug, Default)]
40pub struct OrderBook {
41 orders: HashMap<types::OrderId, BookOrder>,
43 client_orders: HashMap<(types::AccountId, types::RequestId), types::OrderId>,
45 asks: BTreeMap<UD64, BookLevel>,
47 bids: BTreeMap<Reverse<UD64>, BookLevel>,
49}
50
51impl OrderBook {
52 pub(crate) fn new() -> Self { Self::default() }
53
54 pub fn asks(&self) -> &BTreeMap<UD64, BookLevel> { &self.asks }
58
59 pub fn bids(&self) -> &BTreeMap<Reverse<UD64>, BookLevel> { &self.bids }
61
62 pub fn best_ask(&self) -> Option<(UD64, UD64)> {
64 self.asks
65 .iter()
66 .find(|(_, lvl)| lvl.size() > UD64::ZERO)
67 .map(|(k, v)| (*k, v.size()))
68 }
69
70 pub fn best_bid(&self) -> Option<(UD64, UD64)> {
72 self.bids
73 .iter()
74 .find(|(_, lvl)| lvl.size() > UD64::ZERO)
75 .map(|(k, v)| (k.0, v.size()))
76 }
77
78 pub fn ask_impact(&self, want_size: UD64) -> Option<(UD64, UD64, UD64)> {
81 Self::impact(self.asks.iter(), want_size)
82 }
83
84 pub fn bid_impact(&self, want_size: UD64) -> Option<(UD64, UD64, UD64)> {
87 Self::impact(self.bids.iter().map(|(k, v)| (&k.0, v)), want_size)
88 }
89
90 pub fn ask_impact_notional(&self, want_notional: UD128) -> Option<(UD64, UD64, UD64, UD128)> {
95 Self::impact_notional(self.asks.iter(), want_notional)
96 }
97
98 pub fn bid_impact_notional(&self, want_notional: UD128) -> Option<(UD64, UD64, UD64, UD128)> {
103 Self::impact_notional(self.bids.iter().map(|(k, v)| (&k.0, v)), want_notional)
104 }
105
106 pub fn ask_level(&self, price: UD64) -> Option<&BookLevel> { self.asks.get(&price) }
110
111 pub fn bid_level(&self, price: UD64) -> Option<&BookLevel> { self.bids.get(&Reverse(price)) }
113
114 pub fn get_order(&self, order_id: types::OrderId) -> Option<&BookOrder> {
116 self.orders.get(&order_id)
117 }
118
119 pub fn get_order_by_client_id(
121 &self,
122 account_id: types::AccountId,
123 client_id: types::RequestId,
124 ) -> Option<&BookOrder> {
125 self.client_orders
126 .get(&(account_id, client_id))
127 .and_then(|order_id| self.orders.get(order_id))
128 }
129
130 pub fn ask_orders(&self) -> impl Iterator<Item = &BookOrder> {
132 self.asks
133 .values()
134 .flat_map(|level| self.level_orders(level))
135 }
136
137 pub fn bid_orders(&self) -> impl Iterator<Item = &BookOrder> {
139 self.bids
140 .values()
141 .flat_map(|level| self.level_orders(level))
142 }
143
144 pub fn total_orders(&self) -> usize { self.orders.len() }
146
147 pub fn all_orders(&self) -> &HashMap<types::OrderId, BookOrder> { &self.orders }
149
150 pub(crate) fn level_orders<'a>(&'a self, level: &'a BookLevel) -> LevelOrdersIter<'a> {
152 LevelOrdersIter { orders: &self.orders, current: level.head() }
153 }
154
155 #[cfg(feature = "display")]
156 pub fn view<'a>(
157 &'a self,
158 depth: Option<usize>,
159 orders_per_level: Option<usize>,
160 show_expired: bool,
161 ) -> OrderBookView<'a> {
162 OrderBookView::new(self, depth, orders_per_level, show_expired)
163 }
164
165 pub(crate) fn add_order(&mut self, order: &Order) -> OrderBookResult<()> {
176 let order_id = order.order_id();
177
178 if order.size() == UD64::ZERO {
180 return Err(OrderBookError::InvalidOrderSize { order_id, size: order.size() });
181 }
182 if order.price() == UD64::ZERO {
183 return Err(OrderBookError::InvalidOrderPrice { order_id, price: order.price() });
184 }
185
186 if let Some(existing) = self.orders.get(&order_id) {
188 return Err(OrderBookError::OrderAlreadyExists {
189 order_id,
190 existing_price: existing.price(),
191 });
192 }
193
194 let side = order.r#type().side();
196 let old_tail = self.get_or_create_level_mut(side, order.price()).tail();
197
198 let mut l3_order = BookOrder::new(*order);
200 l3_order.set_prev(old_tail);
201
202 self.orders.insert(order_id, l3_order);
204 if let Some(client_order_id) = order.client_order_id() {
205 self.client_orders
206 .insert((order.account_id(), client_order_id), order_id);
207 }
208
209 self.link_at_tail(side, order.price(), old_tail, order_id, order.size());
211
212 Ok(())
213 }
214
215 pub(crate) fn update_order(
224 &mut self,
225 order: &Order,
226 prev_order: &BookOrder,
227 ) -> OrderBookResult<()> {
228 let order_id = order.order_id();
229
230 if order.size() == UD64::ZERO {
232 return Err(OrderBookError::InvalidOrderSize { order_id, size: order.size() });
233 }
234
235 let old_size = prev_order.size();
236 let price = prev_order.price();
237 let side = prev_order.r#type().side();
238
239 self.orders
241 .get_mut(&order_id)
242 .ok_or(OrderBookError::OrderNotFound { order_id })?
243 .update_order(*order);
244
245 let level = self
247 .get_level_mut(side, price)
248 .ok_or(OrderBookError::LevelNotFound { price, side })?;
249 level.update_size(old_size, order.size());
250
251 Ok(())
252 }
253
254 pub(crate) fn remove_order(&mut self, prev_order: &BookOrder) -> OrderBookResult<Order> {
263 let order_id = prev_order.order_id();
264 let prev_id = prev_order.prev();
265 let next_id = prev_order.next();
266 let price = prev_order.price();
267 let size = prev_order.size();
268 let side = prev_order.r#type().side();
269
270 self.unlink_node(prev_id, next_id);
272
273 let level = self
275 .get_level_mut(side, price)
276 .ok_or(OrderBookError::LevelNotFound { price, side })?;
277 if level.head() == Some(order_id) {
278 level.set_head(next_id);
279 }
280 if level.tail() == Some(order_id) {
281 level.set_tail(prev_id);
282 }
283 if !prev_order.is_expired() {
284 level.sub_size(size);
286 }
287 let should_remove_level = level.is_empty();
288
289 if should_remove_level {
291 self.remove_level(side, price);
292 }
293
294 let removed = self
296 .orders
297 .remove(&order_id)
298 .ok_or(OrderBookError::OrderNotFound { order_id })?;
299 if let Some(client_order_id) = removed.client_order_id() {
300 self.client_orders
301 .remove(&(removed.account_id(), client_order_id));
302 }
303
304 Ok(*removed)
305 }
306
307 pub(crate) fn move_to_back(
314 &mut self,
315 order: &Order,
316 prev_order: &BookOrder,
317 ) -> OrderBookResult<()> {
318 let order_id = order.order_id();
319
320 let prev_id = prev_order.prev();
321 let next_id = prev_order.next();
322 let price = prev_order.price();
323 let old_size = prev_order.size();
324 let side = prev_order.r#type().side();
325
326 let is_at_tail = self
328 .get_level(side, price)
329 .ok_or(OrderBookError::LevelNotFound { price, side })?
330 .tail()
331 == Some(order_id);
332
333 if is_at_tail {
334 if let Some(l3_order) = self.orders.get_mut(&order_id) {
336 l3_order.update_order(*order);
337 }
338 let level = self
339 .get_level_mut(side, price)
340 .ok_or(OrderBookError::LevelNotFound { price, side })?;
341 if prev_order.is_expired() {
342 level.add_size(order.size());
344 } else {
345 level.update_size(old_size, order.size());
346 }
347 return Ok(());
348 }
349
350 self.unlink_node(prev_id, next_id);
352
353 let level = self
355 .get_level_mut(side, price)
356 .ok_or(OrderBookError::LevelNotFound { price, side })?;
357 if level.head() == Some(order_id) {
358 level.set_head(next_id);
359 }
360
361 let old_tail = level.tail();
363
364 if let Some(old_tail_id) = old_tail
366 && let Some(old_tail_order) = self.orders.get_mut(&old_tail_id)
367 {
368 old_tail_order.set_next(Some(order_id));
369 }
370
371 if let Some(l3_order) = self.orders.get_mut(&order_id) {
373 l3_order.set_prev(old_tail);
374 l3_order.set_next(None);
375 l3_order.update_order(*order);
376 } else {
377 return Err(OrderBookError::OrderNotFound { order_id });
378 }
379
380 let level = self
382 .get_level_mut(side, price)
383 .ok_or(OrderBookError::LevelNotFound { price, side })?;
384 level.set_tail(Some(order_id));
385 if prev_order.is_expired() {
386 level.add_size(order.size());
388 } else {
389 level.update_size(old_size, order.size());
390 }
391
392 Ok(())
393 }
394
395 pub(crate) fn add_orders_from_snapshot(&mut self, orders: &[Order]) -> OrderBookResult<()> {
405 let order_ids: std::collections::HashSet<types::OrderId> =
407 orders.iter().map(|o| o.order_id()).collect();
408
409 for order in orders {
411 let order_id = order.order_id();
412
413 if order.size() == UD64::ZERO {
414 return Err(OrderBookError::InvalidOrderSize { order_id, size: order.size() });
415 }
416 if order.price() == UD64::ZERO {
417 return Err(OrderBookError::InvalidOrderPrice { order_id, price: order.price() });
418 }
419
420 if let Some(prev_id) = order.prev_order_id()
422 && !order_ids.contains(&prev_id)
423 {
424 return Err(OrderBookError::DanglingOrderReference {
425 order_id,
426 referenced_id: prev_id,
427 pointer: "prev",
428 });
429 }
430 if let Some(next_id) = order.next_order_id()
431 && !order_ids.contains(&next_id)
432 {
433 return Err(OrderBookError::DanglingOrderReference {
434 order_id,
435 referenced_id: next_id,
436 pointer: "next",
437 });
438 }
439
440 let mut l3_order = BookOrder::new(*order);
442 l3_order.set_prev(order.prev_order_id());
443 l3_order.set_next(order.next_order_id());
444
445 self.orders.insert(order_id, l3_order);
446 }
447
448 let mut level_orders: HashMap<(UD64, types::OrderSide), Vec<types::OrderId>> =
451 HashMap::new();
452 for order in orders {
453 let key = (order.price(), order.r#type().side());
454 level_orders.entry(key).or_default().push(order.order_id());
455 }
456
457 for ((price, side), order_ids) in level_orders {
458 let head = order_ids.iter().find(|&&id| {
460 self.orders
461 .get(&id)
462 .is_some_and(|o| o.prev().is_none_or(|p| !order_ids.contains(&p)))
463 });
464
465 let tail = order_ids.iter().find(|&&id| {
467 self.orders
468 .get(&id)
469 .is_some_and(|o| o.next().is_none_or(|n| !order_ids.contains(&n)))
470 });
471
472 let mut level = BookLevel::new();
474 level.set_head(head.copied());
475 level.set_tail(tail.copied());
476 for &id in &order_ids {
477 if let Some(order) = self.orders.get(&id)
478 && !order.is_expired()
479 {
480 level.add_size(order.size());
481 }
482 }
483
484 match side {
485 types::OrderSide::Ask => {
486 self.asks.insert(price, level);
487 },
488 types::OrderSide::Bid => {
489 self.bids.insert(Reverse(price), level);
490 },
491 }
492 }
493
494 Ok(())
495 }
496
497 pub(crate) fn check_expired(&mut self, instant: types::StateInstant) {
499 let mut update_levels = vec![];
500 for order in self.orders.values_mut() {
501 if order.update_if_expired(instant) {
502 update_levels.push((order.r#type().side(), order.price(), order.size()));
503 }
504 }
505 for (side, price, size) in update_levels {
506 if let Some(level) = self.get_level_mut(side, price) {
507 level.sub_size(size);
508 }
509 }
510 }
511
512 fn get_level(&self, side: types::OrderSide, price: UD64) -> Option<&BookLevel> {
516 match side {
517 types::OrderSide::Ask => self.asks.get(&price),
518 types::OrderSide::Bid => self.bids.get(&Reverse(price)),
519 }
520 }
521
522 fn get_level_mut(&mut self, side: types::OrderSide, price: UD64) -> Option<&mut BookLevel> {
524 match side {
525 types::OrderSide::Ask => self.asks.get_mut(&price),
526 types::OrderSide::Bid => self.bids.get_mut(&Reverse(price)),
527 }
528 }
529
530 fn get_or_create_level_mut(&mut self, side: types::OrderSide, price: UD64) -> &mut BookLevel {
532 match side {
533 types::OrderSide::Ask => self.asks.entry(price).or_default(),
534 types::OrderSide::Bid => self.bids.entry(Reverse(price)).or_default(),
535 }
536 }
537
538 fn remove_level(&mut self, side: types::OrderSide, price: UD64) {
540 match side {
541 types::OrderSide::Ask => {
542 self.asks.remove(&price);
543 },
544 types::OrderSide::Bid => {
545 self.bids.remove(&Reverse(price));
546 },
547 }
548 }
549
550 #[cfg(test)]
552 pub(crate) fn force_remove_level(&mut self, side: types::OrderSide, price: UD64) {
553 self.remove_level(side, price);
554 }
555
556 fn unlink_node(&mut self, prev_id: Option<types::OrderId>, next_id: Option<types::OrderId>) {
558 if let Some(prev) = prev_id
560 && let Some(prev_order) = self.orders.get_mut(&prev)
561 {
562 prev_order.set_next(next_id);
563 }
564
565 if let Some(next) = next_id
567 && let Some(next_order) = self.orders.get_mut(&next)
568 {
569 next_order.set_prev(prev_id);
570 }
571 }
572
573 fn link_at_tail(
576 &mut self,
577 side: types::OrderSide,
578 price: UD64,
579 old_tail: Option<types::OrderId>,
580 order_id: types::OrderId,
581 size: UD64,
582 ) {
583 if let Some(old_tail_id) = old_tail
585 && let Some(old_tail_order) = self.orders.get_mut(&old_tail_id)
586 {
587 old_tail_order.set_next(Some(order_id));
588 }
589
590 let level = self.get_or_create_level_mut(side, price);
592 if level.head().is_none() {
593 level.set_head(Some(order_id));
594 }
595 level.set_tail(Some(order_id));
596 level.add_size(size);
597 }
598
599 fn impact<'a>(
602 mut side: impl Iterator<Item = (&'a UD64, &'a BookLevel)>,
603 want_size: UD64,
604 ) -> Option<(UD64, UD64, UD64)> {
605 let (price, unfilled, price_size) = side
606 .fold_while(
607 (UD64::ZERO, want_size, UD128::ZERO),
608 |(_, unfilled, price_size), (price, level)| {
609 let level_size = level.size();
610 if unfilled > level_size {
611 FoldWhile::Continue((
612 *price,
613 unfilled - level_size,
614 price_size + (price.resize() * level_size.resize()),
615 ))
616 } else {
617 FoldWhile::Done((
618 *price,
619 UD64::ZERO,
620 price_size + (price.resize() * unfilled.resize()),
621 ))
622 }
623 },
624 )
625 .into_inner();
626 let filled = want_size - unfilled;
627 if filled > UD64::ZERO {
628 Some((price, filled, (price_size / filled.resize()).resize()))
629 } else {
630 None
631 }
632 }
633
634 fn impact_notional<'a>(
644 mut side: impl Iterator<Item = (&'a UD64, &'a BookLevel)>,
645 want_notional: UD128,
646 ) -> Option<(UD64, UD64, UD64, UD128)> {
647 let (price, _, filled_size, filled_notional) = side
648 .fold_while(
649 (UD64::ZERO, want_notional, UD64::ZERO, UD128::ZERO),
650 |(_, remaining, filled_size, filled_notional), (price, level)| {
651 let level_size = level.size();
652 let level_notional = price.resize() * level_size.resize();
653 if remaining > level_notional {
654 FoldWhile::Continue((
655 *price,
656 remaining - level_notional,
657 filled_size + level_size,
658 filled_notional + level_notional,
659 ))
660 } else {
661 let partial_size: UD64 = (remaining / price.resize()).resize();
662 FoldWhile::Done((
663 *price,
664 UD128::ZERO,
665 filled_size + partial_size,
666 filled_notional + remaining,
667 ))
668 }
669 },
670 )
671 .into_inner();
672 if filled_size.is_zero() {
673 None
674 } else {
675 let ctx = Context::default().with_rounding_mode(RoundingMode::HalfUp);
676 let vwap: UD64 =
677 (filled_notional.with_ctx(ctx) / filled_size.resize().with_ctx(ctx)).resize();
678 Some((price, filled_size, vwap, filled_notional))
679 }
680 }
681}
682
683#[cfg(feature = "display")]
684impl std::fmt::Display for OrderBook {
685 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
686 self.view(None, None, true).fmt(f)
687 }
688}
689
690pub(crate) struct LevelOrdersIter<'a> {
692 orders: &'a HashMap<types::OrderId, BookOrder>,
693 current: Option<types::OrderId>,
694}
695
696impl<'a> Iterator for LevelOrdersIter<'a> {
697 type Item = &'a BookOrder;
698
699 fn next(&mut self) -> Option<Self::Item> {
700 let id = self.current?;
701 let order = self.orders.get(&id)?;
702 self.current = order.next();
703 Some(order)
704 }
705}