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