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