1use crate::subscription::book::OrderBookEvent;
2use chrono::{DateTime, Utc};
3use derive_more::Display;
4use rust_decimal::Decimal;
5use serde::{Deserialize, Serialize};
6use std::cmp::Ordering;
7use tracing::debug;
8
9pub mod manager;
12
13pub mod map;
15
16#[derive(Clone, PartialEq, Eq, Debug, Default, Deserialize, Serialize)]
18pub struct OrderBook {
19 sequence: u64,
20 time_engine: Option<DateTime<Utc>>,
21 bids: OrderBookSide<Bids>,
22 asks: OrderBookSide<Asks>,
23}
24
25impl OrderBook {
26 pub fn new<IterBids, IterAsks, L>(
30 sequence: u64,
31 time_engine: Option<DateTime<Utc>>,
32 bids: IterBids,
33 asks: IterAsks,
34 ) -> Self
35 where
36 IterBids: IntoIterator<Item = L>,
37 IterAsks: IntoIterator<Item = L>,
38 L: Into<Level>,
39 {
40 Self {
41 sequence,
42 time_engine,
43 bids: OrderBookSide::bids(bids),
44 asks: OrderBookSide::asks(asks),
45 }
46 }
47
48 pub fn from_sides(
53 sequence: u64,
54 time_engine: Option<DateTime<Utc>>,
55 bids: OrderBookSide<Bids>,
56 asks: OrderBookSide<Asks>,
57 ) -> Self {
58 debug_assert!(
59 bids.levels().windows(2).all(|w| w[0].price >= w[1].price),
60 "bids must be sorted descending by price"
61 );
62 debug_assert!(
63 asks.levels().windows(2).all(|w| w[0].price <= w[1].price),
64 "asks must be sorted ascending by price"
65 );
66 Self {
67 sequence,
68 time_engine,
69 bids,
70 asks,
71 }
72 }
73
74 pub fn sequence(&self) -> u64 {
76 self.sequence
77 }
78
79 pub fn time_engine(&self) -> Option<DateTime<Utc>> {
81 self.time_engine
82 }
83
84 pub fn snapshot(&self, depth: usize) -> Self {
86 Self {
87 sequence: self.sequence,
88 time_engine: self.time_engine,
89 bids: OrderBookSide::bids(self.bids.levels.iter().take(depth).copied()),
90 asks: OrderBookSide::asks(self.asks.levels.iter().take(depth).copied()),
91 }
92 }
93
94 pub fn update(&mut self, event: &OrderBookEvent) {
96 match event {
97 OrderBookEvent::Snapshot(snapshot) => {
98 *self = snapshot.clone();
99 }
100 OrderBookEvent::Update(update) => {
101 self.sequence = update.sequence;
102 self.time_engine = update.time_engine;
103 self.upsert_bids(&update.bids);
104 self.upsert_asks(&update.asks);
105 }
106 }
107 }
108
109 fn upsert_bids(&mut self, update: &OrderBookSide<Bids>) {
111 self.bids.upsert(&update.levels)
112 }
113
114 fn upsert_asks(&mut self, update: &OrderBookSide<Asks>) {
116 self.asks.upsert(&update.levels)
117 }
118
119 pub fn bids(&self) -> &OrderBookSide<Bids> {
121 &self.bids
122 }
123
124 pub fn asks(&self) -> &OrderBookSide<Asks> {
126 &self.asks
127 }
128
129 pub fn mid_price(&self) -> Option<Decimal> {
133 match (self.bids.best(), self.asks.best()) {
134 (Some(best_bid), Some(best_ask)) => Some(mid_price(best_bid.price, best_ask.price)),
135 (Some(best_bid), None) => Some(best_bid.price),
136 (None, Some(best_ask)) => Some(best_ask.price),
137 (None, None) => None,
138 }
139 }
140
141 pub fn volume_weighed_mid_price(&self) -> Option<Decimal> {
146 match (self.bids.best(), self.asks.best()) {
147 (Some(best_bid), Some(best_ask)) => {
148 Some(volume_weighted_mid_price(*best_bid, *best_ask))
149 }
150 (Some(best_bid), None) => Some(best_bid.price),
151 (None, Some(best_ask)) => Some(best_ask.price),
152 (None, None) => None,
153 }
154 }
155}
156
157#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
159pub struct OrderBookSide<Side> {
160 #[serde(skip_serializing)]
161 pub side: Side,
162 levels: Vec<Level>,
163}
164
165#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Display)]
167pub struct Bids;
168
169#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Display)]
171pub struct Asks;
172
173impl OrderBookSide<Bids> {
174 pub fn bids<Iter, L>(levels: Iter) -> Self
176 where
177 Iter: IntoIterator<Item = L>,
178 L: Into<Level>,
179 {
180 let mut levels = levels.into_iter().map(L::into).collect::<Vec<_>>();
181 levels.sort_unstable_by(|a, b| a.price.cmp(&b.price).reverse());
182
183 Self { side: Bids, levels }
184 }
185
186 pub fn upsert<L>(&mut self, levels: &[L])
188 where
189 L: Into<Level> + Copy,
190 {
191 levels.iter().for_each(|upsert| {
192 let upsert: Level = (*upsert).into();
193 self.upsert_single(upsert, |existing| {
194 existing.price.cmp(&upsert.price).reverse()
195 })
196 })
197 }
198}
199
200impl OrderBookSide<Asks> {
201 pub fn asks<Iter, L>(levels: Iter) -> Self
203 where
204 Iter: IntoIterator<Item = L>,
205 L: Into<Level>,
206 {
207 let mut levels = levels.into_iter().map(L::into).collect::<Vec<_>>();
208 levels.sort_unstable_by_key(|a| a.price);
209
210 Self { side: Asks, levels }
211 }
212
213 pub fn upsert<L>(&mut self, levels: &[L])
215 where
216 L: Into<Level> + Copy,
217 {
218 levels.iter().for_each(|upsert| {
219 let upsert = (*upsert).into();
220 self.upsert_single(upsert, |existing| existing.price.cmp(&upsert.price))
221 })
222 }
223}
224
225impl<Side> OrderBookSide<Side>
226where
227 Side: std::fmt::Display + std::fmt::Debug,
228{
229 pub fn best(&self) -> Option<&Level> {
231 self.levels.first()
232 }
233
234 pub fn levels(&self) -> &[Level] {
236 &self.levels
237 }
238
239 pub fn upsert_single<FnOrd>(&mut self, new_level: Level, fn_ord: FnOrd)
250 where
251 FnOrd: Fn(&Level) -> Ordering,
252 {
253 match (self.levels.binary_search_by(fn_ord), new_level.amount) {
254 (Ok(index), new_amount) => {
255 if new_amount.is_zero() {
256 let _removed = self.levels.remove(index);
258 } else {
259 self.levels[index].amount = new_amount;
261 }
262 }
263 (Err(index), new_amount) => {
264 if new_amount.is_zero() {
265 debug!(
267 ?new_level,
268 side = %self.side,
269 "received upsert Level with zero amount (to remove) that was not found"
270 );
271 } else {
272 self.levels.insert(index, new_level);
274 }
275 }
276 }
277 }
278}
279
280impl Default for OrderBookSide<Bids> {
281 fn default() -> Self {
282 Self {
283 side: Bids,
284 levels: vec![],
285 }
286 }
287}
288
289impl Default for OrderBookSide<Asks> {
290 fn default() -> Self {
291 Self {
292 side: Asks,
293 levels: vec![],
294 }
295 }
296}
297
298#[derive(Debug, Copy, Clone, PartialEq, Ord, PartialOrd, Hash, Default, Deserialize, Serialize)]
300pub struct Level {
301 pub price: Decimal,
302 pub amount: Decimal,
303}
304
305impl<T> From<(T, T)> for Level
306where
307 T: Into<Decimal>,
308{
309 fn from((price, amount): (T, T)) -> Self {
310 Self::new(price, amount)
311 }
312}
313
314impl Eq for Level {}
315
316impl Level {
317 pub fn new<T>(price: T, amount: T) -> Self
318 where
319 T: Into<Decimal>,
320 {
321 Self {
322 price: price.into(),
323 amount: amount.into(),
324 }
325 }
326}
327
328pub fn mid_price(best_bid_price: Decimal, best_ask_price: Decimal) -> Decimal {
332 (best_bid_price + best_ask_price) / Decimal::TWO
333}
334
335pub fn volume_weighted_mid_price(best_bid: Level, best_ask: Level) -> Decimal {
340 ((best_bid.price * best_ask.amount) + (best_ask.price * best_bid.amount))
341 / (best_bid.amount + best_ask.amount)
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347
348 mod order_book_l1 {
349 use super::*;
350 use crate::subscription::book::OrderBookL1;
351 use rust_decimal_macros::dec;
352
353 #[test]
354 fn test_mid_price() {
355 struct TestCase {
356 input: OrderBookL1,
357 expected: Option<Decimal>,
358 }
359
360 let tests = vec![
361 TestCase {
362 input: OrderBookL1 {
364 last_update_time: Default::default(),
365 best_bid: Some(Level::new(100, 999999)),
366 best_ask: Some(Level::new(200, 1)),
367 },
368 expected: Some(dec!(150.0)),
369 },
370 TestCase {
371 input: OrderBookL1 {
373 last_update_time: Default::default(),
374 best_bid: Some(Level::new(50, 1)),
375 best_ask: Some(Level::new(250, 999999)),
376 },
377 expected: Some(dec!(150.0)),
378 },
379 TestCase {
380 input: OrderBookL1 {
382 last_update_time: Default::default(),
383 best_bid: Some(Level::new(10, 999999)),
384 best_ask: Some(Level::new(250, 999999)),
385 },
386 expected: Some(dec!(130.0)),
387 },
388 TestCase {
389 input: OrderBookL1 {
391 last_update_time: Default::default(),
392 best_bid: Some(Level::new(10, 999999)),
393 best_ask: None,
394 },
395 expected: None,
396 },
397 TestCase {
398 input: OrderBookL1 {
400 last_update_time: Default::default(),
401 best_bid: None,
402 best_ask: Some(Level::new(250, 999999)),
403 },
404 expected: None,
405 },
406 ];
407
408 for (index, test) in tests.into_iter().enumerate() {
409 assert_eq!(test.input.mid_price(), test.expected, "TC{index} failed")
410 }
411 }
412
413 #[test]
414 fn test_volume_weighted_mid_price() {
415 struct TestCase {
416 input: OrderBookL1,
417 expected: Option<Decimal>,
418 }
419
420 let tests = vec![
421 TestCase {
422 input: OrderBookL1 {
424 last_update_time: Default::default(),
425 best_bid: Some(Level::new(100, 100)),
426 best_ask: Some(Level::new(200, 100)),
427 },
428 expected: Some(dec!(150.0)),
429 },
430 TestCase {
431 input: OrderBookL1 {
433 last_update_time: Default::default(),
434 best_bid: Some(Level::new(100, 600)),
435 best_ask: Some(Level::new(200, 1000)),
436 },
437 expected: Some(dec!(137.5)),
438 },
439 TestCase {
440 input: OrderBookL1 {
442 last_update_time: Default::default(),
443 best_bid: Some(Level::new(1000, 999999)),
444 best_ask: Some(Level::new(1000, 999999)),
445 },
446 expected: Some(dec!(1000.0)),
447 },
448 TestCase {
449 input: OrderBookL1 {
451 last_update_time: Default::default(),
452 best_bid: Some(Level::new(1000, 999999)),
453 best_ask: None,
454 },
455 expected: None,
456 },
457 TestCase {
458 input: OrderBookL1 {
460 last_update_time: Default::default(),
461 best_bid: None,
462 best_ask: Some(Level::new(1000, 999999)),
463 },
464 expected: None,
465 },
466 ];
467
468 for (index, test) in tests.into_iter().enumerate() {
469 assert_eq!(
470 test.input.volume_weighed_mid_price(),
471 test.expected,
472 "TC{index} failed"
473 )
474 }
475 }
476 }
477
478 mod order_book {
479 use super::*;
480 use rust_decimal_macros::dec;
481
482 #[test]
483 fn test_mid_price() {
484 struct TestCase {
485 input: OrderBook,
486 expected: Option<Decimal>,
487 }
488
489 let tests = vec![
490 TestCase {
491 input: OrderBook::new::<Vec<_>, Vec<_>, Level>(
493 0,
494 Default::default(),
495 vec![],
496 vec![],
497 ),
498 expected: None,
499 },
500 TestCase {
501 input: OrderBook::new(
503 0,
504 Default::default(),
505 vec![
506 Level::new(dec!(100.0), dec!(100.0)),
507 Level::new(dec!(50.0), dec!(100.0)),
508 ],
509 vec![],
510 ),
511 expected: Some(dec!(100.0)),
512 },
513 TestCase {
514 input: OrderBook::new(
516 0,
517 Default::default(),
518 vec![],
519 vec![
520 Level::new(dec!(50.0), dec!(100.0)),
521 Level::new(dec!(100.0), dec!(100.0)),
522 ],
523 ),
524 expected: Some(dec!(50.0)),
525 },
526 TestCase {
527 input: OrderBook::new(
529 0,
530 Default::default(),
531 vec![
532 Level::new(dec!(100.0), dec!(100.0)),
533 Level::new(dec!(50.0), dec!(100.0)),
534 ],
535 vec![
536 Level::new(dec!(200.0), dec!(100.0)),
537 Level::new(dec!(300.0), dec!(100.0)),
538 ],
539 ),
540 expected: Some(dec!(150.0)),
541 },
542 ];
543
544 for (index, test) in tests.into_iter().enumerate() {
545 assert_eq!(test.input.mid_price(), test.expected, "TC{index} failed")
546 }
547 }
548
549 #[test]
550 fn test_volume_weighted_mid_price() {
551 struct TestCase {
552 input: OrderBook,
553 expected: Option<Decimal>,
554 }
555
556 let tests = vec![
557 TestCase {
558 input: OrderBook::new::<Vec<_>, Vec<_>, Level>(
560 0,
561 Default::default(),
562 vec![],
563 vec![],
564 ),
565 expected: None,
566 },
567 TestCase {
568 input: OrderBook::new(
570 0,
571 Default::default(),
572 vec![
573 Level::new(dec!(100.0), dec!(100.0)),
574 Level::new(dec!(50.0), dec!(100.0)),
575 ],
576 vec![],
577 ),
578 expected: Some(dec!(100.0)),
579 },
580 TestCase {
581 input: OrderBook::new(
583 0,
584 Default::default(),
585 vec![],
586 vec![
587 Level::new(dec!(50.0), dec!(100.0)),
588 Level::new(dec!(100.0), dec!(100.0)),
589 ],
590 ),
591 expected: Some(dec!(50.0)),
592 },
593 TestCase {
594 input: OrderBook::new(
596 0,
597 Default::default(),
598 vec![
599 Level::new(dec!(100.0), dec!(100.0)),
600 Level::new(dec!(50.0), dec!(100.0)),
601 ],
602 vec![
603 Level::new(dec!(200.0), dec!(100.0)),
604 Level::new(dec!(300.0), dec!(100.0)),
605 ],
606 ),
607 expected: Some(dec!(150.0)),
608 },
609 TestCase {
610 input: OrderBook::new(
612 0,
613 Default::default(),
614 vec![
615 Level::new(dec!(100.0), dec!(3000.0)),
616 Level::new(dec!(50.0), dec!(100.0)),
617 ],
618 vec![
619 Level::new(dec!(200.0), dec!(1000.0)),
620 Level::new(dec!(300.0), dec!(100.0)),
621 ],
622 ),
623 expected: Some(dec!(175.0)),
624 },
625 ];
626
627 for (index, test) in tests.into_iter().enumerate() {
628 assert_eq!(
629 test.input.volume_weighed_mid_price(),
630 test.expected,
631 "TC{index} failed"
632 )
633 }
634 }
635 }
636
637 mod order_book_side {
638 use super::*;
639 use rust_decimal_macros::dec;
640
641 #[test]
642 fn test_upsert_single() {
643 struct TestCase {
644 book_side: OrderBookSide<Bids>,
645 new_level: Level,
646 expected: OrderBookSide<Bids>,
647 }
648
649 let tests = vec![
650 TestCase {
651 book_side: OrderBookSide::bids(vec![
653 Level::new(dec!(80), dec!(1)),
654 Level::new(dec!(90), dec!(1)),
655 Level::new(dec!(100), dec!(1)),
656 ]),
657 new_level: Level::new(dec!(100), dec!(0)),
658 expected: OrderBookSide::bids(vec![
659 Level::new(dec!(80), dec!(1)),
660 Level::new(dec!(90), dec!(1)),
661 ]),
662 },
663 TestCase {
664 book_side: OrderBookSide::bids(vec![
666 Level::new(dec!(80), dec!(1)),
667 Level::new(dec!(90), dec!(1)),
668 Level::new(dec!(100), dec!(1)),
669 ]),
670 new_level: Level::new(dec!(100), dec!(10)),
671 expected: OrderBookSide::bids(vec![
672 Level::new(dec!(80), dec!(1)),
673 Level::new(dec!(90), dec!(1)),
674 Level::new(dec!(100), dec!(10)),
675 ]),
676 },
677 TestCase {
678 book_side: OrderBookSide::bids(vec![
680 Level::new(dec!(80), dec!(1)),
681 Level::new(dec!(90), dec!(1)),
682 Level::new(dec!(100), dec!(1)),
683 ]),
684 new_level: Level::new(dec!(110), dec!(1)),
685 expected: OrderBookSide::bids(vec![
686 Level::new(dec!(80), dec!(1)),
687 Level::new(dec!(90), dec!(1)),
688 Level::new(dec!(100), dec!(1)),
689 Level::new(dec!(110), dec!(1)),
690 ]),
691 },
692 TestCase {
693 book_side: OrderBookSide::bids(vec![
695 Level::new(dec!(80), dec!(1)),
696 Level::new(dec!(90), dec!(1)),
697 Level::new(dec!(100), dec!(1)),
698 ]),
699 new_level: Level::new(dec!(110), dec!(0)),
700 expected: OrderBookSide::bids(vec![
701 Level::new(dec!(80), dec!(1)),
702 Level::new(dec!(90), dec!(1)),
703 Level::new(dec!(100), dec!(1)),
704 ]),
705 },
706 ];
707
708 for (index, mut test) in tests.into_iter().enumerate() {
709 test.book_side.upsert_single(test.new_level, |existing| {
710 existing.price.cmp(&test.new_level.price).reverse()
711 });
712 assert_eq!(test.book_side, test.expected, "TC{} failed", index);
713 }
714 }
715 }
716}