1#![allow(dead_code)]
18#![allow(unused_variables)]
19
20use std::{
21 any::Any,
22 cell::RefCell,
23 cmp::min,
24 collections::HashMap,
25 fmt::Debug,
26 ops::{Add, Sub},
27 rc::Rc,
28};
29
30use chrono::TimeDelta;
31use nautilus_common::{
32 cache::Cache,
33 clock::Clock,
34 messages::execution::{BatchCancelOrders, CancelAllOrders, CancelOrder, ModifyOrder},
35 msgbus,
36};
37use nautilus_core::{UUID4, UnixNanos};
38use nautilus_model::{
39 data::{Bar, BarType, OrderBookDelta, OrderBookDeltas, QuoteTick, TradeTick, order::BookOrder},
40 enums::{
41 AccountType, AggregationSource, AggressorSide, BarAggregation, BookType, ContingencyType,
42 LiquiditySide, MarketStatus, MarketStatusAction, OmsType, OrderSide, OrderSideSpecified,
43 OrderStatus, OrderType, PriceType, TimeInForce,
44 },
45 events::{
46 OrderAccepted, OrderCancelRejected, OrderCanceled, OrderEventAny, OrderExpired,
47 OrderFilled, OrderModifyRejected, OrderRejected, OrderTriggered, OrderUpdated,
48 },
49 identifiers::{
50 AccountId, ClientOrderId, InstrumentId, PositionId, StrategyId, TraderId, Venue,
51 VenueOrderId,
52 },
53 instruments::{EXPIRING_INSTRUMENT_TYPES, Instrument, InstrumentAny},
54 orderbook::OrderBook,
55 orders::{Order, OrderAny, PassiveOrderAny, StopOrderAny},
56 position::Position,
57 types::{Currency, Money, Price, Quantity, fixed::FIXED_PRECISION},
58};
59use ustr::Ustr;
60
61use crate::{
62 matching_core::OrderMatchingCore,
63 matching_engine::{config::OrderMatchingEngineConfig, ids_generator::IdsGenerator},
64 models::{
65 fee::{FeeModel, FeeModelAny},
66 fill::FillModel,
67 },
68 trailing::trailing_stop_calculate,
69};
70
71pub struct OrderMatchingEngine {
73 pub venue: Venue,
75 pub instrument: InstrumentAny,
77 pub raw_id: u32,
79 pub book_type: BookType,
81 pub oms_type: OmsType,
83 pub account_type: AccountType,
85 pub market_status: MarketStatus,
87 pub config: OrderMatchingEngineConfig,
89 clock: Rc<RefCell<dyn Clock>>,
90 cache: Rc<RefCell<Cache>>,
91 book: OrderBook,
92 pub core: OrderMatchingCore,
93 fill_model: FillModel,
94 fee_model: FeeModelAny,
95 target_bid: Option<Price>,
96 target_ask: Option<Price>,
97 target_last: Option<Price>,
98 last_bar_bid: Option<Bar>,
99 last_bar_ask: Option<Bar>,
100 execution_bar_types: HashMap<InstrumentId, BarType>,
101 execution_bar_deltas: HashMap<BarType, TimeDelta>,
102 account_ids: HashMap<TraderId, AccountId>,
103 cached_filled_qty: HashMap<ClientOrderId, Quantity>,
104 ids_generator: IdsGenerator,
105}
106
107impl Debug for OrderMatchingEngine {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 f.debug_struct(stringify!(OrderMatchingEngine))
110 .field("venue", &self.venue)
111 .field("instrument", &self.instrument.id())
112 .finish()
113 }
114}
115
116impl OrderMatchingEngine {
117 #[allow(clippy::too_many_arguments)]
119 pub fn new(
120 instrument: InstrumentAny,
121 raw_id: u32,
122 fill_model: FillModel,
123 fee_model: FeeModelAny,
124 book_type: BookType,
125 oms_type: OmsType,
126 account_type: AccountType,
127 clock: Rc<RefCell<dyn Clock>>,
128 cache: Rc<RefCell<Cache>>,
129 config: OrderMatchingEngineConfig,
130 ) -> Self {
131 let book = OrderBook::new(instrument.id(), book_type);
132 let core = OrderMatchingCore::new(
133 instrument.id(),
134 instrument.price_increment(),
135 None, None, None, );
139 let ids_generator = IdsGenerator::new(
140 instrument.id().venue,
141 oms_type,
142 raw_id,
143 config.use_random_ids,
144 config.use_position_ids,
145 cache.clone(),
146 );
147
148 Self {
149 venue: instrument.id().venue,
150 instrument,
151 raw_id,
152 fill_model,
153 fee_model,
154 book_type,
155 oms_type,
156 account_type,
157 clock,
158 cache,
159 book,
160 core,
161 market_status: MarketStatus::Open,
162 config,
163 target_bid: None,
164 target_ask: None,
165 target_last: None,
166 last_bar_bid: None,
167 last_bar_ask: None,
168 execution_bar_types: HashMap::new(),
169 execution_bar_deltas: HashMap::new(),
170 account_ids: HashMap::new(),
171 cached_filled_qty: HashMap::new(),
172 ids_generator,
173 }
174 }
175
176 pub fn reset(&mut self) {
182 self.book.clear(0, UnixNanos::default());
183 self.execution_bar_types.clear();
184 self.execution_bar_deltas.clear();
185 self.account_ids.clear();
186 self.cached_filled_qty.clear();
187 self.core.reset();
188 self.target_bid = None;
189 self.target_ask = None;
190 self.target_last = None;
191 self.ids_generator.reset();
192
193 log::info!("Reset {}", self.instrument.id());
194 }
195
196 pub const fn set_fill_model(&mut self, fill_model: FillModel) {
198 self.fill_model = fill_model;
199 }
200
201 #[must_use]
202 pub fn best_bid_price(&self) -> Option<Price> {
204 self.book.best_bid_price()
205 }
206
207 #[must_use]
208 pub fn best_ask_price(&self) -> Option<Price> {
210 self.book.best_ask_price()
211 }
212
213 #[must_use]
214 pub const fn get_book(&self) -> &OrderBook {
216 &self.book
217 }
218
219 #[must_use]
220 pub const fn get_open_bid_orders(&self) -> &[PassiveOrderAny] {
222 self.core.get_orders_bid()
223 }
224
225 #[must_use]
226 pub const fn get_open_ask_orders(&self) -> &[PassiveOrderAny] {
228 self.core.get_orders_ask()
229 }
230
231 #[must_use]
232 pub fn get_open_orders(&self) -> Vec<PassiveOrderAny> {
234 let mut orders = Vec::new();
236 orders.extend_from_slice(self.core.get_orders_bid());
237 orders.extend_from_slice(self.core.get_orders_ask());
238 orders
239 }
240
241 #[must_use]
242 pub fn order_exists(&self, client_order_id: ClientOrderId) -> bool {
244 self.core.order_exists(client_order_id)
245 }
246
247 pub fn process_order_book_delta(&mut self, delta: &OrderBookDelta) {
251 log::debug!("Processing {delta}");
252
253 if self.book_type == BookType::L2_MBP || self.book_type == BookType::L3_MBO {
254 self.book.apply_delta(delta);
255 }
256
257 self.iterate(delta.ts_init);
258 }
259
260 pub fn process_order_book_deltas(&mut self, deltas: &OrderBookDeltas) {
261 log::debug!("Processing {deltas}");
262
263 if self.book_type == BookType::L2_MBP || self.book_type == BookType::L3_MBO {
264 self.book.apply_deltas(deltas);
265 }
266
267 self.iterate(deltas.ts_init);
268 }
269
270 pub fn process_quote_tick(&mut self, quote: &QuoteTick) {
274 log::debug!("Processing {quote}");
275
276 if self.book_type == BookType::L1_MBP {
277 self.book.update_quote_tick(quote).unwrap();
278 }
279
280 self.iterate(quote.ts_init);
281 }
282
283 pub fn process_bar(&mut self, bar: &Bar) {
287 log::debug!("Processing {bar}");
288
289 if !self.config.bar_execution || self.book_type != BookType::L1_MBP {
291 return;
292 }
293
294 let bar_type = bar.bar_type;
295 if bar_type.aggregation_source() == AggregationSource::Internal {
297 return;
298 }
299
300 if bar_type.spec().aggregation == BarAggregation::Month {
302 return;
303 }
304
305 let execution_bar_type =
306 if let Some(execution_bar_type) = self.execution_bar_types.get(&bar.instrument_id()) {
307 execution_bar_type.to_owned()
308 } else {
309 self.execution_bar_types
310 .insert(bar.instrument_id(), bar_type);
311 self.execution_bar_deltas
312 .insert(bar_type, bar_type.spec().timedelta());
313 bar_type
314 };
315
316 if execution_bar_type != bar_type {
317 let mut bar_type_timedelta = self.execution_bar_deltas.get(&bar_type).copied();
318 if bar_type_timedelta.is_none() {
319 bar_type_timedelta = Some(bar_type.spec().timedelta());
320 self.execution_bar_deltas
321 .insert(bar_type, bar_type_timedelta.unwrap());
322 }
323 if self.execution_bar_deltas.get(&execution_bar_type).unwrap()
324 >= &bar_type_timedelta.unwrap()
325 {
326 self.execution_bar_types
327 .insert(bar_type.instrument_id(), bar_type);
328 } else {
329 return;
330 }
331 }
332
333 match bar_type.spec().price_type {
334 PriceType::Last | PriceType::Mid => self.process_trade_ticks_from_bar(bar),
335 PriceType::Bid => {
336 self.last_bar_bid = Some(bar.to_owned());
337 self.process_quote_ticks_from_bar(bar);
338 }
339 PriceType::Ask => {
340 self.last_bar_ask = Some(bar.to_owned());
341 self.process_quote_ticks_from_bar(bar);
342 }
343 PriceType::Mark => panic!("Not implemented"),
344 }
345 }
346
347 fn process_trade_ticks_from_bar(&mut self, bar: &Bar) {
348 let size = Quantity::new(bar.volume.as_f64() / 4.0, bar.volume.precision);
350 let aggressor_side = if !self.core.is_last_initialized || bar.open > self.core.last.unwrap()
351 {
352 AggressorSide::Buyer
353 } else {
354 AggressorSide::Seller
355 };
356
357 let mut trade_tick = TradeTick::new(
359 bar.instrument_id(),
360 bar.open,
361 size,
362 aggressor_side,
363 self.ids_generator.generate_trade_id(),
364 bar.ts_init,
365 bar.ts_init,
366 );
367
368 if !self.core.is_last_initialized {
371 self.book.update_trade_tick(&trade_tick).unwrap();
372 self.iterate(trade_tick.ts_init);
373 self.core.set_last_raw(trade_tick.price);
374 }
375
376 if self.core.last.is_some_and(|last| bar.high > last) {
379 trade_tick.price = bar.high;
380 trade_tick.aggressor_side = AggressorSide::Buyer;
381 trade_tick.trade_id = self.ids_generator.generate_trade_id();
382
383 self.book.update_trade_tick(&trade_tick).unwrap();
384 self.iterate(trade_tick.ts_init);
385
386 self.core.set_last_raw(trade_tick.price);
387 }
388
389 if self.core.last.is_some_and(|last| bar.low < last) {
393 trade_tick.price = bar.low;
394 trade_tick.aggressor_side = AggressorSide::Seller;
395 trade_tick.trade_id = self.ids_generator.generate_trade_id();
396
397 self.book.update_trade_tick(&trade_tick).unwrap();
398 self.iterate(trade_tick.ts_init);
399
400 self.core.set_last_raw(trade_tick.price);
401 }
402
403 if self.core.last.is_some_and(|last| bar.close != last) {
408 trade_tick.price = bar.close;
409 trade_tick.aggressor_side = if bar.close > self.core.last.unwrap() {
410 AggressorSide::Buyer
411 } else {
412 AggressorSide::Seller
413 };
414 trade_tick.trade_id = self.ids_generator.generate_trade_id();
415
416 self.book.update_trade_tick(&trade_tick).unwrap();
417 self.iterate(trade_tick.ts_init);
418
419 self.core.set_last_raw(trade_tick.price);
420 }
421 }
422
423 fn process_quote_ticks_from_bar(&mut self, bar: &Bar) {
424 if self.last_bar_bid.is_none()
426 || self.last_bar_ask.is_none()
427 || self.last_bar_bid.unwrap().ts_init != self.last_bar_ask.unwrap().ts_init
428 {
429 return;
430 }
431 let bid_bar = self.last_bar_bid.unwrap();
432 let ask_bar = self.last_bar_ask.unwrap();
433 let bid_size = Quantity::new(bid_bar.volume.as_f64() / 4.0, bar.volume.precision);
434 let ask_size = Quantity::new(ask_bar.volume.as_f64() / 4.0, bar.volume.precision);
435
436 let mut quote_tick = QuoteTick::new(
438 self.book.instrument_id,
439 bid_bar.open,
440 ask_bar.open,
441 bid_size,
442 ask_size,
443 bid_bar.ts_init,
444 bid_bar.ts_init,
445 );
446
447 self.book.update_quote_tick("e_tick).unwrap();
449 self.iterate(quote_tick.ts_init);
450
451 quote_tick.bid_price = bid_bar.high;
453 quote_tick.ask_price = ask_bar.high;
454 self.book.update_quote_tick("e_tick).unwrap();
455 self.iterate(quote_tick.ts_init);
456
457 quote_tick.bid_price = bid_bar.low;
459 quote_tick.ask_price = ask_bar.low;
460 self.book.update_quote_tick("e_tick).unwrap();
461 self.iterate(quote_tick.ts_init);
462
463 quote_tick.bid_price = bid_bar.close;
465 quote_tick.ask_price = ask_bar.close;
466 self.book.update_quote_tick("e_tick).unwrap();
467 self.iterate(quote_tick.ts_init);
468
469 self.last_bar_bid = None;
471 self.last_bar_ask = None;
472 }
473
474 pub fn process_trade_tick(&mut self, trade: &TradeTick) {
478 log::debug!("Processing {trade}");
479
480 if self.book_type == BookType::L1_MBP {
481 self.book.update_trade_tick(trade).unwrap();
482 }
483 self.core.set_last_raw(trade.price);
484
485 self.iterate(trade.ts_init);
486 }
487
488 pub fn process_status(&mut self, action: MarketStatusAction) {
489 log::debug!("Processing {action}");
490
491 if self.market_status == MarketStatus::Closed
493 && (action == MarketStatusAction::Trading || action == MarketStatusAction::PreOpen)
494 {
495 self.market_status = MarketStatus::Open;
496 }
497 if self.market_status == MarketStatus::Open && action == MarketStatusAction::Pause {
499 self.market_status = MarketStatus::Paused;
500 }
501 if self.market_status == MarketStatus::Open && action == MarketStatusAction::Suspend {
503 self.market_status = MarketStatus::Suspended;
504 }
505 if self.market_status == MarketStatus::Open
507 && (action == MarketStatusAction::Halt || action == MarketStatusAction::Close)
508 {
509 self.market_status = MarketStatus::Closed;
510 }
511 }
512
513 #[allow(clippy::needless_return)]
519 pub fn process_order(&mut self, order: &mut OrderAny, account_id: AccountId) {
520 {
522 let cache_borrow = self.cache.as_ref().borrow();
523
524 if self.core.order_exists(order.client_order_id()) {
525 self.generate_order_rejected(order, "Order already exists".into());
526 return;
527 }
528
529 self.account_ids.insert(order.trader_id(), account_id);
531
532 if EXPIRING_INSTRUMENT_TYPES.contains(&self.instrument.instrument_class()) {
534 if let Some(activation_ns) = self.instrument.activation_ns()
535 && self.clock.borrow().timestamp_ns() < activation_ns
536 {
537 self.generate_order_rejected(
538 order,
539 format!(
540 "Contract {} is not yet active, activation {}",
541 self.instrument.id(),
542 self.instrument.activation_ns().unwrap()
543 )
544 .into(),
545 );
546 return;
547 }
548 if let Some(expiration_ns) = self.instrument.expiration_ns()
549 && self.clock.borrow().timestamp_ns() >= expiration_ns
550 {
551 self.generate_order_rejected(
552 order,
553 format!(
554 "Contract {} has expired, expiration {}",
555 self.instrument.id(),
556 self.instrument.expiration_ns().unwrap()
557 )
558 .into(),
559 );
560 return;
561 }
562 }
563
564 if self.config.support_contingent_orders {
566 if let Some(parent_order_id) = order.parent_order_id() {
567 let parent_order = cache_borrow.order(&parent_order_id);
568 if parent_order.is_none()
569 || parent_order.unwrap().contingency_type().unwrap() != ContingencyType::Oto
570 {
571 panic!("OTO parent not found");
572 }
573 if let Some(parent_order) = parent_order {
574 let parent_order_status = parent_order.status();
575 let order_is_open = order.is_open();
576 if parent_order.status() == OrderStatus::Rejected && order.is_open() {
577 self.generate_order_rejected(
578 order,
579 format!("Rejected OTO order from {parent_order_id}").into(),
580 );
581 return;
582 } else if parent_order.status() == OrderStatus::Accepted
583 && parent_order.status() == OrderStatus::Triggered
584 {
585 log::info!(
586 "Pending OTO order {} triggers from {parent_order_id}",
587 order.client_order_id(),
588 );
589 return;
590 }
591 }
592 }
593
594 if let Some(linked_order_ids) = order.linked_order_ids() {
595 for client_order_id in linked_order_ids {
596 match cache_borrow.order(client_order_id) {
597 Some(contingent_order)
598 if (order.contingency_type().unwrap() == ContingencyType::Oco
599 || order.contingency_type().unwrap()
600 == ContingencyType::Ouo)
601 && !order.is_closed()
602 && contingent_order.is_closed() =>
603 {
604 self.generate_order_rejected(
605 order,
606 format!("Contingent order {client_order_id} already closed")
607 .into(),
608 );
609 return;
610 }
611 None => panic!("Cannot find contingent order for {client_order_id}"),
612 _ => {}
613 }
614 }
615 }
616 }
617
618 if order.quantity().precision != self.instrument.size_precision() {
620 self.generate_order_rejected(
621 order,
622 format!(
623 "Invalid order quantity precision for order {}, was {} when {} size precision is {}",
624 order.client_order_id(),
625 order.quantity().precision,
626 self.instrument.id(),
627 self.instrument.size_precision()
628 )
629 .into(),
630 );
631 return;
632 }
633
634 if let Some(price) = order.price()
636 && price.precision != self.instrument.price_precision()
637 {
638 self.generate_order_rejected(
639 order,
640 format!(
641 "Invalid order price precision for order {}, was {} when {} price precision is {}",
642 order.client_order_id(),
643 price.precision,
644 self.instrument.id(),
645 self.instrument.price_precision()
646 )
647 .into(),
648 );
649 return;
650 }
651
652 if let Some(trigger_price) = order.trigger_price()
654 && trigger_price.precision != self.instrument.price_precision()
655 {
656 self.generate_order_rejected(
657 order,
658 format!(
659 "Invalid order trigger price precision for order {}, was {} when {} price precision is {}",
660 order.client_order_id(),
661 trigger_price.precision,
662 self.instrument.id(),
663 self.instrument.price_precision()
664 )
665 .into(),
666 );
667 return;
668 }
669
670 let position: Option<&Position> = cache_borrow
672 .position_for_order(&order.client_order_id())
673 .or_else(|| {
674 if self.oms_type == OmsType::Netting {
675 let position_id = PositionId::new(
676 format!("{}-{}", order.instrument_id(), order.strategy_id()).as_str(),
677 );
678 cache_borrow.position(&position_id)
679 } else {
680 None
681 }
682 });
683
684 if order.order_side() == OrderSide::Sell
686 && self.account_type != AccountType::Margin
687 && matches!(self.instrument, InstrumentAny::Equity(_))
688 && (position.is_none()
689 || !order.would_reduce_only(position.unwrap().side, position.unwrap().quantity))
690 {
691 let position_string = position.map_or("None".to_string(), |pos| pos.id.to_string());
692 self.generate_order_rejected(
693 order,
694 format!(
695 "Short selling not permitted on a CASH account with position {position_string} and order {order}",
696 )
697 .into(),
698 );
699 return;
700 }
701
702 if self.config.use_reduce_only
704 && order.is_reduce_only()
705 && !order.is_closed()
706 && position.is_none_or(|pos| {
707 pos.is_closed()
708 || (order.is_buy() && pos.is_long())
709 || (order.is_sell() && pos.is_short())
710 })
711 {
712 self.generate_order_rejected(
713 order,
714 format!(
715 "Reduce-only order {} ({}-{}) would have increased position",
716 order.client_order_id(),
717 order.order_type().to_string().to_uppercase(),
718 order.order_side().to_string().to_uppercase()
719 )
720 .into(),
721 );
722 return;
723 }
724 }
725
726 match order.order_type() {
727 OrderType::Market => self.process_market_order(order),
728 OrderType::Limit => self.process_limit_order(order),
729 OrderType::MarketToLimit => self.process_market_to_limit_order(order),
730 OrderType::StopMarket => self.process_stop_market_order(order),
731 OrderType::StopLimit => self.process_stop_limit_order(order),
732 OrderType::MarketIfTouched => self.process_market_if_touched_order(order),
733 OrderType::LimitIfTouched => self.process_limit_if_touched_order(order),
734 OrderType::TrailingStopMarket => self.process_trailing_stop_order(order),
735 OrderType::TrailingStopLimit => self.process_trailing_stop_order(order),
736 }
737 }
738
739 pub fn process_modify(&mut self, command: &ModifyOrder, account_id: AccountId) {
740 if let Some(order) = self.core.get_order(command.client_order_id) {
741 self.update_order(
742 &mut order.to_any(),
743 command.quantity,
744 command.price,
745 command.trigger_price,
746 None,
747 );
748 } else {
749 self.generate_order_modify_rejected(
750 command.trader_id,
751 command.strategy_id,
752 command.instrument_id,
753 command.client_order_id,
754 Ustr::from(format!("Order {} not found", command.client_order_id).as_str()),
755 Some(command.venue_order_id),
756 Some(account_id),
757 );
758 }
759 }
760
761 pub fn process_cancel(&mut self, command: &CancelOrder, account_id: AccountId) {
762 match self.core.get_order(command.client_order_id) {
763 Some(passive_order) => {
764 if passive_order.is_inflight() || passive_order.is_open() {
765 self.cancel_order(&OrderAny::from(passive_order.to_owned()), None);
766 }
767 }
768 None => self.generate_order_cancel_rejected(
769 command.trader_id,
770 command.strategy_id,
771 account_id,
772 command.instrument_id,
773 command.client_order_id,
774 command.venue_order_id,
775 Ustr::from(format!("Order {} not found", command.client_order_id).as_str()),
776 ),
777 }
778 }
779
780 pub fn process_cancel_all(&mut self, command: &CancelAllOrders, account_id: AccountId) {
781 let instrument_id = command.instrument_id;
782 let open_orders = self
783 .cache
784 .borrow()
785 .orders_open(None, Some(&instrument_id), None, None)
786 .into_iter()
787 .cloned()
788 .collect::<Vec<OrderAny>>();
789 for order in open_orders {
790 if command.order_side != OrderSide::NoOrderSide
791 && command.order_side != order.order_side()
792 {
793 continue;
794 }
795 if order.is_inflight() || order.is_open() {
796 self.cancel_order(&order, None);
797 }
798 }
799 }
800
801 pub fn process_batch_cancel(&mut self, command: &BatchCancelOrders, account_id: AccountId) {
802 for order in &command.cancels {
803 self.process_cancel(order, account_id);
804 }
805 }
806
807 fn process_market_order(&mut self, order: &mut OrderAny) {
808 if order.time_in_force() == TimeInForce::AtTheOpen
809 || order.time_in_force() == TimeInForce::AtTheClose
810 {
811 log::error!(
812 "Market auction for the time in force {} is currently not supported",
813 order.time_in_force()
814 );
815 return;
816 }
817
818 let order_side = order.order_side();
820 let is_ask_initialized = self.core.is_ask_initialized;
821 let is_bid_initialized = self.core.is_bid_initialized;
822 if (order.order_side() == OrderSide::Buy && !self.core.is_ask_initialized)
823 || (order.order_side() == OrderSide::Sell && !self.core.is_bid_initialized)
824 {
825 self.generate_order_rejected(
826 order,
827 format!("No market for {}", order.instrument_id()).into(),
828 );
829 return;
830 }
831
832 self.fill_market_order(order);
833 }
834
835 fn process_limit_order(&mut self, order: &mut OrderAny) {
836 let limit_px = order.price().expect("Limit order must have a price");
837 if order.is_post_only()
838 && self
839 .core
840 .is_limit_matched(order.order_side_specified(), limit_px)
841 {
842 self.generate_order_rejected(
843 order,
844 format!(
845 "POST_ONLY {} {} order limit px of {} would have been a TAKER: bid={}, ask={}",
846 order.order_type(),
847 order.order_side(),
848 order.price().unwrap(),
849 self.core
850 .bid
851 .map_or_else(|| "None".to_string(), |p| p.to_string()),
852 self.core
853 .ask
854 .map_or_else(|| "None".to_string(), |p| p.to_string())
855 )
856 .into(),
857 );
858 return;
859 }
860
861 self.accept_order(order);
863
864 if self
866 .core
867 .is_limit_matched(order.order_side_specified(), limit_px)
868 {
869 if order.liquidity_side().is_some()
871 && order.liquidity_side().unwrap() == LiquiditySide::NoLiquiditySide
872 {
873 order.set_liquidity_side(LiquiditySide::Taker);
874 }
875 self.fill_limit_order(order);
876 } else if matches!(order.time_in_force(), TimeInForce::Fok | TimeInForce::Ioc) {
877 self.cancel_order(order, None);
878 }
879 }
880
881 fn process_market_to_limit_order(&mut self, order: &mut OrderAny) {
882 if (order.order_side() == OrderSide::Buy && !self.core.is_ask_initialized)
884 || (order.order_side() == OrderSide::Sell && !self.core.is_bid_initialized)
885 {
886 self.generate_order_rejected(
887 order,
888 format!("No market for {}", order.instrument_id()).into(),
889 );
890 return;
891 }
892
893 self.fill_market_order(order);
895
896 if order.is_open() {
897 self.accept_order(order);
898 }
899 }
900
901 fn process_stop_market_order(&mut self, order: &mut OrderAny) {
902 let stop_px = order
903 .trigger_price()
904 .expect("Stop order must have a trigger price");
905 if self
906 .core
907 .is_stop_matched(order.order_side_specified(), stop_px)
908 {
909 if self.config.reject_stop_orders {
910 self.generate_order_rejected(
911 order,
912 format!(
913 "{} {} order stop px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
914 order.order_type(),
915 order.order_side(),
916 order.trigger_price().unwrap(),
917 self.core
918 .bid
919 .map_or_else(|| "None".to_string(), |p| p.to_string()),
920 self.core
921 .ask
922 .map_or_else(|| "None".to_string(), |p| p.to_string())
923 ).into(),
924 );
925 return;
926 }
927 self.fill_market_order(order);
928 return;
929 }
930
931 self.accept_order(order);
933 }
934
935 fn process_stop_limit_order(&mut self, order: &mut OrderAny) {
936 let stop_px = order
937 .trigger_price()
938 .expect("Stop order must have a trigger price");
939 if self
940 .core
941 .is_stop_matched(order.order_side_specified(), stop_px)
942 {
943 if self.config.reject_stop_orders {
944 self.generate_order_rejected(
945 order,
946 format!(
947 "{} {} order stop px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
948 order.order_type(),
949 order.order_side(),
950 order.trigger_price().unwrap(),
951 self.core
952 .bid
953 .map_or_else(|| "None".to_string(), |p| p.to_string()),
954 self.core
955 .ask
956 .map_or_else(|| "None".to_string(), |p| p.to_string())
957 ).into(),
958 );
959 return;
960 }
961
962 self.accept_order(order);
963 self.generate_order_triggered(order);
964
965 let limit_px = order.price().expect("Stop limit order must have a price");
967 if self
968 .core
969 .is_limit_matched(order.order_side_specified(), limit_px)
970 {
971 order.set_liquidity_side(LiquiditySide::Taker);
972 self.fill_limit_order(order);
973 }
974 }
975
976 self.accept_order(order);
978 }
979
980 fn process_market_if_touched_order(&mut self, order: &mut OrderAny) {
981 if self
982 .core
983 .is_touch_triggered(order.order_side_specified(), order.trigger_price().unwrap())
984 {
985 if self.config.reject_stop_orders {
986 self.generate_order_rejected(
987 order,
988 format!(
989 "{} {} order trigger px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
990 order.order_type(),
991 order.order_side(),
992 order.trigger_price().unwrap(),
993 self.core
994 .bid
995 .map_or_else(|| "None".to_string(), |p| p.to_string()),
996 self.core
997 .ask
998 .map_or_else(|| "None".to_string(), |p| p.to_string())
999 ).into(),
1000 );
1001 return;
1002 }
1003 self.fill_market_order(order);
1004 return;
1005 }
1006
1007 self.accept_order(order);
1009 }
1010
1011 fn process_limit_if_touched_order(&mut self, order: &mut OrderAny) {
1012 if self
1013 .core
1014 .is_touch_triggered(order.order_side_specified(), order.trigger_price().unwrap())
1015 {
1016 if self.config.reject_stop_orders {
1017 self.generate_order_rejected(
1018 order,
1019 format!(
1020 "{} {} order trigger px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
1021 order.order_type(),
1022 order.order_side(),
1023 order.trigger_price().unwrap(),
1024 self.core
1025 .bid
1026 .map_or_else(|| "None".to_string(), |p| p.to_string()),
1027 self.core
1028 .ask
1029 .map_or_else(|| "None".to_string(), |p| p.to_string())
1030 ).into(),
1031 );
1032 return;
1033 }
1034 self.accept_order(order);
1035 self.generate_order_triggered(order);
1036
1037 if self
1039 .core
1040 .is_limit_matched(order.order_side_specified(), order.price().unwrap())
1041 {
1042 order.set_liquidity_side(LiquiditySide::Taker);
1043 self.fill_limit_order(order);
1044 }
1045 return;
1046 }
1047
1048 self.accept_order(order);
1050 }
1051
1052 fn process_trailing_stop_order(&mut self, order: &mut OrderAny) {
1053 if let Some(trigger_price) = order.trigger_price()
1054 && self
1055 .core
1056 .is_stop_matched(order.order_side_specified(), trigger_price)
1057 {
1058 self.generate_order_rejected(
1059 order,
1060 format!(
1061 "{} {} order trigger px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
1062 order.order_type(),
1063 order.order_side(),
1064 trigger_price,
1065 self.core
1066 .bid
1067 .map_or_else(|| "None".to_string(), |p| p.to_string()),
1068 self.core
1069 .ask
1070 .map_or_else(|| "None".to_string(), |p| p.to_string())
1071 ).into(),
1072 );
1073 return;
1074 }
1075
1076 self.accept_order(order);
1078 }
1079
1080 pub fn iterate(&mut self, timestamp_ns: UnixNanos) {
1089 if self.book.has_bid() {
1093 self.core.set_bid_raw(self.book.best_bid_price().unwrap());
1094 }
1095 if self.book.has_ask() {
1096 self.core.set_ask_raw(self.book.best_ask_price().unwrap());
1097 }
1098 self.core.iterate();
1099
1100 self.core.bid = self.book.best_bid_price();
1101 self.core.ask = self.book.best_ask_price();
1102
1103 let orders_bid = self.core.get_orders_bid().to_vec();
1104 let orders_ask = self.core.get_orders_ask().to_vec();
1105
1106 self.iterate_orders(timestamp_ns, &orders_bid);
1107 self.iterate_orders(timestamp_ns, &orders_ask);
1108 }
1109
1110 fn maybe_activate_trailing_stop(
1111 &mut self,
1112 order: &mut OrderAny,
1113 bid: Option<Price>,
1114 ask: Option<Price>,
1115 ) -> bool {
1116 match order {
1117 OrderAny::TrailingStopMarket(inner) => {
1118 if inner.is_activated {
1119 return true;
1120 }
1121
1122 if inner.activation_price.is_none() {
1123 let px = match inner.order_side() {
1124 OrderSide::Buy => ask,
1125 OrderSide::Sell => bid,
1126 _ => None,
1127 };
1128 if let Some(p) = px {
1129 inner.activation_price = Some(p);
1130 inner.set_activated();
1131 if let Err(e) = self.cache.borrow_mut().update_order(order) {
1132 log::error!("Failed to update order: {e}");
1133 }
1134 return true;
1135 }
1136 return false;
1137 }
1138
1139 let activation_price = inner.activation_price.unwrap();
1140 let hit = match inner.order_side() {
1141 OrderSide::Buy => ask.is_some_and(|a| a <= activation_price),
1142 OrderSide::Sell => bid.is_some_and(|b| b >= activation_price),
1143 _ => false,
1144 };
1145 if hit {
1146 inner.set_activated();
1147 if let Err(e) = self.cache.borrow_mut().update_order(order) {
1148 log::error!("Failed to update order: {e}");
1149 }
1150 }
1151 hit
1152 }
1153 OrderAny::TrailingStopLimit(inner) => {
1154 if inner.is_activated {
1155 return true;
1156 }
1157
1158 if inner.activation_price.is_none() {
1159 let px = match inner.order_side() {
1160 OrderSide::Buy => ask,
1161 OrderSide::Sell => bid,
1162 _ => None,
1163 };
1164 if let Some(p) = px {
1165 inner.activation_price = Some(p);
1166 inner.set_activated();
1167 if let Err(e) = self.cache.borrow_mut().update_order(order) {
1168 log::error!("Failed to update order: {e}");
1169 }
1170 return true;
1171 }
1172 return false;
1173 }
1174
1175 let activation_price = inner.activation_price.unwrap();
1176 let hit = match inner.order_side() {
1177 OrderSide::Buy => ask.is_some_and(|a| a <= activation_price),
1178 OrderSide::Sell => bid.is_some_and(|b| b >= activation_price),
1179 _ => false,
1180 };
1181 if hit {
1182 inner.set_activated();
1183 if let Err(e) = self.cache.borrow_mut().update_order(order) {
1184 log::error!("Failed to update order: {e}");
1185 }
1186 }
1187 hit
1188 }
1189 _ => true,
1190 }
1191 }
1192
1193 fn iterate_orders(&mut self, timestamp_ns: UnixNanos, orders: &[PassiveOrderAny]) {
1194 for order in orders {
1195 if order.is_closed() {
1196 continue;
1197 }
1198
1199 if self.config.support_gtd_orders
1200 && order
1201 .expire_time()
1202 .is_some_and(|expire_timestamp_ns| timestamp_ns >= expire_timestamp_ns)
1203 {
1204 let _ = self.core.delete_order(order);
1205 self.cached_filled_qty.remove(&order.client_order_id());
1206 self.expire_order(order);
1207 continue;
1208 }
1209
1210 if matches!(
1211 order,
1212 PassiveOrderAny::Stop(
1213 StopOrderAny::TrailingStopMarket(_) | StopOrderAny::TrailingStopLimit(_)
1214 )
1215 ) {
1216 let mut any = OrderAny::from(order.clone());
1217
1218 if !self.maybe_activate_trailing_stop(&mut any, self.core.bid, self.core.ask) {
1219 continue;
1220 }
1221
1222 self.update_trailing_stop_order(&mut any);
1223 }
1224
1225 if let Some(target_bid) = self.target_bid {
1227 self.core.bid = Some(target_bid);
1228 self.target_bid = None;
1229 }
1230 if let Some(target_bid) = self.target_bid.take() {
1231 self.core.bid = Some(target_bid);
1232 self.target_bid = None;
1233 }
1234 if let Some(target_ask) = self.target_ask.take() {
1235 self.core.ask = Some(target_ask);
1236 self.target_ask = None;
1237 }
1238 if let Some(target_last) = self.target_last.take() {
1239 self.core.last = Some(target_last);
1240 self.target_last = None;
1241 }
1242 }
1243
1244 self.target_bid = None;
1246 self.target_ask = None;
1247 self.target_last = None;
1248 }
1249
1250 fn determine_limit_price_and_volume(&mut self, order: &OrderAny) -> Vec<(Price, Quantity)> {
1251 match order.price() {
1252 Some(order_price) => {
1253 let book_order =
1255 BookOrder::new(order.order_side(), order_price, order.quantity(), 1);
1256
1257 let mut fills = self.book.simulate_fills(&book_order);
1258
1259 if fills.is_empty() {
1261 return fills;
1262 }
1263
1264 if let Some(triggered_price) = order.trigger_price() {
1266 if order
1268 .liquidity_side()
1269 .is_some_and(|liquidity_side| liquidity_side == LiquiditySide::Taker)
1270 {
1271 if order.order_side() == OrderSide::Sell && order_price > triggered_price {
1272 let first_fill = fills.first().unwrap();
1274 let triggered_qty = first_fill.1;
1275 fills[0] = (triggered_price, triggered_qty);
1276 self.target_bid = self.core.bid;
1277 self.target_ask = self.core.ask;
1278 self.target_last = self.core.last;
1279 self.core.set_ask_raw(order_price);
1280 self.core.set_last_raw(order_price);
1281 } else if order.order_side() == OrderSide::Buy
1282 && order_price < triggered_price
1283 {
1284 let first_fill = fills.first().unwrap();
1286 let triggered_qty = first_fill.1;
1287 fills[0] = (triggered_price, triggered_qty);
1288 self.target_bid = self.core.bid;
1289 self.target_ask = self.core.ask;
1290 self.target_last = self.core.last;
1291 self.core.set_bid_raw(order_price);
1292 self.core.set_last_raw(order_price);
1293 }
1294 }
1295 }
1296
1297 if order
1299 .liquidity_side()
1300 .is_some_and(|liquidity_side| liquidity_side == LiquiditySide::Maker)
1301 {
1302 match order.order_side().as_specified() {
1303 OrderSideSpecified::Buy => {
1304 let target_price = if order
1305 .trigger_price()
1306 .is_some_and(|trigger_price| order_price > trigger_price)
1307 {
1308 order.trigger_price().unwrap()
1309 } else {
1310 order_price
1311 };
1312 for fill in &fills {
1313 let last_px = fill.0;
1314 if last_px < order_price {
1315 self.target_bid = self.core.bid;
1317 self.target_ask = self.core.ask;
1318 self.target_last = self.core.last;
1319 self.core.set_ask_raw(target_price);
1320 self.core.set_last_raw(target_price);
1321 }
1322 }
1323 }
1324 OrderSideSpecified::Sell => {
1325 let target_price = if order
1326 .trigger_price()
1327 .is_some_and(|trigger_price| order_price < trigger_price)
1328 {
1329 order.trigger_price().unwrap()
1330 } else {
1331 order_price
1332 };
1333 for fill in &fills {
1334 let last_px = fill.0;
1335 if last_px > order_price {
1336 self.target_bid = self.core.bid;
1338 self.target_ask = self.core.ask;
1339 self.target_last = self.core.last;
1340 self.core.set_bid_raw(target_price);
1341 self.core.set_last_raw(target_price);
1342 }
1343 }
1344 }
1345 }
1346 }
1347
1348 fills
1349 }
1350 None => panic!("Limit order must have a price"),
1351 }
1352 }
1353
1354 fn determine_market_price_and_volume(&self, order: &OrderAny) -> Vec<(Price, Quantity)> {
1355 let price = match order.order_side().as_specified() {
1357 OrderSideSpecified::Buy => Price::max(FIXED_PRECISION),
1358 OrderSideSpecified::Sell => Price::min(FIXED_PRECISION),
1359 };
1360
1361 let book_order = BookOrder::new(order.order_side(), price, order.quantity(), 0);
1363 self.book.simulate_fills(&book_order)
1364 }
1365
1366 pub fn fill_market_order(&mut self, order: &mut OrderAny) {
1367 if let Some(filled_qty) = self.cached_filled_qty.get(&order.client_order_id())
1368 && filled_qty >= &order.quantity()
1369 {
1370 log::info!(
1371 "Ignoring fill as already filled pending application of events: {:?}, {:?}, {:?}, {:?}",
1372 filled_qty,
1373 order.quantity(),
1374 order.filled_qty(),
1375 order.quantity()
1376 );
1377 return;
1378 }
1379
1380 let venue_position_id = self.ids_generator.get_position_id(order, Some(true));
1381 let position: Option<Position> = if let Some(venue_position_id) = venue_position_id {
1382 let cache = self.cache.as_ref().borrow();
1383 cache.position(&venue_position_id).cloned()
1384 } else {
1385 None
1386 };
1387
1388 if self.config.use_reduce_only && order.is_reduce_only() && position.is_none() {
1389 log::warn!(
1390 "Canceling REDUCE_ONLY {} as would increase position",
1391 order.order_type()
1392 );
1393 self.cancel_order(order, None);
1394 return;
1395 }
1396 order.set_liquidity_side(LiquiditySide::Taker);
1398 let fills = self.determine_market_price_and_volume(order);
1399 self.apply_fills(order, fills, LiquiditySide::Taker, None, position);
1400 }
1401
1402 pub fn fill_limit_order(&mut self, order: &mut OrderAny) {
1406 match order.price() {
1407 Some(order_price) => {
1408 let cached_filled_qty = self.cached_filled_qty.get(&order.client_order_id());
1409 if cached_filled_qty.is_some() && *cached_filled_qty.unwrap() >= order.quantity() {
1410 log::debug!(
1411 "Ignoring fill as already filled pending pending application of events: {}, {}, {}, {}",
1412 cached_filled_qty.unwrap(),
1413 order.quantity(),
1414 order.filled_qty(),
1415 order.leaves_qty(),
1416 );
1417 return;
1418 }
1419
1420 if order
1421 .liquidity_side()
1422 .is_some_and(|liquidity_side| liquidity_side == LiquiditySide::Maker)
1423 {
1424 if order.order_side() == OrderSide::Buy
1425 && self.core.bid.is_some_and(|bid| bid == order_price)
1426 && !self.fill_model.is_limit_filled()
1427 {
1428 return;
1430 }
1431 if order.order_side() == OrderSide::Sell
1432 && self.core.ask.is_some_and(|ask| ask == order_price)
1433 && !self.fill_model.is_limit_filled()
1434 {
1435 return;
1437 }
1438 }
1439
1440 let venue_position_id = self.ids_generator.get_position_id(order, None);
1441 let position = if let Some(venue_position_id) = venue_position_id {
1442 let cache = self.cache.as_ref().borrow();
1443 cache.position(&venue_position_id).cloned()
1444 } else {
1445 None
1446 };
1447
1448 if self.config.use_reduce_only && order.is_reduce_only() && position.is_none() {
1449 log::warn!(
1450 "Canceling REDUCE_ONLY {} as would increase position",
1451 order.order_type()
1452 );
1453 self.cancel_order(order, None);
1454 return;
1455 }
1456
1457 let fills = self.determine_limit_price_and_volume(order);
1458
1459 self.apply_fills(
1460 order,
1461 fills,
1462 order.liquidity_side().unwrap(),
1463 venue_position_id,
1464 position,
1465 );
1466 }
1467 None => panic!("Limit order must have a price"),
1468 }
1469 }
1470
1471 fn apply_fills(
1472 &mut self,
1473 order: &mut OrderAny,
1474 fills: Vec<(Price, Quantity)>,
1475 liquidity_side: LiquiditySide,
1476 venue_position_id: Option<PositionId>,
1477 position: Option<Position>,
1478 ) {
1479 if order.time_in_force() == TimeInForce::Fok {
1480 let mut total_size = Quantity::zero(order.quantity().precision);
1481 for (fill_px, fill_qty) in &fills {
1482 total_size = total_size.add(*fill_qty);
1483 }
1484
1485 if order.leaves_qty() > total_size {
1486 self.cancel_order(order, None);
1487 return;
1488 }
1489 }
1490
1491 if fills.is_empty() {
1492 if order.status() == OrderStatus::Submitted {
1493 self.generate_order_rejected(
1494 order,
1495 format!("No market for {}", order.instrument_id()).into(),
1496 );
1497 } else {
1498 log::error!(
1499 "Cannot fill order: no fills from book when fills were expected (check size in data)"
1500 );
1501 return;
1502 }
1503 }
1504
1505 if self.oms_type == OmsType::Netting {
1506 let venue_position_id: Option<PositionId> = None;
1507 }
1508
1509 let mut initial_market_to_limit_fill = false;
1510 for &(mut fill_px, ref fill_qty) in &fills {
1511 assert!(
1513 (fill_px.precision == self.instrument.price_precision()),
1514 "Invalid price precision for fill price {} when instrument price precision is {}.\
1515 Check that the data price precision matches the {} instrument",
1516 fill_px.precision,
1517 self.instrument.price_precision(),
1518 self.instrument.id()
1519 );
1520
1521 assert!(
1523 (fill_qty.precision == self.instrument.size_precision()),
1524 "Invalid quantity precision for fill quantity {} when instrument size precision is {}.\
1525 Check that the data quantity precision matches the {} instrument",
1526 fill_qty.precision,
1527 self.instrument.size_precision(),
1528 self.instrument.id()
1529 );
1530
1531 if order.filled_qty() == Quantity::zero(order.filled_qty().precision)
1532 && order.order_type() == OrderType::MarketToLimit
1533 {
1534 self.generate_order_updated(order, order.quantity(), Some(fill_px), None);
1535 initial_market_to_limit_fill = true;
1536 }
1537
1538 if self.book_type == BookType::L1_MBP && self.fill_model.is_slipped() {
1539 fill_px = match order.order_side().as_specified() {
1540 OrderSideSpecified::Buy => fill_px.add(self.instrument.price_increment()),
1541 OrderSideSpecified::Sell => fill_px.sub(self.instrument.price_increment()),
1542 }
1543 }
1544
1545 if self.config.use_reduce_only
1547 && order.is_reduce_only()
1548 && let Some(position) = &position
1549 && *fill_qty > position.quantity
1550 {
1551 if position.quantity == Quantity::zero(position.quantity.precision) {
1552 return;
1554 }
1555
1556 let adjusted_fill_qty =
1558 Quantity::from_raw(position.quantity.raw, fill_qty.precision);
1559
1560 self.generate_order_updated(order, adjusted_fill_qty, None, None);
1561 }
1562
1563 if fill_qty.is_zero() {
1564 if fills.len() == 1 && order.status() == OrderStatus::Submitted {
1565 self.generate_order_rejected(
1566 order,
1567 format!("No market for {}", order.instrument_id()).into(),
1568 );
1569 }
1570 return;
1571 }
1572
1573 self.fill_order(
1574 order,
1575 fill_px,
1576 *fill_qty,
1577 liquidity_side,
1578 venue_position_id,
1579 position.clone(),
1580 );
1581
1582 if order.order_type() == OrderType::MarketToLimit && initial_market_to_limit_fill {
1583 return;
1585 }
1586 }
1587
1588 if order.time_in_force() == TimeInForce::Ioc && order.is_open() {
1589 self.cancel_order(order, None);
1591 return;
1592 }
1593
1594 if order.is_open()
1595 && self.book_type == BookType::L1_MBP
1596 && matches!(
1597 order.order_type(),
1598 OrderType::Market
1599 | OrderType::MarketIfTouched
1600 | OrderType::StopMarket
1601 | OrderType::TrailingStopMarket
1602 )
1603 {
1604 todo!("Exhausted simulated book volume")
1608 }
1609 }
1610
1611 fn fill_order(
1612 &mut self,
1613 order: &mut OrderAny,
1614 last_px: Price,
1615 last_qty: Quantity,
1616 liquidity_side: LiquiditySide,
1617 venue_position_id: Option<PositionId>,
1618 position: Option<Position>,
1619 ) {
1620 match self.cached_filled_qty.get(&order.client_order_id()) {
1621 Some(filled_qty) => {
1622 let leaves_qty = order.quantity() - *filled_qty;
1623 let last_qty = min(last_qty, leaves_qty);
1624 let new_filled_qty = *filled_qty + last_qty;
1625 self.cached_filled_qty
1627 .insert(order.client_order_id(), new_filled_qty);
1628 }
1629 None => {
1630 self.cached_filled_qty
1631 .insert(order.client_order_id(), last_qty);
1632 }
1633 }
1634
1635 let commission = self
1637 .fee_model
1638 .get_commission(order, last_qty, last_px, &self.instrument)
1639 .unwrap();
1640
1641 let venue_order_id = self.ids_generator.get_venue_order_id(order).unwrap();
1642 self.generate_order_filled(
1643 order,
1644 venue_order_id,
1645 venue_position_id,
1646 last_qty,
1647 last_px,
1648 self.instrument.quote_currency(),
1649 commission,
1650 liquidity_side,
1651 );
1652
1653 if order.is_passive() && order.is_closed() {
1654 if self.core.order_exists(order.client_order_id()) {
1656 let _ = self
1657 .core
1658 .delete_order(&PassiveOrderAny::from(order.clone()));
1659 }
1660 self.cached_filled_qty.remove(&order.client_order_id());
1661 }
1662
1663 if !self.config.support_contingent_orders {
1664 return;
1665 }
1666
1667 if let Some(contingency_type) = order.contingency_type() {
1668 match contingency_type {
1669 ContingencyType::Oto => {
1670 if let Some(linked_orders_ids) = order.linked_order_ids() {
1671 for client_order_id in linked_orders_ids {
1672 let mut child_order = match self.cache.borrow().order(client_order_id) {
1673 Some(child_order) => child_order.clone(),
1674 None => panic!("Order {client_order_id} not found in cache"),
1675 };
1676
1677 if child_order.is_closed() || child_order.is_active_local() {
1678 continue;
1679 }
1680
1681 if let (None, Some(position_id)) =
1683 (child_order.position_id(), order.position_id())
1684 {
1685 self.cache
1686 .borrow_mut()
1687 .add_position_id(
1688 &position_id,
1689 &self.venue,
1690 client_order_id,
1691 &child_order.strategy_id(),
1692 )
1693 .unwrap();
1694 log::debug!(
1695 "Added position id {position_id} to cache for order {client_order_id}"
1696 );
1697 }
1698
1699 if (!child_order.is_open())
1700 || (matches!(child_order.status(), OrderStatus::PendingUpdate)
1701 && child_order
1702 .previous_status()
1703 .is_some_and(|s| matches!(s, OrderStatus::Submitted)))
1704 {
1705 let account_id = order.account_id().unwrap_or_else(|| {
1706 *self.account_ids.get(&order.trader_id()).unwrap_or_else(|| {
1707 panic!(
1708 "Account ID not found for trader {}",
1709 order.trader_id()
1710 )
1711 })
1712 });
1713 self.process_order(&mut child_order, account_id);
1714 }
1715 }
1716 } else {
1717 log::error!(
1718 "OTO order {} does not have linked orders",
1719 order.client_order_id()
1720 );
1721 }
1722 }
1723 ContingencyType::Oco => {
1724 if let Some(linked_orders_ids) = order.linked_order_ids() {
1725 for client_order_id in linked_orders_ids {
1726 let child_order = match self.cache.borrow().order(client_order_id) {
1727 Some(child_order) => child_order.clone(),
1728 None => panic!("Order {client_order_id} not found in cache"),
1729 };
1730
1731 if child_order.is_closed() || child_order.is_active_local() {
1732 continue;
1733 }
1734
1735 self.cancel_order(&child_order, None);
1736 }
1737 } else {
1738 log::error!(
1739 "OCO order {} does not have linked orders",
1740 order.client_order_id()
1741 );
1742 }
1743 }
1744 ContingencyType::Ouo => {
1745 if let Some(linked_orders_ids) = order.linked_order_ids() {
1746 for client_order_id in linked_orders_ids {
1747 let mut child_order = match self.cache.borrow().order(client_order_id) {
1748 Some(child_order) => child_order.clone(),
1749 None => panic!("Order {client_order_id} not found in cache"),
1750 };
1751
1752 if child_order.is_active_local() {
1753 continue;
1754 }
1755
1756 if order.is_closed() && child_order.is_open() {
1757 self.cancel_order(&child_order, None);
1758 } else if !order.leaves_qty().is_zero()
1759 && order.leaves_qty() != child_order.leaves_qty()
1760 {
1761 let price = child_order.price();
1762 let trigger_price = child_order.trigger_price();
1763 self.update_order(
1764 &mut child_order,
1765 Some(order.leaves_qty()),
1766 price,
1767 trigger_price,
1768 Some(false),
1769 );
1770 }
1771 }
1772 } else {
1773 log::error!(
1774 "OUO order {} does not have linked orders",
1775 order.client_order_id()
1776 );
1777 }
1778 }
1779 _ => {}
1780 }
1781 }
1782 }
1783
1784 fn update_limit_order(&mut self, order: &mut OrderAny, quantity: Quantity, price: Price) {
1785 if self
1786 .core
1787 .is_limit_matched(order.order_side_specified(), price)
1788 {
1789 if order.is_post_only() {
1790 self.generate_order_modify_rejected(
1791 order.trader_id(),
1792 order.strategy_id(),
1793 order.instrument_id(),
1794 order.client_order_id(),
1795 Ustr::from(format!(
1796 "POST_ONLY {} {} order with new limit px of {} would have been a TAKER: bid={}, ask={}",
1797 order.order_type(),
1798 order.order_side(),
1799 price,
1800 self.core.bid.map_or_else(|| "None".to_string(), |p| p.to_string()),
1801 self.core.ask.map_or_else(|| "None".to_string(), |p| p.to_string())
1802 ).as_str()),
1803 order.venue_order_id(),
1804 order.account_id(),
1805 );
1806 return;
1807 }
1808
1809 self.generate_order_updated(order, quantity, Some(price), None);
1810 order.set_liquidity_side(LiquiditySide::Taker);
1811 self.fill_limit_order(order);
1812 return;
1813 }
1814 self.generate_order_updated(order, quantity, Some(price), None);
1815 }
1816
1817 fn update_stop_market_order(
1818 &mut self,
1819 order: &mut OrderAny,
1820 quantity: Quantity,
1821 trigger_price: Price,
1822 ) {
1823 if self
1824 .core
1825 .is_stop_matched(order.order_side_specified(), trigger_price)
1826 {
1827 self.generate_order_modify_rejected(
1828 order.trader_id(),
1829 order.strategy_id(),
1830 order.instrument_id(),
1831 order.client_order_id(),
1832 Ustr::from(
1833 format!(
1834 "{} {} order new stop px of {} was in the market: bid={}, ask={}",
1835 order.order_type(),
1836 order.order_side(),
1837 trigger_price,
1838 self.core
1839 .bid
1840 .map_or_else(|| "None".to_string(), |p| p.to_string()),
1841 self.core
1842 .ask
1843 .map_or_else(|| "None".to_string(), |p| p.to_string())
1844 )
1845 .as_str(),
1846 ),
1847 order.venue_order_id(),
1848 order.account_id(),
1849 );
1850 return;
1851 }
1852
1853 self.generate_order_updated(order, quantity, None, Some(trigger_price));
1854 }
1855
1856 fn update_stop_limit_order(
1857 &mut self,
1858 order: &mut OrderAny,
1859 quantity: Quantity,
1860 price: Price,
1861 trigger_price: Price,
1862 ) {
1863 if order.is_triggered().is_some_and(|t| t) {
1864 if self
1866 .core
1867 .is_limit_matched(order.order_side_specified(), price)
1868 {
1869 if order.is_post_only() {
1870 self.generate_order_modify_rejected(
1871 order.trader_id(),
1872 order.strategy_id(),
1873 order.instrument_id(),
1874 order.client_order_id(),
1875 Ustr::from(format!(
1876 "POST_ONLY {} {} order with new limit px of {} would have been a TAKER: bid={}, ask={}",
1877 order.order_type(),
1878 order.order_side(),
1879 price,
1880 self.core.bid.map_or_else(|| "None".to_string(), |p| p.to_string()),
1881 self.core.ask.map_or_else(|| "None".to_string(), |p| p.to_string())
1882 ).as_str()),
1883 order.venue_order_id(),
1884 order.account_id(),
1885 );
1886 return;
1887 }
1888 self.generate_order_updated(order, quantity, Some(price), None);
1889 order.set_liquidity_side(LiquiditySide::Taker);
1890 self.fill_limit_order(order);
1891 return; }
1893 } else {
1894 if self
1896 .core
1897 .is_stop_matched(order.order_side_specified(), trigger_price)
1898 {
1899 self.generate_order_modify_rejected(
1900 order.trader_id(),
1901 order.strategy_id(),
1902 order.instrument_id(),
1903 order.client_order_id(),
1904 Ustr::from(
1905 format!(
1906 "{} {} order new stop px of {} was in the market: bid={}, ask={}",
1907 order.order_type(),
1908 order.order_side(),
1909 trigger_price,
1910 self.core
1911 .bid
1912 .map_or_else(|| "None".to_string(), |p| p.to_string()),
1913 self.core
1914 .ask
1915 .map_or_else(|| "None".to_string(), |p| p.to_string())
1916 )
1917 .as_str(),
1918 ),
1919 order.venue_order_id(),
1920 order.account_id(),
1921 );
1922 return;
1923 }
1924 }
1925
1926 self.generate_order_updated(order, quantity, Some(price), Some(trigger_price));
1927 }
1928
1929 fn update_market_if_touched_order(
1930 &mut self,
1931 order: &mut OrderAny,
1932 quantity: Quantity,
1933 trigger_price: Price,
1934 ) {
1935 if self
1936 .core
1937 .is_touch_triggered(order.order_side_specified(), trigger_price)
1938 {
1939 self.generate_order_modify_rejected(
1940 order.trader_id(),
1941 order.strategy_id(),
1942 order.instrument_id(),
1943 order.client_order_id(),
1944 Ustr::from(
1945 format!(
1946 "{} {} order new trigger px of {} was in the market: bid={}, ask={}",
1947 order.order_type(),
1948 order.order_side(),
1949 trigger_price,
1950 self.core
1951 .bid
1952 .map_or_else(|| "None".to_string(), |p| p.to_string()),
1953 self.core
1954 .ask
1955 .map_or_else(|| "None".to_string(), |p| p.to_string())
1956 )
1957 .as_str(),
1958 ),
1959 order.venue_order_id(),
1960 order.account_id(),
1961 );
1962 return;
1964 }
1965
1966 self.generate_order_updated(order, quantity, None, Some(trigger_price));
1967 }
1968
1969 fn update_limit_if_touched_order(
1970 &mut self,
1971 order: &mut OrderAny,
1972 quantity: Quantity,
1973 price: Price,
1974 trigger_price: Price,
1975 ) {
1976 if order.is_triggered().is_some_and(|t| t) {
1977 if self
1979 .core
1980 .is_limit_matched(order.order_side_specified(), price)
1981 {
1982 if order.is_post_only() {
1983 self.generate_order_modify_rejected(
1984 order.trader_id(),
1985 order.strategy_id(),
1986 order.instrument_id(),
1987 order.client_order_id(),
1988 Ustr::from(format!(
1989 "POST_ONLY {} {} order with new limit px of {} would have been a TAKER: bid={}, ask={}",
1990 order.order_type(),
1991 order.order_side(),
1992 price,
1993 self.core.bid.map_or_else(|| "None".to_string(), |p| p.to_string()),
1994 self.core.ask.map_or_else(|| "None".to_string(), |p| p.to_string())
1995 ).as_str()),
1996 order.venue_order_id(),
1997 order.account_id(),
1998 );
1999 return;
2001 }
2002 self.generate_order_updated(order, quantity, Some(price), None);
2003 order.set_liquidity_side(LiquiditySide::Taker);
2004 self.fill_limit_order(order);
2005 return;
2006 }
2007 } else {
2008 if self
2010 .core
2011 .is_touch_triggered(order.order_side_specified(), trigger_price)
2012 {
2013 self.generate_order_modify_rejected(
2014 order.trader_id(),
2015 order.strategy_id(),
2016 order.instrument_id(),
2017 order.client_order_id(),
2018 Ustr::from(
2019 format!(
2020 "{} {} order new trigger px of {} was in the market: bid={}, ask={}",
2021 order.order_type(),
2022 order.order_side(),
2023 trigger_price,
2024 self.core
2025 .bid
2026 .map_or_else(|| "None".to_string(), |p| p.to_string()),
2027 self.core
2028 .ask
2029 .map_or_else(|| "None".to_string(), |p| p.to_string())
2030 )
2031 .as_str(),
2032 ),
2033 order.venue_order_id(),
2034 order.account_id(),
2035 );
2036 return;
2037 }
2038 }
2039
2040 self.generate_order_updated(order, quantity, Some(price), Some(trigger_price));
2041 }
2042
2043 fn update_trailing_stop_order(&mut self, order: &mut OrderAny) {
2044 let (new_trigger_price, new_price) = trailing_stop_calculate(
2045 self.instrument.price_increment(),
2046 order.trigger_price(),
2047 order.activation_price(),
2048 order,
2049 self.core.bid,
2050 self.core.ask,
2051 self.core.last,
2052 )
2053 .unwrap();
2054
2055 if new_trigger_price.is_none() && new_price.is_none() {
2056 return;
2057 }
2058
2059 self.generate_order_updated(order, order.quantity(), new_price, new_trigger_price);
2060 }
2061
2062 fn accept_order(&mut self, order: &mut OrderAny) {
2065 if order.is_closed() {
2066 return;
2068 }
2069 if order.status() != OrderStatus::Accepted {
2070 let venue_order_id = self.ids_generator.get_venue_order_id(order).unwrap();
2071 self.generate_order_accepted(order, venue_order_id);
2072
2073 if matches!(
2074 order.order_type(),
2075 OrderType::TrailingStopLimit | OrderType::TrailingStopMarket
2076 ) && order.trigger_price().is_none()
2077 {
2078 self.update_trailing_stop_order(order);
2079 }
2080 }
2081
2082 let _ = self.core.add_order(order.to_owned().into());
2083 }
2084
2085 fn expire_order(&mut self, order: &PassiveOrderAny) {
2086 if self.config.support_contingent_orders
2087 && order
2088 .contingency_type()
2089 .is_some_and(|c| c != ContingencyType::NoContingency)
2090 {
2091 self.cancel_contingent_orders(&OrderAny::from(order.clone()));
2092 }
2093
2094 self.generate_order_expired(&order.to_any());
2095 }
2096
2097 fn cancel_order(&mut self, order: &OrderAny, cancel_contingencies: Option<bool>) {
2098 let cancel_contingencies = cancel_contingencies.unwrap_or(true);
2099 if order.is_active_local() {
2100 log::error!(
2101 "Cannot cancel an order with {} from the matching engine",
2102 order.status()
2103 );
2104 return;
2105 }
2106
2107 if self.core.order_exists(order.client_order_id()) {
2109 let _ = self
2110 .core
2111 .delete_order(&PassiveOrderAny::from(order.clone()));
2112 }
2113 self.cached_filled_qty.remove(&order.client_order_id());
2114
2115 let venue_order_id = self.ids_generator.get_venue_order_id(order).unwrap();
2116 self.generate_order_canceled(order, venue_order_id);
2117
2118 if self.config.support_contingent_orders
2119 && order.contingency_type().is_some()
2120 && order.contingency_type().unwrap() != ContingencyType::NoContingency
2121 && cancel_contingencies
2122 {
2123 self.cancel_contingent_orders(order);
2124 }
2125 }
2126
2127 fn update_order(
2128 &mut self,
2129 order: &mut OrderAny,
2130 quantity: Option<Quantity>,
2131 price: Option<Price>,
2132 trigger_price: Option<Price>,
2133 update_contingencies: Option<bool>,
2134 ) {
2135 let update_contingencies = update_contingencies.unwrap_or(true);
2136 let quantity = quantity.unwrap_or(order.quantity());
2137
2138 match order {
2139 OrderAny::Limit(_) | OrderAny::MarketToLimit(_) => {
2140 let price = price.unwrap_or(order.price().unwrap());
2141 self.update_limit_order(order, quantity, price);
2142 }
2143 OrderAny::StopMarket(_) => {
2144 let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2145 self.update_stop_market_order(order, quantity, trigger_price);
2146 }
2147 OrderAny::StopLimit(_) => {
2148 let price = price.unwrap_or(order.price().unwrap());
2149 let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2150 self.update_stop_limit_order(order, quantity, price, trigger_price);
2151 }
2152 OrderAny::MarketIfTouched(_) => {
2153 let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2154 self.update_market_if_touched_order(order, quantity, trigger_price);
2155 }
2156 OrderAny::LimitIfTouched(_) => {
2157 let price = price.unwrap_or(order.price().unwrap());
2158 let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2159 self.update_limit_if_touched_order(order, quantity, price, trigger_price);
2160 }
2161 OrderAny::TrailingStopMarket(_) => {
2162 let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2163 self.update_market_if_touched_order(order, quantity, trigger_price);
2164 }
2165 OrderAny::TrailingStopLimit(trailing_stop_limit_order) => {
2166 let price = price.unwrap_or(trailing_stop_limit_order.price().unwrap());
2167 let trigger_price =
2168 trigger_price.unwrap_or(trailing_stop_limit_order.trigger_price().unwrap());
2169 self.update_limit_if_touched_order(order, quantity, price, trigger_price);
2170 }
2171 _ => {
2172 panic!(
2173 "Unsupported order type {} for update_order",
2174 order.order_type()
2175 );
2176 }
2177 }
2178
2179 if self.config.support_contingent_orders
2180 && order
2181 .contingency_type()
2182 .is_some_and(|c| c != ContingencyType::NoContingency)
2183 && update_contingencies
2184 {
2185 self.update_contingent_order(order);
2186 }
2187 }
2188
2189 pub fn trigger_stop_order(&mut self, order: &mut OrderAny) {
2190 todo!("trigger_stop_order")
2191 }
2192
2193 fn update_contingent_order(&mut self, order: &OrderAny) {
2194 log::debug!("Updating OUO orders from {}", order.client_order_id());
2195 if let Some(linked_order_ids) = order.linked_order_ids() {
2196 for client_order_id in linked_order_ids {
2197 let mut child_order = match self.cache.borrow().order(client_order_id) {
2198 Some(order) => order.clone(),
2199 None => panic!("Order {client_order_id} not found in cache."),
2200 };
2201
2202 if child_order.is_active_local() {
2203 continue;
2204 }
2205
2206 if order.leaves_qty().is_zero() {
2207 self.cancel_order(&child_order, None);
2208 } else if child_order.leaves_qty() != order.leaves_qty() {
2209 let price = child_order.price();
2210 let trigger_price = child_order.trigger_price();
2211 self.update_order(
2212 &mut child_order,
2213 Some(order.leaves_qty()),
2214 price,
2215 trigger_price,
2216 Some(false),
2217 );
2218 }
2219 }
2220 }
2221 }
2222
2223 fn cancel_contingent_orders(&mut self, order: &OrderAny) {
2224 if let Some(linked_order_ids) = order.linked_order_ids() {
2225 for client_order_id in linked_order_ids {
2226 let contingent_order = match self.cache.borrow().order(client_order_id) {
2227 Some(order) => order.clone(),
2228 None => panic!("Cannot find contingent order for {client_order_id}"),
2229 };
2230 if contingent_order.is_active_local() {
2231 continue;
2233 }
2234 if !contingent_order.is_closed() {
2235 self.cancel_order(&contingent_order, Some(false));
2236 }
2237 }
2238 }
2239 }
2240
2241 fn generate_order_rejected(&self, order: &OrderAny, reason: Ustr) {
2244 let ts_now = self.clock.borrow().timestamp_ns();
2245 let account_id = order
2246 .account_id()
2247 .unwrap_or(self.account_ids.get(&order.trader_id()).unwrap().to_owned());
2248
2249 let due_post_only = reason.as_str().starts_with("POST_ONLY");
2251
2252 let event = OrderEventAny::Rejected(OrderRejected::new(
2253 order.trader_id(),
2254 order.strategy_id(),
2255 order.instrument_id(),
2256 order.client_order_id(),
2257 account_id,
2258 reason,
2259 UUID4::new(),
2260 ts_now,
2261 ts_now,
2262 false,
2263 due_post_only,
2264 ));
2265 msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2266 }
2267
2268 fn generate_order_accepted(&self, order: &mut OrderAny, venue_order_id: VenueOrderId) {
2269 let ts_now = self.clock.borrow().timestamp_ns();
2270 let account_id = order
2271 .account_id()
2272 .unwrap_or(self.account_ids.get(&order.trader_id()).unwrap().to_owned());
2273 let event = OrderEventAny::Accepted(OrderAccepted::new(
2274 order.trader_id(),
2275 order.strategy_id(),
2276 order.instrument_id(),
2277 order.client_order_id(),
2278 venue_order_id,
2279 account_id,
2280 UUID4::new(),
2281 ts_now,
2282 ts_now,
2283 false,
2284 ));
2285 msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2286
2287 order.apply(event).expect("Failed to apply order event");
2289 }
2290
2291 #[allow(clippy::too_many_arguments)]
2292 fn generate_order_modify_rejected(
2293 &self,
2294 trader_id: TraderId,
2295 strategy_id: StrategyId,
2296 instrument_id: InstrumentId,
2297 client_order_id: ClientOrderId,
2298 reason: Ustr,
2299 venue_order_id: Option<VenueOrderId>,
2300 account_id: Option<AccountId>,
2301 ) {
2302 let ts_now = self.clock.borrow().timestamp_ns();
2303 let event = OrderEventAny::ModifyRejected(OrderModifyRejected::new(
2304 trader_id,
2305 strategy_id,
2306 instrument_id,
2307 client_order_id,
2308 reason,
2309 UUID4::new(),
2310 ts_now,
2311 ts_now,
2312 false,
2313 venue_order_id,
2314 account_id,
2315 ));
2316 msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2317 }
2318
2319 #[allow(clippy::too_many_arguments)]
2320 fn generate_order_cancel_rejected(
2321 &self,
2322 trader_id: TraderId,
2323 strategy_id: StrategyId,
2324 account_id: AccountId,
2325 instrument_id: InstrumentId,
2326 client_order_id: ClientOrderId,
2327 venue_order_id: VenueOrderId,
2328 reason: Ustr,
2329 ) {
2330 let ts_now = self.clock.borrow().timestamp_ns();
2331 let event = OrderEventAny::CancelRejected(OrderCancelRejected::new(
2332 trader_id,
2333 strategy_id,
2334 instrument_id,
2335 client_order_id,
2336 reason,
2337 UUID4::new(),
2338 ts_now,
2339 ts_now,
2340 false,
2341 Some(venue_order_id),
2342 Some(account_id),
2343 ));
2344 msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2345 }
2346
2347 fn generate_order_updated(
2348 &self,
2349 order: &mut OrderAny,
2350 quantity: Quantity,
2351 price: Option<Price>,
2352 trigger_price: Option<Price>,
2353 ) {
2354 let ts_now = self.clock.borrow().timestamp_ns();
2355 let event = OrderEventAny::Updated(OrderUpdated::new(
2356 order.trader_id(),
2357 order.strategy_id(),
2358 order.instrument_id(),
2359 order.client_order_id(),
2360 quantity,
2361 UUID4::new(),
2362 ts_now,
2363 ts_now,
2364 false,
2365 order.venue_order_id(),
2366 order.account_id(),
2367 price,
2368 trigger_price,
2369 ));
2370 msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2371
2372 order.apply(event).expect("Failed to apply order event");
2374 }
2375
2376 fn generate_order_canceled(&self, order: &OrderAny, venue_order_id: VenueOrderId) {
2377 let ts_now = self.clock.borrow().timestamp_ns();
2378 let event = OrderEventAny::Canceled(OrderCanceled::new(
2379 order.trader_id(),
2380 order.strategy_id(),
2381 order.instrument_id(),
2382 order.client_order_id(),
2383 UUID4::new(),
2384 ts_now,
2385 ts_now,
2386 false,
2387 Some(venue_order_id),
2388 order.account_id(),
2389 ));
2390 msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2391 }
2392
2393 fn generate_order_triggered(&self, order: &OrderAny) {
2394 let ts_now = self.clock.borrow().timestamp_ns();
2395 let event = OrderEventAny::Triggered(OrderTriggered::new(
2396 order.trader_id(),
2397 order.strategy_id(),
2398 order.instrument_id(),
2399 order.client_order_id(),
2400 UUID4::new(),
2401 ts_now,
2402 ts_now,
2403 false,
2404 order.venue_order_id(),
2405 order.account_id(),
2406 ));
2407 msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2408 }
2409
2410 fn generate_order_expired(&self, order: &OrderAny) {
2411 let ts_now = self.clock.borrow().timestamp_ns();
2412 let event = OrderEventAny::Expired(OrderExpired::new(
2413 order.trader_id(),
2414 order.strategy_id(),
2415 order.instrument_id(),
2416 order.client_order_id(),
2417 UUID4::new(),
2418 ts_now,
2419 ts_now,
2420 false,
2421 order.venue_order_id(),
2422 order.account_id(),
2423 ));
2424 msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2425 }
2426
2427 #[allow(clippy::too_many_arguments)]
2428 fn generate_order_filled(
2429 &mut self,
2430 order: &mut OrderAny,
2431 venue_order_id: VenueOrderId,
2432 venue_position_id: Option<PositionId>,
2433 last_qty: Quantity,
2434 last_px: Price,
2435 quote_currency: Currency,
2436 commission: Money,
2437 liquidity_side: LiquiditySide,
2438 ) {
2439 let ts_now = self.clock.borrow().timestamp_ns();
2440 let account_id = order
2441 .account_id()
2442 .unwrap_or(self.account_ids.get(&order.trader_id()).unwrap().to_owned());
2443 let event = OrderEventAny::Filled(OrderFilled::new(
2444 order.trader_id(),
2445 order.strategy_id(),
2446 order.instrument_id(),
2447 order.client_order_id(),
2448 venue_order_id,
2449 account_id,
2450 self.ids_generator.generate_trade_id(),
2451 order.order_side(),
2452 order.order_type(),
2453 last_qty,
2454 last_px,
2455 quote_currency,
2456 liquidity_side,
2457 UUID4::new(),
2458 ts_now,
2459 ts_now,
2460 false,
2461 venue_position_id,
2462 Some(commission),
2463 ));
2464 msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2465
2466 order.apply(event).expect("Failed to apply order event");
2468 }
2469}