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