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 sequence(&self) -> u64 {
50 self.sequence
51 }
52
53 pub fn time_engine(&self) -> Option<DateTime<Utc>> {
55 self.time_engine
56 }
57
58 pub fn snapshot(&self, depth: usize) -> Self {
60 Self {
61 sequence: self.sequence,
62 time_engine: self.time_engine,
63 bids: OrderBookSide::bids(self.bids.levels.iter().take(depth).copied()),
64 asks: OrderBookSide::asks(self.asks.levels.iter().take(depth).copied()),
65 }
66 }
67
68 pub fn update(&mut self, event: &OrderBookEvent) {
70 match event {
71 OrderBookEvent::Snapshot(snapshot) => {
72 *self = snapshot.clone();
73 }
74 OrderBookEvent::Update(update) => {
75 self.sequence = update.sequence;
76 self.time_engine = update.time_engine;
77 self.upsert_bids(&update.bids);
78 self.upsert_asks(&update.asks);
79 }
80 }
81 }
82
83 fn upsert_bids(&mut self, update: &OrderBookSide<Bids>) {
85 self.bids.upsert(&update.levels)
86 }
87
88 fn upsert_asks(&mut self, update: &OrderBookSide<Asks>) {
90 self.asks.upsert(&update.levels)
91 }
92
93 pub fn bids(&self) -> &OrderBookSide<Bids> {
95 &self.bids
96 }
97
98 pub fn asks(&self) -> &OrderBookSide<Asks> {
100 &self.asks
101 }
102
103 pub fn mid_price(&self) -> Option<Decimal> {
107 match (self.bids.best(), self.asks.best()) {
108 (Some(best_bid), Some(best_ask)) => Some(mid_price(best_bid.price, best_ask.price)),
109 (Some(best_bid), None) => Some(best_bid.price),
110 (None, Some(best_ask)) => Some(best_ask.price),
111 (None, None) => None,
112 }
113 }
114
115 pub fn volume_weighed_mid_price(&self) -> Option<Decimal> {
120 match (self.bids.best(), self.asks.best()) {
121 (Some(best_bid), Some(best_ask)) => {
122 Some(volume_weighted_mid_price(*best_bid, *best_ask))
123 }
124 (Some(best_bid), None) => Some(best_bid.price),
125 (None, Some(best_ask)) => Some(best_ask.price),
126 (None, None) => None,
127 }
128 }
129}
130
131#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
133pub struct OrderBookSide<Side> {
134 #[serde(skip_serializing)]
135 pub side: Side,
136 levels: Vec<Level>,
137}
138
139#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Display)]
141pub struct Bids;
142
143#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Display)]
145pub struct Asks;
146
147impl OrderBookSide<Bids> {
148 pub fn bids<Iter, L>(levels: Iter) -> Self
150 where
151 Iter: IntoIterator<Item = L>,
152 L: Into<Level>,
153 {
154 let mut levels = levels.into_iter().map(L::into).collect::<Vec<_>>();
155 levels.sort_unstable_by(|a, b| a.price.cmp(&b.price).reverse());
156
157 Self { side: Bids, levels }
158 }
159
160 pub fn upsert<L>(&mut self, levels: &[L])
162 where
163 L: Into<Level> + Copy,
164 {
165 levels.iter().for_each(|upsert| {
166 let upsert: Level = (*upsert).into();
167 self.upsert_single(upsert, |existing| {
168 existing.price.cmp(&upsert.price).reverse()
169 })
170 })
171 }
172}
173
174impl OrderBookSide<Asks> {
175 pub fn asks<Iter, L>(levels: Iter) -> Self
177 where
178 Iter: IntoIterator<Item = L>,
179 L: Into<Level>,
180 {
181 let mut levels = levels.into_iter().map(L::into).collect::<Vec<_>>();
182 levels.sort_unstable_by(|a, b| a.price.cmp(&b.price));
183
184 Self { side: Asks, levels }
185 }
186
187 pub fn upsert<L>(&mut self, levels: &[L])
189 where
190 L: Into<Level> + Copy,
191 {
192 levels.into_iter().for_each(|upsert| {
193 let upsert = (*upsert).into();
194 self.upsert_single(upsert, |existing| existing.price.cmp(&upsert.price))
195 })
196 }
197}
198
199impl<Side> OrderBookSide<Side>
200where
201 Side: std::fmt::Display + std::fmt::Debug,
202{
203 pub fn best(&self) -> Option<&Level> {
205 self.levels.first()
206 }
207
208 pub fn levels(&self) -> &[Level] {
210 &self.levels
211 }
212
213 pub fn upsert_single<FnOrd>(&mut self, new_level: Level, fn_ord: FnOrd)
224 where
225 FnOrd: Fn(&Level) -> Ordering,
226 {
227 match (self.levels.binary_search_by(fn_ord), new_level.amount) {
228 (Ok(index), new_amount) => {
229 if new_amount.is_zero() {
230 let _removed = self.levels.remove(index);
232 } else {
233 self.levels[index].amount = new_amount;
235 }
236 }
237 (Err(index), new_amount) => {
238 if new_amount.is_zero() {
239 debug!(
241 ?new_level,
242 side = %self.side,
243 "received upsert Level with zero amount (to remove) that was not found"
244 );
245 } else {
246 self.levels.insert(index, new_level);
248 }
249 }
250 }
251 }
252}
253
254impl Default for OrderBookSide<Bids> {
255 fn default() -> Self {
256 Self {
257 side: Bids,
258 levels: vec![],
259 }
260 }
261}
262
263impl Default for OrderBookSide<Asks> {
264 fn default() -> Self {
265 Self {
266 side: Asks,
267 levels: vec![],
268 }
269 }
270}
271
272#[derive(Debug, Copy, Clone, PartialEq, Ord, PartialOrd, Hash, Default, Deserialize, Serialize)]
274pub struct Level {
275 pub price: Decimal,
276 pub amount: Decimal,
277}
278
279impl<T> From<(T, T)> for Level
280where
281 T: Into<Decimal>,
282{
283 fn from((price, amount): (T, T)) -> Self {
284 Self::new(price, amount)
285 }
286}
287
288impl Eq for Level {}
289
290impl Level {
291 pub fn new<T>(price: T, amount: T) -> Self
292 where
293 T: Into<Decimal>,
294 {
295 Self {
296 price: price.into(),
297 amount: amount.into(),
298 }
299 }
300}
301
302pub fn mid_price(best_bid_price: Decimal, best_ask_price: Decimal) -> Decimal {
306 (best_bid_price + best_ask_price) / Decimal::TWO
307}
308
309pub fn volume_weighted_mid_price(best_bid: Level, best_ask: Level) -> Decimal {
314 ((best_bid.price * best_ask.amount) + (best_ask.price * best_bid.amount))
315 / (best_bid.amount + best_ask.amount)
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321
322 mod order_book_l1 {
323 use super::*;
324 use crate::subscription::book::OrderBookL1;
325 use rust_decimal_macros::dec;
326
327 #[test]
328 fn test_mid_price() {
329 struct TestCase {
330 input: OrderBookL1,
331 expected: Option<Decimal>,
332 }
333
334 let tests = vec![
335 TestCase {
336 input: OrderBookL1 {
338 last_update_time: Default::default(),
339 best_bid: Some(Level::new(100, 999999)),
340 best_ask: Some(Level::new(200, 1)),
341 },
342 expected: Some(dec!(150.0)),
343 },
344 TestCase {
345 input: OrderBookL1 {
347 last_update_time: Default::default(),
348 best_bid: Some(Level::new(50, 1)),
349 best_ask: Some(Level::new(250, 999999)),
350 },
351 expected: Some(dec!(150.0)),
352 },
353 TestCase {
354 input: OrderBookL1 {
356 last_update_time: Default::default(),
357 best_bid: Some(Level::new(10, 999999)),
358 best_ask: Some(Level::new(250, 999999)),
359 },
360 expected: Some(dec!(130.0)),
361 },
362 TestCase {
363 input: OrderBookL1 {
365 last_update_time: Default::default(),
366 best_bid: Some(Level::new(10, 999999)),
367 best_ask: None,
368 },
369 expected: None,
370 },
371 TestCase {
372 input: OrderBookL1 {
374 last_update_time: Default::default(),
375 best_bid: None,
376 best_ask: Some(Level::new(250, 999999)),
377 },
378 expected: None,
379 },
380 ];
381
382 for (index, test) in tests.into_iter().enumerate() {
383 assert_eq!(test.input.mid_price(), test.expected, "TC{index} failed")
384 }
385 }
386
387 #[test]
388 fn test_volume_weighted_mid_price() {
389 struct TestCase {
390 input: OrderBookL1,
391 expected: Option<Decimal>,
392 }
393
394 let tests = vec![
395 TestCase {
396 input: OrderBookL1 {
398 last_update_time: Default::default(),
399 best_bid: Some(Level::new(100, 100)),
400 best_ask: Some(Level::new(200, 100)),
401 },
402 expected: Some(dec!(150.0)),
403 },
404 TestCase {
405 input: OrderBookL1 {
407 last_update_time: Default::default(),
408 best_bid: Some(Level::new(100, 600)),
409 best_ask: Some(Level::new(200, 1000)),
410 },
411 expected: Some(dec!(137.5)),
412 },
413 TestCase {
414 input: OrderBookL1 {
416 last_update_time: Default::default(),
417 best_bid: Some(Level::new(1000, 999999)),
418 best_ask: Some(Level::new(1000, 999999)),
419 },
420 expected: Some(dec!(1000.0)),
421 },
422 TestCase {
423 input: OrderBookL1 {
425 last_update_time: Default::default(),
426 best_bid: Some(Level::new(1000, 999999)),
427 best_ask: None,
428 },
429 expected: None,
430 },
431 TestCase {
432 input: OrderBookL1 {
434 last_update_time: Default::default(),
435 best_bid: None,
436 best_ask: Some(Level::new(1000, 999999)),
437 },
438 expected: None,
439 },
440 ];
441
442 for (index, test) in tests.into_iter().enumerate() {
443 assert_eq!(
444 test.input.volume_weighed_mid_price(),
445 test.expected,
446 "TC{index} failed"
447 )
448 }
449 }
450 }
451
452 mod order_book {
453 use super::*;
454 use rust_decimal_macros::dec;
455
456 #[test]
457 fn test_mid_price() {
458 struct TestCase {
459 input: OrderBook,
460 expected: Option<Decimal>,
461 }
462
463 let tests = vec![
464 TestCase {
465 input: OrderBook::new::<Vec<_>, Vec<_>, Level>(
467 0,
468 Default::default(),
469 vec![],
470 vec![],
471 ),
472 expected: None,
473 },
474 TestCase {
475 input: OrderBook::new(
477 0,
478 Default::default(),
479 vec![
480 Level::new(dec!(100.0), dec!(100.0)),
481 Level::new(dec!(50.0), dec!(100.0)),
482 ],
483 vec![],
484 ),
485 expected: Some(dec!(100.0)),
486 },
487 TestCase {
488 input: OrderBook::new(
490 0,
491 Default::default(),
492 vec![],
493 vec![
494 Level::new(dec!(50.0), dec!(100.0)),
495 Level::new(dec!(100.0), dec!(100.0)),
496 ],
497 ),
498 expected: Some(dec!(50.0)),
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 Level::new(dec!(200.0), dec!(100.0)),
511 Level::new(dec!(300.0), dec!(100.0)),
512 ],
513 ),
514 expected: Some(dec!(150.0)),
515 },
516 ];
517
518 for (index, test) in tests.into_iter().enumerate() {
519 assert_eq!(test.input.mid_price(), test.expected, "TC{index} failed")
520 }
521 }
522
523 #[test]
524 fn test_volume_weighted_mid_price() {
525 struct TestCase {
526 input: OrderBook,
527 expected: Option<Decimal>,
528 }
529
530 let tests = vec![
531 TestCase {
532 input: OrderBook::new::<Vec<_>, Vec<_>, Level>(
534 0,
535 Default::default(),
536 vec![],
537 vec![],
538 ),
539 expected: None,
540 },
541 TestCase {
542 input: OrderBook::new(
544 0,
545 Default::default(),
546 vec![
547 Level::new(dec!(100.0), dec!(100.0)),
548 Level::new(dec!(50.0), dec!(100.0)),
549 ],
550 vec![],
551 ),
552 expected: Some(dec!(100.0)),
553 },
554 TestCase {
555 input: OrderBook::new(
557 0,
558 Default::default(),
559 vec![],
560 vec![
561 Level::new(dec!(50.0), dec!(100.0)),
562 Level::new(dec!(100.0), dec!(100.0)),
563 ],
564 ),
565 expected: Some(dec!(50.0)),
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 Level::new(dec!(200.0), dec!(100.0)),
578 Level::new(dec!(300.0), dec!(100.0)),
579 ],
580 ),
581 expected: Some(dec!(150.0)),
582 },
583 TestCase {
584 input: OrderBook::new(
586 0,
587 Default::default(),
588 vec![
589 Level::new(dec!(100.0), dec!(3000.0)),
590 Level::new(dec!(50.0), dec!(100.0)),
591 ],
592 vec![
593 Level::new(dec!(200.0), dec!(1000.0)),
594 Level::new(dec!(300.0), dec!(100.0)),
595 ],
596 ),
597 expected: Some(dec!(175.0)),
598 },
599 ];
600
601 for (index, test) in tests.into_iter().enumerate() {
602 assert_eq!(
603 test.input.volume_weighed_mid_price(),
604 test.expected,
605 "TC{index} failed"
606 )
607 }
608 }
609 }
610
611 mod order_book_side {
612 use super::*;
613 use rust_decimal_macros::dec;
614
615 #[test]
616 fn test_upsert_single() {
617 struct TestCase {
618 book_side: OrderBookSide<Bids>,
619 new_level: Level,
620 expected: OrderBookSide<Bids>,
621 }
622
623 let tests = vec![
624 TestCase {
625 book_side: OrderBookSide::bids(vec![
627 Level::new(dec!(80), dec!(1)),
628 Level::new(dec!(90), dec!(1)),
629 Level::new(dec!(100), dec!(1)),
630 ]),
631 new_level: Level::new(dec!(100), dec!(0)),
632 expected: OrderBookSide::bids(vec![
633 Level::new(dec!(80), dec!(1)),
634 Level::new(dec!(90), dec!(1)),
635 ]),
636 },
637 TestCase {
638 book_side: OrderBookSide::bids(vec![
640 Level::new(dec!(80), dec!(1)),
641 Level::new(dec!(90), dec!(1)),
642 Level::new(dec!(100), dec!(1)),
643 ]),
644 new_level: Level::new(dec!(100), dec!(10)),
645 expected: OrderBookSide::bids(vec![
646 Level::new(dec!(80), dec!(1)),
647 Level::new(dec!(90), dec!(1)),
648 Level::new(dec!(100), dec!(10)),
649 ]),
650 },
651 TestCase {
652 book_side: OrderBookSide::bids(vec![
654 Level::new(dec!(80), dec!(1)),
655 Level::new(dec!(90), dec!(1)),
656 Level::new(dec!(100), dec!(1)),
657 ]),
658 new_level: Level::new(dec!(110), dec!(1)),
659 expected: OrderBookSide::bids(vec![
660 Level::new(dec!(80), dec!(1)),
661 Level::new(dec!(90), dec!(1)),
662 Level::new(dec!(100), dec!(1)),
663 Level::new(dec!(110), dec!(1)),
664 ]),
665 },
666 TestCase {
667 book_side: OrderBookSide::bids(vec![
669 Level::new(dec!(80), dec!(1)),
670 Level::new(dec!(90), dec!(1)),
671 Level::new(dec!(100), dec!(1)),
672 ]),
673 new_level: Level::new(dec!(110), dec!(0)),
674 expected: OrderBookSide::bids(vec![
675 Level::new(dec!(80), dec!(1)),
676 Level::new(dec!(90), dec!(1)),
677 Level::new(dec!(100), dec!(1)),
678 ]),
679 },
680 ];
681
682 for (index, mut test) in tests.into_iter().enumerate() {
683 test.book_side.upsert_single(test.new_level, |existing| {
684 existing.price.cmp(&test.new_level.price).reverse()
685 });
686 assert_eq!(test.book_side, test.expected, "TC{} failed", index);
687 }
688 }
689 }
690}