1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use chrono::{DateTime, Utc};
use rust_decimal::Decimal;

use std::borrow::BorrowMut;
use std::collections::BTreeMap;

use crate::utils::{WrapIterator, WrappedIterator};

/// Alias for price type.
pub type Price = Decimal;

/// Alias for stock/contract amount type.
pub type Quantity = Decimal;

/// Represents raw trade.
#[derive(Clone)]
pub struct Trade {
    pub price: Price,
    pub quantity: Quantity,
    /// The trade ID officially marked by the exchange.
    pub trade_id: String,
    /// Received timestamp of the trade
    pub timestamp: DateTime<Utc>,
    /// Is buyer maker of this trade?
    pub is_buyer_maker: Option<bool>,
}

/// Type alias for orderbook iterators.
type OrderbookIterator<'s> = WrappedIterator<'s, (&'s Price, &'s Quantity)>;

/// Trait for orderbook.
/// The reason why I made a trait for this is because
/// there can be several different structs of an Orderbook.
///
/// Following list contains variations on level;
/// - L1: Best ask/bid
/// - L2: Aggregated asks/bids
/// - L3: Non-aggregated asks/bids (Currently not supported)
///
/// And also an orderbook can be unsized or have fixed size.
pub trait Orderbook {
    /// Return an iterator that yields asks from the best to the worst.
    fn iter_ask<'s>(&'s self) -> OrderbookIterator<'s>;
    /// Return an iterator that yields bids from the best to the worst.
    fn iter_bid<'s>(&'s self) -> OrderbookIterator<'s>;

    /// Apply delta on current orderbook.
    /// This method does not guarantee that the modified state is valid.
    fn apply_delta(&mut self, price: Price, quantity: Quantity, is_ask: bool) -> ();

    /// Return first ask price, first ask quantity, and iterator of remaining ask levels.
    /// If there is no ask, return `Price::MIN` as price and `Quantity::ZERO` as quantity.
    /// You can safely drop iterator if there is no needs.
    fn best_ask<'s>(&'s self) -> (&'s Price, &'s Quantity, OrderbookIterator<'s>) {
        let mut iter_ask = self.iter_ask();
        let (price, quantity) = iter_ask.next().unwrap_or((&Price::MIN, &Quantity::ZERO));
        (price, quantity, iter_ask)
    }

    /// Return first bid price, first bid quantity, and iterator of remaining bid levels.
    /// If there is no bid, return `Price::MAX` as price and `Quantity::ZERO` as quantity.
    /// You can safely drop iterator if there is no needs.
    fn best_bid<'s>(&'s self) -> (&'s Price, &'s Quantity, OrderbookIterator<'s>) {
        let mut iter_bid = self.iter_bid();
        let (price, quantity) = iter_bid.next().unwrap_or((&Price::MAX, &Quantity::ZERO));
        (price, quantity, iter_bid)
    }

    /// Validate if current state of this orderbook is valid.
    fn validate(&self) -> bool {
        let (mut prev_ask_p, prev_ask_q, mut iter_ask) = self.best_ask();
        let (mut prev_bid_p, prev_bid_q, mut iter_bid) = self.best_bid();
        if (prev_ask_p != &Price::MIN && prev_bid_p != &Price::MIN && prev_ask_p < prev_bid_p)
            || (prev_ask_p != &Price::MIN && prev_ask_q.is_zero())
            || (prev_bid_p != &Price::MAX && prev_bid_q.is_zero())
        {
            // 1. Both ask and bid are not empty, but price is reversed
            // 2. Ask is not empty but ask quantity is zero
            // 3. Bid is not empty but bid quantity is zero
            return false;
        }

        while let Some((worse_ask_p, worse_ask_q)) = iter_ask.next() {
            // Ask prices should be in increasing order
            if prev_ask_p > worse_ask_p || worse_ask_q.is_zero() {
                return false;
            }
            prev_ask_p = worse_ask_p;
        }
        while let Some((worse_bid_p, worse_bid_q)) = iter_bid.next() {
            // Bid prices should be in decreasing order
            if prev_bid_p < worse_bid_p || worse_bid_q.is_zero() {
                return false;
            }
            prev_bid_p = worse_bid_p;
        }
        true
    }
}

/// Represents unsized orderbook.
#[derive(Clone, Default)]
pub struct UnsizedOrderbook {
    asks: BTreeMap<Price, Quantity>,
    bids: BTreeMap<Price, Quantity>,
}

impl Orderbook for UnsizedOrderbook {
    fn iter_ask<'s>(&'s self) -> WrappedIterator<'s, (&'s Price, &'s Quantity)> {
        self.asks.iter().wrap_iter()
    }

    fn iter_bid<'s>(&'s self) -> WrappedIterator<'s, (&'s Price, &'s Quantity)> {
        self.bids.iter().wrap_iter()
    }

    fn apply_delta(&mut self, price: Price, quantity: Quantity, is_ask: bool) -> () {
        let mapping = if is_ask {
            self.asks.borrow_mut()
        } else {
            self.bids.borrow_mut()
        };
        let entry = mapping.entry(price).or_default();
        *entry += quantity;
    }
}