nautilus_execution/matching_engine/
inflight.rs1use std::{cell::RefCell, rc::Rc};
19
20use ahash::AHashSet;
21use nautilus_common::messages::execution::TradingCommand;
22use nautilus_model::identifiers::ClientOrderId;
23
24#[derive(Debug, Clone, Default)]
26pub struct InflightOrders {
27 orders: Rc<RefCell<AHashSet<ClientOrderId>>>,
28}
29
30impl InflightOrders {
31 pub fn insert(&self, command: &TradingCommand) {
35 self.orders.borrow_mut().extend(submit_ids(command));
36 }
37
38 pub fn remove(&self, command: &TradingCommand) {
43 let mut orders = self.orders.borrow_mut();
44 for id in submit_ids(command) {
45 orders.remove(id);
46 }
47 }
48
49 pub fn contains(&self, id: ClientOrderId) -> bool {
51 self.orders.borrow().contains(&id)
52 }
53
54 pub fn clear(&self) {
56 self.orders.borrow_mut().clear();
57 }
58}
59
60fn submit_ids(command: &TradingCommand) -> &[ClientOrderId] {
61 match command {
62 TradingCommand::SubmitOrder(command) => std::slice::from_ref(&command.client_order_id),
63 TradingCommand::SubmitOrderList(command) => &command.order_list.client_order_ids,
64 _ => &[],
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use nautilus_common::messages::execution::{SubmitOrder, SubmitOrderList};
71 use nautilus_core::UUID4;
72 use nautilus_model::{
73 enums::OrderType,
74 identifiers::{InstrumentId, OrderListId, StrategyId, TraderId},
75 orders::{Order, OrderList, OrderTestBuilder},
76 types::Quantity,
77 };
78 use rstest::rstest;
79
80 use super::*;
81
82 #[rstest]
83 #[case::single(false)]
84 #[case::list(true)]
85 fn test_first_receipt_releases_duplicate_submits(#[case] list: bool) {
86 let orders: Vec<_> = ["O-1", "O-2"]
87 .iter()
88 .map(|id| {
89 OrderTestBuilder::new(OrderType::Market)
90 .instrument_id(InstrumentId::from("ETHUSDT.BINANCE"))
91 .quantity(Quantity::from("1.000"))
92 .client_order_id(ClientOrderId::from(*id))
93 .order_list_id(OrderListId::from("OL-1"))
94 .build()
95 })
96 .collect();
97
98 let trader_id = TraderId::from("TRADER-001");
99
100 let command = if list {
101 TradingCommand::SubmitOrderList(SubmitOrderList::new(
102 trader_id,
103 None,
104 StrategyId::from("STRATEGY-001"),
105 OrderList::from_orders(&orders, 0.into()),
106 orders
107 .iter()
108 .map(|order| order.init_event().clone())
109 .collect(),
110 None,
111 None,
112 None,
113 UUID4::new(),
114 0.into(),
115 None,
116 ))
117 } else {
118 TradingCommand::SubmitOrder(SubmitOrder::from_order(
119 &orders[0],
120 trader_id,
121 None,
122 None,
123 UUID4::new(),
124 0.into(),
125 ))
126 };
127
128 let queue = InflightOrders::default();
129 let engine = queue.clone();
130 queue.insert(&command);
131 queue.insert(&command);
132 assert!(engine.contains(orders[0].client_order_id()));
133 assert_eq!(engine.contains(orders[1].client_order_id()), list);
134
135 queue.remove(&command);
136 assert!(!engine.contains(orders[0].client_order_id()));
137 assert!(!engine.contains(orders[1].client_order_id()));
138 queue.remove(&command);
139 assert!(!engine.contains(orders[0].client_order_id()));
140
141 queue.insert(&command);
142 queue.clear();
143 assert!(!engine.contains(orders[0].client_order_id()));
144 assert!(!engine.contains(orders[1].client_order_id()));
145 }
146}