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    fn test_order_book_delta_new_with_zero_size_panics() {
283        let instrument_id = InstrumentId::from("AAPL.XNAS");
284        let action = BookAction::Add;
285        let price = Price::from("100.00");
286        let zero_size = Quantity::from(0);
287        let side = OrderSide::Buy;
288        let order_id = 123_456;
289        let flags = 0;
290        let sequence = 1;
291        let ts_event = UnixNanos::from(0);
292        let ts_init = UnixNanos::from(1);
293
294        let order = BookOrder::new(side, price, zero_size, order_id);
295
296        let result = std::panic::catch_unwind(|| {
297            let _ = OrderBookDelta::new(
298                instrument_id,
299                action,
300                order,
301                flags,
302                sequence,
303                ts_event,
304                ts_init,
305            );
306        });
307        assert!(result.is_err());
308    }
309
310    #[rstest]
311    fn test_order_book_delta_new_checked_with_zero_size_error() {
312        let instrument_id = InstrumentId::from("AAPL.XNAS");
313        let action = BookAction::Add;
314        let price = Price::from("100.00");
315        let zero_size = Quantity::from(0);
316        let side = OrderSide::Buy;
317        let order_id = 123_456;
318        let flags = 0;
319        let sequence = 1;
320        let ts_event = UnixNanos::from(0);
321        let ts_init = UnixNanos::from(1);
322
323        let order = BookOrder::new(side, price, zero_size, order_id);
324
325        let result = OrderBookDelta::new_checked(
326            instrument_id,
327            action,
328            order,
329            flags,
330            sequence,
331            ts_event,
332            ts_init,
333        );
334
335        assert!(result.is_err());
336        assert!(
337            result
338                .unwrap_err()
339                .to_string()
340                .contains("invalid `Quantity` for 'order.size' not positive")
341        );
342    }
343
344    #[rstest]
345    fn test_order_book_delta_new_checked_delete_with_zero_size_ok() {
346        let order = BookOrder::new(
347            OrderSide::Buy,
348            Price::from("100.00"),
349            Quantity::from(0),
350            123_456,
351        );
352        let result = OrderBookDelta::new_checked(
353            InstrumentId::from("TEST.SIM"),
354            BookAction::Delete,
355            order,
356            0,
357            1,
358            UnixNanos::from(0),
359            UnixNanos::from(1),
360        );
361
362        assert!(result.is_ok());
363    }
364
365    #[rstest]
366    fn test_order_book_delta_clear() {
367        let instrument_id = InstrumentId::from("BTCUSD.CRYPTO");
368        let sequence = 999;
369        let ts_event = UnixNanos::from(3_000_000_000);
370        let ts_init = UnixNanos::from(4_000_000_000);
371
372        let delta = OrderBookDelta::clear(instrument_id, sequence, ts_event, ts_init);
373
374        assert_eq!(delta.instrument_id, instrument_id);
375        assert_eq!(delta.action, BookAction::Clear);
376        assert!(delta.order.price.is_zero());
377        assert!(delta.order.size.is_zero());
378        assert_eq!(delta.order.side, OrderSide::NoOrderSide);
379        assert_eq!(delta.order.order_id, 0);
380        assert_eq!(delta.flags, RecordFlag::F_SNAPSHOT as u8);
381        assert_eq!(delta.sequence, sequence);
382        assert_eq!(delta.ts_event, ts_event);
383        assert_eq!(delta.ts_init, ts_init);
384    }
385
386    #[rstest]
387    fn test_get_metadata() {
388        let instrument_id = InstrumentId::from("EURUSD.SIM");
389        let metadata = OrderBookDelta::get_metadata(&instrument_id, 5, 8);
390
391        assert_eq!(metadata.len(), 3);
392        assert_eq!(
393            metadata.get("instrument_id"),
394            Some(&"EURUSD.SIM".to_string())
395        );
396        assert_eq!(metadata.get("price_precision"), Some(&"5".to_string()));
397        assert_eq!(metadata.get("size_precision"), Some(&"8".to_string()));
398    }
399
400    #[rstest]
401    fn test_get_fields() {
402        let fields = OrderBookDelta::get_fields();
403
404        assert_eq!(fields.len(), 9);
405        assert_eq!(fields.get("action"), Some(&"UInt8".to_string()));
406        assert_eq!(fields.get("side"), Some(&"UInt8".to_string()));
407
408        #[cfg(feature = "high-precision")]
409        {
410            assert_eq!(
411                fields.get("price"),
412                Some(&"FixedSizeBinary(16)".to_string())
413            );
414            assert_eq!(fields.get("size"), Some(&"FixedSizeBinary(16)".to_string()));
415        }
416        #[cfg(not(feature = "high-precision"))]
417        {
418            assert_eq!(fields.get("price"), Some(&"FixedSizeBinary(8)".to_string()));
419            assert_eq!(fields.get("size"), Some(&"FixedSizeBinary(8)".to_string()));
420        }
421
422        assert_eq!(fields.get("order_id"), Some(&"UInt64".to_string()));
423        assert_eq!(fields.get("flags"), Some(&"UInt8".to_string()));
424        assert_eq!(fields.get("sequence"), Some(&"UInt64".to_string()));
425        assert_eq!(fields.get("ts_event"), Some(&"UInt64".to_string()));
426        assert_eq!(fields.get("ts_init"), Some(&"UInt64".to_string()));
427    }
428
429    #[rstest]
430    #[case(BookAction::Add)]
431    #[case(BookAction::Update)]
432    #[case(BookAction::Delete)]
433    #[case(BookAction::Clear)]
434    fn test_order_book_delta_with_different_actions(#[case] action: BookAction) {
435        let order = BookOrder::new(
436            OrderSide::Buy,
437            Price::from("100.00"),
438            if matches!(action, BookAction::Delete | BookAction::Clear) {
439                Quantity::from(0)
440            } else {
441                Quantity::from("1000")
442            },
443            123_456,
444        );
445
446        let result = if matches!(action, BookAction::Clear) {
447            Ok(OrderBookDelta::clear(
448                InstrumentId::from("TEST.SIM"),
449                1,
450                UnixNanos::from(1_000_000_000),
451                UnixNanos::from(2_000_000_000),
452            ))
453        } else {
454            OrderBookDelta::new_checked(
455                InstrumentId::from("TEST.SIM"),
456                action,
457                order,
458                0,
459                1,
460                UnixNanos::from(1_000_000_000),
461                UnixNanos::from(2_000_000_000),
462            )
463        };
464
465        assert!(result.is_ok());
466        let delta = result.unwrap();
467        assert_eq!(delta.action, action);
468    }
469
470    #[rstest]
471    #[case(OrderSide::Buy)]
472    #[case(OrderSide::Sell)]
473    fn test_order_book_delta_with_different_sides(#[case] side: OrderSide) {
474        let order = BookOrder::new(side, Price::from("100.00"), Quantity::from("1000"), 123_456);
475
476        let delta = OrderBookDelta::new(
477            InstrumentId::from("TEST.SIM"),
478            BookAction::Add,
479            order,
480            0,
481            1,
482            UnixNanos::from(1_000_000_000),
483            UnixNanos::from(2_000_000_000),
484        );
485
486        assert_eq!(delta.order.side, side);
487    }
488
489    #[rstest]
490    fn test_order_book_delta_has_ts_init() {
491        let delta = create_test_delta();
492        assert_eq!(delta.ts_init(), UnixNanos::from(2_000_000_000));
493    }
494
495    #[rstest]
496    fn test_order_book_delta_display() {
497        let delta = create_test_delta();
498        let display_str = format!("{delta}");
499
500        assert!(display_str.contains("EURUSD.SIM"));
501        assert!(display_str.contains("ADD"));
502        assert!(display_str.contains("BUY"));
503        assert!(display_str.contains("1.0500"));
504        assert!(display_str.contains("100000"));
505        assert!(display_str.contains("12345"));
506        assert!(display_str.contains("123"));
507    }
508
509    #[rstest]
510    fn test_order_book_delta_with_zero_timestamps() {
511        let order = BookOrder::new(
512            OrderSide::Buy,
513            Price::from("100.00"),
514            Quantity::from("1000"),
515            123_456,
516        );
517        let delta = OrderBookDelta::new(
518            InstrumentId::from("TEST.SIM"),
519            BookAction::Add,
520            order,
521            0,
522            0,
523            UnixNanos::from(0),
524            UnixNanos::from(0),
525        );
526
527        assert_eq!(delta.sequence, 0);
528        assert_eq!(delta.ts_event, UnixNanos::from(0));
529        assert_eq!(delta.ts_init, UnixNanos::from(0));
530    }
531
532    #[rstest]
533    fn test_order_book_delta_with_max_values() {
534        let order = BookOrder::new(
535            OrderSide::Sell,
536            Price::from("999999.9999"),
537            Quantity::from("999999999.9999"),
538            u64::MAX,
539        );
540        let delta = OrderBookDelta::new(
541            InstrumentId::from("TEST.SIM"),
542            BookAction::Update,
543            order,
544            u8::MAX,
545            u64::MAX,
546            UnixNanos::from(u64::MAX),
547            UnixNanos::from(u64::MAX),
548        );
549
550        assert_eq!(delta.flags, u8::MAX);
551        assert_eq!(delta.sequence, u64::MAX);
552        assert_eq!(delta.order.order_id, u64::MAX);
553        assert_eq!(delta.ts_event, UnixNanos::from(u64::MAX));
554        assert_eq!(delta.ts_init, UnixNanos::from(u64::MAX));
555    }
556
557    #[rstest]
558    fn test_new() {
559        let instrument_id = InstrumentId::from("AAPL.XNAS");
560        let action = BookAction::Add;
561        let price = Price::from("100.00");
562        let size = Quantity::from("10");
563        let side = OrderSide::Buy;
564        let order_id = 123_456;
565        let flags = 0;
566        let sequence = 1;
567        let ts_event = 1;
568        let ts_init = 2;
569
570        let order = BookOrder::new(side, price, size, order_id);
571
572        let delta = OrderBookDelta::new(
573            instrument_id,
574            action,
575            order,
576            flags,
577            sequence,
578            ts_event.into(),
579            ts_init.into(),
580        );
581
582        assert_eq!(delta.instrument_id, instrument_id);
583        assert_eq!(delta.action, action);
584        assert_eq!(delta.order.price, price);
585        assert_eq!(delta.order.size, size);
586        assert_eq!(delta.order.side, side);
587        assert_eq!(delta.order.order_id, order_id);
588        assert_eq!(delta.flags, flags);
589        assert_eq!(delta.sequence, sequence);
590        assert_eq!(delta.ts_event, ts_event);
591        assert_eq!(delta.ts_init, ts_init);
592    }
593
594    #[rstest]
595    fn test_clear() {
596        let instrument_id = InstrumentId::from("AAPL.XNAS");
597        let sequence = 1;
598        let ts_event = 2;
599        let ts_init = 3;
600
601        let delta = OrderBookDelta::clear(instrument_id, sequence, ts_event.into(), ts_init.into());
602
603        assert_eq!(delta.instrument_id, instrument_id);
604        assert_eq!(delta.action, BookAction::Clear);
605        assert!(delta.order.price.is_zero());
606        assert!(delta.order.size.is_zero());
607        assert_eq!(delta.order.side, OrderSide::NoOrderSide);
608        assert_eq!(delta.order.order_id, 0);
609        assert_eq!(delta.flags, 32);
610        assert_eq!(delta.sequence, sequence);
611        assert_eq!(delta.ts_event, ts_event);
612        assert_eq!(delta.ts_init, ts_init);
613    }
614
615    #[rstest]
616    fn test_order_book_delta_hash() {
617        let delta1 = create_test_delta();
618        let delta2 = create_test_delta();
619
620        let mut hasher1 = DefaultHasher::new();
621        let mut hasher2 = DefaultHasher::new();
622
623        delta1.hash(&mut hasher1);
624        delta2.hash(&mut hasher2);
625
626        assert_eq!(hasher1.finish(), hasher2.finish());
627    }
628
629    #[rstest]
630    fn test_order_book_delta_hash_different_deltas() {
631        let delta1 = create_test_delta();
632        let order2 = BookOrder::new(
633            OrderSide::Sell,
634            Price::from("1.0505"),
635            Quantity::from("50000"),
636            67890,
637        );
638        let delta2 = OrderBookDelta::new(
639            InstrumentId::from("EURUSD.SIM"),
640            BookAction::Add,
641            order2,
642            0,
643            123,
644            UnixNanos::from(1_000_000_000),
645            UnixNanos::from(2_000_000_000),
646        );
647
648        let mut hasher1 = DefaultHasher::new();
649        let mut hasher2 = DefaultHasher::new();
650
651        delta1.hash(&mut hasher1);
652        delta2.hash(&mut hasher2);
653
654        assert_ne!(hasher1.finish(), hasher2.finish());
655    }
656
657    #[rstest]
658    fn test_order_book_delta_partial_eq() {
659        let delta1 = create_test_delta();
660        let delta2 = create_test_delta();
661
662        // Test equality
663        assert_eq!(delta1, delta2);
664
665        // Test inequality with different instrument
666        let order3 = BookOrder::new(
667            OrderSide::Buy,
668            Price::from("1.0500"),
669            Quantity::from("100000"),
670            12345,
671        );
672        let delta3 = OrderBookDelta::new(
673            InstrumentId::from("GBPUSD.SIM"),
674            BookAction::Add,
675            order3,
676            0,
677            123,
678            UnixNanos::from(1_000_000_000),
679            UnixNanos::from(2_000_000_000),
680        );
681
682        assert_ne!(delta1, delta3);
683    }
684
685    #[rstest]
686    fn test_order_book_delta_clone() {
687        let delta1 = create_test_delta();
688        let delta2 = delta1;
689
690        assert_eq!(delta1, delta2);
691        assert_eq!(delta1.instrument_id, delta2.instrument_id);
692        assert_eq!(delta1.action, delta2.action);
693        assert_eq!(delta1.order, delta2.order);
694        assert_eq!(delta1.flags, delta2.flags);
695        assert_eq!(delta1.sequence, delta2.sequence);
696        assert_eq!(delta1.ts_event, delta2.ts_event);
697        assert_eq!(delta1.ts_init, delta2.ts_init);
698    }
699
700    #[rstest]
701    fn test_order_book_delta_debug() {
702        let delta = create_test_delta();
703        let debug_str = format!("{delta:?}");
704
705        assert!(debug_str.contains("OrderBookDelta"));
706        assert!(debug_str.contains("EURUSD.SIM"));
707        assert!(debug_str.contains("Add"));
708        assert!(debug_str.contains("BUY"));
709        assert!(debug_str.contains("1.0500"));
710    }
711
712    #[rstest]
713    fn test_order_book_delta_serialization() {
714        let delta = create_test_delta();
715
716        let json = serde_json::to_string(&delta).unwrap();
717        let deserialized: OrderBookDelta = serde_json::from_str(&json).unwrap();
718
719        assert_eq!(delta, deserialized);
720    }
721
722    #[rstest]
723    fn test_json_serialization(stub_delta: OrderBookDelta) {
724        let delta = stub_delta;
725        let serialized = delta.to_json_bytes().unwrap();
726        let deserialized = OrderBookDelta::from_json_bytes(serialized.as_ref()).unwrap();
727        assert_eq!(deserialized, delta);
728    }
729
730    #[rstest]
731    fn test_msgpack_serialization(stub_delta: OrderBookDelta) {
732        let delta = stub_delta;
733        let serialized = delta.to_msgpack_bytes().unwrap();
734        let deserialized = OrderBookDelta::from_msgpack_bytes(serialized.as_ref()).unwrap();
735        assert_eq!(deserialized, delta);
736    }
737}