1use prettytable::{row, table, Cell, Row, Table};
2use quoter::Quoter;
3use rust_decimal::{prelude::Zero, Decimal};
4use serde::Deserialize;
5use std::collections::BTreeMap;
6
7pub mod managed;
8pub mod quoter;
9
10pub trait OrderBook {
11 fn price_decimal_points(&self) -> u32;
13 fn quantity_decimal_points(&self) -> u32;
15 fn tick_size(&self) -> Decimal;
17 fn step_size(&self) -> Decimal;
19 fn bids(&self) -> &BTreeMap<Decimal, Decimal>;
21 fn asks(&self) -> &BTreeMap<Decimal, Decimal>;
22 fn bids_mut(&mut self) -> &mut BTreeMap<Decimal, Decimal>;
23 fn asks_mut(&mut self) -> &mut BTreeMap<Decimal, Decimal>;
24 fn best_bid(&self) -> (&Decimal, &Decimal) {
26 let bids = self.bids();
27 bids.last_key_value().unwrap()
28 }
29 fn best_ask(&self) -> (&Decimal, &Decimal) {
31 let asks = self.asks();
32 asks.first_key_value().unwrap()
33 }
34
35 fn update_book(book: &mut BTreeMap<Decimal, Decimal>, update: Vec<(Decimal, Decimal)>) {
36 for (price, quantity) in update {
37 if quantity.is_zero() {
38 book.remove(&price);
39 } else {
40 book.insert(price, quantity);
41 }
42 }
43 }
44
45 fn update_asks(&mut self, asks: Vec<(Decimal, Decimal)>) {
46 let mut book = self.asks_mut();
47 Self::update_book(&mut book, asks);
48 }
49
50 fn update_bids(&mut self, bids: Vec<(Decimal, Decimal)>) {
51 let mut book = self.bids_mut();
52 Self::update_book(&mut book, bids);
53 }
54}
55
56#[derive(Debug)]
57pub struct BinanceOrderBook {
58 symbol: String,
59 last_update_id: u128,
60 processed_events_count: u128,
61 bids: BTreeMap<Decimal, Decimal>,
62 asks: BTreeMap<Decimal, Decimal>,
63}
64
65impl BinanceOrderBook {
66 pub fn new(symbol: String, depth: Depth) -> Self {
67 let Depth {
68 bids,
69 asks,
70 last_update_id,
71 ..
72 } = depth;
73 let bids = BTreeMap::from_iter(bids.into_iter());
74 let asks = BTreeMap::from_iter(asks.into_iter());
75 Self {
76 symbol,
77 bids,
78 asks,
79 last_update_id,
80 processed_events_count: 0,
81 }
82 }
83
84 pub fn update(&mut self, depth_update: DepthUpdate) -> anyhow::Result<()> {
85 let DepthUpdate {
86 event_type,
87 event_time,
88 transaction_time,
89 symbol,
90 first_update_id,
91 final_update_id,
92 last_stream_final_update_id,
93 bids,
94 asks,
95 } = depth_update;
96
97 if self.processed_events_count == 0 {
98 if self.last_update_id < first_update_id || final_update_id < self.last_update_id {
99 return Err(anyhow::anyhow!("First event out of sync"));
100 }
101 } else {
102 if self.last_update_id != last_stream_final_update_id {
103 return Err(anyhow::anyhow!("Event out of sync"));
104 }
105 }
106
107 self.update_asks(asks);
108 self.update_bids(bids);
109
110 self.last_update_id = final_update_id;
111 self.processed_events_count += 1;
112
113 Ok(())
114 }
115
116 pub fn print(&self, levels: usize) {
117 let mut bid_table = Table::new();
118
119 bid_table.add_row(row!["Size", "Price"]);
120
121 for (price, quantity) in self.bids().iter().rev().take(levels) {
122 bid_table.add_row(Row::new(vec![
123 Cell::new(&quantity.to_string()),
124 Cell::new(&price.to_string()),
125 ]));
126 }
127
128 let mut ask_table = Table::new();
129
130 ask_table.add_row(row!["Price", "Size"]);
131
132 for (price, quantity) in self.asks().iter().take(levels) {
133 ask_table.add_row(Row::new(vec![
134 Cell::new(&price.to_string()),
135 Cell::new(&quantity.to_string()),
136 ]));
137 }
138
139 let table = table!(["Bids", "Asks"], [bid_table, ask_table]);
140
141 table.printstd();
142 }
143}
144
145impl OrderBook for BinanceOrderBook {
146 fn price_decimal_points(&self) -> u32 {
147 todo!()
148 }
149
150 fn quantity_decimal_points(&self) -> u32 {
151 todo!()
152 }
153
154 fn tick_size(&self) -> Decimal {
155 todo!()
156 }
157
158 fn step_size(&self) -> Decimal {
159 todo!()
160 }
161
162 fn bids(&self) -> &BTreeMap<Decimal, Decimal> {
163 &self.bids
164 }
165
166 fn asks(&self) -> &BTreeMap<Decimal, Decimal> {
167 &self.asks
168 }
169
170 fn bids_mut(&mut self) -> &mut BTreeMap<Decimal, Decimal> {
171 &mut self.bids
172 }
173
174 fn asks_mut(&mut self) -> &mut BTreeMap<Decimal, Decimal> {
175 &mut self.asks
176 }
177}
178
179impl Quoter for BinanceOrderBook {
180 fn buy(&self, size: &Decimal) -> Decimal {
181 let book = self.asks().iter();
182 <BinanceOrderBook as Quoter>::quote(size, book)
183 }
184
185 fn sell(&self, size: &Decimal) -> Decimal {
186 let book = self.bids().iter().rev();
187 <BinanceOrderBook as Quoter>::quote(size, book)
188 }
189}
190
191#[derive(Debug, Deserialize, Clone)]
192#[serde(rename_all = "camelCase")]
193pub struct Depth {
194 last_update_id: u128,
195 #[serde(rename = "E")]
196 event_time: u64,
197 #[serde(rename = "T")]
198 transaction_time: u64,
199 bids: Vec<(Decimal, Decimal)>,
200 asks: Vec<(Decimal, Decimal)>,
201}
202
203#[derive(Debug, Deserialize, Clone)]
204#[serde(rename_all = "camelCase")]
205pub struct DepthUpdate {
206 #[serde(rename = "e")]
207 event_type: String,
208 #[serde(rename = "E")]
209 event_time: u64,
210 #[serde(rename = "T")]
211 transaction_time: u64,
212 #[serde(rename = "s")]
213 symbol: String,
214 #[serde(rename = "U")]
215 first_update_id: u128,
216 #[serde(rename = "u")]
217 final_update_id: u128,
218 #[serde(rename = "pu")]
219 last_stream_final_update_id: u128,
220 #[serde(rename = "b")]
221 bids: Vec<(Decimal, Decimal)>,
222 #[serde(rename = "a")]
223 asks: Vec<(Decimal, Decimal)>,
224}
225
226#[cfg(test)]
227mod tests {
228
229 use super::*;
230
231 #[test]
232 fn it_works() {
233 let symbol = "RONINUSDT";
234 let data = r#"{"lastUpdateId":5573970560080,"E":1729534992908,"T":1729534992900,"bids":[["1.663900","31.0"],["1.663800","71.3"],["1.663700","30.6"],["1.663600","110.2"],["1.663500","76.7"]],"asks":[["1.664500","40.3"],["1.664700","10.0"],["1.664800","133.9"],["1.664900","214.9"],["1.665000","110.2"]]}"#;
235 let depth: Depth = serde_json::from_str(&data).unwrap();
236 let mut binance_order_book = BinanceOrderBook::new(symbol.to_string(), depth);
237 let best_bid = binance_order_book.best_bid();
238 let best_ask = binance_order_book.best_ask();
239 let data = r#"{"e":"depthUpdate","E":1729534992913,"T":1729534992900,"s":"RONINUSDT","U":5573970557923,"u":5573970560080,"pu":5573970557478,"b":[["1.331200","45.0"],["1.658900","1013.1"]],"a":[["1.669200","2277.2"],["1.670000","783.4"],["1.670100","1136.5"],["1.670900","739.9"],["1.675100","1103.4"],["1.676600","120.6"],["1.677800","2083.7"]]}"#;
242 let depth_update: DepthUpdate = serde_json::from_str(&data).unwrap();
243 let result = binance_order_book.update(depth_update);
245 dbg!(result);
246 let data = r#"{"e":"depthUpdate","E":1729534993015,"T":1729534993006,"s":"RONINUSDT","U":5573970560466,"u":5573970562617,"pu":5573970560080,"b":[["1.331200","0.0"],["1.655500","372.0"],["1.660700","2010.4"],["1.660800","2022.0"],["1.660900","759.9"],["1.662000","698.6"],["1.662200","139.2"],["1.662900","448.4"],["1.663000","143.0"],["1.663700","40.6"],["1.664000","30.3"]],"a":[["1.665500","72.5"],["1.666000","47.3"],["1.667700","795.3"],["1.669200","2081.3"],["1.669300","203.8"],["1.669600","757.7"],["1.670000","298.1"],["1.670800","1890.1"]]}"#;
248 let depth_update: DepthUpdate = serde_json::from_str(&data).unwrap();
249 let result = binance_order_book.update(depth_update);
251 dbg!(result);
252 dbg!(binance_order_book);
253 }
254}