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