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