Skip to main content

nautilus_model/data/
delta.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 `OrderBookDelta` data type intended to carry book state information.
17
18use std::{collections::HashMap, fmt::Display, hash::Hash};
19
20use indexmap::IndexMap;
21use nautilus_core::{UnixNanos, correctness::FAILED, serialization::Serializable};
22use serde::{Deserialize, Serialize};
23
24use super::{
25    HasTsInit,
26    order::{BookOrder, NULL_ORDER},
27};
28use crate::{
29    enums::{BookAction, RecordFlag},
30    identifiers::InstrumentId,
31    types::{fixed::FIXED_SIZE_BINARY, quantity::check_positive_quantity},
32};
33
34/// Represents a single change/delta in an order book.
35#[repr(C)]
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
37#[serde(tag = "type")]
38#[cfg_attr(
39    feature = "python",
40    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model", from_py_object)
41)]
42#[cfg_attr(
43    feature = "python",
44    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
45)]
46pub struct OrderBookDelta {
47    /// The instrument ID for the book.
48    pub instrument_id: InstrumentId,
49    /// The order book delta action.
50    pub action: BookAction,
51    /// The order to apply.
52    pub order: BookOrder,
53    /// The record flags bit field indicating event end and data information.
54    pub flags: u8,
55    /// The message sequence number assigned at the venue.
56    pub sequence: u64,
57    /// UNIX timestamp (nanoseconds) when the book event occurred.
58    pub ts_event: UnixNanos,
59    /// UNIX timestamp (nanoseconds) when the instance was created.
60    pub ts_init: UnixNanos,
61}
62
63impl OrderBookDelta {
64    /// Creates a new [`OrderBookDelta`] instance with correctness checking.
65    ///
66    /// # Errors
67    ///
68    /// Returns an error if `action` is [`BookAction::Add`] or [`BookAction::Update`] and `size` is not positive (> 0).
69    ///
70    /// # Notes
71    ///
72    /// PyO3 requires a `Result` type for proper error handling and stacktrace printing in Python.
73    pub fn new_checked(
74        instrument_id: InstrumentId,
75        action: BookAction,
76        order: BookOrder,
77        flags: u8,
78        sequence: u64,
79        ts_event: UnixNanos,
80        ts_init: UnixNanos,
81    ) -> anyhow::Result<Self> {
82        if matches!(action, BookAction::Add | BookAction::Update) {
83            check_positive_quantity(order.size, stringify!(order.size))?;
84        }
85
86        Ok(Self {
87            instrument_id,
88            action,
89            order,
90            flags,
91            sequence,
92            ts_event,
93            ts_init,
94        })
95    }
96
97    /// Creates a new [`OrderBookDelta`] instance.
98    ///
99    /// # Panics
100    ///
101    /// Panics if `action` is [`BookAction::Add`] or [`BookAction::Update`] and `size` is not positive (> 0).
102    #[must_use]
103    pub fn new(
104        instrument_id: InstrumentId,
105        action: BookAction,
106        order: BookOrder,
107        flags: u8,
108        sequence: u64,
109        ts_event: UnixNanos,
110        ts_init: UnixNanos,
111    ) -> Self {
112        Self::new_checked(
113            instrument_id,
114            action,
115            order,
116            flags,
117            sequence,
118            ts_event,
119            ts_init,
120        )
121        .expect(FAILED)
122    }
123
124    /// Creates a new [`OrderBookDelta`] instance with a `Clear` action and NULL order.
125    #[must_use]
126    pub fn clear(
127        instrument_id: InstrumentId,
128        sequence: u64,
129        ts_event: UnixNanos,
130        ts_init: UnixNanos,
131    ) -> Self {
132        Self {
133            instrument_id,
134            action: BookAction::Clear,
135            order: NULL_ORDER,
136            flags: RecordFlag::F_SNAPSHOT as u8,
137            sequence,
138            ts_event,
139            ts_init,
140        }
141    }
142
143    /// Returns the metadata for the type, for use with serialization formats.
144    #[must_use]
145    pub fn get_metadata(
146        instrument_id: &InstrumentId,
147        price_precision: u8,
148        size_precision: u8,
149    ) -> HashMap<String, String> {
150        let mut metadata = HashMap::new();
151        metadata.insert("instrument_id".to_string(), instrument_id.to_string());
152        metadata.insert("price_precision".to_string(), price_precision.to_string());
153        metadata.insert("size_precision".to_string(), size_precision.to_string());
154        metadata
155    }
156
157    /// Returns the field map for the type, for use with Arrow schemas.
158    #[must_use]
159    pub fn get_fields() -> IndexMap<String, String> {
160        let mut metadata = IndexMap::new();
161        metadata.insert("action".to_string(), "UInt8".to_string());
162        metadata.insert("side".to_string(), "UInt8".to_string());
163        metadata.insert("price".to_string(), FIXED_SIZE_BINARY.to_string());
164        metadata.insert("size".to_string(), FIXED_SIZE_BINARY.to_string());
165        metadata.insert("order_id".to_string(), "UInt64".to_string());
166        metadata.insert("flags".to_string(), "UInt8".to_string());
167        metadata.insert("sequence".to_string(), "UInt64".to_string());
168        metadata.insert("ts_event".to_string(), "UInt64".to_string());
169        metadata.insert("ts_init".to_string(), "UInt64".to_string());
170        metadata
171    }
172}
173
174impl Display for OrderBookDelta {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        write!(
177            f,
178            "{},{},{},{},{},{},{}",
179            self.instrument_id,
180            self.action,
181            self.order,
182            self.flags,
183            self.sequence,
184            self.ts_event,
185            self.ts_init
186        )
187    }
188}
189
190impl Serializable for OrderBookDelta {}
191
192impl HasTsInit for OrderBookDelta {
193    fn ts_init(&self) -> UnixNanos {
194        self.ts_init
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use std::{
201        collections::hash_map::DefaultHasher,
202        hash::{Hash, Hasher},
203    };
204
205    use nautilus_core::{
206        UnixNanos,
207        serialization::{
208            Serializable,
209            msgpack::{FromMsgPack, ToMsgPack},
210        },
211    };
212    use rstest::rstest;
213
214    use crate::{
215        data::{BookOrder, HasTsInit, OrderBookDelta, stubs::*},
216        enums::{BookAction, OrderSide, RecordFlag},
217        identifiers::InstrumentId,
218        types::{Price, Quantity},
219    };
220
221    fn create_test_delta() -> OrderBookDelta {
222        let order = BookOrder::new(
223            OrderSide::Buy,
224            Price::from("1.0500"),
225            Quantity::from("100000"),
226            12345,
227        );
228        OrderBookDelta::new(
229            InstrumentId::from("EURUSD.SIM"),
230            BookAction::Add,
231            order,
232            0,
233            123,
234            UnixNanos::from(1_000_000_000),
235            UnixNanos::from(2_000_000_000),
236        )
237    }
238
239    #[rstest]
240    fn test_order_book_delta_new() {
241        let delta = create_test_delta();
242
243        assert_eq!(delta.instrument_id, InstrumentId::from("EURUSD.SIM"));
244        assert_eq!(delta.action, BookAction::Add);
245        assert_eq!(delta.order.side, OrderSide::Buy);
246        assert_eq!(delta.order.price, Price::from("1.0500"));
247        assert_eq!(delta.order.size, Quantity::from("100000"));
248        assert_eq!(delta.order.order_id, 12345);
249        assert_eq!(delta.flags, 0);
250        assert_eq!(delta.sequence, 123);
251        assert_eq!(delta.ts_event, UnixNanos::from(1_000_000_000));
252        assert_eq!(delta.ts_init, UnixNanos::from(2_000_000_000));
253    }
254
255    #[rstest]
256    fn test_order_book_delta_new_checked_valid() {
257        let order = BookOrder::new(
258            OrderSide::Sell,
259            Price::from("1.0505"),
260            Quantity::from("50000"),
261            67890,
262        );
263        let result = OrderBookDelta::new_checked(
264            InstrumentId::from("GBPUSD.SIM"),
265            BookAction::Update,
266            order,
267            16,
268            456,
269            UnixNanos::from(500_000_000),
270            UnixNanos::from(1_500_000_000),
271        );
272
273        assert!(result.is_ok());
274        let delta = result.unwrap();
275        assert_eq!(delta.instrument_id, InstrumentId::from("GBPUSD.SIM"));
276        assert_eq!(delta.action, BookAction::Update);
277        assert_eq!(delta.order.side, OrderSide::Sell);
278        assert_eq!(delta.flags, 16);
279    }
280
281    #[rstest]
282    #[should_panic(expected = "invalid `Quantity` for 'order.size' not positive, was 0")]
283    fn test_order_book_delta_new_with_zero_size_panics() {
284        let instrument_id = InstrumentId::from("AAPL.XNAS");
285        let action = BookAction::Add;
286        let price = Price::from("100.00");
287        let zero_size = Quantity::from(0);
288        let side = OrderSide::Buy;
289        let order_id = 123_456;
290        let flags = 0;
291        let sequence = 1;
292        let ts_event = UnixNanos::from(0);
293        let ts_init = UnixNanos::from(1);
294
295        let order = BookOrder::new(side, price, zero_size, order_id);
296
297        let _ = OrderBookDelta::new(
298            instrument_id,
299            action,
300            order,
301            flags,
302            sequence,
303            ts_event,
304            ts_init,
305        );
306    }
307
308    #[rstest]
309    fn test_order_book_delta_new_checked_with_zero_size_error() {
310        let instrument_id = InstrumentId::from("AAPL.XNAS");
311        let action = BookAction::Add;
312        let price = Price::from("100.00");
313        let zero_size = Quantity::from(0);
314        let side = OrderSide::Buy;
315        let order_id = 123_456;
316        let flags = 0;
317        let sequence = 1;
318        let ts_event = UnixNanos::from(0);
319        let ts_init = UnixNanos::from(1);
320
321        let order = BookOrder::new(side, price, zero_size, order_id);
322
323        let result = OrderBookDelta::new_checked(
324            instrument_id,
325            action,
326            order,
327            flags,
328            sequence,
329            ts_event,
330            ts_init,
331        );
332
333        assert!(result.is_err());
334        assert!(
335            result
336                .unwrap_err()
337                .to_string()
338                .contains("invalid `Quantity` for 'order.size' not positive")
339        );
340    }
341
342    #[rstest]
343    fn test_order_book_delta_new_checked_delete_with_zero_size_ok() {
344        let order = BookOrder::new(
345            OrderSide::Buy,
346            Price::from("100.00"),
347            Quantity::from(0),
348            123_456,
349        );
350        let result = OrderBookDelta::new_checked(
351            InstrumentId::from("TEST.SIM"),
352            BookAction::Delete,
353            order,
354            0,
355            1,
356            UnixNanos::from(0),
357            UnixNanos::from(1),
358        );
359
360        assert!(result.is_ok());
361    }
362
363    #[rstest]
364    fn test_order_book_delta_clear() {
365        let instrument_id = InstrumentId::from("BTCUSD.CRYPTO");
366        let sequence = 999;
367        let ts_event = UnixNanos::from(3_000_000_000);
368        let ts_init = UnixNanos::from(4_000_000_000);
369
370        let delta = OrderBookDelta::clear(instrument_id, sequence, ts_event, ts_init);
371
372        assert_eq!(delta.instrument_id, instrument_id);
373        assert_eq!(delta.action, BookAction::Clear);
374        assert!(delta.order.price.is_zero());
375        assert!(delta.order.size.is_zero());
376        assert_eq!(delta.order.side, OrderSide::NoOrderSide);
377        assert_eq!(delta.order.order_id, 0);
378        assert_eq!(delta.flags, RecordFlag::F_SNAPSHOT as u8);
379        assert_eq!(delta.sequence, sequence);
380        assert_eq!(delta.ts_event, ts_event);
381        assert_eq!(delta.ts_init, ts_init);
382    }
383
384    #[rstest]
385    fn test_get_metadata() {
386        let instrument_id = InstrumentId::from("EURUSD.SIM");
387        let metadata = OrderBookDelta::get_metadata(&instrument_id, 5, 8);
388
389        assert_eq!(metadata.len(), 3);
390        assert_eq!(
391            metadata.get("instrument_id"),
392            Some(&"EURUSD.SIM".to_string())
393        );
394        assert_eq!(metadata.get("price_precision"), Some(&"5".to_string()));
395        assert_eq!(metadata.get("size_precision"), Some(&"8".to_string()));
396    }
397
398    #[rstest]
399    fn test_get_fields() {
400        let fields = OrderBookDelta::get_fields();
401
402        assert_eq!(fields.len(), 9);
403        assert_eq!(fields.get("action"), Some(&"UInt8".to_string()));
404        assert_eq!(fields.get("side"), Some(&"UInt8".to_string()));
405
406        #[cfg(feature = "high-precision")]
407        {
408            assert_eq!(
409                fields.get("price"),
410                Some(&"FixedSizeBinary(16)".to_string())
411            );
412            assert_eq!(fields.get("size"), Some(&"FixedSizeBinary(16)".to_string()));
413        }
414        #[cfg(not(feature = "high-precision"))]
415        {
416            assert_eq!(fields.get("price"), Some(&"FixedSizeBinary(8)".to_string()));
417            assert_eq!(fields.get("size"), Some(&"FixedSizeBinary(8)".to_string()));
418        }
419
420        assert_eq!(fields.get("order_id"), Some(&"UInt64".to_string()));
421        assert_eq!(fields.get("flags"), Some(&"UInt8".to_string()));
422        assert_eq!(fields.get("sequence"), Some(&"UInt64".to_string()));
423        assert_eq!(fields.get("ts_event"), Some(&"UInt64".to_string()));
424        assert_eq!(fields.get("ts_init"), Some(&"UInt64".to_string()));
425    }
426
427    #[rstest]
428    #[case(BookAction::Add)]
429    #[case(BookAction::Update)]
430    #[case(BookAction::Delete)]
431    #[case(BookAction::Clear)]
432    fn test_order_book_delta_with_different_actions(#[case] action: BookAction) {
433        let order = BookOrder::new(
434            OrderSide::Buy,
435            Price::from("100.00"),
436            if matches!(action, BookAction::Delete | BookAction::Clear) {
437                Quantity::from(0)
438            } else {
439                Quantity::from("1000")
440            },
441            123_456,
442        );
443
444        let result = if matches!(action, BookAction::Clear) {
445            Ok(OrderBookDelta::clear(
446                InstrumentId::from("TEST.SIM"),
447                1,
448                UnixNanos::from(1_000_000_000),
449                UnixNanos::from(2_000_000_000),
450            ))
451        } else {
452            OrderBookDelta::new_checked(
453                InstrumentId::from("TEST.SIM"),
454                action,
455                order,
456                0,
457                1,
458                UnixNanos::from(1_000_000_000),
459                UnixNanos::from(2_000_000_000),
460            )
461        };
462
463        assert!(result.is_ok());
464        let delta = result.unwrap();
465        assert_eq!(delta.action, action);
466    }
467
468    #[rstest]
469    #[case(OrderSide::Buy)]
470    #[case(OrderSide::Sell)]
471    fn test_order_book_delta_with_different_sides(#[case] side: OrderSide) {
472        let order = BookOrder::new(side, Price::from("100.00"), Quantity::from("1000"), 123_456);
473
474        let delta = OrderBookDelta::new(
475            InstrumentId::from("TEST.SIM"),
476            BookAction::Add,
477            order,
478            0,
479            1,
480            UnixNanos::from(1_000_000_000),
481            UnixNanos::from(2_000_000_000),
482        );
483
484        assert_eq!(delta.order.side, side);
485    }
486
487    #[rstest]
488    fn test_order_book_delta_has_ts_init() {
489        let delta = create_test_delta();
490        assert_eq!(delta.ts_init(), UnixNanos::from(2_000_000_000));
491    }
492
493    #[rstest]
494    fn test_order_book_delta_display() {
495        let delta = create_test_delta();
496        let display_str = format!("{delta}");
497
498        assert!(display_str.contains("EURUSD.SIM"));
499        assert!(display_str.contains("ADD"));
500        assert!(display_str.contains("BUY"));
501        assert!(display_str.contains("1.0500"));
502        assert!(display_str.contains("100000"));
503        assert!(display_str.contains("12345"));
504        assert!(display_str.contains("123"));
505    }
506
507    #[rstest]
508    fn test_order_book_delta_with_zero_timestamps() {
509        let order = BookOrder::new(
510            OrderSide::Buy,
511            Price::from("100.00"),
512            Quantity::from("1000"),
513            123_456,
514        );
515        let delta = OrderBookDelta::new(
516            InstrumentId::from("TEST.SIM"),
517            BookAction::Add,
518            order,
519            0,
520            0,
521            UnixNanos::from(0),
522            UnixNanos::from(0),
523        );
524
525        assert_eq!(delta.sequence, 0);
526        assert_eq!(delta.ts_event, UnixNanos::from(0));
527        assert_eq!(delta.ts_init, UnixNanos::from(0));
528    }
529
530    #[rstest]
531    fn test_order_book_delta_with_max_values() {
532        let order = BookOrder::new(
533            OrderSide::Sell,
534            Price::from("999999.9999"),
535            Quantity::from("999999999.9999"),
536            u64::MAX,
537        );
538        let delta = OrderBookDelta::new(
539            InstrumentId::from("TEST.SIM"),
540            BookAction::Update,
541            order,
542            u8::MAX,
543            u64::MAX,
544            UnixNanos::from(u64::MAX),
545            UnixNanos::from(u64::MAX),
546        );
547
548        assert_eq!(delta.flags, u8::MAX);
549        assert_eq!(delta.sequence, u64::MAX);
550        assert_eq!(delta.order.order_id, u64::MAX);
551        assert_eq!(delta.ts_event, UnixNanos::from(u64::MAX));
552        assert_eq!(delta.ts_init, UnixNanos::from(u64::MAX));
553    }
554
555    #[rstest]
556    fn test_new() {
557        let instrument_id = InstrumentId::from("AAPL.XNAS");
558        let action = BookAction::Add;
559        let price = Price::from("100.00");
560        let size = Quantity::from("10");
561        let side = OrderSide::Buy;
562        let order_id = 123_456;
563        let flags = 0;
564        let sequence = 1;
565        let ts_event = 1;
566        let ts_init = 2;
567
568        let order = BookOrder::new(side, price, size, order_id);
569
570        let delta = OrderBookDelta::new(
571            instrument_id,
572            action,
573            order,
574            flags,
575            sequence,
576            ts_event.into(),
577            ts_init.into(),
578        );
579
580        assert_eq!(delta.instrument_id, instrument_id);
581        assert_eq!(delta.action, action);
582        assert_eq!(delta.order.price, price);
583        assert_eq!(delta.order.size, size);
584        assert_eq!(delta.order.side, side);
585        assert_eq!(delta.order.order_id, order_id);
586        assert_eq!(delta.flags, flags);
587        assert_eq!(delta.sequence, sequence);
588        assert_eq!(delta.ts_event, ts_event);
589        assert_eq!(delta.ts_init, ts_init);
590    }
591
592    #[rstest]
593    fn test_clear() {
594        let instrument_id = InstrumentId::from("AAPL.XNAS");
595        let sequence = 1;
596        let ts_event = 2;
597        let ts_init = 3;
598
599        let delta = OrderBookDelta::clear(instrument_id, sequence, ts_event.into(), ts_init.into());
600
601        assert_eq!(delta.instrument_id, instrument_id);
602        assert_eq!(delta.action, BookAction::Clear);
603        assert!(delta.order.price.is_zero());
604        assert!(delta.order.size.is_zero());
605        assert_eq!(delta.order.side, OrderSide::NoOrderSide);
606        assert_eq!(delta.order.order_id, 0);
607        assert_eq!(delta.flags, 32);
608        assert_eq!(delta.sequence, sequence);
609        assert_eq!(delta.ts_event, ts_event);
610        assert_eq!(delta.ts_init, ts_init);
611    }
612
613    #[rstest]
614    fn test_order_book_delta_hash() {
615        let delta1 = create_test_delta();
616        let delta2 = create_test_delta();
617
618        let mut hasher1 = DefaultHasher::new();
619        let mut hasher2 = DefaultHasher::new();
620
621        delta1.hash(&mut hasher1);
622        delta2.hash(&mut hasher2);
623
624        assert_eq!(hasher1.finish(), hasher2.finish());
625    }
626
627    #[rstest]
628    fn test_order_book_delta_hash_different_deltas() {
629        let delta1 = create_test_delta();
630        let order2 = BookOrder::new(
631            OrderSide::Sell,
632            Price::from("1.0505"),
633            Quantity::from("50000"),
634            67890,
635        );
636        let delta2 = OrderBookDelta::new(
637            InstrumentId::from("EURUSD.SIM"),
638            BookAction::Add,
639            order2,
640            0,
641            123,
642            UnixNanos::from(1_000_000_000),
643            UnixNanos::from(2_000_000_000),
644        );
645
646        let mut hasher1 = DefaultHasher::new();
647        let mut hasher2 = DefaultHasher::new();
648
649        delta1.hash(&mut hasher1);
650        delta2.hash(&mut hasher2);
651
652        assert_ne!(hasher1.finish(), hasher2.finish());
653    }
654
655    #[rstest]
656    fn test_order_book_delta_partial_eq() {
657        let delta1 = create_test_delta();
658        let delta2 = create_test_delta();
659
660        // Test equality
661        assert_eq!(delta1, delta2);
662
663        // Test inequality with different instrument
664        let order3 = BookOrder::new(
665            OrderSide::Buy,
666            Price::from("1.0500"),
667            Quantity::from("100000"),
668            12345,
669        );
670        let delta3 = OrderBookDelta::new(
671            InstrumentId::from("GBPUSD.SIM"),
672            BookAction::Add,
673            order3,
674            0,
675            123,
676            UnixNanos::from(1_000_000_000),
677            UnixNanos::from(2_000_000_000),
678        );
679
680        assert_ne!(delta1, delta3);
681    }
682
683    #[rstest]
684    fn test_order_book_delta_clone() {
685        let delta1 = create_test_delta();
686        let delta2 = delta1;
687
688        assert_eq!(delta1, delta2);
689        assert_eq!(delta1.instrument_id, delta2.instrument_id);
690        assert_eq!(delta1.action, delta2.action);
691        assert_eq!(delta1.order, delta2.order);
692        assert_eq!(delta1.flags, delta2.flags);
693        assert_eq!(delta1.sequence, delta2.sequence);
694        assert_eq!(delta1.ts_event, delta2.ts_event);
695        assert_eq!(delta1.ts_init, delta2.ts_init);
696    }
697
698    #[rstest]
699    fn test_order_book_delta_debug() {
700        let delta = create_test_delta();
701        let debug_str = format!("{delta:?}");
702
703        assert!(debug_str.contains("OrderBookDelta"));
704        assert!(debug_str.contains("EURUSD.SIM"));
705        assert!(debug_str.contains("Add"));
706        assert!(debug_str.contains("BUY"));
707        assert!(debug_str.contains("1.0500"));
708    }
709
710    #[rstest]
711    fn test_order_book_delta_serialization() {
712        let delta = create_test_delta();
713
714        let json = serde_json::to_string(&delta).unwrap();
715        let deserialized: OrderBookDelta = serde_json::from_str(&json).unwrap();
716
717        assert_eq!(delta, deserialized);
718    }
719
720    #[rstest]
721    fn test_json_serialization(stub_delta: OrderBookDelta) {
722        let delta = stub_delta;
723        let serialized = delta.to_json_bytes().unwrap();
724        let deserialized = OrderBookDelta::from_json_bytes(serialized.as_ref()).unwrap();
725        assert_eq!(deserialized, delta);
726    }
727
728    #[rstest]
729    fn test_msgpack_serialization(stub_delta: OrderBookDelta) {
730        let delta = stub_delta;
731        let serialized = delta.to_msgpack_bytes().unwrap();
732        let deserialized = OrderBookDelta::from_msgpack_bytes(serialized.as_ref()).unwrap();
733        assert_eq!(deserialized, delta);
734    }
735}