1use crate::subscription::book::OrderBookEvent;
2use chrono::{DateTime, Utc};
3use derive_more::Display;
4use rust_decimal::Decimal;
5use serde::{Deserialize, Serialize, Serializer};
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 pub sequence: u64,
20 pub 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 snapshot(&self, depth: usize) -> Self {
50 Self {
51 sequence: self.sequence,
52 time_engine: self.time_engine,
53 bids: OrderBookSide::bids(self.bids.levels.iter().take(depth).copied()),
54 asks: OrderBookSide::asks(self.asks.levels.iter().take(depth).copied()),
55 }
56 }
57
58 pub fn update(&mut self, event: OrderBookEvent) {
60 match event {
61 OrderBookEvent::Snapshot(snapshot) => {
62 *self = snapshot;
63 }
64 OrderBookEvent::Update(update) => {
65 self.sequence = update.sequence;
66 self.time_engine = update.time_engine;
67 self.upsert_bids(update.bids);
68 self.upsert_asks(update.asks);
69 }
70 }
71 }
72
73 pub fn upsert_bids(&mut self, update: OrderBookSide<Bids>) {
75 self.bids.upsert(update.levels)
76 }
77
78 pub fn upsert_asks(&mut self, update: OrderBookSide<Asks>) {
80 self.asks.upsert(update.levels)
81 }
82
83 pub fn bids(&self) -> &OrderBookSide<Bids> {
85 &self.bids
86 }
87
88 pub fn asks(&self) -> &OrderBookSide<Asks> {
90 &self.asks
91 }
92
93 pub fn mid_price(&self) -> Option<Decimal> {
97 match (self.bids.levels.first(), self.asks.levels.first()) {
98 (Some(best_bid), Some(best_ask)) => Some(mid_price(best_bid.price, best_ask.price)),
99 (Some(best_bid), None) => Some(best_bid.price),
100 (None, Some(best_ask)) => Some(best_ask.price),
101 (None, None) => None,
102 }
103 }
104
105 pub fn volume_weighed_mid_price(&self) -> Option<Decimal> {
110 match (self.bids.levels.first(), self.asks.levels.first()) {
111 (Some(best_bid), Some(best_ask)) => {
112 Some(volume_weighted_mid_price(*best_bid, *best_ask))
113 }
114 (Some(best_bid), None) => Some(best_bid.price),
115 (None, Some(best_ask)) => Some(best_ask.price),
116 (None, None) => None,
117 }
118 }
119}
120
121#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
123pub struct OrderBookSide<Side> {
124 #[serde(skip_serializing)]
125 pub side: Side,
126 levels: Vec<Level>,
127}
128
129#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Display)]
131pub struct Bids;
132
133#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Display)]
135pub struct Asks;
136
137impl Serialize for Asks {
138 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139 where
140 S: Serializer,
141 {
142 serializer.serialize_str("asks")
143 }
144}
145
146impl OrderBookSide<Bids> {
147 pub fn bids<Iter, L>(levels: Iter) -> Self
149 where
150 Iter: IntoIterator<Item = L>,
151 L: Into<Level>,
152 {
153 let mut levels = levels.into_iter().map(L::into).collect::<Vec<_>>();
154 levels.sort_unstable_by(|a, b| a.price.cmp(&b.price).reverse());
155
156 Self { side: Bids, levels }
157 }
158
159 pub fn upsert<Iter, L>(&mut self, levels: Iter)
161 where
162 Iter: IntoIterator<Item = L>,
163 L: Into<Level>,
164 {
165 levels.into_iter().for_each(|upsert| {
166 let upsert = 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<Iter, L>(&mut self, levels: Iter)
189 where
190 Iter: IntoIterator<Item = L>,
191 L: Into<Level>,
192 {
193 levels.into_iter().for_each(|upsert| {
194 let upsert = upsert.into();
195 self.upsert_single(upsert, |existing| existing.price.cmp(&upsert.price))
196 })
197 }
198}
199
200impl<Side> OrderBookSide<Side>
201where
202 Side: std::fmt::Display + std::fmt::Debug,
203{
204 pub fn levels(&self) -> &[Level] {
206 &self.levels
207 }
208
209 pub fn upsert_single<FnOrd>(&mut self, new_level: Level, fn_ord: FnOrd)
220 where
221 FnOrd: Fn(&Level) -> Ordering,
222 {
223 match (self.levels.binary_search_by(fn_ord), new_level.amount) {
224 (Ok(index), new_amount) => {
225 if new_amount.is_zero() {
226 let _removed = self.levels.remove(index);
228 } else {
229 self.levels[index].amount = new_amount;
231 }
232 }
233 (Err(index), new_amount) => {
234 if new_amount.is_zero() {
235 debug!(
237 ?new_level,
238 side = %self.side,
239 "received upsert Level with zero amount (to remove) that was not found"
240 );
241 } else {
242 self.levels.insert(index, new_level);
244 }
245 }
246 }
247 }
248}
249
250impl Default for OrderBookSide<Bids> {
251 fn default() -> Self {
252 Self {
253 side: Bids,
254 levels: vec![],
255 }
256 }
257}
258
259impl Default for OrderBookSide<Asks> {
260 fn default() -> Self {
261 Self {
262 side: Asks,
263 levels: vec![],
264 }
265 }
266}
267
268#[derive(Debug, Copy, Clone, PartialEq, Ord, PartialOrd, Hash, Default, Deserialize, Serialize)]
270pub struct Level {
271 pub price: Decimal,
272 pub amount: Decimal,
273}
274
275impl<T> From<(T, T)> for Level
276where
277 T: Into<Decimal>,
278{
279 fn from((price, amount): (T, T)) -> Self {
280 Self::new(price, amount)
281 }
282}
283
284impl Eq for Level {}
285
286impl Level {
287 pub fn new<T>(price: T, amount: T) -> Self
288 where
289 T: Into<Decimal>,
290 {
291 Self {
292 price: price.into(),
293 amount: amount.into(),
294 }
295 }
296}
297
298pub fn mid_price(best_bid_price: Decimal, best_ask_price: Decimal) -> Decimal {
302 (best_bid_price + best_ask_price) / Decimal::TWO
303}
304
305pub fn volume_weighted_mid_price(best_bid: Level, best_ask: Level) -> Decimal {
310 ((best_bid.price * best_ask.amount) + (best_ask.price * best_bid.amount))
311 / (best_bid.amount + best_ask.amount)
312}
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317
318 mod order_book_l1 {
319 use super::*;
320 use crate::subscription::book::OrderBookL1;
321 use rust_decimal_macros::dec;
322
323 #[test]
324 fn test_mid_price() {
325 struct TestCase {
326 input: OrderBookL1,
327 expected: Option<Decimal>,
328 }
329
330 let tests = vec![
331 TestCase {
332 input: OrderBookL1 {
334 last_update_time: Default::default(),
335 best_bid: Some(Level::new(100, 999999)),
336 best_ask: Some(Level::new(200, 1)),
337 },
338 expected: Some(dec!(150.0)),
339 },
340 TestCase {
341 input: OrderBookL1 {
343 last_update_time: Default::default(),
344 best_bid: Some(Level::new(50, 1)),
345 best_ask: Some(Level::new(250, 999999)),
346 },
347 expected: Some(dec!(150.0)),
348 },
349 TestCase {
350 input: OrderBookL1 {
352 last_update_time: Default::default(),
353 best_bid: Some(Level::new(10, 999999)),
354 best_ask: Some(Level::new(250, 999999)),
355 },
356 expected: Some(dec!(130.0)),
357 },
358 TestCase {
359 input: OrderBookL1 {
361 last_update_time: Default::default(),
362 best_bid: Some(Level::new(10, 999999)),
363 best_ask: None,
364 },
365 expected: None,
366 },
367 TestCase {
368 input: OrderBookL1 {
370 last_update_time: Default::default(),
371 best_bid: None,
372 best_ask: Some(Level::new(250, 999999)),
373 },
374 expected: None,
375 },
376 ];
377
378 for (index, test) in tests.into_iter().enumerate() {
379 assert_eq!(test.input.mid_price(), test.expected, "TC{index} failed")
380 }
381 }
382
383 #[test]
384 fn test_volume_weighted_mid_price() {
385 struct TestCase {
386 input: OrderBookL1,
387 expected: Option<Decimal>,
388 }
389
390 let tests = vec![
391 TestCase {
392 input: OrderBookL1 {
394 last_update_time: Default::default(),
395 best_bid: Some(Level::new(100, 100)),
396 best_ask: Some(Level::new(200, 100)),
397 },
398 expected: Some(dec!(150.0)),
399 },
400 TestCase {
401 input: OrderBookL1 {
403 last_update_time: Default::default(),
404 best_bid: Some(Level::new(100, 600)),
405 best_ask: Some(Level::new(200, 1000)),
406 },
407 expected: Some(dec!(137.5)),
408 },
409 TestCase {
410 input: OrderBookL1 {
412 last_update_time: Default::default(),
413 best_bid: Some(Level::new(1000, 999999)),
414 best_ask: Some(Level::new(1000, 999999)),
415 },
416 expected: Some(dec!(1000.0)),
417 },
418 TestCase {
419 input: OrderBookL1 {
421 last_update_time: Default::default(),
422 best_bid: Some(Level::new(1000, 999999)),
423 best_ask: None,
424 },
425 expected: None,
426 },
427 TestCase {
428 input: OrderBookL1 {
430 last_update_time: Default::default(),
431 best_bid: None,
432 best_ask: Some(Level::new(1000, 999999)),
433 },
434 expected: None,
435 },
436 ];
437
438 for (index, test) in tests.into_iter().enumerate() {
439 assert_eq!(
440 test.input.volume_weighed_mid_price(),
441 test.expected,
442 "TC{index} failed"
443 )
444 }
445 }
446 }
447
448 mod order_book {
449 use super::*;
450 use rust_decimal_macros::dec;
451
452 #[test]
453 fn test_mid_price() {
454 struct TestCase {
455 input: OrderBook,
456 expected: Option<Decimal>,
457 }
458
459 let tests = vec![
460 TestCase {
461 input: OrderBook::new::<Vec<_>, Vec<_>, Level>(
463 0,
464 Default::default(),
465 vec![],
466 vec![],
467 ),
468 expected: None,
469 },
470 TestCase {
471 input: OrderBook::new(
473 0,
474 Default::default(),
475 vec![
476 Level::new(dec!(100.0), dec!(100.0)),
477 Level::new(dec!(50.0), dec!(100.0)),
478 ],
479 vec![],
480 ),
481 expected: Some(dec!(100.0)),
482 },
483 TestCase {
484 input: OrderBook::new(
486 0,
487 Default::default(),
488 vec![],
489 vec![
490 Level::new(dec!(50.0), dec!(100.0)),
491 Level::new(dec!(100.0), dec!(100.0)),
492 ],
493 ),
494 expected: Some(dec!(50.0)),
495 },
496 TestCase {
497 input: OrderBook::new(
499 0,
500 Default::default(),
501 vec![
502 Level::new(dec!(100.0), dec!(100.0)),
503 Level::new(dec!(50.0), dec!(100.0)),
504 ],
505 vec![
506 Level::new(dec!(200.0), dec!(100.0)),
507 Level::new(dec!(300.0), dec!(100.0)),
508 ],
509 ),
510 expected: Some(dec!(150.0)),
511 },
512 ];
513
514 for (index, test) in tests.into_iter().enumerate() {
515 assert_eq!(test.input.mid_price(), test.expected, "TC{index} failed")
516 }
517 }
518
519 #[test]
520 fn test_volume_weighted_mid_price() {
521 struct TestCase {
522 input: OrderBook,
523 expected: Option<Decimal>,
524 }
525
526 let tests = vec![
527 TestCase {
528 input: OrderBook::new::<Vec<_>, Vec<_>, Level>(
530 0,
531 Default::default(),
532 vec![],
533 vec![],
534 ),
535 expected: None,
536 },
537 TestCase {
538 input: OrderBook::new(
540 0,
541 Default::default(),
542 vec![
543 Level::new(dec!(100.0), dec!(100.0)),
544 Level::new(dec!(50.0), dec!(100.0)),
545 ],
546 vec![],
547 ),
548 expected: Some(dec!(100.0)),
549 },
550 TestCase {
551 input: OrderBook::new(
553 0,
554 Default::default(),
555 vec![],
556 vec![
557 Level::new(dec!(50.0), dec!(100.0)),
558 Level::new(dec!(100.0), dec!(100.0)),
559 ],
560 ),
561 expected: Some(dec!(50.0)),
562 },
563 TestCase {
564 input: OrderBook::new(
566 0,
567 Default::default(),
568 vec![
569 Level::new(dec!(100.0), dec!(100.0)),
570 Level::new(dec!(50.0), dec!(100.0)),
571 ],
572 vec![
573 Level::new(dec!(200.0), dec!(100.0)),
574 Level::new(dec!(300.0), dec!(100.0)),
575 ],
576 ),
577 expected: Some(dec!(150.0)),
578 },
579 TestCase {
580 input: OrderBook::new(
582 0,
583 Default::default(),
584 vec![
585 Level::new(dec!(100.0), dec!(3000.0)),
586 Level::new(dec!(50.0), dec!(100.0)),
587 ],
588 vec![
589 Level::new(dec!(200.0), dec!(1000.0)),
590 Level::new(dec!(300.0), dec!(100.0)),
591 ],
592 ),
593 expected: Some(dec!(175.0)),
594 },
595 ];
596
597 for (index, test) in tests.into_iter().enumerate() {
598 assert_eq!(
599 test.input.volume_weighed_mid_price(),
600 test.expected,
601 "TC{index} failed"
602 )
603 }
604 }
605 }
606
607 mod order_book_side {
608 use super::*;
609 use rust_decimal_macros::dec;
610
611 #[test]
612 fn test_upsert_single() {
613 struct TestCase {
614 book_side: OrderBookSide<Bids>,
615 new_level: Level,
616 expected: OrderBookSide<Bids>,
617 }
618
619 let tests = vec![
620 TestCase {
621 book_side: OrderBookSide::bids(vec![
623 Level::new(dec!(80), dec!(1)),
624 Level::new(dec!(90), dec!(1)),
625 Level::new(dec!(100), dec!(1)),
626 ]),
627 new_level: Level::new(dec!(100), dec!(0)),
628 expected: OrderBookSide::bids(vec![
629 Level::new(dec!(80), dec!(1)),
630 Level::new(dec!(90), dec!(1)),
631 ]),
632 },
633 TestCase {
634 book_side: OrderBookSide::bids(vec![
636 Level::new(dec!(80), dec!(1)),
637 Level::new(dec!(90), dec!(1)),
638 Level::new(dec!(100), dec!(1)),
639 ]),
640 new_level: Level::new(dec!(100), dec!(10)),
641 expected: OrderBookSide::bids(vec![
642 Level::new(dec!(80), dec!(1)),
643 Level::new(dec!(90), dec!(1)),
644 Level::new(dec!(100), dec!(10)),
645 ]),
646 },
647 TestCase {
648 book_side: OrderBookSide::bids(vec![
650 Level::new(dec!(80), dec!(1)),
651 Level::new(dec!(90), dec!(1)),
652 Level::new(dec!(100), dec!(1)),
653 ]),
654 new_level: Level::new(dec!(110), dec!(1)),
655 expected: OrderBookSide::bids(vec![
656 Level::new(dec!(80), dec!(1)),
657 Level::new(dec!(90), dec!(1)),
658 Level::new(dec!(100), dec!(1)),
659 Level::new(dec!(110), dec!(1)),
660 ]),
661 },
662 TestCase {
663 book_side: OrderBookSide::bids(vec![
665 Level::new(dec!(80), dec!(1)),
666 Level::new(dec!(90), dec!(1)),
667 Level::new(dec!(100), dec!(1)),
668 ]),
669 new_level: Level::new(dec!(110), dec!(0)),
670 expected: OrderBookSide::bids(vec![
671 Level::new(dec!(80), dec!(1)),
672 Level::new(dec!(90), dec!(1)),
673 Level::new(dec!(100), dec!(1)),
674 ]),
675 },
676 ];
677
678 for (index, mut test) in tests.into_iter().enumerate() {
679 test.book_side.upsert_single(test.new_level, |existing| {
680 existing.price.cmp(&test.new_level.price).reverse()
681 });
682 assert_eq!(test.book_side, test.expected, "TC{} failed", index);
683 }
684 }
685 }
686}