1use std::{
19 fmt::Display,
20 hash::{Hash, Hasher},
21 ops::{Deref, DerefMut},
22};
23
24use nautilus_core::{
25 UnixNanos,
26 correctness::{FAILED, check_predicate_true},
27};
28use serde::{Deserialize, Serialize};
29
30use super::{HasTsInit, OrderBookDelta};
31use crate::identifiers::InstrumentId;
32
33#[derive(Clone, Debug, Serialize, Deserialize)]
37#[cfg_attr(
38 feature = "python",
39 pyo3::pyclass(module = "posei_trader.core.nautilus_pyo3.model")
40)]
41pub struct OrderBookDeltas {
42 pub instrument_id: InstrumentId,
44 pub deltas: Vec<OrderBookDelta>,
46 pub flags: u8,
48 pub sequence: u64,
50 pub ts_event: UnixNanos,
52 pub ts_init: UnixNanos,
54}
55
56impl OrderBookDeltas {
57 #[must_use]
63 #[allow(clippy::too_many_arguments)]
64 pub fn new(instrument_id: InstrumentId, deltas: Vec<OrderBookDelta>) -> Self {
65 Self::new_checked(instrument_id, deltas).expect(FAILED)
66 }
67
68 #[allow(clippy::too_many_arguments)]
74 pub fn new_checked(
84 instrument_id: InstrumentId,
85 deltas: Vec<OrderBookDelta>,
86 ) -> anyhow::Result<Self> {
87 check_predicate_true(!deltas.is_empty(), "`deltas` cannot be empty")?;
88 let last = deltas.last().unwrap();
90 let flags = last.flags;
91 let sequence = last.sequence;
92 let ts_event = last.ts_event;
93 let ts_init = last.ts_init;
94 Ok(Self {
95 instrument_id,
96 deltas,
97 flags,
98 sequence,
99 ts_event,
100 ts_init,
101 })
102 }
103}
104
105impl PartialEq<Self> for OrderBookDeltas {
106 fn eq(&self, other: &Self) -> bool {
107 self.instrument_id == other.instrument_id && self.sequence == other.sequence
108 }
109}
110
111impl Eq for OrderBookDeltas {}
112
113impl Hash for OrderBookDeltas {
114 fn hash<H: Hasher>(&self, state: &mut H) {
115 self.instrument_id.hash(state);
116 self.sequence.hash(state);
117 }
118}
119
120impl Display for OrderBookDeltas {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 write!(
127 f,
128 "{},len={},flags={},sequence={},ts_event={},ts_init={}",
129 self.instrument_id,
130 self.deltas.len(),
131 self.flags,
132 self.sequence,
133 self.ts_event,
134 self.ts_init
135 )
136 }
137}
138
139impl HasTsInit for OrderBookDeltas {
140 fn ts_init(&self) -> UnixNanos {
141 self.ts_init
142 }
143}
144
145#[repr(C)]
154#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
155#[allow(non_camel_case_types)]
156pub struct OrderBookDeltas_API(Box<OrderBookDeltas>);
157
158impl OrderBookDeltas_API {
160 #[must_use]
161 pub fn new(deltas: OrderBookDeltas) -> Self {
162 Self(Box::new(deltas))
163 }
164
165 #[must_use]
167 pub fn into_inner(self) -> OrderBookDeltas {
168 *self.0
169 }
170}
171
172impl Deref for OrderBookDeltas_API {
173 type Target = OrderBookDeltas;
174
175 fn deref(&self) -> &Self::Target {
176 &self.0
177 }
178}
179
180impl DerefMut for OrderBookDeltas_API {
181 fn deref_mut(&mut self) -> &mut Self::Target {
182 &mut self.0
183 }
184}
185
186#[cfg(test)]
190mod tests {
191 use rstest::rstest;
192
193 use super::*;
194 use crate::{
195 data::{order::BookOrder, stubs::stub_deltas},
196 enums::{BookAction, OrderSide},
197 types::{Price, Quantity},
198 };
199
200 #[rstest]
201 fn test_new() {
202 let instrument_id = InstrumentId::from("AAPL.XNAS");
203 let flags = 32; let sequence = 0;
205 let ts_event = 1;
206 let ts_init = 2;
207
208 let delta0 =
209 OrderBookDelta::clear(instrument_id, sequence, ts_event.into(), ts_init.into());
210 let delta1 = OrderBookDelta::new(
211 instrument_id,
212 BookAction::Add,
213 BookOrder::new(
214 OrderSide::Sell,
215 Price::from("102.00"),
216 Quantity::from("300"),
217 1,
218 ),
219 flags,
220 sequence,
221 ts_event.into(),
222 ts_init.into(),
223 );
224 let delta2 = OrderBookDelta::new(
225 instrument_id,
226 BookAction::Add,
227 BookOrder::new(
228 OrderSide::Sell,
229 Price::from("101.00"),
230 Quantity::from("200"),
231 2,
232 ),
233 flags,
234 sequence,
235 ts_event.into(),
236 ts_init.into(),
237 );
238 let delta3 = OrderBookDelta::new(
239 instrument_id,
240 BookAction::Add,
241 BookOrder::new(
242 OrderSide::Sell,
243 Price::from("100.00"),
244 Quantity::from("100"),
245 3,
246 ),
247 flags,
248 sequence,
249 ts_event.into(),
250 ts_init.into(),
251 );
252 let delta4 = OrderBookDelta::new(
253 instrument_id,
254 BookAction::Add,
255 BookOrder::new(
256 OrderSide::Buy,
257 Price::from("99.00"),
258 Quantity::from("100"),
259 4,
260 ),
261 flags,
262 sequence,
263 ts_event.into(),
264 ts_init.into(),
265 );
266 let delta5 = OrderBookDelta::new(
267 instrument_id,
268 BookAction::Add,
269 BookOrder::new(
270 OrderSide::Buy,
271 Price::from("98.00"),
272 Quantity::from("200"),
273 5,
274 ),
275 flags,
276 sequence,
277 ts_event.into(),
278 ts_init.into(),
279 );
280 let delta6 = OrderBookDelta::new(
281 instrument_id,
282 BookAction::Add,
283 BookOrder::new(
284 OrderSide::Buy,
285 Price::from("97.00"),
286 Quantity::from("300"),
287 6,
288 ),
289 flags,
290 sequence,
291 ts_event.into(),
292 ts_init.into(),
293 );
294
295 let deltas = OrderBookDeltas::new(
296 instrument_id,
297 vec![delta0, delta1, delta2, delta3, delta4, delta5, delta6],
298 );
299
300 assert_eq!(deltas.instrument_id, instrument_id);
301 assert_eq!(deltas.deltas.len(), 7);
302 assert_eq!(deltas.flags, flags);
303 assert_eq!(deltas.sequence, sequence);
304 assert_eq!(deltas.ts_event, ts_event);
305 assert_eq!(deltas.ts_init, ts_init);
306 }
307
308 #[rstest]
310 fn test_display(stub_deltas: OrderBookDeltas) {
311 let deltas = stub_deltas;
312 assert_eq!(
313 format!("{deltas}"),
314 "AAPL.XNAS,len=7,flags=32,sequence=0,ts_event=1,ts_init=2".to_string()
315 );
316 }
317}