obrewin_data_structures/
market.rs1use 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
11pub type Price = Decimal;
13
14pub type Quantity = Decimal;
16
17#[derive(Clone, Deserialize)]
19pub struct Trade {
20 pub price: Price,
21 pub quantity: Quantity,
22 pub trade_id: String,
24 pub timestamp: DateTimeUTC,
26 pub is_buyer_maker: Option<bool>,
28}
29
30pub enum DirectOrderbookChange {
32 Set { price: Price, quantity: Quantity },
34 Delta { price: Price, quantity: Quantity },
36}
37
38pub enum OrderbookChange {
40 DirectChange(Vec<DirectOrderbookChange>),
41 Snapshot(Box<dyn Orderbook>),
42}
43
44impl DirectOrderbookChange {
45 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 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
95type OrderbookIterator<'s> = WrappedIterator<'s, (&'s Price, &'s Quantity)>;
97
98pub trait Orderbook {
109 fn iter_ask<'s>(&'s self) -> OrderbookIterator<'s>;
111 fn iter_bid<'s>(&'s self) -> OrderbookIterator<'s>;
113
114 fn apply_change(&mut self, change: &DirectOrderbookChange, is_ask: bool) -> ();
117
118 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 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 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 return false;
148 }
149
150 while let Some((worse_ask_p, worse_ask_q)) = iter_ask.next() {
151 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 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#[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}