1use std::{
21 cmp::Ordering,
22 collections::{BTreeMap, HashMap},
23 fmt::{Debug, Display},
24 hash::{Hash, Hasher},
25};
26
27use indexmap::IndexMap;
28use nautilus_core::UnixNanos;
29use rust_decimal::Decimal;
30
31use super::display::pprint_own_book;
32use crate::{
33 enums::{OrderSideSpecified, OrderStatus, OrderType, TimeInForce},
34 identifiers::{ClientOrderId, InstrumentId},
35 orderbook::BookPrice,
36 types::{Price, Quantity},
37};
38
39#[repr(C)]
44#[derive(Clone, Copy, Eq)]
45#[cfg_attr(
46 feature = "python",
47 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
48)]
49pub struct OwnBookOrder {
50 pub client_order_id: ClientOrderId,
52 pub side: OrderSideSpecified,
54 pub price: Price,
56 pub size: Quantity,
58 pub order_type: OrderType,
60 pub time_in_force: TimeInForce,
62 pub status: OrderStatus,
64 pub ts_last: UnixNanos,
66 pub ts_init: UnixNanos,
68}
69
70impl OwnBookOrder {
71 #[must_use]
73 #[allow(clippy::too_many_arguments)]
74 pub fn new(
75 client_order_id: ClientOrderId,
76 side: OrderSideSpecified,
77 price: Price,
78 size: Quantity,
79 order_type: OrderType,
80 time_in_force: TimeInForce,
81 status: OrderStatus,
82 ts_last: UnixNanos,
83 ts_init: UnixNanos,
84 ) -> Self {
85 Self {
86 client_order_id,
87 side,
88 price,
89 size,
90 order_type,
91 time_in_force,
92 status,
93 ts_last,
94 ts_init,
95 }
96 }
97
98 #[must_use]
100 pub fn to_book_price(&self) -> BookPrice {
101 BookPrice::new(self.price, self.side)
102 }
103
104 #[must_use]
106 pub fn exposure(&self) -> f64 {
107 self.price.as_f64() * self.size.as_f64()
108 }
109
110 #[must_use]
112 pub fn signed_size(&self) -> f64 {
113 match self.side {
114 OrderSideSpecified::Buy => self.size.as_f64(),
115 OrderSideSpecified::Sell => -(self.size.as_f64()),
116 }
117 }
118}
119
120impl Ord for OwnBookOrder {
121 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
122 self.ts_init.cmp(&other.ts_init)
124 }
125}
126
127impl PartialOrd for OwnBookOrder {
128 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
129 Some(self.cmp(other))
130 }
131}
132
133impl PartialEq for OwnBookOrder {
134 fn eq(&self, other: &Self) -> bool {
135 self.client_order_id == other.client_order_id && self.ts_init == other.ts_init
136 }
137}
138
139impl Hash for OwnBookOrder {
140 fn hash<H: Hasher>(&self, state: &mut H) {
141 self.client_order_id.hash(state);
142 }
143}
144
145impl Debug for OwnBookOrder {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 write!(
148 f,
149 "{}(client_order_id={}, side={}, price={}, size={}, order_type={}, time_in_force={}, ts_init={})",
150 stringify!(OwnBookOrder),
151 self.client_order_id,
152 self.side,
153 self.price,
154 self.size,
155 self.order_type,
156 self.time_in_force,
157 self.ts_init,
158 )
159 }
160}
161
162impl Display for OwnBookOrder {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 write!(
165 f,
166 "{},{},{},{},{},{},{}",
167 self.client_order_id,
168 self.side,
169 self.price,
170 self.size,
171 self.order_type,
172 self.time_in_force,
173 self.ts_init,
174 )
175 }
176}
177
178#[derive(Debug)]
179#[cfg_attr(
180 feature = "python",
181 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
182)]
183pub struct OwnOrderBook {
184 pub instrument_id: InstrumentId,
186 pub ts_last: UnixNanos,
188 pub event_count: u64,
190 pub(crate) bids: OwnBookLadder,
191 pub(crate) asks: OwnBookLadder,
192}
193
194impl PartialEq for OwnOrderBook {
195 fn eq(&self, other: &Self) -> bool {
196 self.instrument_id == other.instrument_id
197 }
198}
199
200impl Display for OwnOrderBook {
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 write!(
203 f,
204 "{}(instrument_id={}, event_count={})",
205 stringify!(OwnOrderBook),
206 self.instrument_id,
207 self.event_count,
208 )
209 }
210}
211
212impl OwnOrderBook {
213 #[must_use]
215 pub fn new(instrument_id: InstrumentId) -> Self {
216 Self {
217 instrument_id,
218 ts_last: UnixNanos::default(),
219 event_count: 0,
220 bids: OwnBookLadder::new(OrderSideSpecified::Buy),
221 asks: OwnBookLadder::new(OrderSideSpecified::Sell),
222 }
223 }
224
225 fn increment(&mut self, order: &OwnBookOrder) {
226 self.ts_last = order.ts_last;
227 self.event_count += 1;
228 }
229
230 pub fn reset(&mut self) {
232 self.bids.clear();
233 self.asks.clear();
234 self.ts_last = UnixNanos::default();
235 self.event_count = 0;
236 }
237
238 pub fn add(&mut self, order: OwnBookOrder) {
240 self.increment(&order);
241 match order.side {
242 OrderSideSpecified::Buy => self.bids.add(order),
243 OrderSideSpecified::Sell => self.asks.add(order),
244 }
245 }
246
247 pub fn update(&mut self, order: OwnBookOrder) -> anyhow::Result<()> {
249 self.increment(&order);
250 match order.side {
251 OrderSideSpecified::Buy => self.bids.update(order),
252 OrderSideSpecified::Sell => self.asks.update(order),
253 }
254 }
255
256 pub fn delete(&mut self, order: OwnBookOrder) -> anyhow::Result<()> {
258 self.increment(&order);
259 match order.side {
260 OrderSideSpecified::Buy => self.bids.delete(order),
261 OrderSideSpecified::Sell => self.asks.delete(order),
262 }
263 }
264
265 pub fn clear(&mut self) {
267 self.bids.clear();
268 self.asks.clear();
269 }
270
271 pub fn bids(&self) -> impl Iterator<Item = &OwnBookLevel> {
273 self.bids.levels.values()
274 }
275
276 pub fn asks(&self) -> impl Iterator<Item = &OwnBookLevel> {
278 self.asks.levels.values()
279 }
280
281 pub fn bids_as_map(&self) -> IndexMap<Decimal, Vec<OwnBookOrder>> {
283 self.bids()
284 .map(|level| {
285 (
286 level.price.value.as_decimal(),
287 level.orders.values().cloned().collect(),
288 )
289 })
290 .collect()
291 }
292
293 pub fn asks_as_map(&self) -> IndexMap<Decimal, Vec<OwnBookOrder>> {
295 self.asks()
296 .map(|level| {
297 (
298 level.price.value.as_decimal(),
299 level.orders.values().cloned().collect(),
300 )
301 })
302 .collect()
303 }
304
305 pub fn bid_quantity(&self) -> IndexMap<Decimal, Decimal> {
307 self.bids()
308 .map(|level| {
309 (
310 level.price.value.as_decimal(),
311 level.orders.values().fold(Decimal::ZERO, |total, order| {
312 total + order.size.as_decimal()
313 }),
314 )
315 })
316 .collect()
317 }
318
319 pub fn ask_quantity(&self) -> IndexMap<Decimal, Decimal> {
321 self.asks()
322 .map(|level| {
323 (
324 level.price.value.as_decimal(),
325 level.orders.values().fold(Decimal::ZERO, |total, order| {
326 total + order.size.as_decimal()
327 }),
328 )
329 })
330 .collect()
331 }
332
333 #[must_use]
335 pub fn pprint(&self, num_levels: usize) -> String {
336 pprint_own_book(&self.bids, &self.asks, num_levels)
337 }
338}
339
340pub(crate) struct OwnBookLadder {
342 pub side: OrderSideSpecified,
343 pub levels: BTreeMap<BookPrice, OwnBookLevel>,
344 pub cache: HashMap<ClientOrderId, BookPrice>,
345}
346
347impl OwnBookLadder {
348 #[must_use]
350 pub fn new(side: OrderSideSpecified) -> Self {
351 Self {
352 side,
353 levels: BTreeMap::new(),
354 cache: HashMap::new(),
355 }
356 }
357
358 #[must_use]
360 #[allow(dead_code)] pub fn len(&self) -> usize {
362 self.levels.len()
363 }
364
365 #[must_use]
367 #[allow(dead_code)] pub fn is_empty(&self) -> bool {
369 self.levels.is_empty()
370 }
371
372 pub fn clear(&mut self) {
374 self.levels.clear();
375 self.cache.clear();
376 }
377
378 pub fn add(&mut self, order: OwnBookOrder) {
380 let book_price = order.to_book_price();
381 self.cache.insert(order.client_order_id, book_price);
382
383 match self.levels.get_mut(&book_price) {
384 Some(level) => {
385 level.add(order);
386 }
387 None => {
388 let level = OwnBookLevel::from_order(order);
389 self.levels.insert(book_price, level);
390 }
391 }
392 }
393
394 pub fn update(&mut self, order: OwnBookOrder) -> anyhow::Result<()> {
396 let price = self.cache.get(&order.client_order_id).copied();
397 if let Some(price) = price {
398 if let Some(level) = self.levels.get_mut(&price) {
399 if order.price == level.price.value {
400 level.update(order);
402 return Ok(());
403 }
404
405 self.cache.remove(&order.client_order_id);
407 level.delete(&order.client_order_id)?;
408 if level.is_empty() {
409 self.levels.remove(&price);
410 }
411 }
412 }
413
414 self.add(order);
415 Ok(())
416 }
417
418 pub fn delete(&mut self, order: OwnBookOrder) -> anyhow::Result<()> {
420 self.remove(order.client_order_id)
421 }
422
423 pub fn remove(&mut self, client_order_id: ClientOrderId) -> anyhow::Result<()> {
425 if let Some(price) = self.cache.remove(&client_order_id) {
426 if let Some(level) = self.levels.get_mut(&price) {
427 level.delete(&client_order_id)?;
428 if level.is_empty() {
429 self.levels.remove(&price);
430 }
431 }
432 }
433
434 Ok(())
435 }
436
437 #[must_use]
439 #[allow(dead_code)] pub fn sizes(&self) -> f64 {
441 self.levels.values().map(OwnBookLevel::size).sum()
442 }
443
444 #[must_use]
446 #[allow(dead_code)] pub fn exposures(&self) -> f64 {
448 self.levels.values().map(OwnBookLevel::exposure).sum()
449 }
450
451 #[must_use]
453 #[allow(dead_code)] pub fn top(&self) -> Option<&OwnBookLevel> {
455 match self.levels.iter().next() {
456 Some((_, l)) => Option::Some(l),
457 None => Option::None,
458 }
459 }
460}
461
462impl Debug for OwnBookLadder {
463 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
464 f.debug_struct(stringify!(OwnBookLadder))
465 .field("side", &self.side)
466 .field("levels", &self.levels)
467 .finish()
468 }
469}
470
471impl Display for OwnBookLadder {
472 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
473 writeln!(f, "{}(side={})", stringify!(OwnBookLadder), self.side)?;
474 for (price, level) in &self.levels {
475 writeln!(f, " {} -> {} orders", price, level.len())?;
476 }
477 Ok(())
478 }
479}
480
481#[derive(Clone, Debug)]
482pub struct OwnBookLevel {
483 pub price: BookPrice,
484 pub orders: IndexMap<ClientOrderId, OwnBookOrder>,
485}
486
487impl OwnBookLevel {
488 #[must_use]
490 pub fn new(price: BookPrice) -> Self {
491 Self {
492 price,
493 orders: IndexMap::new(),
494 }
495 }
496
497 #[must_use]
499 pub fn from_order(order: OwnBookOrder) -> Self {
500 let mut level = Self {
501 price: order.to_book_price(),
502 orders: IndexMap::new(),
503 };
504 level.orders.insert(order.client_order_id, order);
505 level
506 }
507
508 #[must_use]
510 pub fn len(&self) -> usize {
511 self.orders.len()
512 }
513
514 #[must_use]
516 pub fn is_empty(&self) -> bool {
517 self.orders.is_empty()
518 }
519
520 #[must_use]
522 pub fn first(&self) -> Option<&OwnBookOrder> {
523 self.orders.get_index(0).map(|(_key, order)| order)
524 }
525
526 pub fn iter(&self) -> impl Iterator<Item = &OwnBookOrder> {
528 self.orders.values()
529 }
530
531 #[must_use]
533 pub fn get_orders(&self) -> Vec<OwnBookOrder> {
534 self.orders.values().copied().collect()
535 }
536
537 #[must_use]
539 pub fn size(&self) -> f64 {
540 self.orders.iter().map(|(_, o)| o.size.as_f64()).sum()
541 }
542
543 #[must_use]
545 pub fn size_decimal(&self) -> Decimal {
546 self.orders.iter().map(|(_, o)| o.size.as_decimal()).sum()
547 }
548
549 #[must_use]
551 pub fn exposure(&self) -> f64 {
552 self.orders
553 .iter()
554 .map(|(_, o)| o.price.as_f64() * o.size.as_f64())
555 .sum()
556 }
557
558 pub fn add_bulk(&mut self, orders: Vec<OwnBookOrder>) {
560 for order in orders {
561 self.add(order);
562 }
563 }
564
565 pub fn add(&mut self, order: OwnBookOrder) {
567 debug_assert_eq!(order.price, self.price.value);
568
569 self.orders.insert(order.client_order_id, order);
570 }
571
572 pub fn update(&mut self, order: OwnBookOrder) {
575 debug_assert_eq!(order.price, self.price.value);
576
577 self.orders[&order.client_order_id] = order;
578 }
579
580 pub fn delete(&mut self, client_order_id: &ClientOrderId) -> anyhow::Result<()> {
582 if self.orders.shift_remove(client_order_id).is_none() {
583 anyhow::bail!("Order {client_order_id} not found for delete");
585 };
586 Ok(())
587 }
588}
589
590impl PartialEq for OwnBookLevel {
591 fn eq(&self, other: &Self) -> bool {
592 self.price == other.price
593 }
594}
595
596impl Eq for OwnBookLevel {}
597
598impl PartialOrd for OwnBookLevel {
599 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
600 Some(self.cmp(other))
601 }
602}
603
604impl Ord for OwnBookLevel {
605 fn cmp(&self, other: &Self) -> Ordering {
606 self.price.cmp(&other.price)
607 }
608}
609
610#[cfg(test)]
615mod tests {
616 use nautilus_core::UnixNanos;
617 use rstest::{fixture, rstest};
618 use rust_decimal_macros::dec;
619
620 use super::*;
621
622 #[fixture]
623 fn own_order() -> OwnBookOrder {
624 let client_order_id = ClientOrderId::from("O-123456789");
625 let side = OrderSideSpecified::Buy;
626 let price = Price::from("100.00");
627 let size = Quantity::from("10");
628 let order_type = OrderType::Limit;
629 let time_in_force = TimeInForce::Gtc;
630 let status = OrderStatus::Submitted;
631 let ts_last = UnixNanos::default();
632 let ts_init = UnixNanos::default();
633
634 OwnBookOrder::new(
635 client_order_id,
636 side,
637 price,
638 size,
639 order_type,
640 time_in_force,
641 status,
642 ts_last,
643 ts_init,
644 )
645 }
646
647 #[rstest]
648 fn test_to_book_price(own_order: OwnBookOrder) {
649 let book_price = own_order.to_book_price();
650 assert_eq!(book_price.value, Price::from("100.00"));
651 assert_eq!(book_price.side, OrderSideSpecified::Buy);
652 }
653
654 #[rstest]
655 fn test_exposure(own_order: OwnBookOrder) {
656 let exposure = own_order.exposure();
657 assert_eq!(exposure, 1000.0);
658 }
659
660 #[rstest]
661 fn test_signed_size(own_order: OwnBookOrder) {
662 let own_order_buy = own_order;
663 let own_order_sell = OwnBookOrder::new(
664 ClientOrderId::from("O-123456789"),
665 OrderSideSpecified::Sell,
666 Price::from("101.0"),
667 Quantity::from("10"),
668 OrderType::Limit,
669 TimeInForce::Gtc,
670 OrderStatus::Accepted,
671 UnixNanos::default(),
672 UnixNanos::default(),
673 );
674
675 assert_eq!(own_order_buy.signed_size(), 10.0);
676 assert_eq!(own_order_sell.signed_size(), -10.0);
677 }
678
679 #[rstest]
680 fn test_debug(own_order: OwnBookOrder) {
681 assert_eq!(
682 format!("{own_order:?}"),
683 "OwnBookOrder(client_order_id=O-123456789, side=BUY, price=100.00, size=10, order_type=LIMIT, time_in_force=GTC, ts_init=0)"
684 );
685 }
686
687 #[rstest]
688 fn test_display(own_order: OwnBookOrder) {
689 assert_eq!(
690 own_order.to_string(),
691 "O-123456789,BUY,100.00,10,LIMIT,GTC,0".to_string()
692 );
693 }
694
695 #[rstest]
696 fn test_own_book_level_size_and_exposure() {
697 let mut level = OwnBookLevel::new(BookPrice::new(
698 Price::from("100.00"),
699 OrderSideSpecified::Buy,
700 ));
701 let order1 = OwnBookOrder::new(
702 ClientOrderId::from("O-1"),
703 OrderSideSpecified::Buy,
704 Price::from("100.00"),
705 Quantity::from("10"),
706 OrderType::Limit,
707 TimeInForce::Gtc,
708 OrderStatus::Accepted,
709 UnixNanos::default(),
710 UnixNanos::default(),
711 );
712 let order2 = OwnBookOrder::new(
713 ClientOrderId::from("O-2"),
714 OrderSideSpecified::Buy,
715 Price::from("100.00"),
716 Quantity::from("20"),
717 OrderType::Limit,
718 TimeInForce::Gtc,
719 OrderStatus::Accepted,
720 UnixNanos::default(),
721 UnixNanos::default(),
722 );
723 level.add(order1);
724 level.add(order2);
725
726 assert_eq!(level.len(), 2);
727 assert_eq!(level.size(), 30.0);
728 assert_eq!(level.exposure(), 3000.0);
729 }
730
731 #[rstest]
732 fn test_own_book_level_add_update_delete() {
733 let mut level = OwnBookLevel::new(BookPrice::new(
734 Price::from("100.00"),
735 OrderSideSpecified::Buy,
736 ));
737 let order = OwnBookOrder::new(
738 ClientOrderId::from("O-1"),
739 OrderSideSpecified::Buy,
740 Price::from("100.00"),
741 Quantity::from("10"),
742 OrderType::Limit,
743 TimeInForce::Gtc,
744 OrderStatus::Accepted,
745 UnixNanos::default(),
746 UnixNanos::default(),
747 );
748 level.add(order);
749 assert_eq!(level.len(), 1);
750
751 let order_updated = OwnBookOrder::new(
753 ClientOrderId::from("O-1"),
754 OrderSideSpecified::Buy,
755 Price::from("100.00"),
756 Quantity::from("15"),
757 OrderType::Limit,
758 TimeInForce::Gtc,
759 OrderStatus::Accepted,
760 UnixNanos::default(),
761 UnixNanos::default(),
762 );
763 level.update(order_updated);
764 let orders = level.get_orders();
765 assert_eq!(orders[0].size, Quantity::from("15"));
766
767 level.delete(&ClientOrderId::from("O-1")).unwrap();
769 assert!(level.is_empty());
770 }
771
772 #[rstest]
773 fn test_own_book_ladder_add_update_delete() {
774 let mut ladder = OwnBookLadder::new(OrderSideSpecified::Buy);
775 let order1 = OwnBookOrder::new(
776 ClientOrderId::from("O-1"),
777 OrderSideSpecified::Buy,
778 Price::from("100.00"),
779 Quantity::from("10"),
780 OrderType::Limit,
781 TimeInForce::Gtc,
782 OrderStatus::Accepted,
783 UnixNanos::default(),
784 UnixNanos::default(),
785 );
786 let order2 = OwnBookOrder::new(
787 ClientOrderId::from("O-2"),
788 OrderSideSpecified::Buy,
789 Price::from("100.00"),
790 Quantity::from("20"),
791 OrderType::Limit,
792 TimeInForce::Gtc,
793 OrderStatus::Accepted,
794 UnixNanos::default(),
795 UnixNanos::default(),
796 );
797 ladder.add(order1);
798 ladder.add(order2);
799 assert_eq!(ladder.len(), 1);
800 assert_eq!(ladder.sizes(), 30.0);
801
802 let order2_updated = OwnBookOrder::new(
804 ClientOrderId::from("O-2"),
805 OrderSideSpecified::Buy,
806 Price::from("100.00"),
807 Quantity::from("25"),
808 OrderType::Limit,
809 TimeInForce::Gtc,
810 OrderStatus::Accepted,
811 UnixNanos::default(),
812 UnixNanos::default(),
813 );
814 ladder.update(order2_updated).unwrap();
815 assert_eq!(ladder.sizes(), 35.0);
816
817 ladder.delete(order1).unwrap();
819 assert_eq!(ladder.sizes(), 25.0);
820 }
821
822 #[rstest]
823 fn test_own_order_book_add_update_delete_clear() {
824 let instrument_id = InstrumentId::from("AAPL.XNAS");
825 let mut book = OwnOrderBook::new(instrument_id);
826 let order_buy = OwnBookOrder::new(
827 ClientOrderId::from("O-1"),
828 OrderSideSpecified::Buy,
829 Price::from("100.00"),
830 Quantity::from("10"),
831 OrderType::Limit,
832 TimeInForce::Gtc,
833 OrderStatus::Accepted,
834 UnixNanos::default(),
835 UnixNanos::default(),
836 );
837 let order_sell = OwnBookOrder::new(
838 ClientOrderId::from("O-2"),
839 OrderSideSpecified::Sell,
840 Price::from("101.00"),
841 Quantity::from("20"),
842 OrderType::Limit,
843 TimeInForce::Gtc,
844 OrderStatus::Accepted,
845 UnixNanos::default(),
846 UnixNanos::default(),
847 );
848
849 book.add(order_buy);
851 book.add(order_sell);
852 assert!(!book.bids.is_empty());
853 assert!(!book.asks.is_empty());
854
855 let order_buy_updated = OwnBookOrder::new(
857 ClientOrderId::from("O-1"),
858 OrderSideSpecified::Buy,
859 Price::from("100.00"),
860 Quantity::from("15"),
861 OrderType::Limit,
862 TimeInForce::Gtc,
863 OrderStatus::Accepted,
864 UnixNanos::default(),
865 UnixNanos::default(),
866 );
867 book.update(order_buy_updated).unwrap();
868 book.delete(order_sell).unwrap();
869
870 assert_eq!(book.bids.sizes(), 15.0);
871 assert!(book.asks.is_empty());
872
873 book.clear();
875 assert!(book.bids.is_empty());
876 assert!(book.asks.is_empty());
877 }
878
879 #[rstest]
880 fn test_own_order_book_bids_and_asks_as_map() {
881 let instrument_id = InstrumentId::from("AAPL.XNAS");
882 let mut book = OwnOrderBook::new(instrument_id);
883 let order1 = OwnBookOrder::new(
884 ClientOrderId::from("O-1"),
885 OrderSideSpecified::Buy,
886 Price::from("100.00"),
887 Quantity::from("10"),
888 OrderType::Limit,
889 TimeInForce::Gtc,
890 OrderStatus::Accepted,
891 UnixNanos::default(),
892 UnixNanos::default(),
893 );
894 let order2 = OwnBookOrder::new(
895 ClientOrderId::from("O-2"),
896 OrderSideSpecified::Sell,
897 Price::from("101.00"),
898 Quantity::from("20"),
899 OrderType::Limit,
900 TimeInForce::Gtc,
901 OrderStatus::Accepted,
902 UnixNanos::default(),
903 UnixNanos::default(),
904 );
905 book.add(order1);
906 book.add(order2);
907 let bids_map = book.bids_as_map();
908 let asks_map = book.asks_as_map();
909
910 assert_eq!(bids_map.len(), 1);
911 let bid_price = Price::from("100.00").as_decimal();
912 let bid_orders = bids_map.get(&bid_price).unwrap();
913 assert_eq!(bid_orders.len(), 1);
914 assert_eq!(bid_orders[0], order1);
915
916 assert_eq!(asks_map.len(), 1);
917 let ask_price = Price::from("101.00").as_decimal();
918 let ask_orders = asks_map.get(&ask_price).unwrap();
919 assert_eq!(ask_orders.len(), 1);
920 assert_eq!(ask_orders[0], order2);
921 }
922
923 #[rstest]
924 fn test_own_order_book_quantity_empty_levels() {
925 let instrument_id = InstrumentId::from("AAPL.XNAS");
926 let book = OwnOrderBook::new(instrument_id);
927
928 let bid_quantities = book.bid_quantity();
929 let ask_quantities = book.ask_quantity();
930
931 assert!(bid_quantities.is_empty());
932 assert!(ask_quantities.is_empty());
933 }
934
935 #[rstest]
936 fn test_own_order_book_bid_ask_quantity() {
937 let instrument_id = InstrumentId::from("AAPL.XNAS");
938 let mut book = OwnOrderBook::new(instrument_id);
939
940 let bid_order1 = OwnBookOrder::new(
942 ClientOrderId::from("O-1"),
943 OrderSideSpecified::Buy,
944 Price::from("100.00"),
945 Quantity::from("10"),
946 OrderType::Limit,
947 TimeInForce::Gtc,
948 OrderStatus::Accepted,
949 UnixNanos::default(),
950 UnixNanos::default(),
951 );
952 let bid_order2 = OwnBookOrder::new(
953 ClientOrderId::from("O-2"),
954 OrderSideSpecified::Buy,
955 Price::from("100.00"),
956 Quantity::from("15"),
957 OrderType::Limit,
958 TimeInForce::Gtc,
959 OrderStatus::Accepted,
960 UnixNanos::default(),
961 UnixNanos::default(),
962 );
963 let bid_order3 = OwnBookOrder::new(
965 ClientOrderId::from("O-3"),
966 OrderSideSpecified::Buy,
967 Price::from("99.50"),
968 Quantity::from("20"),
969 OrderType::Limit,
970 TimeInForce::Gtc,
971 OrderStatus::Accepted,
972 UnixNanos::default(),
973 UnixNanos::default(),
974 );
975
976 let ask_order1 = OwnBookOrder::new(
978 ClientOrderId::from("O-4"),
979 OrderSideSpecified::Sell,
980 Price::from("101.00"),
981 Quantity::from("12"),
982 OrderType::Limit,
983 TimeInForce::Gtc,
984 OrderStatus::Accepted,
985 UnixNanos::default(),
986 UnixNanos::default(),
987 );
988 let ask_order2 = OwnBookOrder::new(
989 ClientOrderId::from("O-5"),
990 OrderSideSpecified::Sell,
991 Price::from("101.00"),
992 Quantity::from("8"),
993 OrderType::Limit,
994 TimeInForce::Gtc,
995 OrderStatus::Accepted,
996 UnixNanos::default(),
997 UnixNanos::default(),
998 );
999
1000 book.add(bid_order1);
1001 book.add(bid_order2);
1002 book.add(bid_order3);
1003 book.add(ask_order1);
1004 book.add(ask_order2);
1005
1006 let bid_quantities = book.bid_quantity();
1007 assert_eq!(bid_quantities.len(), 2);
1008 assert_eq!(bid_quantities.get(&dec!(100.00)), Some(&dec!(25)));
1009 assert_eq!(bid_quantities.get(&dec!(99.50)), Some(&dec!(20)));
1010
1011 let ask_quantities = book.ask_quantity();
1012 assert_eq!(ask_quantities.len(), 1);
1013 assert_eq!(ask_quantities.get(&dec!(101.00)), Some(&dec!(20)));
1014 }
1015}