obrewin_data_structures/
market.rs

1use rust_decimal::Decimal;
2use serde::Deserialize;
3
4use std::borrow::BorrowMut;
5use std::collections::BTreeMap;
6use std::ops::Neg;
7
8use crate::misc::DateTimeUTC;
9use crate::utils::{WrapIterator, WrappedIterator};
10
11/// Alias for price type.
12pub type Price = Decimal;
13
14/// Alias for stock/contract amount type.
15pub type Quantity = Decimal;
16
17/// Represents raw trade.
18#[derive(Clone, Deserialize)]
19pub struct Trade {
20    pub price: Price,
21    pub quantity: Quantity,
22    /// The trade ID officially marked by the exchange.
23    pub trade_id: String,
24    /// Received timestamp of the trade
25    pub timestamp: DateTimeUTC,
26    /// Is buyer maker of this trade?
27    pub is_buyer_maker: Option<bool>,
28}
29
30/// Represents single change on an orderbook.
31pub enum DirectOrderbookChange {
32    /// Set `= quantity` at given `price` level.
33    Set { price: Price, quantity: Quantity },
34    /// Set `+= quantity` at given `price` level.
35    Delta { price: Price, quantity: Quantity },
36}
37
38/// Represents any orderbook change.
39pub enum OrderbookChange {
40    DirectChange(Vec<DirectOrderbookChange>),
41    Snapshot(Box<dyn Orderbook>),
42}
43
44impl DirectOrderbookChange {
45    /// Get price target level.
46    fn get_price_level(&self) -> Price {
47        match self {
48            Self::Set {
49                price,
50                quantity: _q,
51            } => *price,
52            Self::Delta {
53                price,
54                quantity: _q,
55            } => *price,
56        }
57    }
58
59    /// Apply current change into given `quantity`.
60    fn apply(&self, quantity: &mut Quantity) -> () {
61        match self {
62            Self::Set {
63                price: _p,
64                quantity: q,
65            } => {
66                *quantity = *q;
67            }
68            Self::Delta {
69                price: _p,
70                quantity: q,
71            } => {
72                *quantity += q;
73            }
74        }
75    }
76}
77
78impl Neg for DirectOrderbookChange {
79    type Output = Self;
80
81    fn neg(self) -> Self::Output {
82        match self {
83            Self::Set { price, quantity } => Self::Set {
84                price,
85                quantity: -quantity,
86            },
87            Self::Delta { price, quantity } => Self::Delta {
88                price,
89                quantity: -quantity,
90            },
91        }
92    }
93}
94
95/// Type alias for orderbook iterators.
96type OrderbookIterator<'s> = WrappedIterator<'s, (&'s Price, &'s Quantity)>;
97
98/// Trait for orderbook.
99/// The reason why I made a trait for this is because
100/// there can be several different structs of an Orderbook.
101///
102/// Following list contains variations on level;
103/// - L1: Best ask/bid
104/// - L2: Aggregated asks/bids
105/// - L3: Non-aggregated asks/bids (Currently not supported)
106///
107/// And also an orderbook can be unsized or have fixed size.
108pub trait Orderbook {
109    /// Return an iterator that yields asks from the best to the worst.
110    fn iter_ask<'s>(&'s self) -> OrderbookIterator<'s>;
111    /// Return an iterator that yields bids from the best to the worst.
112    fn iter_bid<'s>(&'s self) -> OrderbookIterator<'s>;
113
114    /// Apply change on current orderbook.
115    /// This method does not guarantee that the modified state is valid.
116    fn apply_change(&mut self, change: &DirectOrderbookChange, is_ask: bool) -> ();
117
118    /// Return first ask price, first ask quantity, and iterator of remaining ask levels.
119    /// If there is no ask, return `Price::MIN` as price and `Quantity::ZERO` as quantity.
120    /// You can safely drop iterator if there is no needs.
121    fn best_ask<'s>(&'s self) -> (&'s Price, &'s Quantity, OrderbookIterator<'s>) {
122        let mut iter_ask = self.iter_ask();
123        let (price, quantity) = iter_ask.next().unwrap_or((&Price::MIN, &Quantity::ZERO));
124        (price, quantity, iter_ask)
125    }
126
127    /// Return first bid price, first bid quantity, and iterator of remaining bid levels.
128    /// If there is no bid, return `Price::MAX` as price and `Quantity::ZERO` as quantity.
129    /// You can safely drop iterator if there is no needs.
130    fn best_bid<'s>(&'s self) -> (&'s Price, &'s Quantity, OrderbookIterator<'s>) {
131        let mut iter_bid = self.iter_bid();
132        let (price, quantity) = iter_bid.next().unwrap_or((&Price::MAX, &Quantity::ZERO));
133        (price, quantity, iter_bid)
134    }
135
136    /// Validate if current state of this orderbook is valid.
137    fn validate(&self) -> bool {
138        let (mut prev_ask_p, prev_ask_q, mut iter_ask) = self.best_ask();
139        let (mut prev_bid_p, prev_bid_q, mut iter_bid) = self.best_bid();
140        if (prev_ask_p != &Price::MIN && prev_bid_p != &Price::MIN && prev_ask_p < prev_bid_p)
141            || (prev_ask_p != &Price::MIN && prev_ask_q.is_zero())
142            || (prev_bid_p != &Price::MAX && prev_bid_q.is_zero())
143        {
144            // 1. Both ask and bid are not empty, but price is reversed
145            // 2. Ask is not empty but ask quantity is zero
146            // 3. Bid is not empty but bid quantity is zero
147            return false;
148        }
149
150        while let Some((worse_ask_p, worse_ask_q)) = iter_ask.next() {
151            // Ask prices should be in increasing order
152            if prev_ask_p > worse_ask_p || worse_ask_q.is_zero() {
153                return false;
154            }
155            prev_ask_p = worse_ask_p;
156        }
157        while let Some((worse_bid_p, worse_bid_q)) = iter_bid.next() {
158            // Bid prices should be in decreasing order
159            if prev_bid_p < worse_bid_p || worse_bid_q.is_zero() {
160                return false;
161            }
162            prev_bid_p = worse_bid_p;
163        }
164        true
165    }
166}
167
168/// Represents unsized orderbook.
169#[derive(Clone, Default)]
170pub struct UnsizedOrderbook {
171    asks: BTreeMap<Price, Quantity>,
172    bids: BTreeMap<Price, Quantity>,
173}
174
175impl Orderbook for UnsizedOrderbook {
176    fn iter_ask<'s>(&'s self) -> WrappedIterator<'s, (&'s Price, &'s Quantity)> {
177        self.asks.iter().wrap_iter()
178    }
179
180    fn iter_bid<'s>(&'s self) -> WrappedIterator<'s, (&'s Price, &'s Quantity)> {
181        self.bids.iter().wrap_iter()
182    }
183
184    fn apply_change(&mut self, change: &DirectOrderbookChange, is_ask: bool) -> () {
185        let mapping = if is_ask {
186            self.asks.borrow_mut()
187        } else {
188            self.bids.borrow_mut()
189        };
190        let entry = mapping.entry(change.get_price_level()).or_default();
191        change.apply(entry);
192    }
193}