From fbf68ceb6038915ad3cbe6a16468031080be74ed Mon Sep 17 00:00:00 2001 From: Filip Macek Date: Mon, 12 Aug 2024 08:56:04 +0200 Subject: [PATCH] process_market_order in OrderMatchingEngine --- nautilus_core/backtest/src/matching_engine.rs | 136 +++++++++++++++++- nautilus_core/execution/src/matching_core.rs | 4 + 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/nautilus_core/backtest/src/matching_engine.rs b/nautilus_core/backtest/src/matching_engine.rs index 3cd7dd0b851..fb4ce56d83c 100644 --- a/nautilus_core/backtest/src/matching_engine.rs +++ b/nautilus_core/backtest/src/matching_engine.rs @@ -448,7 +448,21 @@ impl OrderMatchingEngine { } fn process_market_order(&mut self, order: &OrderAny) { - todo!("process_market_order") + // Check if market exists + let order_side = order.order_side(); + let is_ask_initialized = self.core.is_ask_initialized; + let is_bid_initialized = self.core.is_bid_initialized; + if (order.order_side() == OrderSide::Buy && !self.core.is_ask_initialized) + || (order.order_side() == OrderSide::Sell && !self.core.is_bid_initialized) + { + self.generate_order_rejected( + order, + format!("No market for {}", order.instrument_id()).into(), + ); + return; + } + + self.fill_market_order(order); } fn process_limit_order(&mut self, order: &OrderAny) { @@ -538,8 +552,43 @@ impl OrderMatchingEngine { self.target_last = None; } - fn expire_order(&mut self, order: &PassiveOrderAny) { - todo!(); + fn determine_limit_price_and_volume(&self, order: &OrderAny) { + todo!("determine_limit_price_and_volume") + } + + fn determine_market_price_and_volume(&self, order: &OrderAny) { + todo!("determine_market_price_and_volume") + } + + fn fill_market_order(&mut self, order: &OrderAny) { + todo!("fill_market_order") + } + + fn fill_limit_order(&mut self, order: &OrderAny) { + todo!("fill_limit_order") + } + + fn apply_fills( + &mut self, + order: &OrderAny, + fills: Vec<(Price, Quantity)>, + liquidity_side: LiquiditySide, + venue_position_id: Option, + position: Option, + ) { + todo!("apply_fills") + } + + fn fill_order( + &mut self, + order: &OrderAny, + price: Price, + quantity: Quantity, + liquidity_side: LiquiditySide, + venue_position_id: Option, + position: Option, + ) { + todo!("fill_order") } fn update_trailing_stop_market(&mut self, order: &TrailingStopMarketOrder) { @@ -562,6 +611,36 @@ impl OrderMatchingEngine { TradeId::from(trade_id.as_str()) } + // -- EVENT HANDLING ----------------------------------------------------- + + fn accept_order(&mut self, order: &OrderAny) { + todo!("accept_order") + } + + fn expire_order(&mut self, order: &PassiveOrderAny) { + todo!("expire_order") + } + + fn cancel_order(&mut self, order: &OrderAny) { + todo!("cancel_order") + } + + fn update_order(&mut self, order: &OrderAny) { + todo!("update_order") + } + + fn trigger_stop_order(&mut self, order: &OrderAny) { + todo!("trigger_stop_order") + } + + fn update_contingent_order(&mut self, order: &OrderAny) { + todo!("update_contingent_order") + } + + fn cancel_contingent_orders(&mut self, order: &OrderAny) { + todo!("cancel_contingent_orders") + } + // -- EVENT GENERATORS ----------------------------------------------------- fn generate_order_rejected(&self, order: &OrderAny, reason: Ustr) { @@ -1429,4 +1508,55 @@ mod tests { ) ); } + + #[rstest] + fn test_process_market_order_no_market_rejected( + mut msgbus: MessageBus, + order_event_handler: ShareableMessageHandler, + account_id: AccountId, + time: AtomicTime, + instrument_es: InstrumentAny, + ) { + // Register saving message handler to exec engine endpoint + msgbus.register( + msgbus.switchboard.exec_engine_process, + order_event_handler.clone(), + ); + + // Create engine and process order + let mut engine = + get_order_matching_engine(instrument_es.clone(), Rc::new(msgbus), None, None, None); + let market_order_buy = TestOrderStubs::market_order( + instrument_es.id(), + OrderSide::Buy, + Quantity::from("1"), + None, + None, + ); + let market_order_sell = TestOrderStubs::market_order( + instrument_es.id(), + OrderSide::Sell, + Quantity::from("1"), + None, + None, + ); + engine.process_order(&market_order_buy, account_id); + engine.process_order(&market_order_sell, account_id); + + // Get messages and test + let saved_messages = get_order_event_handler_messages(order_event_handler); + assert_eq!(saved_messages.len(), 2); + let first = saved_messages.first().unwrap(); + let second = saved_messages.get(1).unwrap(); + assert_eq!(first.event_type(), OrderEventType::Rejected); + assert_eq!(second.event_type(), OrderEventType::Rejected); + assert_eq!( + first.message().unwrap(), + Ustr::from("No market for ESZ1.GLBX") + ); + assert_eq!( + second.message().unwrap(), + Ustr::from("No market for ESZ1.GLBX") + ); + } } diff --git a/nautilus_core/execution/src/matching_core.rs b/nautilus_core/execution/src/matching_core.rs index b998f0602a1..43018f5251b 100644 --- a/nautilus_core/execution/src/matching_core.rs +++ b/nautilus_core/execution/src/matching_core.rs @@ -42,6 +42,8 @@ pub struct OrderMatchingCore { pub ask: Option, /// The last price for the matching core. pub last: Option, + pub is_bid_initialized: bool, + pub is_ask_initialized: bool, orders_bid: Vec, orders_ask: Vec, trigger_stop_order: Option, @@ -65,6 +67,8 @@ impl OrderMatchingCore { bid: None, ask: None, last: None, + is_bid_initialized: false, + is_ask_initialized: false, orders_bid: Vec::new(), orders_ask: Vec::new(), trigger_stop_order,