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};
22
23use nautilus_core::{
24    UnixNanos,
25    correctness::{FAILED, check_predicate_true},
26    serialization::Serializable,
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.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 or contains an instrument ID that does not match
66    /// `instrument_id`.
67    #[must_use]
68    pub fn new(instrument_id: InstrumentId, deltas: Vec<OrderBookDelta>) -> Self {
69        Self::new_checked(instrument_id, deltas).expect(FAILED)
70    }
71
72    /// Creates a new [`OrderBookDeltas`] instance with correctness checking.
73    ///
74    /// # Errors
75    ///
76    /// Returns an error if `deltas` is empty or contains an instrument ID that does not match
77    /// `instrument_id`.
78    ///
79    /// # Notes
80    ///
81    /// PyO3 requires a `Result` type for proper error handling and stacktrace printing in Python.
82    #[expect(
83        clippy::missing_panics_doc,
84        reason = "the unwrapped last element is guarded by the non-empty check"
85    )]
86    pub fn new_checked(
87        instrument_id: InstrumentId,
88        deltas: Vec<OrderBookDelta>,
89    ) -> anyhow::Result<Self> {
90        check_predicate_true(!deltas.is_empty(), "`deltas` cannot be empty")?;
91
92        let mismatch = deltas.iter().enumerate().find(|(_, delta)| {
93            instrument_id != delta.instrument_id
94                && (instrument_id.symbol.as_str() != delta.instrument_id.symbol.as_str()
95                    || instrument_id.venue.as_str() != delta.instrument_id.venue.as_str())
96        });
97
98        if let Some((index, delta)) = mismatch {
99            check_predicate_true(
100                false,
101                &format!(
102                    "`deltas` instrument IDs must match `instrument_id` {instrument_id}, but \
103                     delta at index {index} of {} has {}",
104                    deltas.len(),
105                    delta.instrument_id,
106                ),
107            )?;
108        }
109        let last = deltas.last().expect("deltas not empty");
110        let flags = last.flags;
111        let sequence = last.sequence;
112        let ts_event = last.ts_event;
113        let ts_init = last.ts_init;
114        Ok(Self {
115            instrument_id,
116            deltas,
117            flags,
118            sequence,
119            ts_event,
120            ts_init,
121        })
122    }
123}
124
125impl PartialEq<Self> for OrderBookDeltas {
126    fn eq(&self, other: &Self) -> bool {
127        self.instrument_id == other.instrument_id && self.sequence == other.sequence
128    }
129}
130
131impl Eq for OrderBookDeltas {}
132
133impl Hash for OrderBookDeltas {
134    fn hash<H: Hasher>(&self, state: &mut H) {
135        self.instrument_id.hash(state);
136        self.sequence.hash(state);
137    }
138}
139
140impl Display for OrderBookDeltas {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        write!(
143            f,
144            "{},len={},flags={},sequence={},ts_event={},ts_init={}",
145            self.instrument_id,
146            self.deltas.len(),
147            self.flags,
148            self.sequence,
149            self.ts_event,
150            self.ts_init
151        )
152    }
153}
154
155impl Serializable for OrderBookDeltas {}
156
157impl HasTsInit for OrderBookDeltas {
158    fn ts_init(&self) -> UnixNanos {
159        self.ts_init
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use std::{
166        collections::hash_map::DefaultHasher,
167        hash::{Hash, Hasher},
168    };
169
170    use nautilus_core::serialization::{
171        Serializable,
172        msgpack::{FromMsgPack, ToMsgPack},
173    };
174    use rstest::rstest;
175    use serde_json;
176
177    use super::*;
178    use crate::{
179        data::{order::BookOrder, stubs::stub_deltas},
180        enums::{BookAction, OrderSide},
181        types::{Price, Quantity},
182    };
183
184    fn create_test_delta() -> OrderBookDelta {
185        let instrument_id = InstrumentId::from("EURUSD.SIM");
186        OrderBookDelta::new(
187            instrument_id,
188            BookAction::Add,
189            BookOrder::new(
190                OrderSide::Buy,
191                Price::from("1.0500"),
192                Quantity::from("100000"),
193                1,
194            ),
195            0,
196            123,
197            UnixNanos::from(1_000_000_000),
198            UnixNanos::from(2_000_000_000),
199        )
200    }
201
202    fn create_test_deltas() -> OrderBookDeltas {
203        let instrument_id = InstrumentId::from("EURUSD.SIM");
204        let flags = 32;
205        let sequence = 123;
206        let ts_event = UnixNanos::from(1_000_000_000);
207        let ts_init = UnixNanos::from(2_000_000_000);
208
209        let delta1 = OrderBookDelta::new(
210            instrument_id,
211            BookAction::Add,
212            BookOrder::new(
213                OrderSide::Sell,
214                Price::from("1.0520"),
215                Quantity::from("50000"),
216                1,
217            ),
218            flags,
219            sequence,
220            ts_event,
221            ts_init,
222        );
223        let delta2 = OrderBookDelta::new(
224            instrument_id,
225            BookAction::Add,
226            BookOrder::new(
227                OrderSide::Buy,
228                Price::from("1.0500"),
229                Quantity::from("75000"),
230                2,
231            ),
232            flags,
233            sequence,
234            ts_event,
235            ts_init,
236        );
237
238        OrderBookDeltas::new(instrument_id, vec![delta1, delta2])
239    }
240
241    fn create_test_deltas_multiple() -> OrderBookDeltas {
242        let instrument_id = InstrumentId::from("GBPUSD.SIM");
243        let flags = 16;
244        let sequence = 456;
245        let ts_event = UnixNanos::from(3_000_000_000);
246        let ts_init = UnixNanos::from(4_000_000_000);
247
248        let deltas = vec![
249            OrderBookDelta::clear(instrument_id, sequence, ts_event, ts_init),
250            OrderBookDelta::new(
251                instrument_id,
252                BookAction::Add,
253                BookOrder::new(
254                    OrderSide::Sell,
255                    Price::from("1.2550"),
256                    Quantity::from("100000"),
257                    1,
258                ),
259                flags,
260                sequence,
261                ts_event,
262                ts_init,
263            ),
264            OrderBookDelta::new(
265                instrument_id,
266                BookAction::Update,
267                BookOrder::new(
268                    OrderSide::Buy,
269                    Price::from("1.2530"),
270                    Quantity::from("200000"),
271                    2,
272                ),
273                flags,
274                sequence,
275                ts_event,
276                ts_init,
277            ),
278            OrderBookDelta::new(
279                instrument_id,
280                BookAction::Delete,
281                BookOrder::new(
282                    OrderSide::Sell,
283                    Price::from("1.2560"),
284                    Quantity::from("0"),
285                    3,
286                ),
287                flags,
288                sequence,
289                ts_event,
290                ts_init,
291            ),
292        ];
293
294        OrderBookDeltas::new(instrument_id, deltas)
295    }
296
297    // Compared field by field, as `PartialEq` covers only `instrument_id` and `sequence` here,
298    // and only `order_id` on the nested `BookOrder`.
299    fn assert_book_order_fields(expected: &BookOrder, actual: &BookOrder) {
300        assert_eq!(expected.side, actual.side);
301        assert_eq!(expected.price, actual.price);
302        assert_eq!(expected.size, actual.size);
303        assert_eq!(expected.order_id, actual.order_id);
304    }
305
306    fn assert_order_book_delta_fields(expected: &OrderBookDelta, actual: &OrderBookDelta) {
307        assert_eq!(expected.instrument_id, actual.instrument_id);
308        assert_eq!(expected.action, actual.action);
309        assert_book_order_fields(&expected.order, &actual.order);
310        assert_eq!(expected.flags, actual.flags);
311        assert_eq!(expected.sequence, actual.sequence);
312        assert_eq!(expected.ts_event, actual.ts_event);
313        assert_eq!(expected.ts_init, actual.ts_init);
314    }
315
316    fn assert_order_book_deltas_fields(expected: &OrderBookDeltas, actual: &OrderBookDeltas) {
317        assert_eq!(expected.instrument_id, actual.instrument_id);
318        assert_eq!(expected.flags, actual.flags);
319        assert_eq!(expected.sequence, actual.sequence);
320        assert_eq!(expected.ts_event, actual.ts_event);
321        assert_eq!(expected.ts_init, actual.ts_init);
322        assert_eq!(expected.deltas.len(), actual.deltas.len());
323
324        for (expected_delta, actual_delta) in expected.deltas.iter().zip(&actual.deltas) {
325            assert_order_book_delta_fields(expected_delta, actual_delta);
326        }
327    }
328
329    #[rstest]
330    fn test_order_book_deltas_new() {
331        let deltas = create_test_deltas();
332
333        assert_eq!(deltas.instrument_id, InstrumentId::from("EURUSD.SIM"));
334        assert_eq!(deltas.deltas.len(), 2);
335        assert_eq!(deltas.flags, 32);
336        assert_eq!(deltas.sequence, 123);
337        assert_eq!(deltas.ts_event, UnixNanos::from(1_000_000_000));
338        assert_eq!(deltas.ts_init, UnixNanos::from(2_000_000_000));
339    }
340
341    #[rstest]
342    fn test_order_book_deltas_new_checked_valid() {
343        let instrument_id = InstrumentId::from("EURUSD.SIM");
344        let delta = create_test_delta();
345
346        let result = OrderBookDeltas::new_checked(instrument_id, vec![delta]);
347
348        assert!(result.is_ok());
349        let deltas = result.unwrap();
350        assert_eq!(deltas.instrument_id, instrument_id);
351        assert_eq!(deltas.deltas.len(), 1);
352    }
353
354    #[rstest]
355    fn test_order_book_deltas_new_checked_accepts_homogeneous_deltas() {
356        let instrument_id = InstrumentId::from("EURUSD.SIM");
357        let delta1 = create_test_delta();
358        let mut delta2 = create_test_delta();
359        delta2.sequence = 124;
360
361        let result = OrderBookDeltas::new_checked(instrument_id, vec![delta1, delta2]);
362
363        assert!(result.is_ok());
364        assert_eq!(result.unwrap().deltas.len(), 2);
365    }
366
367    #[rstest]
368    #[case::first(0)]
369    #[case::later(1)]
370    fn test_order_book_deltas_new_checked_rejects_mismatched_instrument(
371        #[case] mismatch_index: usize,
372    ) {
373        let instrument_id = InstrumentId::from("EURUSD.SIM");
374        let mut deltas = vec![create_test_delta(), create_test_delta()];
375        deltas[mismatch_index].instrument_id = InstrumentId::from("GBPUSD.SIM");
376
377        let result = OrderBookDeltas::new_checked(instrument_id, deltas);
378
379        assert_eq!(
380            result.unwrap_err().to_string(),
381            format!(
382                "`deltas` instrument IDs must match `instrument_id` EURUSD.SIM, but delta at \
383                 index {mismatch_index} of 2 has GBPUSD.SIM"
384            )
385        );
386    }
387
388    #[rstest]
389    fn test_order_book_deltas_new_checked_empty_deltas() {
390        let instrument_id = InstrumentId::from("EURUSD.SIM");
391
392        let result = OrderBookDeltas::new_checked(instrument_id, vec![]);
393
394        assert!(result.is_err());
395        assert!(
396            result
397                .unwrap_err()
398                .to_string()
399                .contains("`deltas` cannot be empty")
400        );
401    }
402
403    #[rstest]
404    #[should_panic(expected = "Condition failed")]
405    fn test_order_book_deltas_new_empty_deltas_panics() {
406        let instrument_id = InstrumentId::from("EURUSD.SIM");
407        let _ = OrderBookDeltas::new(instrument_id, vec![]);
408    }
409
410    #[rstest]
411    fn test_order_book_deltas_uses_last_delta_properties() {
412        let instrument_id = InstrumentId::from("EURUSD.SIM");
413
414        let delta1 = OrderBookDelta::new(
415            instrument_id,
416            BookAction::Add,
417            BookOrder::new(
418                OrderSide::Buy,
419                Price::from("1.0500"),
420                Quantity::from("100000"),
421                1,
422            ),
423            16,                             // Different flags
424            100,                            // Different sequence
425            UnixNanos::from(500_000_000),   // Different ts_event
426            UnixNanos::from(1_000_000_000), // Different ts_init
427        );
428
429        let delta2 = OrderBookDelta::new(
430            instrument_id,
431            BookAction::Add,
432            BookOrder::new(
433                OrderSide::Sell,
434                Price::from("1.0520"),
435                Quantity::from("50000"),
436                2,
437            ),
438            32,                             // Final flags
439            200,                            // Final sequence
440            UnixNanos::from(1_500_000_000), // Final ts_event
441            UnixNanos::from(2_000_000_000), // Final ts_init
442        );
443
444        let deltas = OrderBookDeltas::new(instrument_id, vec![delta1, delta2]);
445
446        // Should use properties from the last delta
447        assert_eq!(deltas.flags, 32);
448        assert_eq!(deltas.sequence, 200);
449        assert_eq!(deltas.ts_event, UnixNanos::from(1_500_000_000));
450        assert_eq!(deltas.ts_init, UnixNanos::from(2_000_000_000));
451    }
452
453    #[rstest]
454    fn test_order_book_deltas_hash_different_objects() {
455        let deltas1 = create_test_deltas();
456        let deltas2 = create_test_deltas_multiple();
457
458        let mut hasher1 = DefaultHasher::new();
459        let mut hasher2 = DefaultHasher::new();
460
461        deltas1.hash(&mut hasher1);
462        deltas2.hash(&mut hasher2);
463
464        assert_ne!(hasher1.finish(), hasher2.finish()); // Different objects should have different hashes
465    }
466
467    #[rstest]
468    fn test_order_book_deltas_hash_uses_instrument_id_and_sequence() {
469        let instrument_id = InstrumentId::from("EURUSD.SIM");
470        let sequence = 123u64;
471
472        // Create separate hasher to verify what's being hashed
473        let mut expected_hasher = DefaultHasher::new();
474        instrument_id.hash(&mut expected_hasher);
475        sequence.hash(&mut expected_hasher);
476        let expected_hash = expected_hasher.finish();
477
478        let delta = OrderBookDelta::new(
479            instrument_id,
480            BookAction::Add,
481            BookOrder::new(
482                OrderSide::Buy,
483                Price::from("1.0500"),
484                Quantity::from("100000"),
485                1,
486            ),
487            0,
488            sequence,
489            UnixNanos::from(1_000_000_000),
490            UnixNanos::from(2_000_000_000),
491        );
492
493        let deltas = OrderBookDeltas::new(instrument_id, vec![delta]);
494
495        let mut deltas_hasher = DefaultHasher::new();
496        deltas.hash(&mut deltas_hasher);
497
498        assert_eq!(deltas_hasher.finish(), expected_hash);
499    }
500
501    #[rstest]
502    fn test_order_book_deltas_display() {
503        let deltas = create_test_deltas();
504        let display_str = format!("{deltas}");
505
506        assert!(display_str.contains("EURUSD.SIM"));
507        assert!(display_str.contains("len=2"));
508        assert!(display_str.contains("flags=32"));
509        assert!(display_str.contains("sequence=123"));
510        assert!(display_str.contains("ts_event=1000000000"));
511        assert!(display_str.contains("ts_init=2000000000"));
512    }
513
514    #[rstest]
515    fn test_order_book_deltas_display_format() {
516        let deltas = create_test_deltas();
517        let expected =
518            "EURUSD.SIM,len=2,flags=32,sequence=123,ts_event=1000000000,ts_init=2000000000";
519
520        assert_eq!(format!("{deltas}"), expected);
521    }
522
523    #[rstest]
524    fn test_order_book_deltas_has_ts_init() {
525        let deltas = create_test_deltas();
526
527        assert_eq!(deltas.ts_init(), UnixNanos::from(2_000_000_000));
528    }
529
530    #[rstest]
531    fn test_order_book_deltas_clone() {
532        let deltas1 = create_test_deltas();
533        let deltas2 = deltas1.clone();
534
535        assert_eq!(deltas1.instrument_id, deltas2.instrument_id);
536        assert_eq!(deltas1.deltas.len(), deltas2.deltas.len());
537        assert_eq!(deltas1.flags, deltas2.flags);
538        assert_eq!(deltas1.sequence, deltas2.sequence);
539        assert_eq!(deltas1.ts_event, deltas2.ts_event);
540        assert_eq!(deltas1.ts_init, deltas2.ts_init);
541        assert_eq!(deltas1, deltas2);
542    }
543
544    #[rstest]
545    fn test_order_book_deltas_debug() {
546        let deltas = create_test_deltas();
547        let debug_str = format!("{deltas:?}");
548
549        assert!(debug_str.contains("OrderBookDeltas"));
550        assert!(debug_str.contains("EURUSD.SIM"));
551        assert!(debug_str.contains("flags: 32"));
552        assert!(debug_str.contains("sequence: 123"));
553    }
554
555    #[rstest]
556    fn test_order_book_deltas_serialization() {
557        let deltas = create_test_deltas();
558
559        // Test JSON serialization
560        let json = serde_json::to_string(&deltas).unwrap();
561        let deserialized: OrderBookDeltas = serde_json::from_str(&json).unwrap();
562
563        assert_eq!(deltas.instrument_id, deserialized.instrument_id);
564        assert_eq!(deltas.deltas.len(), deserialized.deltas.len());
565        assert_eq!(deltas.flags, deserialized.flags);
566        assert_eq!(deltas.sequence, deserialized.sequence);
567        assert_eq!(deltas.ts_event, deserialized.ts_event);
568        assert_eq!(deltas.ts_init, deserialized.ts_init);
569    }
570
571    #[rstest]
572    fn test_json_serialization(stub_deltas: OrderBookDeltas) {
573        let deltas = stub_deltas;
574        let serialized = deltas.to_json_bytes().unwrap();
575        let deserialized = OrderBookDeltas::from_json_bytes(serialized.as_ref()).unwrap();
576
577        assert_order_book_deltas_fields(&deltas, &deserialized);
578    }
579
580    #[rstest]
581    fn test_msgpack_serialization() {
582        let deltas = create_test_deltas_multiple();
583        let serialized = deltas.to_msgpack_bytes().unwrap();
584        let deserialized = OrderBookDeltas::from_msgpack_bytes(serialized.as_ref()).unwrap();
585
586        assert_order_book_deltas_fields(&deltas, &deserialized);
587    }
588
589    #[rstest]
590    fn test_order_book_deltas_single_delta() {
591        let delta = create_test_delta();
592        let instrument_id = delta.instrument_id;
593
594        let deltas = OrderBookDeltas::new(instrument_id, vec![delta]);
595
596        assert_eq!(deltas.instrument_id, instrument_id);
597        assert_eq!(deltas.deltas.len(), 1);
598        assert_eq!(deltas.flags, delta.flags);
599        assert_eq!(deltas.sequence, delta.sequence);
600        assert_eq!(deltas.ts_event, delta.ts_event);
601        assert_eq!(deltas.ts_init, delta.ts_init);
602    }
603
604    #[rstest]
605    fn test_order_book_deltas_large_number_of_deltas() {
606        let instrument_id = InstrumentId::from("ETHUSD.CRYPTO");
607        let mut delta_vec = Vec::new();
608
609        // Create 100 deltas
610        for i in 0..100 {
611            let delta = OrderBookDelta::new(
612                instrument_id,
613                BookAction::Add,
614                BookOrder::new(
615                    OrderSide::Buy,
616                    Price::from(&format!("1000.{i:02}")),
617                    Quantity::from("1000"),
618                    i as u64,
619                ),
620                0,
621                i as u64,
622                UnixNanos::from(1_000_000_000 + i as u64),
623                UnixNanos::from(2_000_000_000 + i as u64),
624            );
625            delta_vec.push(delta);
626        }
627
628        let deltas = OrderBookDeltas::new(instrument_id, delta_vec);
629
630        assert_eq!(deltas.deltas.len(), 100);
631        assert_eq!(deltas.sequence, 99); // Last delta's sequence
632        assert_eq!(deltas.ts_event, UnixNanos::from(1_000_000_000 + 99));
633        assert_eq!(deltas.ts_init, UnixNanos::from(2_000_000_000 + 99));
634    }
635
636    #[rstest]
637    fn test_order_book_deltas_different_action_types() {
638        let deltas = create_test_deltas_multiple();
639
640        assert_eq!(deltas.deltas.len(), 4);
641
642        // Verify different action types are preserved
643        assert_eq!(deltas.deltas[0].action, BookAction::Clear);
644        assert_eq!(deltas.deltas[1].action, BookAction::Add);
645        assert_eq!(deltas.deltas[2].action, BookAction::Update);
646        assert_eq!(deltas.deltas[3].action, BookAction::Delete);
647    }
648
649    #[rstest]
650    fn test_order_book_deltas_with_stub(stub_deltas: OrderBookDeltas) {
651        let deltas = stub_deltas;
652
653        assert_eq!(deltas.instrument_id, InstrumentId::from("AAPL.XNAS"));
654        assert_eq!(deltas.deltas.len(), 7);
655        assert_eq!(deltas.flags, 32);
656        assert_eq!(deltas.sequence, 0);
657        assert_eq!(deltas.ts_event, UnixNanos::from(1));
658        assert_eq!(deltas.ts_init, UnixNanos::from(2));
659    }
660
661    #[rstest]
662    fn test_display_with_stub(stub_deltas: OrderBookDeltas) {
663        let deltas = stub_deltas;
664        assert_eq!(
665            format!("{deltas}"),
666            "AAPL.XNAS,len=7,flags=32,sequence=0,ts_event=1,ts_init=2".to_string()
667        );
668    }
669
670    #[rstest]
671    fn test_order_book_deltas_zero_sequence() {
672        let instrument_id = InstrumentId::from("ZERO.TEST");
673        let delta = OrderBookDelta::new(
674            instrument_id,
675            BookAction::Add,
676            BookOrder::new(
677                OrderSide::Buy,
678                Price::from("100.0"),
679                Quantity::from("1000"),
680                1,
681            ),
682            0,
683            0,                  // Zero sequence
684            UnixNanos::from(0), // Zero timestamp
685            UnixNanos::from(0),
686        );
687
688        let deltas = OrderBookDeltas::new(instrument_id, vec![delta]);
689
690        assert_eq!(deltas.sequence, 0);
691        assert_eq!(deltas.ts_event, UnixNanos::from(0));
692        assert_eq!(deltas.ts_init, UnixNanos::from(0));
693    }
694
695    #[rstest]
696    fn test_order_book_deltas_max_values() {
697        let instrument_id = InstrumentId::from("MAX.TEST");
698        let delta = OrderBookDelta::new(
699            instrument_id,
700            BookAction::Add,
701            BookOrder::new(
702                OrderSide::Buy,
703                Price::from("999999.99"),
704                Quantity::from("999999999"),
705                u64::MAX,
706            ),
707            u8::MAX,
708            u64::MAX,
709            UnixNanos::from(u64::MAX),
710            UnixNanos::from(u64::MAX),
711        );
712
713        let deltas = OrderBookDeltas::new(instrument_id, vec![delta]);
714
715        assert_eq!(deltas.flags, u8::MAX);
716        assert_eq!(deltas.sequence, u64::MAX);
717        assert_eq!(deltas.ts_event, UnixNanos::from(u64::MAX));
718        assert_eq!(deltas.ts_init, UnixNanos::from(u64::MAX));
719    }
720
721    #[rstest]
722    fn test_new() {
723        let instrument_id = InstrumentId::from("AAPL.XNAS");
724        let flags = 32; // Snapshot flag
725        let sequence = 0;
726        let ts_event = 1;
727        let ts_init = 2;
728
729        let delta0 =
730            OrderBookDelta::clear(instrument_id, sequence, ts_event.into(), ts_init.into());
731        let delta1 = OrderBookDelta::new(
732            instrument_id,
733            BookAction::Add,
734            BookOrder::new(
735                OrderSide::Sell,
736                Price::from("102.00"),
737                Quantity::from("300"),
738                1,
739            ),
740            flags,
741            sequence,
742            ts_event.into(),
743            ts_init.into(),
744        );
745        let delta2 = OrderBookDelta::new(
746            instrument_id,
747            BookAction::Add,
748            BookOrder::new(
749                OrderSide::Sell,
750                Price::from("101.00"),
751                Quantity::from("200"),
752                2,
753            ),
754            flags,
755            sequence,
756            ts_event.into(),
757            ts_init.into(),
758        );
759        let delta3 = OrderBookDelta::new(
760            instrument_id,
761            BookAction::Add,
762            BookOrder::new(
763                OrderSide::Sell,
764                Price::from("100.00"),
765                Quantity::from("100"),
766                3,
767            ),
768            flags,
769            sequence,
770            ts_event.into(),
771            ts_init.into(),
772        );
773        let delta4 = OrderBookDelta::new(
774            instrument_id,
775            BookAction::Add,
776            BookOrder::new(
777                OrderSide::Buy,
778                Price::from("99.00"),
779                Quantity::from("100"),
780                4,
781            ),
782            flags,
783            sequence,
784            ts_event.into(),
785            ts_init.into(),
786        );
787        let delta5 = OrderBookDelta::new(
788            instrument_id,
789            BookAction::Add,
790            BookOrder::new(
791                OrderSide::Buy,
792                Price::from("98.00"),
793                Quantity::from("200"),
794                5,
795            ),
796            flags,
797            sequence,
798            ts_event.into(),
799            ts_init.into(),
800        );
801        let delta6 = OrderBookDelta::new(
802            instrument_id,
803            BookAction::Add,
804            BookOrder::new(
805                OrderSide::Buy,
806                Price::from("97.00"),
807                Quantity::from("300"),
808                6,
809            ),
810            flags,
811            sequence,
812            ts_event.into(),
813            ts_init.into(),
814        );
815
816        let deltas = OrderBookDeltas::new(
817            instrument_id,
818            vec![delta0, delta1, delta2, delta3, delta4, delta5, delta6],
819        );
820
821        assert_eq!(deltas.instrument_id, instrument_id);
822        assert_eq!(deltas.deltas.len(), 7);
823        assert_eq!(deltas.flags, flags);
824        assert_eq!(deltas.sequence, sequence);
825        assert_eq!(deltas.ts_event, ts_event);
826        assert_eq!(deltas.ts_init, ts_init);
827    }
828}