1use std::{collections::HashMap, fmt::Display};
19
20use indexmap::IndexMap;
21use nautilus_core::{UnixNanos, serialization::Serializable};
22use serde::{Deserialize, Serialize};
23
24use super::{HasTsInit, order::BookOrder};
25use crate::{identifiers::InstrumentId, types::fixed::FIXED_SIZE_BINARY};
26
27pub const DEPTH10_LEN: usize = 10;
28
29#[repr(C)]
39#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
40#[cfg_attr(
41 feature = "python",
42 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model", from_py_object)
43)]
44#[cfg_attr(
45 feature = "python",
46 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
47)]
48pub struct OrderBookDepth10 {
49 pub instrument_id: InstrumentId,
51 pub bids: [BookOrder; DEPTH10_LEN],
53 pub asks: [BookOrder; DEPTH10_LEN],
55 pub bid_counts: [u32; DEPTH10_LEN],
57 pub ask_counts: [u32; DEPTH10_LEN],
59 pub flags: u8,
61 pub sequence: u64,
63 pub ts_event: UnixNanos,
65 pub ts_init: UnixNanos,
67}
68
69impl OrderBookDepth10 {
70 #[allow(clippy::too_many_arguments)]
72 #[must_use]
73 pub fn new(
74 instrument_id: InstrumentId,
75 bids: [BookOrder; DEPTH10_LEN],
76 asks: [BookOrder; DEPTH10_LEN],
77 bid_counts: [u32; DEPTH10_LEN],
78 ask_counts: [u32; DEPTH10_LEN],
79 flags: u8,
80 sequence: u64,
81 ts_event: UnixNanos,
82 ts_init: UnixNanos,
83 ) -> Self {
84 Self {
85 instrument_id,
86 bids,
87 asks,
88 bid_counts,
89 ask_counts,
90 flags,
91 sequence,
92 ts_event,
93 ts_init,
94 }
95 }
96
97 #[must_use]
99 pub fn get_metadata(
100 instrument_id: &InstrumentId,
101 price_precision: u8,
102 size_precision: u8,
103 ) -> HashMap<String, String> {
104 let mut metadata = HashMap::new();
105 metadata.insert("instrument_id".to_string(), instrument_id.to_string());
106 metadata.insert("price_precision".to_string(), price_precision.to_string());
107 metadata.insert("size_precision".to_string(), size_precision.to_string());
108 metadata
109 }
110
111 #[must_use]
113 pub fn get_fields() -> IndexMap<String, String> {
114 let mut metadata = IndexMap::new();
115 metadata.insert("bid_price_0".to_string(), FIXED_SIZE_BINARY.to_string());
116 metadata.insert("bid_price_1".to_string(), FIXED_SIZE_BINARY.to_string());
117 metadata.insert("bid_price_2".to_string(), FIXED_SIZE_BINARY.to_string());
118 metadata.insert("bid_price_3".to_string(), FIXED_SIZE_BINARY.to_string());
119 metadata.insert("bid_price_4".to_string(), FIXED_SIZE_BINARY.to_string());
120 metadata.insert("bid_price_5".to_string(), FIXED_SIZE_BINARY.to_string());
121 metadata.insert("bid_price_6".to_string(), FIXED_SIZE_BINARY.to_string());
122 metadata.insert("bid_price_7".to_string(), FIXED_SIZE_BINARY.to_string());
123 metadata.insert("bid_price_8".to_string(), FIXED_SIZE_BINARY.to_string());
124 metadata.insert("bid_price_9".to_string(), FIXED_SIZE_BINARY.to_string());
125 metadata.insert("ask_price_0".to_string(), FIXED_SIZE_BINARY.to_string());
126 metadata.insert("ask_price_1".to_string(), FIXED_SIZE_BINARY.to_string());
127 metadata.insert("ask_price_2".to_string(), FIXED_SIZE_BINARY.to_string());
128 metadata.insert("ask_price_3".to_string(), FIXED_SIZE_BINARY.to_string());
129 metadata.insert("ask_price_4".to_string(), FIXED_SIZE_BINARY.to_string());
130 metadata.insert("ask_price_5".to_string(), FIXED_SIZE_BINARY.to_string());
131 metadata.insert("ask_price_6".to_string(), FIXED_SIZE_BINARY.to_string());
132 metadata.insert("ask_price_7".to_string(), FIXED_SIZE_BINARY.to_string());
133 metadata.insert("ask_price_8".to_string(), FIXED_SIZE_BINARY.to_string());
134 metadata.insert("ask_price_9".to_string(), FIXED_SIZE_BINARY.to_string());
135 metadata.insert("bid_size_0".to_string(), FIXED_SIZE_BINARY.to_string());
136 metadata.insert("bid_size_1".to_string(), FIXED_SIZE_BINARY.to_string());
137 metadata.insert("bid_size_2".to_string(), FIXED_SIZE_BINARY.to_string());
138 metadata.insert("bid_size_3".to_string(), FIXED_SIZE_BINARY.to_string());
139 metadata.insert("bid_size_4".to_string(), FIXED_SIZE_BINARY.to_string());
140 metadata.insert("bid_size_5".to_string(), FIXED_SIZE_BINARY.to_string());
141 metadata.insert("bid_size_6".to_string(), FIXED_SIZE_BINARY.to_string());
142 metadata.insert("bid_size_7".to_string(), FIXED_SIZE_BINARY.to_string());
143 metadata.insert("bid_size_8".to_string(), FIXED_SIZE_BINARY.to_string());
144 metadata.insert("bid_size_9".to_string(), FIXED_SIZE_BINARY.to_string());
145 metadata.insert("ask_size_0".to_string(), FIXED_SIZE_BINARY.to_string());
146 metadata.insert("ask_size_1".to_string(), FIXED_SIZE_BINARY.to_string());
147 metadata.insert("ask_size_2".to_string(), FIXED_SIZE_BINARY.to_string());
148 metadata.insert("ask_size_3".to_string(), FIXED_SIZE_BINARY.to_string());
149 metadata.insert("ask_size_4".to_string(), FIXED_SIZE_BINARY.to_string());
150 metadata.insert("ask_size_5".to_string(), FIXED_SIZE_BINARY.to_string());
151 metadata.insert("ask_size_6".to_string(), FIXED_SIZE_BINARY.to_string());
152 metadata.insert("ask_size_7".to_string(), FIXED_SIZE_BINARY.to_string());
153 metadata.insert("ask_size_8".to_string(), FIXED_SIZE_BINARY.to_string());
154 metadata.insert("ask_size_9".to_string(), FIXED_SIZE_BINARY.to_string());
155 metadata.insert("bid_count_0".to_string(), "UInt32".to_string());
156 metadata.insert("bid_count_1".to_string(), "UInt32".to_string());
157 metadata.insert("bid_count_2".to_string(), "UInt32".to_string());
158 metadata.insert("bid_count_3".to_string(), "UInt32".to_string());
159 metadata.insert("bid_count_4".to_string(), "UInt32".to_string());
160 metadata.insert("bid_count_5".to_string(), "UInt32".to_string());
161 metadata.insert("bid_count_6".to_string(), "UInt32".to_string());
162 metadata.insert("bid_count_7".to_string(), "UInt32".to_string());
163 metadata.insert("bid_count_8".to_string(), "UInt32".to_string());
164 metadata.insert("bid_count_9".to_string(), "UInt32".to_string());
165 metadata.insert("ask_count_0".to_string(), "UInt32".to_string());
166 metadata.insert("ask_count_1".to_string(), "UInt32".to_string());
167 metadata.insert("ask_count_2".to_string(), "UInt32".to_string());
168 metadata.insert("ask_count_3".to_string(), "UInt32".to_string());
169 metadata.insert("ask_count_4".to_string(), "UInt32".to_string());
170 metadata.insert("ask_count_5".to_string(), "UInt32".to_string());
171 metadata.insert("ask_count_6".to_string(), "UInt32".to_string());
172 metadata.insert("ask_count_7".to_string(), "UInt32".to_string());
173 metadata.insert("ask_count_8".to_string(), "UInt32".to_string());
174 metadata.insert("ask_count_9".to_string(), "UInt32".to_string());
175 metadata.insert("flags".to_string(), "UInt8".to_string());
176 metadata.insert("sequence".to_string(), "UInt64".to_string());
177 metadata.insert("ts_event".to_string(), "UInt64".to_string());
178 metadata.insert("ts_init".to_string(), "UInt64".to_string());
179 metadata
180 }
181}
182
183impl Display for OrderBookDepth10 {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 write!(
187 f,
188 "{},flags={},sequence={},ts_event={},ts_init={}",
189 self.instrument_id, self.flags, self.sequence, self.ts_event, self.ts_init
190 )
191 }
192}
193
194impl Serializable for OrderBookDepth10 {}
195
196impl HasTsInit for OrderBookDepth10 {
197 fn ts_init(&self) -> UnixNanos {
198 self.ts_init
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use std::{
205 collections::hash_map::DefaultHasher,
206 hash::{Hash, Hasher},
207 };
208
209 use rstest::rstest;
210 use serde_json;
211
212 use super::*;
213 use crate::{
214 data::{order::BookOrder, stubs::*},
215 enums::OrderSide,
216 types::{Price, Quantity},
217 };
218
219 fn create_test_book_order(
220 side: OrderSide,
221 price: &str,
222 size: &str,
223 order_id: u64,
224 ) -> BookOrder {
225 BookOrder::new(side, Price::from(price), Quantity::from(size), order_id)
226 }
227
228 fn create_test_depth10() -> OrderBookDepth10 {
229 let instrument_id = InstrumentId::from("EURUSD.SIM");
230
231 let bids = [
233 create_test_book_order(OrderSide::Buy, "1.0500", "100000", 1),
234 create_test_book_order(OrderSide::Buy, "1.0499", "150000", 2),
235 create_test_book_order(OrderSide::Buy, "1.0498", "200000", 3),
236 create_test_book_order(OrderSide::Buy, "1.0497", "125000", 4),
237 create_test_book_order(OrderSide::Buy, "1.0496", "175000", 5),
238 create_test_book_order(OrderSide::Buy, "1.0495", "100000", 6),
239 create_test_book_order(OrderSide::Buy, "1.0494", "225000", 7),
240 create_test_book_order(OrderSide::Buy, "1.0493", "150000", 8),
241 create_test_book_order(OrderSide::Buy, "1.0492", "300000", 9),
242 create_test_book_order(OrderSide::Buy, "1.0491", "175000", 10),
243 ];
244
245 let asks = [
247 create_test_book_order(OrderSide::Sell, "1.0501", "100000", 11),
248 create_test_book_order(OrderSide::Sell, "1.0502", "125000", 12),
249 create_test_book_order(OrderSide::Sell, "1.0503", "150000", 13),
250 create_test_book_order(OrderSide::Sell, "1.0504", "175000", 14),
251 create_test_book_order(OrderSide::Sell, "1.0505", "200000", 15),
252 create_test_book_order(OrderSide::Sell, "1.0506", "100000", 16),
253 create_test_book_order(OrderSide::Sell, "1.0507", "250000", 17),
254 create_test_book_order(OrderSide::Sell, "1.0508", "125000", 18),
255 create_test_book_order(OrderSide::Sell, "1.0509", "300000", 19),
256 create_test_book_order(OrderSide::Sell, "1.0510", "175000", 20),
257 ];
258
259 let bid_counts = [1, 2, 1, 3, 1, 2, 1, 4, 1, 2];
260 let ask_counts = [2, 1, 3, 1, 2, 1, 4, 1, 2, 3];
261
262 OrderBookDepth10::new(
263 instrument_id,
264 bids,
265 asks,
266 bid_counts,
267 ask_counts,
268 32, 12345, UnixNanos::from(1_000_000_000), UnixNanos::from(2_000_000_000), )
273 }
274
275 fn create_empty_depth10() -> OrderBookDepth10 {
276 let instrument_id = InstrumentId::from("EMPTY.TEST");
277
278 let empty_bid = create_test_book_order(OrderSide::Buy, "0.0", "0", 0);
280 let empty_ask = create_test_book_order(OrderSide::Sell, "0.0", "0", 0);
281
282 OrderBookDepth10::new(
283 instrument_id,
284 [empty_bid; DEPTH10_LEN],
285 [empty_ask; DEPTH10_LEN],
286 [0; DEPTH10_LEN],
287 [0; DEPTH10_LEN],
288 0,
289 0,
290 UnixNanos::from(0),
291 UnixNanos::from(0),
292 )
293 }
294
295 #[rstest]
296 fn test_order_book_depth10_new() {
297 let depth = create_test_depth10();
298
299 assert_eq!(depth.instrument_id, InstrumentId::from("EURUSD.SIM"));
300 assert_eq!(depth.bids.len(), DEPTH10_LEN);
301 assert_eq!(depth.asks.len(), DEPTH10_LEN);
302 assert_eq!(depth.bid_counts.len(), DEPTH10_LEN);
303 assert_eq!(depth.ask_counts.len(), DEPTH10_LEN);
304 assert_eq!(depth.flags, 32);
305 assert_eq!(depth.sequence, 12345);
306 assert_eq!(depth.ts_event, UnixNanos::from(1_000_000_000));
307 assert_eq!(depth.ts_init, UnixNanos::from(2_000_000_000));
308 }
309
310 #[rstest]
311 fn test_order_book_depth10_new_with_all_parameters() {
312 let instrument_id = InstrumentId::from("GBPUSD.SIM");
313 let bid = create_test_book_order(OrderSide::Buy, "1.2500", "50000", 1);
314 let ask = create_test_book_order(OrderSide::Sell, "1.2501", "75000", 2);
315 let flags = 64u8;
316 let sequence = 999u64;
317 let ts_event = UnixNanos::from(5_000_000_000);
318 let ts_init = UnixNanos::from(6_000_000_000);
319
320 let depth = OrderBookDepth10::new(
321 instrument_id,
322 [bid; DEPTH10_LEN],
323 [ask; DEPTH10_LEN],
324 [5; DEPTH10_LEN],
325 [3; DEPTH10_LEN],
326 flags,
327 sequence,
328 ts_event,
329 ts_init,
330 );
331
332 assert_eq!(depth.instrument_id, instrument_id);
333 assert_eq!(depth.bids[0], bid);
334 assert_eq!(depth.asks[0], ask);
335 assert_eq!(depth.bid_counts[0], 5);
336 assert_eq!(depth.ask_counts[0], 3);
337 assert_eq!(depth.flags, flags);
338 assert_eq!(depth.sequence, sequence);
339 assert_eq!(depth.ts_event, ts_event);
340 assert_eq!(depth.ts_init, ts_init);
341 }
342
343 #[rstest]
344 fn test_order_book_depth10_fixed_array_sizes() {
345 let depth = create_test_depth10();
346
347 assert_eq!(depth.bids.len(), 10);
349 assert_eq!(depth.asks.len(), 10);
350 assert_eq!(depth.bid_counts.len(), 10);
351 assert_eq!(depth.ask_counts.len(), 10);
352
353 assert_eq!(DEPTH10_LEN, 10);
355 }
356
357 #[rstest]
358 fn test_order_book_depth10_array_indexing() {
359 let depth = create_test_depth10();
360
361 assert_eq!(depth.bids[0].price, Price::from("1.0500"));
363 assert_eq!(depth.bids[9].price, Price::from("1.0491"));
364 assert_eq!(depth.asks[0].price, Price::from("1.0501"));
365 assert_eq!(depth.asks[9].price, Price::from("1.0510"));
366 assert_eq!(depth.bid_counts[0], 1);
367 assert_eq!(depth.bid_counts[9], 2);
368 assert_eq!(depth.ask_counts[0], 2);
369 assert_eq!(depth.ask_counts[9], 3);
370 }
371
372 #[rstest]
373 fn test_order_book_depth10_bid_ask_ordering() {
374 let depth = create_test_depth10();
375
376 for i in 0..9 {
378 assert!(
379 depth.bids[i].price >= depth.bids[i + 1].price,
380 "Bid prices should be in descending order: {} >= {}",
381 depth.bids[i].price,
382 depth.bids[i + 1].price
383 );
384 }
385
386 for i in 0..9 {
388 assert!(
389 depth.asks[i].price <= depth.asks[i + 1].price,
390 "Ask prices should be in ascending order: {} <= {}",
391 depth.asks[i].price,
392 depth.asks[i + 1].price
393 );
394 }
395
396 assert!(
398 depth.bids[0].price < depth.asks[0].price,
399 "Best bid {} should be less than best ask {}",
400 depth.bids[0].price,
401 depth.asks[0].price
402 );
403 }
404
405 #[rstest]
406 fn test_order_book_depth10_clone() {
407 let depth1 = create_test_depth10();
408 let depth2 = depth1;
409
410 assert_eq!(depth1.instrument_id, depth2.instrument_id);
411 assert_eq!(depth1.bids, depth2.bids);
412 assert_eq!(depth1.asks, depth2.asks);
413 assert_eq!(depth1.bid_counts, depth2.bid_counts);
414 assert_eq!(depth1.ask_counts, depth2.ask_counts);
415 assert_eq!(depth1.flags, depth2.flags);
416 assert_eq!(depth1.sequence, depth2.sequence);
417 assert_eq!(depth1.ts_event, depth2.ts_event);
418 assert_eq!(depth1.ts_init, depth2.ts_init);
419 }
420
421 #[rstest]
422 fn test_order_book_depth10_copy() {
423 let depth1 = create_test_depth10();
424 let depth2 = depth1;
425
426 assert_eq!(depth1, depth2);
429 }
430
431 #[rstest]
432 fn test_order_book_depth10_debug() {
433 let depth = create_test_depth10();
434 let debug_str = format!("{depth:?}");
435
436 assert!(debug_str.contains("OrderBookDepth10"));
437 assert!(debug_str.contains("EURUSD.SIM"));
438 assert!(debug_str.contains("flags: 32"));
439 assert!(debug_str.contains("sequence: 12345"));
440 }
441
442 #[rstest]
443 fn test_order_book_depth10_partial_eq() {
444 let depth1 = create_test_depth10();
445 let depth2 = create_test_depth10();
446 let depth3 = create_empty_depth10();
447
448 assert_eq!(depth1, depth2); assert_ne!(depth1, depth3); assert_ne!(depth2, depth3); }
452
453 #[rstest]
454 fn test_order_book_depth10_eq_consistency() {
455 let depth1 = create_test_depth10();
456 let depth2 = create_test_depth10();
457
458 assert_eq!(depth1, depth2);
459 assert_eq!(depth2, depth1); assert_eq!(depth1, depth1); }
462
463 #[rstest]
464 fn test_order_book_depth10_hash() {
465 let depth1 = create_test_depth10();
466 let depth2 = create_test_depth10();
467
468 let mut hasher1 = DefaultHasher::new();
469 let mut hasher2 = DefaultHasher::new();
470
471 depth1.hash(&mut hasher1);
472 depth2.hash(&mut hasher2);
473
474 assert_eq!(hasher1.finish(), hasher2.finish()); }
476
477 #[rstest]
478 fn test_order_book_depth10_hash_different_objects() {
479 let depth1 = create_test_depth10();
480 let depth2 = create_empty_depth10();
481
482 let mut hasher1 = DefaultHasher::new();
483 let mut hasher2 = DefaultHasher::new();
484
485 depth1.hash(&mut hasher1);
486 depth2.hash(&mut hasher2);
487
488 assert_ne!(hasher1.finish(), hasher2.finish()); }
490
491 #[rstest]
492 fn test_order_book_depth10_display() {
493 let depth = create_test_depth10();
494 let display_str = format!("{depth}");
495
496 assert!(display_str.contains("EURUSD.SIM"));
497 assert!(display_str.contains("flags=32"));
498 assert!(display_str.contains("sequence=12345"));
499 assert!(display_str.contains("ts_event=1000000000"));
500 assert!(display_str.contains("ts_init=2000000000"));
501 }
502
503 #[rstest]
504 fn test_order_book_depth10_display_format() {
505 let depth = create_test_depth10();
506 let expected = "EURUSD.SIM,flags=32,sequence=12345,ts_event=1000000000,ts_init=2000000000";
507
508 assert_eq!(format!("{depth}"), expected);
509 }
510
511 #[rstest]
512 fn test_order_book_depth10_serialization() {
513 let depth = create_test_depth10();
514
515 let json = serde_json::to_string(&depth).unwrap();
517 let deserialized: OrderBookDepth10 = serde_json::from_str(&json).unwrap();
518
519 assert_eq!(depth, deserialized);
520 }
521
522 #[rstest]
523 fn test_order_book_depth10_serializable_trait() {
524 fn assert_serializable<T: Serializable>(_: &T) {}
525
526 let depth = create_test_depth10();
527
528 assert_serializable(&depth);
530 }
531
532 #[rstest]
533 fn test_order_book_depth10_has_ts_init() {
534 let depth = create_test_depth10();
535
536 assert_eq!(depth.ts_init(), UnixNanos::from(2_000_000_000));
537 }
538
539 #[rstest]
540 fn test_order_book_depth10_get_metadata() {
541 let instrument_id = InstrumentId::from("EURUSD.SIM");
542 let price_precision = 5u8;
543 let size_precision = 0u8;
544
545 let metadata =
546 OrderBookDepth10::get_metadata(&instrument_id, price_precision, size_precision);
547
548 assert_eq!(
549 metadata.get("instrument_id"),
550 Some(&"EURUSD.SIM".to_string())
551 );
552 assert_eq!(metadata.get("price_precision"), Some(&"5".to_string()));
553 assert_eq!(metadata.get("size_precision"), Some(&"0".to_string()));
554 assert_eq!(metadata.len(), 3);
555 }
556
557 #[rstest]
558 fn test_order_book_depth10_get_fields() {
559 let fields = OrderBookDepth10::get_fields();
560
561 for i in 0..10 {
563 assert_eq!(
564 fields.get(&format!("bid_price_{i}")),
565 Some(&FIXED_SIZE_BINARY.to_string())
566 );
567 assert_eq!(
568 fields.get(&format!("ask_price_{i}")),
569 Some(&FIXED_SIZE_BINARY.to_string())
570 );
571 }
572
573 for i in 0..10 {
575 assert_eq!(
576 fields.get(&format!("bid_size_{i}")),
577 Some(&FIXED_SIZE_BINARY.to_string())
578 );
579 assert_eq!(
580 fields.get(&format!("ask_size_{i}")),
581 Some(&FIXED_SIZE_BINARY.to_string())
582 );
583 }
584
585 for i in 0..10 {
587 assert_eq!(
588 fields.get(&format!("bid_count_{i}")),
589 Some(&"UInt32".to_string())
590 );
591 assert_eq!(
592 fields.get(&format!("ask_count_{i}")),
593 Some(&"UInt32".to_string())
594 );
595 }
596
597 assert_eq!(fields.get("flags"), Some(&"UInt8".to_string()));
599 assert_eq!(fields.get("sequence"), Some(&"UInt64".to_string()));
600 assert_eq!(fields.get("ts_event"), Some(&"UInt64".to_string()));
601 assert_eq!(fields.get("ts_init"), Some(&"UInt64".to_string()));
602
603 assert_eq!(fields.len(), 64);
606 }
607
608 #[rstest]
609 fn test_order_book_depth10_get_fields_order() {
610 let fields = OrderBookDepth10::get_fields();
611 let keys: Vec<&String> = fields.keys().collect();
612
613 assert_eq!(keys[0], "bid_price_0");
615 assert_eq!(keys[9], "bid_price_9");
616 assert_eq!(keys[10], "ask_price_0");
617 assert_eq!(keys[19], "ask_price_9");
618 assert_eq!(keys[20], "bid_size_0");
619 assert_eq!(keys[29], "bid_size_9");
620 assert_eq!(keys[30], "ask_size_0");
621 assert_eq!(keys[39], "ask_size_9");
622 assert_eq!(keys[40], "bid_count_0");
623 assert_eq!(keys[41], "bid_count_1");
624 }
625
626 #[rstest]
627 fn test_order_book_depth10_empty_values() {
628 let depth = create_empty_depth10();
629
630 assert_eq!(depth.instrument_id, InstrumentId::from("EMPTY.TEST"));
631 assert_eq!(depth.flags, 0);
632 assert_eq!(depth.sequence, 0);
633 assert_eq!(depth.ts_event, UnixNanos::from(0));
634 assert_eq!(depth.ts_init, UnixNanos::from(0));
635
636 for bid in &depth.bids {
638 assert_eq!(bid.price, Price::from("0.0"));
639 assert_eq!(bid.size, Quantity::from("0"));
640 assert_eq!(bid.order_id, 0);
641 }
642
643 for ask in &depth.asks {
644 assert_eq!(ask.price, Price::from("0.0"));
645 assert_eq!(ask.size, Quantity::from("0"));
646 assert_eq!(ask.order_id, 0);
647 }
648
649 for &count in &depth.bid_counts {
651 assert_eq!(count, 0);
652 }
653
654 for &count in &depth.ask_counts {
655 assert_eq!(count, 0);
656 }
657 }
658
659 #[rstest]
660 fn test_order_book_depth10_max_values() {
661 let instrument_id = InstrumentId::from("MAX.TEST");
662 let max_bid = create_test_book_order(OrderSide::Buy, "999999.99", "999999999", u64::MAX);
663 let max_ask = create_test_book_order(OrderSide::Sell, "1000000.00", "999999999", u64::MAX);
664
665 let depth = OrderBookDepth10::new(
666 instrument_id,
667 [max_bid; DEPTH10_LEN],
668 [max_ask; DEPTH10_LEN],
669 [u32::MAX; DEPTH10_LEN],
670 [u32::MAX; DEPTH10_LEN],
671 u8::MAX,
672 u64::MAX,
673 UnixNanos::from(u64::MAX),
674 UnixNanos::from(u64::MAX),
675 );
676
677 assert_eq!(depth.flags, u8::MAX);
678 assert_eq!(depth.sequence, u64::MAX);
679 assert_eq!(depth.ts_event, UnixNanos::from(u64::MAX));
680 assert_eq!(depth.ts_init, UnixNanos::from(u64::MAX));
681
682 for &count in &depth.bid_counts {
683 assert_eq!(count, u32::MAX);
684 }
685
686 for &count in &depth.ask_counts {
687 assert_eq!(count, u32::MAX);
688 }
689 }
690
691 #[rstest]
692 fn test_order_book_depth10_different_instruments() {
693 let instruments = [
694 "EURUSD.SIM",
695 "GBPUSD.SIM",
696 "USDJPY.SIM",
697 "AUDUSD.SIM",
698 "USDCHF.SIM",
699 ];
700
701 for instrument_str in &instruments {
702 let instrument_id = InstrumentId::from(*instrument_str);
703 let bid = create_test_book_order(OrderSide::Buy, "1.0000", "100000", 1);
704 let ask = create_test_book_order(OrderSide::Sell, "1.0001", "100000", 2);
705
706 let depth = OrderBookDepth10::new(
707 instrument_id,
708 [bid; DEPTH10_LEN],
709 [ask; DEPTH10_LEN],
710 [1; DEPTH10_LEN],
711 [1; DEPTH10_LEN],
712 0,
713 1,
714 UnixNanos::from(1_000_000_000),
715 UnixNanos::from(2_000_000_000),
716 );
717
718 assert_eq!(depth.instrument_id, instrument_id);
719 assert!(format!("{depth}").contains(instrument_str));
720 }
721 }
722
723 #[rstest]
724 fn test_order_book_depth10_realistic_forex_spread() {
725 let instrument_id = InstrumentId::from("EURUSD.SIM");
726
727 let best_bid = create_test_book_order(OrderSide::Buy, "1.08500", "1000000", 1);
729 let best_ask = create_test_book_order(OrderSide::Sell, "1.08501", "1000000", 2);
730
731 let depth = OrderBookDepth10::new(
732 instrument_id,
733 [best_bid; DEPTH10_LEN],
734 [best_ask; DEPTH10_LEN],
735 [5; DEPTH10_LEN], [3; DEPTH10_LEN],
737 16, 123456, UnixNanos::from(1_672_531_200_000_000_000), UnixNanos::from(1_672_531_200_000_100_000),
741 );
742
743 assert_eq!(depth.bids[0].price, Price::from("1.08500"));
744 assert_eq!(depth.asks[0].price, Price::from("1.08501"));
745 assert!(depth.bids[0].price < depth.asks[0].price); assert_eq!(depth.bids[0].size, Quantity::from("1000000"));
749 assert_eq!(depth.bid_counts[0], 5);
750 assert_eq!(depth.ask_counts[0], 3);
751 }
752
753 #[rstest]
754 fn test_order_book_depth10_with_stub(stub_depth10: OrderBookDepth10) {
755 let depth = stub_depth10;
756
757 assert_eq!(depth.instrument_id, InstrumentId::from("AAPL.XNAS"));
758 assert_eq!(depth.bids.len(), 10);
759 assert_eq!(depth.asks.len(), 10);
760 assert_eq!(depth.asks[9].price, Price::from("109.0"));
761 assert_eq!(depth.asks[0].price, Price::from("100.0"));
762 assert_eq!(depth.bids[0].price, Price::from("99.0"));
763 assert_eq!(depth.bids[9].price, Price::from("90.0"));
764 assert_eq!(depth.bid_counts.len(), 10);
765 assert_eq!(depth.ask_counts.len(), 10);
766 assert_eq!(depth.bid_counts[0], 1);
767 assert_eq!(depth.ask_counts[0], 1);
768 assert_eq!(depth.flags, 0);
769 assert_eq!(depth.sequence, 0);
770 assert_eq!(depth.ts_event, UnixNanos::from(1));
771 assert_eq!(depth.ts_init, UnixNanos::from(2));
772 }
773
774 #[rstest]
775 fn test_new(stub_depth10: OrderBookDepth10) {
776 let depth = stub_depth10;
777 let instrument_id = InstrumentId::from("AAPL.XNAS");
778 let flags = 0;
779 let sequence = 0;
780 let ts_event = 1;
781 let ts_init = 2;
782
783 assert_eq!(depth.instrument_id, instrument_id);
784 assert_eq!(depth.bids.len(), 10);
785 assert_eq!(depth.asks.len(), 10);
786 assert_eq!(depth.asks[9].price, Price::from("109.0"));
787 assert_eq!(depth.asks[0].price, Price::from("100.0"));
788 assert_eq!(depth.bids[0].price, Price::from("99.0"));
789 assert_eq!(depth.bids[9].price, Price::from("90.0"));
790 assert_eq!(depth.bid_counts.len(), 10);
791 assert_eq!(depth.ask_counts.len(), 10);
792 assert_eq!(depth.bid_counts[0], 1);
793 assert_eq!(depth.ask_counts[0], 1);
794 assert_eq!(depth.flags, flags);
795 assert_eq!(depth.sequence, sequence);
796 assert_eq!(depth.ts_event, ts_event);
797 assert_eq!(depth.ts_init, ts_init);
798 }
799
800 #[rstest]
801 fn test_display(stub_depth10: OrderBookDepth10) {
802 let depth = stub_depth10;
803 assert_eq!(
804 format!("{depth}"),
805 "AAPL.XNAS,flags=0,sequence=0,ts_event=1,ts_init=2".to_string()
806 );
807 }
808}