Skip to main content

nautilus_model/data/
deltas.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! An `OrderBookDeltas` container type to carry a bulk of `OrderBookDelta` records.
17
18use 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/// Represents a grouped batch of `OrderBookDelta` updates for an `OrderBook`.
34///
35/// This type cannot be `repr(C)` due to the `deltas` vec.
36#[derive(Clone, Debug, Serialize, Deserialize)]
37#[cfg_attr(
38    feature = "python",
39    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model", from_py_object)
40)]
41#[cfg_attr(
42    feature = "python",
43    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
44)]
45pub struct OrderBookDeltas {
46    /// The instrument ID for the book.
47    pub instrument_id: InstrumentId,
48    /// The order book deltas.
49    pub deltas: Vec<OrderBookDelta>,
50    /// The record flags bit field, indicating event end and data information.
51    pub flags: u8,
52    /// The message sequence number assigned at the venue.
53    pub sequence: u64,
54    /// UNIX timestamp (nanoseconds) when the book event occurred.
55    pub ts_event: UnixNanos,
56    /// UNIX timestamp (nanoseconds) when the instance was created.
57    pub ts_init: UnixNanos,
58}
59
60impl OrderBookDeltas {
61    /// Creates a new [`OrderBookDeltas`] instance.
62    ///
63    /// # Panics
64    ///
65    /// Panics if `deltas` is empty and correctness check fails.
66    #[must_use]
67    pub fn new(instrument_id: InstrumentId, deltas: Vec<OrderBookDelta>) -> Self {
68        Self::new_checked(instrument_id, deltas).expect(FAILED)
69    }
70
71    /// Creates a new [`OrderBookDeltas`] instance with correctness checking.
72    ///
73    /// # Notes
74    ///
75    /// PyO3 requires a `Result` type for proper error handling and stacktrace printing in Python.
76    /// Creates a new [`OrderBookDeltas`] instance with correctness checking.
77    ///
78    /// # Errors
79    ///
80    /// Returns an error if `deltas` is empty.
81    #[expect(clippy::missing_panics_doc)] // Guarded by predicate check above
82    pub fn new_checked(
83        instrument_id: InstrumentId,
84        deltas: Vec<OrderBookDelta>,
85    ) -> anyhow::Result<Self> {
86        check_predicate_true(!deltas.is_empty(), "`deltas` cannot be empty")?;
87        let last = deltas.last().expect("deltas not empty");
88        let flags = last.flags;
89        let sequence = last.sequence;
90        let ts_event = last.ts_event;
91        let ts_init = last.ts_init;
92        Ok(Self {
93            instrument_id,
94            deltas,
95            flags,
96            sequence,
97            ts_event,
98            ts_init,
99        })
100    }
101}
102
103impl PartialEq<Self> for OrderBookDeltas {
104    fn eq(&self, other: &Self) -> bool {
105        self.instrument_id == other.instrument_id && self.sequence == other.sequence
106    }
107}
108
109impl Eq for OrderBookDeltas {}
110
111impl Hash for OrderBookDeltas {
112    fn hash<H: Hasher>(&self, state: &mut H) {
113        self.instrument_id.hash(state);
114        self.sequence.hash(state);
115    }
116}
117
118// TODO: Implement
119// impl Serializable for OrderBookDeltas {}
120
121// TODO: Exact format for Debug and Display TBD
122impl Display for OrderBookDeltas {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        write!(
125            f,
126            "{},len={},flags={},sequence={},ts_event={},ts_init={}",
127            self.instrument_id,
128            self.deltas.len(),
129            self.flags,
130            self.sequence,
131            self.ts_event,
132            self.ts_init
133        )
134    }
135}
136
137impl HasTsInit for OrderBookDeltas {
138    fn ts_init(&self) -> UnixNanos {
139        self.ts_init
140    }
141}
142
143/// C compatible Foreign Function Interface (FFI) for an underlying [`OrderBookDeltas`].
144///
145/// This struct wraps `OrderBookDeltas` in a way that makes it compatible with C function
146/// calls, enabling interaction with `OrderBookDeltas` in a C environment.
147///
148/// It implements the `Deref` trait, allowing instances of `OrderBookDeltas_API` to be
149/// dereferenced to `OrderBookDeltas`, providing access to `OrderBookDeltas`'s methods without
150/// having to manually access the underlying `OrderBookDeltas` instance.
151#[repr(C)]
152#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
153#[allow(non_camel_case_types)]
154pub struct OrderBookDeltas_API(Box<OrderBookDeltas>);
155
156// TODO: This wrapper will go along with Cython
157impl OrderBookDeltas_API {
158    #[must_use]
159    pub fn new(deltas: OrderBookDeltas) -> Self {
160        Self(Box::new(deltas))
161    }
162
163    /// Consumes the wrapper and returns the inner `OrderBookDeltas`.
164    #[must_use]
165    pub fn into_inner(self) -> OrderBookDeltas {
166        *self.0
167    }
168}
169
170impl Deref for OrderBookDeltas_API {
171    type Target = OrderBookDeltas;
172
173    fn deref(&self) -> &Self::Target {
174        &self.0
175    }
176}
177
178impl DerefMut for OrderBookDeltas_API {
179    fn deref_mut(&mut self) -> &mut Self::Target {
180        &mut self.0
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use std::{
187        collections::hash_map::DefaultHasher,
188        hash::{Hash, Hasher},
189    };
190
191    use rstest::rstest;
192    use serde_json;
193
194    use super::*;
195    use crate::{
196        data::{order::BookOrder, stubs::stub_deltas},
197        enums::{BookAction, OrderSide},
198        types::{Price, Quantity},
199    };
200
201    fn create_test_delta() -> OrderBookDelta {
202        let instrument_id = InstrumentId::from("EURUSD.SIM");
203        OrderBookDelta::new(
204            instrument_id,
205            BookAction::Add,
206            BookOrder::new(
207                OrderSide::Buy,
208                Price::from("1.0500"),
209                Quantity::from("100000"),
210                1,
211            ),
212            0,
213            123,
214            UnixNanos::from(1_000_000_000),
215            UnixNanos::from(2_000_000_000),
216        )
217    }
218
219    fn create_test_deltas() -> OrderBookDeltas {
220        let instrument_id = InstrumentId::from("EURUSD.SIM");
221        let flags = 32;
222        let sequence = 123;
223        let ts_event = UnixNanos::from(1_000_000_000);
224        let ts_init = UnixNanos::from(2_000_000_000);
225
226        let delta1 = OrderBookDelta::new(
227            instrument_id,
228            BookAction::Add,
229            BookOrder::new(
230                OrderSide::Sell,
231                Price::from("1.0520"),
232                Quantity::from("50000"),
233                1,
234            ),
235            flags,
236            sequence,
237            ts_event,
238            ts_init,
239        );
240        let delta2 = OrderBookDelta::new(
241            instrument_id,
242            BookAction::Add,
243            BookOrder::new(
244                OrderSide::Buy,
245                Price::from("1.0500"),
246                Quantity::from("75000"),
247                2,
248            ),
249            flags,
250            sequence,
251            ts_event,
252            ts_init,
253        );
254
255        OrderBookDeltas::new(instrument_id, vec![delta1, delta2])
256    }
257
258    fn create_test_deltas_multiple() -> OrderBookDeltas {
259        let instrument_id = InstrumentId::from("GBPUSD.SIM");
260        let flags = 16;
261        let sequence = 456;
262        let ts_event = UnixNanos::from(3_000_000_000);
263        let ts_init = UnixNanos::from(4_000_000_000);
264
265        let deltas = vec![
266            OrderBookDelta::clear(instrument_id, sequence, ts_event, ts_init),
267            OrderBookDelta::new(
268                instrument_id,
269                BookAction::Add,
270                BookOrder::new(
271                    OrderSide::Sell,
272                    Price::from("1.2550"),
273                    Quantity::from("100000"),
274                    1,
275                ),
276                flags,
277                sequence,
278                ts_event,
279                ts_init,
280            ),
281            OrderBookDelta::new(
282                instrument_id,
283                BookAction::Update,
284                BookOrder::new(
285                    OrderSide::Buy,
286                    Price::from("1.2530"),
287                    Quantity::from("200000"),
288                    2,
289                ),
290                flags,
291                sequence,
292                ts_event,
293                ts_init,
294            ),
295            OrderBookDelta::new(
296                instrument_id,
297                BookAction::Delete,
298                BookOrder::new(
299                    OrderSide::Sell,
300                    Price::from("1.2560"),
301                    Quantity::from("0"),
302                    3,
303                ),
304                flags,
305                sequence,
306                ts_event,
307                ts_init,
308            ),
309        ];
310
311        OrderBookDeltas::new(instrument_id, deltas)
312    }
313
314    #[rstest]
315    fn test_order_book_deltas_new() {
316        let deltas = create_test_deltas();
317
318        assert_eq!(deltas.instrument_id, InstrumentId::from("EURUSD.SIM"));
319        assert_eq!(deltas.deltas.len(), 2);
320        assert_eq!(deltas.flags, 32);
321        assert_eq!(deltas.sequence, 123);
322        assert_eq!(deltas.ts_event, UnixNanos::from(1_000_000_000));
323        assert_eq!(deltas.ts_init, UnixNanos::from(2_000_000_000));
324    }
325
326    #[rstest]
327    fn test_order_book_deltas_new_checked_valid() {
328        let instrument_id = InstrumentId::from("EURUSD.SIM");
329        let delta = create_test_delta();
330
331        let result = OrderBookDeltas::new_checked(instrument_id, vec![delta]);
332
333        assert!(result.is_ok());
334        let deltas = result.unwrap();
335        assert_eq!(deltas.instrument_id, instrument_id);
336        assert_eq!(deltas.deltas.len(), 1);
337    }
338
339    #[rstest]
340    fn test_order_book_deltas_new_checked_empty_deltas() {
341        let instrument_id = InstrumentId::from("EURUSD.SIM");
342
343        let result = OrderBookDeltas::new_checked(instrument_id, vec![]);
344
345        assert!(result.is_err());
346        assert!(
347            result
348                .unwrap_err()
349                .to_string()
350                .contains("`deltas` cannot be empty")
351        );
352    }
353
354    #[rstest]
355    #[should_panic(expected = "Condition failed")]
356    fn test_order_book_deltas_new_empty_deltas_panics() {
357        let instrument_id = InstrumentId::from("EURUSD.SIM");
358        let _ = OrderBookDeltas::new(instrument_id, vec![]);
359    }
360
361    #[rstest]
362    fn test_order_book_deltas_uses_last_delta_properties() {
363        let instrument_id = InstrumentId::from("EURUSD.SIM");
364
365        let delta1 = OrderBookDelta::new(
366            instrument_id,
367            BookAction::Add,
368            BookOrder::new(
369                OrderSide::Buy,
370                Price::from("1.0500"),
371                Quantity::from("100000"),
372                1,
373            ),
374            16,                             // Different flags
375            100,                            // Different sequence
376            UnixNanos::from(500_000_000),   // Different ts_event
377            UnixNanos::from(1_000_000_000), // Different ts_init
378        );
379
380        let delta2 = OrderBookDelta::new(
381            instrument_id,
382            BookAction::Add,
383            BookOrder::new(
384                OrderSide::Sell,
385                Price::from("1.0520"),
386                Quantity::from("50000"),
387                2,
388            ),
389            32,                             // Final flags
390            200,                            // Final sequence
391            UnixNanos::from(1_500_000_000), // Final ts_event
392            UnixNanos::from(2_000_000_000), // Final ts_init
393        );
394
395        let deltas = OrderBookDeltas::new(instrument_id, vec![delta1, delta2]);
396
397        // Should use properties from the last delta
398        assert_eq!(deltas.flags, 32);
399        assert_eq!(deltas.sequence, 200);
400        assert_eq!(deltas.ts_event, UnixNanos::from(1_500_000_000));
401        assert_eq!(deltas.ts_init, UnixNanos::from(2_000_000_000));
402    }
403
404    #[rstest]
405    fn test_order_book_deltas_hash_different_objects() {
406        let deltas1 = create_test_deltas();
407        let deltas2 = create_test_deltas_multiple();
408
409        let mut hasher1 = DefaultHasher::new();
410        let mut hasher2 = DefaultHasher::new();
411
412        deltas1.hash(&mut hasher1);
413        deltas2.hash(&mut hasher2);
414
415        assert_ne!(hasher1.finish(), hasher2.finish()); // Different objects should have different hashes
416    }
417
418    #[rstest]
419    fn test_order_book_deltas_hash_uses_instrument_id_and_sequence() {
420        let instrument_id = InstrumentId::from("EURUSD.SIM");
421        let sequence = 123u64;
422
423        // Create separate hasher to verify what's being hashed
424        let mut expected_hasher = DefaultHasher::new();
425        instrument_id.hash(&mut expected_hasher);
426        sequence.hash(&mut expected_hasher);
427        let expected_hash = expected_hasher.finish();
428
429        let delta = OrderBookDelta::new(
430            instrument_id,
431            BookAction::Add,
432            BookOrder::new(
433                OrderSide::Buy,
434                Price::from("1.0500"),
435                Quantity::from("100000"),
436                1,
437            ),
438            0,
439            sequence,
440            UnixNanos::from(1_000_000_000),
441            UnixNanos::from(2_000_000_000),
442        );
443
444        let deltas = OrderBookDeltas::new(instrument_id, vec![delta]);
445
446        let mut deltas_hasher = DefaultHasher::new();
447        deltas.hash(&mut deltas_hasher);
448
449        assert_eq!(deltas_hasher.finish(), expected_hash);
450    }
451
452    #[rstest]
453    fn test_order_book_deltas_display() {
454        let deltas = create_test_deltas();
455        let display_str = format!("{deltas}");
456
457        assert!(display_str.contains("EURUSD.SIM"));
458        assert!(display_str.contains("len=2"));
459        assert!(display_str.contains("flags=32"));
460        assert!(display_str.contains("sequence=123"));
461        assert!(display_str.contains("ts_event=1000000000"));
462        assert!(display_str.contains("ts_init=2000000000"));
463    }
464
465    #[rstest]
466    fn test_order_book_deltas_display_format() {
467        let deltas = create_test_deltas();
468        let expected =
469            "EURUSD.SIM,len=2,flags=32,sequence=123,ts_event=1000000000,ts_init=2000000000";
470
471        assert_eq!(format!("{deltas}"), expected);
472    }
473
474    #[rstest]
475    fn test_order_book_deltas_has_ts_init() {
476        let deltas = create_test_deltas();
477
478        assert_eq!(deltas.ts_init(), UnixNanos::from(2_000_000_000));
479    }
480
481    #[rstest]
482    fn test_order_book_deltas_clone() {
483        let deltas1 = create_test_deltas();
484        let deltas2 = deltas1.clone();
485
486        assert_eq!(deltas1.instrument_id, deltas2.instrument_id);
487        assert_eq!(deltas1.deltas.len(), deltas2.deltas.len());
488        assert_eq!(deltas1.flags, deltas2.flags);
489        assert_eq!(deltas1.sequence, deltas2.sequence);
490        assert_eq!(deltas1.ts_event, deltas2.ts_event);
491        assert_eq!(deltas1.ts_init, deltas2.ts_init);
492        assert_eq!(deltas1, deltas2);
493    }
494
495    #[rstest]
496    fn test_order_book_deltas_debug() {
497        let deltas = create_test_deltas();
498        let debug_str = format!("{deltas:?}");
499
500        assert!(debug_str.contains("OrderBookDeltas"));
501        assert!(debug_str.contains("EURUSD.SIM"));
502        assert!(debug_str.contains("flags: 32"));
503        assert!(debug_str.contains("sequence: 123"));
504    }
505
506    #[rstest]
507    fn test_order_book_deltas_serialization() {
508        let deltas = create_test_deltas();
509
510        // Test JSON serialization
511        let json = serde_json::to_string(&deltas).unwrap();
512        let deserialized: OrderBookDeltas = serde_json::from_str(&json).unwrap();
513
514        assert_eq!(deltas.instrument_id, deserialized.instrument_id);
515        assert_eq!(deltas.deltas.len(), deserialized.deltas.len());
516        assert_eq!(deltas.flags, deserialized.flags);
517        assert_eq!(deltas.sequence, deserialized.sequence);
518        assert_eq!(deltas.ts_event, deserialized.ts_event);
519        assert_eq!(deltas.ts_init, deserialized.ts_init);
520    }
521
522    #[rstest]
523    fn test_order_book_deltas_single_delta() {
524        let instrument_id = InstrumentId::from("BTCUSD.CRYPTO");
525        let delta = create_test_delta();
526
527        let deltas = OrderBookDeltas::new(instrument_id, vec![delta]);
528
529        assert_eq!(deltas.instrument_id, instrument_id);
530        assert_eq!(deltas.deltas.len(), 1);
531        assert_eq!(deltas.flags, delta.flags);
532        assert_eq!(deltas.sequence, delta.sequence);
533        assert_eq!(deltas.ts_event, delta.ts_event);
534        assert_eq!(deltas.ts_init, delta.ts_init);
535    }
536
537    #[rstest]
538    fn test_order_book_deltas_large_number_of_deltas() {
539        let instrument_id = InstrumentId::from("ETHUSD.CRYPTO");
540        let mut delta_vec = Vec::new();
541
542        // Create 100 deltas
543        for i in 0..100 {
544            let delta = OrderBookDelta::new(
545                instrument_id,
546                BookAction::Add,
547                BookOrder::new(
548                    OrderSide::Buy,
549                    Price::from(&format!("1000.{i:02}")),
550                    Quantity::from("1000"),
551                    i as u64,
552                ),
553                0,
554                i as u64,
555                UnixNanos::from(1_000_000_000 + i as u64),
556                UnixNanos::from(2_000_000_000 + i as u64),
557            );
558            delta_vec.push(delta);
559        }
560
561        let deltas = OrderBookDeltas::new(instrument_id, delta_vec);
562
563        assert_eq!(deltas.deltas.len(), 100);
564        assert_eq!(deltas.sequence, 99); // Last delta's sequence
565        assert_eq!(deltas.ts_event, UnixNanos::from(1_000_000_000 + 99));
566        assert_eq!(deltas.ts_init, UnixNanos::from(2_000_000_000 + 99));
567    }
568
569    #[rstest]
570    fn test_order_book_deltas_different_action_types() {
571        let deltas = create_test_deltas_multiple();
572
573        assert_eq!(deltas.deltas.len(), 4);
574
575        // Verify different action types are preserved
576        assert_eq!(deltas.deltas[0].action, BookAction::Clear);
577        assert_eq!(deltas.deltas[1].action, BookAction::Add);
578        assert_eq!(deltas.deltas[2].action, BookAction::Update);
579        assert_eq!(deltas.deltas[3].action, BookAction::Delete);
580    }
581
582    #[rstest]
583    fn test_order_book_deltas_api_new() {
584        let deltas = create_test_deltas();
585        let api_wrapper = OrderBookDeltas_API::new(deltas.clone());
586
587        assert_eq!(api_wrapper.instrument_id, deltas.instrument_id);
588        assert_eq!(api_wrapper.deltas.len(), deltas.deltas.len());
589        assert_eq!(api_wrapper.flags, deltas.flags);
590        assert_eq!(api_wrapper.sequence, deltas.sequence);
591    }
592
593    #[rstest]
594    fn test_order_book_deltas_api_into_inner() {
595        let deltas = create_test_deltas();
596        let api_wrapper = OrderBookDeltas_API::new(deltas.clone());
597        let inner_deltas = api_wrapper.into_inner();
598
599        assert_eq!(inner_deltas, deltas);
600    }
601
602    #[rstest]
603    fn test_order_book_deltas_api_deref() {
604        let deltas = create_test_deltas();
605        let api_wrapper = OrderBookDeltas_API::new(deltas.clone());
606
607        // Test Deref functionality
608        assert_eq!(api_wrapper.instrument_id, deltas.instrument_id);
609        assert_eq!(api_wrapper.ts_init(), deltas.ts_init());
610
611        // Test accessing methods through Deref
612        let display_str = format!("{}", &*api_wrapper);
613        assert!(display_str.contains("EURUSD.SIM"));
614    }
615
616    #[rstest]
617    fn test_order_book_deltas_api_deref_mut() {
618        let deltas = create_test_deltas();
619        let mut api_wrapper = OrderBookDeltas_API::new(deltas);
620
621        // Test DerefMut functionality by modifying through the wrapper
622        let original_flags = api_wrapper.flags;
623        api_wrapper.flags = 64;
624
625        assert_ne!(api_wrapper.flags, original_flags);
626        assert_eq!(api_wrapper.flags, 64);
627    }
628
629    #[rstest]
630    fn test_order_book_deltas_api_clone() {
631        let deltas = create_test_deltas();
632        let api_wrapper1 = OrderBookDeltas_API::new(deltas);
633        let api_wrapper2 = api_wrapper1.clone();
634
635        assert_eq!(api_wrapper1.instrument_id, api_wrapper2.instrument_id);
636        assert_eq!(api_wrapper1.sequence, api_wrapper2.sequence);
637        assert_eq!(api_wrapper1, api_wrapper2);
638    }
639
640    #[rstest]
641    fn test_order_book_deltas_api_debug() {
642        let deltas = create_test_deltas();
643        let api_wrapper = OrderBookDeltas_API::new(deltas);
644        let debug_str = format!("{api_wrapper:?}");
645
646        assert!(debug_str.contains("OrderBookDeltas_API"));
647        assert!(debug_str.contains("EURUSD.SIM"));
648    }
649
650    #[rstest]
651    fn test_order_book_deltas_api_serialization() {
652        let deltas = create_test_deltas();
653        let api_wrapper = OrderBookDeltas_API::new(deltas);
654
655        // Test JSON serialization
656        let json = serde_json::to_string(&api_wrapper).unwrap();
657        let deserialized: OrderBookDeltas_API = serde_json::from_str(&json).unwrap();
658
659        assert_eq!(api_wrapper.instrument_id, deserialized.instrument_id);
660        assert_eq!(api_wrapper.sequence, deserialized.sequence);
661        assert_eq!(api_wrapper, deserialized);
662    }
663
664    #[rstest]
665    fn test_order_book_deltas_with_stub(stub_deltas: OrderBookDeltas) {
666        let deltas = stub_deltas;
667
668        assert_eq!(deltas.instrument_id, InstrumentId::from("AAPL.XNAS"));
669        assert_eq!(deltas.deltas.len(), 7);
670        assert_eq!(deltas.flags, 32);
671        assert_eq!(deltas.sequence, 0);
672        assert_eq!(deltas.ts_event, UnixNanos::from(1));
673        assert_eq!(deltas.ts_init, UnixNanos::from(2));
674    }
675
676    #[rstest]
677    fn test_display_with_stub(stub_deltas: OrderBookDeltas) {
678        let deltas = stub_deltas;
679        assert_eq!(
680            format!("{deltas}"),
681            "AAPL.XNAS,len=7,flags=32,sequence=0,ts_event=1,ts_init=2".to_string()
682        );
683    }
684
685    #[rstest]
686    fn test_order_book_deltas_zero_sequence() {
687        let instrument_id = InstrumentId::from("ZERO.TEST");
688        let delta = OrderBookDelta::new(
689            instrument_id,
690            BookAction::Add,
691            BookOrder::new(
692                OrderSide::Buy,
693                Price::from("100.0"),
694                Quantity::from("1000"),
695                1,
696            ),
697            0,
698            0,                  // Zero sequence
699            UnixNanos::from(0), // Zero timestamp
700            UnixNanos::from(0),
701        );
702
703        let deltas = OrderBookDeltas::new(instrument_id, vec![delta]);
704
705        assert_eq!(deltas.sequence, 0);
706        assert_eq!(deltas.ts_event, UnixNanos::from(0));
707        assert_eq!(deltas.ts_init, UnixNanos::from(0));
708    }
709
710    #[rstest]
711    fn test_order_book_deltas_max_values() {
712        let instrument_id = InstrumentId::from("MAX.TEST");
713        let delta = OrderBookDelta::new(
714            instrument_id,
715            BookAction::Add,
716            BookOrder::new(
717                OrderSide::Buy,
718                Price::from("999999.99"),
719                Quantity::from("999999999"),
720                u64::MAX,
721            ),
722            u8::MAX,
723            u64::MAX,
724            UnixNanos::from(u64::MAX),
725            UnixNanos::from(u64::MAX),
726        );
727
728        let deltas = OrderBookDeltas::new(instrument_id, vec![delta]);
729
730        assert_eq!(deltas.flags, u8::MAX);
731        assert_eq!(deltas.sequence, u64::MAX);
732        assert_eq!(deltas.ts_event, UnixNanos::from(u64::MAX));
733        assert_eq!(deltas.ts_init, UnixNanos::from(u64::MAX));
734    }
735
736    #[rstest]
737    fn test_new() {
738        let instrument_id = InstrumentId::from("AAPL.XNAS");
739        let flags = 32; // Snapshot flag
740        let sequence = 0;
741        let ts_event = 1;
742        let ts_init = 2;
743
744        let delta0 =
745            OrderBookDelta::clear(instrument_id, sequence, ts_event.into(), ts_init.into());
746        let delta1 = OrderBookDelta::new(
747            instrument_id,
748            BookAction::Add,
749            BookOrder::new(
750                OrderSide::Sell,
751                Price::from("102.00"),
752                Quantity::from("300"),
753                1,
754            ),
755            flags,
756            sequence,
757            ts_event.into(),
758            ts_init.into(),
759        );
760        let delta2 = OrderBookDelta::new(
761            instrument_id,
762            BookAction::Add,
763            BookOrder::new(
764                OrderSide::Sell,
765                Price::from("101.00"),
766                Quantity::from("200"),
767                2,
768            ),
769            flags,
770            sequence,
771            ts_event.into(),
772            ts_init.into(),
773        );
774        let delta3 = OrderBookDelta::new(
775            instrument_id,
776            BookAction::Add,
777            BookOrder::new(
778                OrderSide::Sell,
779                Price::from("100.00"),
780                Quantity::from("100"),
781                3,
782            ),
783            flags,
784            sequence,
785            ts_event.into(),
786            ts_init.into(),
787        );
788        let delta4 = OrderBookDelta::new(
789            instrument_id,
790            BookAction::Add,
791            BookOrder::new(
792                OrderSide::Buy,
793                Price::from("99.00"),
794                Quantity::from("100"),
795                4,
796            ),
797            flags,
798            sequence,
799            ts_event.into(),
800            ts_init.into(),
801        );
802        let delta5 = OrderBookDelta::new(
803            instrument_id,
804            BookAction::Add,
805            BookOrder::new(
806                OrderSide::Buy,
807                Price::from("98.00"),
808                Quantity::from("200"),
809                5,
810            ),
811            flags,
812            sequence,
813            ts_event.into(),
814            ts_init.into(),
815        );
816        let delta6 = OrderBookDelta::new(
817            instrument_id,
818            BookAction::Add,
819            BookOrder::new(
820                OrderSide::Buy,
821                Price::from("97.00"),
822                Quantity::from("300"),
823                6,
824            ),
825            flags,
826            sequence,
827            ts_event.into(),
828            ts_init.into(),
829        );
830
831        let deltas = OrderBookDeltas::new(
832            instrument_id,
833            vec![delta0, delta1, delta2, delta3, delta4, delta5, delta6],
834        );
835
836        assert_eq!(deltas.instrument_id, instrument_id);
837        assert_eq!(deltas.deltas.len(), 7);
838        assert_eq!(deltas.flags, flags);
839        assert_eq!(deltas.sequence, sequence);
840        assert_eq!(deltas.ts_event, ts_event);
841        assert_eq!(deltas.ts_init, ts_init);
842    }
843}