nautilus_model/data/
deltas.rs

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