rusty_model/data/
book_snapshot.rs1use std::fmt::{Display, Formatter};
2
3use parking_lot::RwLock;
4use rust_decimal::Decimal;
5use smallvec::SmallVec;
6use std::sync::Arc;
7
8use crate::data::orderbook::PriceLevel;
9use crate::instruments::InstrumentId;
10
11#[derive(Debug, Clone)]
17#[repr(align(64))]
18pub struct OrderBookSnapshot<const N: usize = 64> {
19 pub instrument_id: InstrumentId,
21
22 pub bids: SmallVec<[PriceLevel; N]>,
25
26 pub asks: SmallVec<[PriceLevel; N]>,
29
30 pub sequence_id: u64,
32
33 pub timestamp_event: u64,
35
36 pub timestamp_init: u64,
38}
39
40impl<const N: usize> OrderBookSnapshot<N> {
41 #[inline]
43 #[must_use]
44 pub const fn new(
45 instrument_id: InstrumentId,
46 bids: SmallVec<[PriceLevel; N]>,
47 asks: SmallVec<[PriceLevel; N]>,
48 sequence_id: u64,
49 timestamp_event: u64,
50 timestamp_init: u64,
51 ) -> Self {
52 Self {
53 instrument_id,
54 bids,
55 asks,
56 sequence_id,
57 timestamp_event,
58 timestamp_init,
59 }
60 }
61
62 #[inline]
64 #[must_use]
65 pub fn new_empty(instrument_id: InstrumentId, timestamp_init: u64, sequence_id: u64) -> Self {
66 Self {
67 instrument_id,
68 bids: SmallVec::with_capacity(20), asks: SmallVec::with_capacity(20), sequence_id,
71 timestamp_event: 0,
72 timestamp_init,
73 }
74 }
75
76 #[inline]
78 #[must_use]
79 pub fn from_arrays(
80 instrument_id: InstrumentId,
81 bids: &[(Decimal, Decimal)],
82 asks: &[(Decimal, Decimal)],
83 sequence_id: u64,
84 timestamp_event: u64,
85 timestamp_init: u64,
86 ) -> Self {
87 let mut bids_vec = SmallVec::with_capacity(bids.len());
88 let mut asks_vec = SmallVec::with_capacity(asks.len());
89
90 for &(price, size) in bids {
91 bids_vec.push(PriceLevel::new(price, size));
92 }
93
94 for &(price, size) in asks {
95 asks_vec.push(PriceLevel::new(price, size));
96 }
97
98 bids_vec.sort_unstable_by(|a: &PriceLevel, b: &PriceLevel| b.price.cmp(&a.price));
100
101 asks_vec.sort_unstable_by(|a: &PriceLevel, b: &PriceLevel| a.price.cmp(&b.price));
103
104 Self::new(
105 instrument_id,
106 bids_vec,
107 asks_vec,
108 sequence_id,
109 timestamp_event,
110 timestamp_init,
111 )
112 }
113
114 #[inline]
116 #[must_use]
117 pub fn best_bid(&self) -> Option<PriceLevel> {
118 self.bids.first().copied()
119 }
120
121 #[inline]
123 #[must_use]
124 pub fn best_ask(&self) -> Option<PriceLevel> {
125 self.asks.first().copied()
126 }
127
128 #[inline]
130 #[must_use]
131 pub fn mid_price(&self) -> Option<Decimal> {
132 match (self.best_bid(), self.best_ask()) {
133 (Some(bid), Some(ask)) => Some((bid.price + ask.price) / Decimal::TWO),
134 _ => None,
135 }
136 }
137
138 #[inline]
140 #[must_use]
141 pub fn spread(&self) -> Option<Decimal> {
142 match (self.best_bid(), self.best_ask()) {
143 (Some(bid), Some(ask)) => Some(ask.price - bid.price),
144 _ => None,
145 }
146 }
147
148 #[inline]
150 #[must_use]
151 pub const fn latency(&self) -> u64 {
152 if self.timestamp_event == 0 {
153 return 0;
154 }
155 self.timestamp_init.saturating_sub(self.timestamp_event)
156 }
157
158 #[inline]
160 pub fn add_bid(&mut self, price: Decimal, size: Decimal) {
161 self.bids.push(PriceLevel::new(price, size));
163
164 self.bids.sort_unstable_by(|a, b| b.price.cmp(&a.price));
166 }
167
168 #[inline]
170 pub fn add_ask(&mut self, price: Decimal, size: Decimal) {
171 self.asks.push(PriceLevel::new(price, size));
173
174 self.asks.sort_unstable_by(|a, b| a.price.cmp(&b.price));
176 }
177}
178
179#[derive(Clone)]
181pub struct SharedOrderBookSnapshot<const N: usize = 64> {
182 inner: Arc<RwLock<OrderBookSnapshot<N>>>,
184}
185
186impl<const N: usize> SharedOrderBookSnapshot<N> {
187 #[inline]
189 #[must_use]
190 pub fn new(book: OrderBookSnapshot<N>) -> Self {
191 Self {
192 inner: Arc::new(RwLock::new(book)),
193 }
194 }
195
196 #[inline]
198 pub fn update(&self, book: OrderBookSnapshot<N>) {
199 let mut write_guard = self.inner.write();
200 *write_guard = book;
201 }
202
203 #[inline]
205 pub fn read<F, R>(&self, f: F) -> R
206 where
207 F: FnOnce(&OrderBookSnapshot<N>) -> R,
208 {
209 let read_guard = self.inner.read();
210 f(&read_guard)
211 }
212
213 #[inline]
215 pub fn write<F, R>(&self, f: F) -> R
216 where
217 F: FnOnce(&mut OrderBookSnapshot<N>) -> R,
218 {
219 let mut write_guard = self.inner.write();
220 f(&mut write_guard)
221 }
222}
223
224impl<const N: usize> Display for OrderBookSnapshot<N> {
225 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
226 write!(
227 f,
228 "OrderBookSnapshot(instrument_id: {}, bids: {} levels, asks: {} levels, timestamp_event: {}, timestamp_init: {})",
229 self.instrument_id,
230 self.bids.len(),
231 self.asks.len(),
232 self.timestamp_event,
233 self.timestamp_init,
234 )
235 }
236}
237
238pub type OrderBookSnapshot64 = OrderBookSnapshot<64>;
241pub type OrderBookSnapshot32 = OrderBookSnapshot<32>;
243pub type OrderBookSnapshot128 = OrderBookSnapshot<128>;
245
246pub type SharedOrderBookSnapshot64 = SharedOrderBookSnapshot<64>;
248pub type SharedOrderBookSnapshot32 = SharedOrderBookSnapshot<32>;
250pub type SharedOrderBookSnapshot128 = SharedOrderBookSnapshot<128>;
252
253pub use OrderBookSnapshot64 as DefaultOrderBookSnapshot;
255pub use SharedOrderBookSnapshot64 as DefaultSharedOrderBookSnapshot;