use std::{cell::RefCell, rc::Rc};
use ahash::AHashSet;
use nautilus_common::messages::execution::TradingCommand;
use nautilus_model::identifiers::ClientOrderId;
#[derive(Debug, Clone, Default)]
pub struct InflightOrders {
orders: Rc<RefCell<AHashSet<ClientOrderId>>>,
}
impl InflightOrders {
pub fn insert(&self, command: &TradingCommand) {
self.orders.borrow_mut().extend(submit_ids(command));
}
pub fn remove(&self, command: &TradingCommand) {
let mut orders = self.orders.borrow_mut();
for id in submit_ids(command) {
orders.remove(id);
}
}
pub fn contains(&self, id: ClientOrderId) -> bool {
self.orders.borrow().contains(&id)
}
pub fn clear(&self) {
self.orders.borrow_mut().clear();
}
}
fn submit_ids(command: &TradingCommand) -> &[ClientOrderId] {
match command {
TradingCommand::SubmitOrder(command) => std::slice::from_ref(&command.client_order_id),
TradingCommand::SubmitOrderList(command) => &command.order_list.client_order_ids,
_ => &[],
}
}
#[cfg(test)]
mod tests {
use nautilus_common::messages::execution::{SubmitOrder, SubmitOrderList};
use nautilus_core::UUID4;
use nautilus_model::{
enums::OrderType,
identifiers::{InstrumentId, OrderListId, StrategyId, TraderId},
orders::{Order, OrderList, OrderTestBuilder},
types::Quantity,
};
use rstest::rstest;
use super::*;
#[rstest]
#[case::single(false)]
#[case::list(true)]
fn test_first_receipt_releases_duplicate_submits(#[case] list: bool) {
let orders: Vec<_> = ["O-1", "O-2"]
.iter()
.map(|id| {
OrderTestBuilder::new(OrderType::Market)
.instrument_id(InstrumentId::from("ETHUSDT.BINANCE"))
.quantity(Quantity::from("1.000"))
.client_order_id(ClientOrderId::from(*id))
.order_list_id(OrderListId::from("OL-1"))
.build()
})
.collect();
let trader_id = TraderId::from("TRADER-001");
let command = if list {
TradingCommand::SubmitOrderList(SubmitOrderList::new(
trader_id,
None,
StrategyId::from("STRATEGY-001"),
OrderList::from_orders(&orders, 0.into()),
orders
.iter()
.map(|order| order.init_event().clone())
.collect(),
None,
None,
None,
UUID4::new(),
0.into(),
None,
))
} else {
TradingCommand::SubmitOrder(SubmitOrder::from_order(
&orders[0],
trader_id,
None,
None,
UUID4::new(),
0.into(),
))
};
let queue = InflightOrders::default();
let engine = queue.clone();
queue.insert(&command);
queue.insert(&command);
assert!(engine.contains(orders[0].client_order_id()));
assert_eq!(engine.contains(orders[1].client_order_id()), list);
queue.remove(&command);
assert!(!engine.contains(orders[0].client_order_id()));
assert!(!engine.contains(orders[1].client_order_id()));
queue.remove(&command);
assert!(!engine.contains(orders[0].client_order_id()));
queue.insert(&command);
queue.clear();
assert!(!engine.contains(orders[0].client_order_id()));
assert!(!engine.contains(orders[1].client_order_id()));
}
}