1use std::collections::{BTreeMap, HashMap};
17use std::fmt;
18use std::ops::{Add, Div, Sub};
19
20use crate::enums::{JournalOp, OrderOptions};
21use crate::journal::Snapshot;
22use crate::order::{OrderId, Price, Quantity};
23use crate::report::ExecutionReportParams;
24use crate::utils::{current_timestamp_millis, safe_add};
25use crate::{
26 error::{make_error, ErrorType, Result},
27 journal::JournalLog,
28 order::{LimitOrder, LimitOrderOptions, MarketOrder, MarketOrderOptions},
29 {OrderStatus, OrderType, Side, TimeInForce},
30};
31use crate::{ExecutionReport, FillReport};
32use std::collections::VecDeque;
33
34#[derive(Debug, Clone, Default)]
44pub struct OrderBookOptions {
45 pub journaling: bool,
46 pub snapshot: Option<Snapshot>,
47 pub replay_logs: Option<Vec<JournalLog>>,
48}
49
50#[derive(Debug, PartialEq)]
51pub struct Depth {
52 pub asks: Vec<(Price, Quantity)>, pub bids: Vec<(Price, Quantity)>, }
55
56pub struct OrderBook {
62 pub(crate) last_op: u64,
63 pub(crate) symbol: String,
64 pub(crate) next_order_id: OrderId,
65 pub(crate) orders: HashMap<OrderId, LimitOrder>,
66 pub(crate) asks: BTreeMap<Price, VecDeque<OrderId>>,
67 pub(crate) bids: BTreeMap<Price, VecDeque<OrderId>>,
68 pub(crate) journaling: bool,
69}
70
71impl OrderBook {
72 pub fn new(symbol: &str, opts: OrderBookOptions) -> Self {
86 Self {
87 symbol: symbol.to_string(),
88 last_op: 0,
89 next_order_id: OrderId(0),
90 orders: HashMap::with_capacity(100_000),
91 asks: BTreeMap::new(),
92 bids: BTreeMap::new(),
93 journaling: opts.journaling,
94 }
95 }
96
97 pub fn symbol(&self) -> &str {
99 &self.symbol
100 }
101
102 pub fn market(&mut self, options: MarketOrderOptions) -> Result<ExecutionReport> {
116 self.validate_market_order(&options)?;
117
118 let mut order = MarketOrder::new(self.new_order_id(), options);
119 let mut report = ExecutionReport::new(ExecutionReportParams {
120 id: order.id,
121 order_type: OrderType::Market,
122 side: order.side,
123 quantity: order.remaining_qty(),
124 status: order.status,
125 time_in_force: None,
126 price: None,
127 post_only: false,
128 });
129
130 let mut fills = Vec::new();
131 let remaining_qty = match order.side {
132 Side::Buy => self.match_with_asks(order.remaining_qty(), &mut fills, None),
133 Side::Sell => self.match_with_bids(order.remaining_qty(), &mut fills, None),
134 };
135 order.executed_qty = order.orig_qty.sub(remaining_qty);
136 order.status = if order.remaining_qty().value() > 0 {
137 OrderStatus::PartiallyFilled
138 } else {
139 OrderStatus::Filled
140 };
141
142 report.remaining_qty = order.remaining_qty();
143 report.executed_qty = order.executed_qty;
144 report.status = order.status;
145 report.taker_qty = order.executed_qty;
146
147 if self.journaling {
148 self.last_op = safe_add(self.last_op, 1);
149 report.log = Some(JournalLog {
150 op_id: self.last_op,
151 ts: current_timestamp_millis(),
152 op: JournalOp::Market,
153 o: OrderOptions::Market(options),
154 })
155 }
156
157 Ok(report)
158 }
159 pub fn market_raw(&mut self, side: Side, quantity: u64) -> Result<ExecutionReport> {
160 self.market(MarketOrderOptions { side, quantity: Quantity(quantity) })
161 }
162
163 pub fn limit(&mut self, options: LimitOrderOptions) -> Result<ExecutionReport> {
177 self.validate_limit_order(&options)?;
178
179 let mut order = LimitOrder::new(self.new_order_id(), options);
180 let mut report = ExecutionReport::new(ExecutionReportParams {
181 id: order.id,
182 order_type: OrderType::Limit,
183 side: order.side,
184 quantity: order.orig_qty,
185 status: order.status,
186 time_in_force: Some(order.time_in_force),
187 price: Some(order.price), post_only: order.post_only,
189 });
190
191 let mut fills = Vec::new();
192 let remaining_qty = match order.side {
193 Side::Buy => self.match_with_asks(order.remaining_qty(), &mut fills, Some(order.price)),
194 Side::Sell => {
195 self.match_with_bids(order.remaining_qty(), &mut fills, Some(order.price))
196 }
197 };
198 order.executed_qty = order.orig_qty.sub(remaining_qty);
199 order.taker_qty = order.orig_qty.sub(order.remaining_qty());
200 order.maker_qty = order.remaining_qty();
201
202 if order.remaining_qty().value() > 0 {
203 if order.time_in_force == TimeInForce::IOC {
204 order.status = OrderStatus::Canceled;
207 } else {
208 order.status = OrderStatus::PartiallyFilled;
209 self.orders.insert(order.id, order);
210 if order.side == Side::Buy {
211 self.bids.entry(order.price).or_default().push_back(order.id);
212 } else {
213 self.asks.entry(order.price).or_default().push_back(order.id);
214 }
215 }
216 } else {
217 order.status = OrderStatus::Filled;
218 }
219
220 report.remaining_qty = order.remaining_qty();
221 report.executed_qty = order.executed_qty;
222 report.taker_qty = order.taker_qty;
223 report.maker_qty = order.maker_qty;
224 report.status = order.status;
225
226 if self.journaling {
227 self.last_op = safe_add(self.last_op, 1);
228 report.log = Some(JournalLog {
229 op_id: self.last_op,
230 ts: current_timestamp_millis(),
231 op: JournalOp::Limit,
232 o: OrderOptions::Limit(options),
233 })
234 }
235
236 Ok(report)
237 }
238 pub fn limit_raw(
239 &mut self,
240 side: Side,
241 quantity: u64,
242 price: u64,
243 time_in_force: Option<TimeInForce>,
244 post_only: Option<bool>,
245 ) -> Result<ExecutionReport> {
246 self.limit(LimitOrderOptions {
247 side,
248 quantity: Quantity(quantity),
249 price: Price(price),
250 time_in_force,
251 post_only,
252 })
253 }
254
255 pub fn cancel(&mut self, id: OrderId) -> Result<ExecutionReport> {
266 let mut order = match self.orders.remove(&id) {
267 Some(o) => o,
268 None => return Err(make_error(ErrorType::OrderNotFound)),
269 };
270
271 let book_side = match order.side {
272 Side::Buy => &mut self.bids,
273 Side::Sell => &mut self.asks,
274 };
275
276 if let Some(queue) = book_side.get_mut(&order.price) {
277 if let Some(pos) = queue.iter().position(|x| *x == id) {
278 queue.remove(pos);
279 }
280 if queue.is_empty() {
281 book_side.remove(&order.price);
282 }
283 }
284
285 order.status = OrderStatus::Canceled;
286
287 let mut report = ExecutionReport {
288 order_id: order.id,
289 orig_qty: order.orig_qty,
290 executed_qty: order.executed_qty,
291 remaining_qty: order.remaining_qty(),
292 taker_qty: order.taker_qty,
293 maker_qty: order.maker_qty,
294 order_type: order.order_type,
295 side: order.side,
296 price: order.price,
297 status: order.status,
298 time_in_force: order.time_in_force,
299 post_only: order.post_only,
300 fills: Vec::new(),
301 log: None,
302 };
303
304 if self.journaling {
305 self.last_op = safe_add(self.last_op, 1);
306 report.log = Some(JournalLog {
307 op_id: self.last_op,
308 ts: current_timestamp_millis(),
309 op: JournalOp::Cancel,
310 o: OrderOptions::Cancel(order.id),
311 })
312 }
313
314 Ok(report)
315 }
316
317 pub fn cancel_raw(&mut self, id: u64) -> Result<ExecutionReport> {
318 self.cancel(OrderId(id))
319 }
320
321 pub fn modify(
342 &mut self,
343 id: OrderId,
344 price: Option<Price>,
345 quantity: Option<Quantity>,
346 ) -> Result<ExecutionReport> {
347 let old_journaling = self.journaling;
348 self.journaling = false;
350 let report = match self.cancel(id) {
351 Ok(o) => o,
352 Err(e) => {
353 self.journaling = old_journaling;
355 return Err(e);
356 }
357 };
358
359 let mut report = match (price, quantity) {
360 (None, Some(quantity)) => self.limit(LimitOrderOptions {
361 side: report.side,
362 quantity,
363 price: report.price,
364 time_in_force: Some(report.time_in_force),
365 post_only: Some(report.post_only),
366 }),
367 (Some(price), None) => self.limit(LimitOrderOptions {
368 side: report.side,
369 quantity: report.remaining_qty,
370 price,
371 time_in_force: Some(report.time_in_force),
372 post_only: Some(report.post_only),
373 }),
374 (Some(price), Some(quantity)) => self.limit(LimitOrderOptions {
375 side: report.side,
376 quantity,
377 price,
378 time_in_force: Some(report.time_in_force),
379 post_only: Some(report.post_only),
380 }),
381 (None, None) => {
382 self.journaling = old_journaling;
384 return Err(make_error(ErrorType::InvalidPriceOrQuantity));
385 }
386 };
387
388 self.journaling = old_journaling;
390
391 if let Ok(r) = report.as_mut() {
392 if self.journaling {
393 self.last_op = safe_add(self.last_op, 1);
394 r.log = Some(JournalLog {
395 op_id: self.last_op,
396 ts: current_timestamp_millis(),
397 op: JournalOp::Modify,
398 o: OrderOptions::Modify { id, price, quantity },
399 });
400 }
401 }
402 report
403 }
404
405 pub fn modify_raw(
406 &mut self,
407 id: u64,
408 price: Option<u64>,
409 quantity: Option<u64>,
410 ) -> Result<ExecutionReport> {
411 self.modify(OrderId(id), price.map(Price), quantity.map(Quantity))
412 }
413
414 pub fn get_orders_at_price(&self, price: Price, side: Side) -> Vec<LimitOrder> {
416 let mut orders = Vec::new();
417 let queue = match side {
418 Side::Buy => self.bids.get(&price),
419 Side::Sell => self.asks.get(&price),
420 };
421
422 if let Some(q) = queue {
423 for id in q {
424 if let Some(order) = self.orders.get(id) {
425 orders.push(*order);
426 }
427 }
428 }
429 orders
430 }
431
432 pub fn get_order(&self, id: OrderId) -> Result<LimitOrder> {
433 match self.orders.get(&id) {
434 Some(o) => Ok(*o),
435 None => Err(make_error(ErrorType::OrderNotFound)),
436 }
437 }
438
439 pub fn best_bid(&self) -> Option<Price> {
441 self.bids.last_key_value().map(|(price, _)| *price)
442 }
443
444 pub fn best_ask(&self) -> Option<Price> {
446 self.asks.first_key_value().map(|(price, _)| *price)
447 }
448
449 pub fn mid_price(&self) -> Option<Price> {
451 match (self.best_bid(), self.best_ask()) {
452 (Some(bid), Some(ask)) => Some(bid.add(ask).div(Price(2))),
453 _ => None,
454 }
455 }
456
457 pub fn spread(&self) -> Option<Price> {
459 match (self.best_bid(), self.best_ask()) {
460 (Some(bid), Some(ask)) => Some(ask.sub(bid)),
461 _ => None,
462 }
463 }
464
465 pub fn snapshot(&self) -> Snapshot {
478 Snapshot {
479 orders: self.orders.clone(),
480 bids: self.bids.clone(),
481 asks: self.asks.clone(),
482 last_op: self.last_op,
483 next_order_id: self.next_order_id,
484 ts: current_timestamp_millis(),
485 }
486 }
487
488 pub fn restore_snapshot(&mut self, snapshot: Snapshot) {
496 self.orders = snapshot.orders;
497 self.bids = snapshot.bids;
498 self.asks = snapshot.asks;
499 self.last_op = snapshot.last_op;
500 self.next_order_id = snapshot.next_order_id;
501 }
502
503 pub fn replay_logs(&mut self, mut logs: Vec<JournalLog>) -> Result<()> {
518 logs.sort_by_key(|log| log.op_id);
520
521 for log in &logs {
522 match &log.o {
523 OrderOptions::Market(opts) => self.market(*opts)?,
524 OrderOptions::Limit(opts) => self.limit(*opts)?,
525 OrderOptions::Cancel(id) => self.cancel(*id)?,
526 OrderOptions::Modify { id, price, quantity } => {
527 self.modify(*id, *price, *quantity)?
528 }
529 };
530 }
531 Ok(())
532 }
533
534 pub fn depth(&self, limit: Option<usize>) -> Depth {
545 let levels = limit.unwrap_or(100);
546 Depth {
547 asks: self.get_asks_prices_and_volume(levels),
548 bids: self.get_bids_prices_and_volume(levels),
549 }
550 }
551
552 fn get_asks_prices_and_volume(&self, levels: usize) -> Vec<(Price, Quantity)> {
553 let mut asks = Vec::with_capacity(levels);
554 for (ask_price, queue) in self.asks.iter() {
555 let volume: Quantity = queue
556 .iter()
557 .filter_map(|id| self.orders.get(id))
558 .map(|order| order.remaining_qty())
559 .sum();
560 asks.push((*ask_price, volume));
561 }
562 asks
563 }
564
565 fn get_bids_prices_and_volume(&self, levels: usize) -> Vec<(Price, Quantity)> {
566 let mut bids = Vec::with_capacity(levels);
567 for (bid_price, queue) in self.bids.iter().rev() {
568 let volume: Quantity = queue
569 .iter()
570 .filter_map(|id| self.orders.get(id))
571 .map(|order| order.remaining_qty())
572 .sum();
573 bids.push((*bid_price, volume));
574 }
575 bids
576 }
577
578 fn match_with_asks(
579 &mut self,
580 quantity_to_fill: Quantity,
581 fills: &mut Vec<FillReport>,
582 limit_price: Option<Price>,
583 ) -> Quantity {
584 if self.asks.is_empty() {
586 return quantity_to_fill;
587 }
588 let mut remaining_qty = quantity_to_fill;
589 let mut filled_prices = Vec::new();
590 for (ask_price, queue) in self.asks.iter_mut() {
591 if remaining_qty.value() == 0 {
592 break;
593 }
594 if let Some(limit_price) = limit_price {
595 if limit_price < *ask_price {
596 break;
597 }
598 }
599 remaining_qty = Self::process_queue(&mut self.orders, queue, remaining_qty, fills);
600 if queue.is_empty() {
601 filled_prices.push(*ask_price);
602 }
603 }
604 for price in filled_prices {
605 self.asks.remove(&price);
606 }
607 remaining_qty
608 }
609
610 fn match_with_bids(
611 &mut self,
612 quantity_to_fill: Quantity,
613 fills: &mut Vec<FillReport>,
614 limit_price: Option<Price>,
615 ) -> Quantity {
616 if self.bids.is_empty() {
618 return quantity_to_fill;
619 }
620 let mut remaining_qty = quantity_to_fill;
621 let mut filled_prices = Vec::new();
622 for (bid_price, queue) in self.bids.iter_mut().rev() {
623 if remaining_qty.value() == 0 {
624 break;
625 }
626 if let Some(limit_price) = limit_price {
627 if limit_price > *bid_price {
628 break;
629 }
630 }
631 remaining_qty = Self::process_queue(&mut self.orders, queue, remaining_qty, fills);
632 if queue.is_empty() {
633 filled_prices.push(*bid_price);
634 }
635 }
636 for price in filled_prices {
637 self.bids.remove(&price);
638 }
639 remaining_qty
640 }
641
642 fn process_queue(
643 orders: &mut HashMap<OrderId, LimitOrder>,
644 order_queue: &mut VecDeque<OrderId>,
645 remaining_qty: Quantity,
646 fills: &mut Vec<FillReport>,
647 ) -> Quantity {
648 let mut quantity_left = remaining_qty;
649 while !order_queue.is_empty() && quantity_left.value() > 0 {
650 let Some(head_order_uuid) = order_queue.front() else { break };
651 let Some(mut head_order) = orders.remove(head_order_uuid) else { break };
652
653 if quantity_left < head_order.remaining_qty() {
654 head_order.executed_qty = head_order.executed_qty.add(quantity_left);
655 head_order.status = OrderStatus::PartiallyFilled;
656 fills.push(FillReport {
657 order_id: head_order.id,
658 price: head_order.price,
659 quantity: quantity_left,
660 status: head_order.status,
661 });
662 orders.insert(head_order.id, head_order);
663
664 quantity_left = Quantity(0);
665 } else {
666 order_queue.pop_front();
667 quantity_left = quantity_left.sub(head_order.remaining_qty());
668
669 head_order.executed_qty = head_order.executed_qty.add(head_order.remaining_qty());
670 head_order.status = OrderStatus::Filled;
671 fills.push(FillReport {
672 order_id: head_order.id,
673 price: head_order.price,
674 quantity: head_order.executed_qty,
675 status: head_order.status,
676 });
677 }
678 }
679 quantity_left
680 }
681
682 fn validate_market_order(&self, options: &MarketOrderOptions) -> Result<()> {
683 if options.quantity.value() == 0 {
684 return Err(make_error(ErrorType::InvalidQuantity));
685 }
686 if (options.side == Side::Buy && self.asks.is_empty())
687 || (options.side == Side::Sell && self.bids.is_empty())
688 {
689 return Err(make_error(ErrorType::OrderBookEmpty));
690 }
691 Ok(())
692 }
693
694 fn validate_limit_order(&self, options: &LimitOrderOptions) -> Result<()> {
695 if options.quantity.value() == 0 {
696 return Err(make_error(ErrorType::InvalidQuantity));
697 }
698 if options.price.value() == 0 {
699 return Err(make_error(ErrorType::InvalidPrice));
700 }
701 let time_in_force = options.time_in_force.unwrap_or(TimeInForce::GTC);
702 if time_in_force == TimeInForce::FOK
703 && !self.limit_order_is_fillable(options.side, options.quantity, options.price)
704 {
705 return Err(make_error(ErrorType::OrderFOK));
706 }
707 if options.post_only.unwrap_or(false) {
708 let crosses = match options.side {
709 Side::Buy => {
710 if let Some((best_ask, _)) = self.asks.first_key_value() {
711 options.price >= *best_ask
712 } else {
713 false
714 }
715 }
716 Side::Sell => {
717 if let Some((best_bid, _)) = self.bids.last_key_value() {
718 options.price <= *best_bid
719 } else {
720 false
721 }
722 }
723 };
724
725 if crosses {
726 return Err(make_error(ErrorType::OrderPostOnly));
727 }
728 }
729 Ok(())
730 }
731
732 fn limit_order_is_fillable(&self, side: Side, quantity: Quantity, price: Price) -> bool {
733 if side == Side::Buy {
734 self.limit_buy_order_is_fillable(quantity, price)
735 } else {
736 self.limit_sell_order_is_fillable(quantity, price)
737 }
738 }
739
740 fn limit_buy_order_is_fillable(&self, quantity: Quantity, price: Price) -> bool {
741 let mut cumulative_qty = Quantity(0);
742 for (ask_price, queue) in self.asks.iter() {
743 if price >= *ask_price && cumulative_qty < quantity {
744 for id in queue.iter() {
745 if let Some(order) = self.orders.get(id) {
746 cumulative_qty += order.remaining_qty().value();
747 }
748 }
749 } else {
750 break;
751 }
752 }
753 cumulative_qty >= quantity
754 }
755
756 fn limit_sell_order_is_fillable(&self, quantity: Quantity, price: Price) -> bool {
757 let mut cumulative_qty = Quantity(0);
758 for (bid_price, queue) in self.bids.iter().rev() {
759 if price <= *bid_price && cumulative_qty < quantity {
760 for id in queue.iter() {
761 if let Some(order) = self.orders.get(id) {
762 cumulative_qty += order.remaining_qty().value()
763 }
764 }
765 } else {
766 break;
767 }
768 }
769 cumulative_qty >= quantity
770 }
771
772 fn new_order_id(&mut self) -> OrderId {
773 let id = self.next_order_id;
774 self.next_order_id += 1;
775 id
776 }
777}
778
779impl fmt::Display for OrderBook {
780 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
781 for (price, order_ids) in self.asks.iter().rev() {
783 let volume: Quantity = order_ids
784 .iter()
785 .filter_map(|id| self.orders.get(id))
786 .map(|order| order.remaining_qty())
787 .sum();
788
789 writeln!(f, "{} -> {}", price.value(), volume.value())?;
790 }
791
792 writeln!(f, "------------------------------------")?;
793
794 for (price, order_ids) in self.bids.iter().rev() {
796 let volume: Quantity = order_ids
797 .iter()
798 .filter_map(|id| self.orders.get(id))
799 .map(|order| order.remaining_qty())
800 .sum();
801
802 writeln!(f, "{} -> {}", price.value(), volume.value())?;
803 }
804
805 Ok(())
806 }
807}
808
809#[cfg(test)]
810mod tests;