rusty_model/data/
orderbook.rs1use parking_lot::RwLock;
4use rust_decimal::Decimal;
5use smallvec::SmallVec;
6use smartstring::alias::String;
7use std::sync::Arc;
8
9#[repr(align(16))] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct PriceLevel {
13 pub price: Decimal,
15 pub quantity: Decimal,
17}
18
19impl PriceLevel {
20 #[must_use]
22 pub const fn new(price: Decimal, quantity: Decimal) -> Self {
23 Self { price, quantity }
24 }
25}
26
27#[repr(align(64))] #[derive(Debug, Clone)]
30pub struct OrderBook<const N: usize = 64> {
31 pub symbol: String,
33 pub exchange_timestamp_ns: u64,
35 pub system_timestamp_ns: u64,
37
38 pub bids: SmallVec<[PriceLevel; N]>, pub asks: SmallVec<[PriceLevel; N]>, }
43
44impl<const N: usize> OrderBook<N> {
45 #[must_use]
47 pub fn new(
48 symbol: impl AsRef<str>,
49 exchange_timestamp_ns: u64,
50 system_timestamp_ns: u64,
51 bids: SmallVec<[PriceLevel; N]>,
52 asks: SmallVec<[PriceLevel; N]>,
53 ) -> Self {
54 Self {
55 symbol: String::from(symbol.as_ref()),
56 exchange_timestamp_ns,
57 system_timestamp_ns,
58 bids,
59 asks,
60 }
61 }
62
63 pub fn from_iters<B, A>(
65 symbol: impl AsRef<str>,
66 exchange_timestamp_ns: u64,
67 system_timestamp_ns: u64,
68 bids: B,
69 asks: A,
70 ) -> Self
71 where
72 B: IntoIterator<Item = PriceLevel>,
73 A: IntoIterator<Item = PriceLevel>,
74 {
75 Self {
76 symbol: String::from(symbol.as_ref()),
77 exchange_timestamp_ns,
78 system_timestamp_ns,
79 bids: bids.into_iter().collect(),
80 asks: asks.into_iter().collect(),
81 }
82 }
83
84 #[must_use]
86 pub fn new_empty(instrument_id: crate::instruments::InstrumentId) -> Self {
87 Self {
88 symbol: instrument_id.symbol,
89 exchange_timestamp_ns: 0,
90 system_timestamp_ns: 0,
91 bids: SmallVec::with_capacity(20),
92 asks: SmallVec::with_capacity(20),
93 }
94 }
95
96 #[must_use]
98 pub fn bids(&self) -> &[PriceLevel] {
99 &self.bids
100 }
101
102 #[must_use]
104 pub fn asks(&self) -> &[PriceLevel] {
105 &self.asks
106 }
107
108 #[must_use]
110 pub fn best_bid(&self) -> Option<&PriceLevel> {
111 self.bids.first()
112 }
113
114 #[must_use]
116 pub fn best_ask(&self) -> Option<&PriceLevel> {
117 self.asks.first()
118 }
119
120 #[must_use]
122 pub fn spread(&self) -> Option<Decimal> {
123 match (self.best_bid(), self.best_ask()) {
124 (Some(bid), Some(ask)) => Some(ask.price - bid.price),
125 _ => None,
126 }
127 }
128
129 pub fn add_bid(&mut self, price: Decimal, quantity: Decimal) {
131 let level = PriceLevel::new(price, quantity);
132 match self
133 .bids
134 .binary_search_by(|probe| probe.price.cmp(&price).reverse())
135 {
136 Ok(i) => self.bids[i].quantity += quantity,
137 Err(i) => self.bids.insert(i, level),
138 }
139 }
140
141 pub fn add_ask(&mut self, price: Decimal, quantity: Decimal) {
143 let level = PriceLevel::new(price, quantity);
144 match self.asks.binary_search_by(|probe| probe.price.cmp(&price)) {
145 Ok(i) => self.asks[i].quantity += quantity,
146 Err(i) => self.asks.insert(i, level),
147 }
148 }
149
150 pub fn remove_bid(&mut self, price: Decimal) {
152 self.bids.retain(|level| level.price != price);
153 }
154
155 pub fn remove_ask(&mut self, price: Decimal) {
157 self.asks.retain(|level| level.price != price);
158 }
159
160 pub fn apply_snapshot(&mut self, snapshot: crate::data::book_snapshot::OrderBookSnapshot<N>) {
163 self.exchange_timestamp_ns = snapshot.timestamp_event;
164 self.system_timestamp_ns = crate::common::current_time_ns();
165
166 self.bids.clear();
168 self.bids.extend(
169 snapshot
170 .bids
171 .into_iter()
172 .map(|level| PriceLevel::new(level.price, level.quantity)),
173 );
174
175 self.asks.clear();
177 self.asks.extend(
178 snapshot
179 .asks
180 .into_iter()
181 .map(|level| PriceLevel::new(level.price, level.quantity)),
182 );
183 }
184}
185
186#[derive(Debug, Clone)]
188pub struct SharedOrderBook<const N: usize = 64>(Arc<RwLock<OrderBook<N>>>);
189
190impl<const N: usize> SharedOrderBook<N> {
191 #[must_use]
193 pub fn new(order_book: OrderBook<N>) -> Self {
194 Self(Arc::new(RwLock::new(order_book)))
195 }
196
197 pub fn read<R, F>(&self, f: F) -> R
199 where
200 F: FnOnce(&OrderBook<N>) -> R,
201 {
202 let guard = self.0.read();
203 f(&guard)
204 }
205
206 pub fn write<R, F>(&self, f: F) -> R
208 where
209 F: FnOnce(&mut OrderBook<N>) -> R,
210 {
211 let mut guard = self.0.write();
212 f(&mut guard)
213 }
214}
215
216pub type OrderBook64 = OrderBook<64>;
219pub type OrderBook32 = OrderBook<32>;
221pub type OrderBook128 = OrderBook<128>;
223
224pub type SharedOrderBook64 = SharedOrderBook<64>;
226pub type SharedOrderBook32 = SharedOrderBook<32>;
228pub type SharedOrderBook128 = SharedOrderBook<128>;
230
231pub use OrderBook64 as DefaultOrderBook;
233pub use SharedOrderBook64 as DefaultSharedOrderBook;