nautilus_execution/matching_engine/
engine.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16// Under development
17#![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
71/// An order matching engine for a single market.
72pub struct OrderMatchingEngine {
73    /// The venue for the matching engine.
74    pub venue: Venue,
75    /// The instrument for the matching engine.
76    pub instrument: InstrumentAny,
77    /// The instruments raw integer ID for the venue.
78    pub raw_id: u32,
79    /// The order book type for the matching engine.
80    pub book_type: BookType,
81    /// The order management system (OMS) type for the matching engine.
82    pub oms_type: OmsType,
83    /// The account type for the matching engine.
84    pub account_type: AccountType,
85    /// The market status for the matching engine.
86    pub market_status: MarketStatus,
87    /// The config for the matching engine.
88    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    /// Creates a new [`OrderMatchingEngine`] instance.
118    #[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, // TBD (will be a function on the engine)
136            None, // TBD (will be a function on the engine)
137            None, // TBD (will be a function on the engine)
138        );
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    /// Resets the matching engine to its initial state.
177    ///
178    /// Clears the order book, execution state, cached data, and resets all
179    /// internal components. This is typically used for backtesting scenarios
180    /// where the engine needs to be reset between test runs.
181    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    /// Sets the fill model for the matching engine.
197    pub const fn set_fill_model(&mut self, fill_model: FillModel) {
198        self.fill_model = fill_model;
199    }
200
201    #[must_use]
202    /// Returns the best bid price from the order book.
203    pub fn best_bid_price(&self) -> Option<Price> {
204        self.book.best_bid_price()
205    }
206
207    #[must_use]
208    /// Returns the best ask price from the order book.
209    pub fn best_ask_price(&self) -> Option<Price> {
210        self.book.best_ask_price()
211    }
212
213    #[must_use]
214    /// Returns a reference to the internal order book.
215    pub const fn get_book(&self) -> &OrderBook {
216        &self.book
217    }
218
219    #[must_use]
220    /// Returns all open bid orders managed by the matching core.
221    pub const fn get_open_bid_orders(&self) -> &[PassiveOrderAny] {
222        self.core.get_orders_bid()
223    }
224
225    #[must_use]
226    /// Returns all open ask orders managed by the matching core.
227    pub const fn get_open_ask_orders(&self) -> &[PassiveOrderAny] {
228        self.core.get_orders_ask()
229    }
230
231    #[must_use]
232    /// Returns all open orders from both bid and ask sides.
233    pub fn get_open_orders(&self) -> Vec<PassiveOrderAny> {
234        // Get orders from both open bid orders and open ask orders
235        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    /// Returns true if an order with the given client order ID exists in the matching engine.
243    pub fn order_exists(&self, client_order_id: ClientOrderId) -> bool {
244        self.core.order_exists(client_order_id)
245    }
246
247    // -- DATA PROCESSING -------------------------------------------------------------------------
248
249    /// Process the venues market for the given order book delta.
250    ///
251    /// # Errors
252    ///
253    /// Returns an error if applying the delta to the book fails.
254    pub fn process_order_book_delta(&mut self, delta: &OrderBookDelta) -> anyhow::Result<()> {
255        log::debug!("Processing {delta}");
256
257        if self.book_type == BookType::L2_MBP || self.book_type == BookType::L3_MBO {
258            self.book.apply_delta(delta)?;
259        }
260
261        self.iterate(delta.ts_init);
262        Ok(())
263    }
264
265    /// Process the venues market for the given order book deltas.
266    ///
267    /// # Errors
268    ///
269    /// Returns an error if applying the deltas to the book fails.
270    pub fn process_order_book_deltas(&mut self, deltas: &OrderBookDeltas) -> anyhow::Result<()> {
271        log::debug!("Processing {deltas}");
272
273        if self.book_type == BookType::L2_MBP || self.book_type == BookType::L3_MBO {
274            self.book.apply_deltas(deltas)?;
275        }
276
277        self.iterate(deltas.ts_init);
278        Ok(())
279    }
280
281    /// # Panics
282    ///
283    /// Panics if updating the order book with the quote tick fails.
284    pub fn process_quote_tick(&mut self, quote: &QuoteTick) {
285        log::debug!("Processing {quote}");
286
287        if self.book_type == BookType::L1_MBP {
288            self.book.update_quote_tick(quote).unwrap();
289        }
290
291        self.iterate(quote.ts_init);
292    }
293
294    /// # Panics
295    ///
296    /// Panics if the bar type configuration is missing a time delta.
297    pub fn process_bar(&mut self, bar: &Bar) {
298        log::debug!("Processing {bar}");
299
300        // Check if configured for bar execution can only process an L1 book with bars
301        if !self.config.bar_execution || self.book_type != BookType::L1_MBP {
302            return;
303        }
304
305        let bar_type = bar.bar_type;
306        // Do not process internally aggregated bars
307        if bar_type.aggregation_source() == AggregationSource::Internal {
308            return;
309        }
310
311        // Do not process monthly bars (no `timedelta` available)
312        if bar_type.spec().aggregation == BarAggregation::Month {
313            return;
314        }
315
316        let execution_bar_type =
317            if let Some(execution_bar_type) = self.execution_bar_types.get(&bar.instrument_id()) {
318                execution_bar_type.to_owned()
319            } else {
320                self.execution_bar_types
321                    .insert(bar.instrument_id(), bar_type);
322                self.execution_bar_deltas
323                    .insert(bar_type, bar_type.spec().timedelta());
324                bar_type
325            };
326
327        if execution_bar_type != bar_type {
328            let mut bar_type_timedelta = self.execution_bar_deltas.get(&bar_type).copied();
329            if bar_type_timedelta.is_none() {
330                bar_type_timedelta = Some(bar_type.spec().timedelta());
331                self.execution_bar_deltas
332                    .insert(bar_type, bar_type_timedelta.unwrap());
333            }
334            if self.execution_bar_deltas.get(&execution_bar_type).unwrap()
335                >= &bar_type_timedelta.unwrap()
336            {
337                self.execution_bar_types
338                    .insert(bar_type.instrument_id(), bar_type);
339            } else {
340                return;
341            }
342        }
343
344        match bar_type.spec().price_type {
345            PriceType::Last | PriceType::Mid => self.process_trade_ticks_from_bar(bar),
346            PriceType::Bid => {
347                self.last_bar_bid = Some(bar.to_owned());
348                self.process_quote_ticks_from_bar(bar);
349            }
350            PriceType::Ask => {
351                self.last_bar_ask = Some(bar.to_owned());
352                self.process_quote_ticks_from_bar(bar);
353            }
354            PriceType::Mark => panic!("Not implemented"),
355        }
356    }
357
358    fn process_trade_ticks_from_bar(&mut self, bar: &Bar) {
359        // Split the bar into 4 trades with quarter volume
360        let size = Quantity::new(bar.volume.as_f64() / 4.0, bar.volume.precision);
361        let aggressor_side = if !self.core.is_last_initialized || bar.open > self.core.last.unwrap()
362        {
363            AggressorSide::Buyer
364        } else {
365            AggressorSide::Seller
366        };
367
368        // Create reusable trade tick
369        let mut trade_tick = TradeTick::new(
370            bar.instrument_id(),
371            bar.open,
372            size,
373            aggressor_side,
374            self.ids_generator.generate_trade_id(),
375            bar.ts_init,
376            bar.ts_init,
377        );
378
379        // Open
380        // Check if not initialized, if it is, it will be updated by the close or last
381        if !self.core.is_last_initialized {
382            self.book.update_trade_tick(&trade_tick).unwrap();
383            self.iterate(trade_tick.ts_init);
384            self.core.set_last_raw(trade_tick.price);
385        }
386
387        // High
388        // Check if higher than last
389        if self.core.last.is_some_and(|last| bar.high > last) {
390            trade_tick.price = bar.high;
391            trade_tick.aggressor_side = AggressorSide::Buyer;
392            trade_tick.trade_id = self.ids_generator.generate_trade_id();
393
394            self.book.update_trade_tick(&trade_tick).unwrap();
395            self.iterate(trade_tick.ts_init);
396
397            self.core.set_last_raw(trade_tick.price);
398        }
399
400        // Low
401        // Check if lower than last
402        // Assumption: market traded down, aggressor hitting the bid(setting aggressor to seller)
403        if self.core.last.is_some_and(|last| bar.low < last) {
404            trade_tick.price = bar.low;
405            trade_tick.aggressor_side = AggressorSide::Seller;
406            trade_tick.trade_id = self.ids_generator.generate_trade_id();
407
408            self.book.update_trade_tick(&trade_tick).unwrap();
409            self.iterate(trade_tick.ts_init);
410
411            self.core.set_last_raw(trade_tick.price);
412        }
413
414        // Close
415        // Check if not the same as last
416        // Assumption: if close price is higher then last, aggressor is buyer
417        // Assumption: if close price is lower then last, aggressor is seller
418        if self.core.last.is_some_and(|last| bar.close != last) {
419            trade_tick.price = bar.close;
420            trade_tick.aggressor_side = if bar.close > self.core.last.unwrap() {
421                AggressorSide::Buyer
422            } else {
423                AggressorSide::Seller
424            };
425            trade_tick.trade_id = self.ids_generator.generate_trade_id();
426
427            self.book.update_trade_tick(&trade_tick).unwrap();
428            self.iterate(trade_tick.ts_init);
429
430            self.core.set_last_raw(trade_tick.price);
431        }
432    }
433
434    fn process_quote_ticks_from_bar(&mut self, bar: &Bar) {
435        // Wait for next bar
436        if self.last_bar_bid.is_none()
437            || self.last_bar_ask.is_none()
438            || self.last_bar_bid.unwrap().ts_init != self.last_bar_ask.unwrap().ts_init
439        {
440            return;
441        }
442        let bid_bar = self.last_bar_bid.unwrap();
443        let ask_bar = self.last_bar_ask.unwrap();
444        let bid_size = Quantity::new(bid_bar.volume.as_f64() / 4.0, bar.volume.precision);
445        let ask_size = Quantity::new(ask_bar.volume.as_f64() / 4.0, bar.volume.precision);
446
447        // Create reusable quote tick
448        let mut quote_tick = QuoteTick::new(
449            self.book.instrument_id,
450            bid_bar.open,
451            ask_bar.open,
452            bid_size,
453            ask_size,
454            bid_bar.ts_init,
455            bid_bar.ts_init,
456        );
457
458        // Open
459        self.book.update_quote_tick(&quote_tick).unwrap();
460        self.iterate(quote_tick.ts_init);
461
462        // High
463        quote_tick.bid_price = bid_bar.high;
464        quote_tick.ask_price = ask_bar.high;
465        self.book.update_quote_tick(&quote_tick).unwrap();
466        self.iterate(quote_tick.ts_init);
467
468        // Low
469        quote_tick.bid_price = bid_bar.low;
470        quote_tick.ask_price = ask_bar.low;
471        self.book.update_quote_tick(&quote_tick).unwrap();
472        self.iterate(quote_tick.ts_init);
473
474        // Close
475        quote_tick.bid_price = bid_bar.close;
476        quote_tick.ask_price = ask_bar.close;
477        self.book.update_quote_tick(&quote_tick).unwrap();
478        self.iterate(quote_tick.ts_init);
479
480        // Reset last bars
481        self.last_bar_bid = None;
482        self.last_bar_ask = None;
483    }
484
485    /// # Panics
486    ///
487    /// Panics if updating the order book with the trade tick fails.
488    pub fn process_trade_tick(&mut self, trade: &TradeTick) {
489        log::debug!("Processing {trade}");
490
491        if self.book_type == BookType::L1_MBP {
492            self.book.update_trade_tick(trade).unwrap();
493        }
494        self.core.set_last_raw(trade.price);
495
496        self.iterate(trade.ts_init);
497    }
498
499    pub fn process_status(&mut self, action: MarketStatusAction) {
500        log::debug!("Processing {action}");
501
502        // Check if market is closed and market opens with trading or pre-open status
503        if self.market_status == MarketStatus::Closed
504            && (action == MarketStatusAction::Trading || action == MarketStatusAction::PreOpen)
505        {
506            self.market_status = MarketStatus::Open;
507        }
508        // Check if market is open and market pauses
509        if self.market_status == MarketStatus::Open && action == MarketStatusAction::Pause {
510            self.market_status = MarketStatus::Paused;
511        }
512        // Check if market is open and market suspends
513        if self.market_status == MarketStatus::Open && action == MarketStatusAction::Suspend {
514            self.market_status = MarketStatus::Suspended;
515        }
516        // Check if market is open and we halt or close
517        if self.market_status == MarketStatus::Open
518            && (action == MarketStatusAction::Halt || action == MarketStatusAction::Close)
519        {
520            self.market_status = MarketStatus::Closed;
521        }
522    }
523
524    // -- TRADING COMMANDS ------------------------------------------------------------------------
525
526    /// # Panics
527    ///
528    /// Panics if the instrument activation timestamp is missing.
529    #[allow(clippy::needless_return)]
530    pub fn process_order(&mut self, order: &mut OrderAny, account_id: AccountId) {
531        // Enter the scope where you will borrow a cache
532        {
533            let cache_borrow = self.cache.as_ref().borrow();
534
535            if self.core.order_exists(order.client_order_id()) {
536                self.generate_order_rejected(order, "Order already exists".into());
537                return;
538            }
539
540            // Index identifiers
541            self.account_ids.insert(order.trader_id(), account_id);
542
543            // Check for instrument expiration or activation
544            if EXPIRING_INSTRUMENT_TYPES.contains(&self.instrument.instrument_class()) {
545                if let Some(activation_ns) = self.instrument.activation_ns()
546                    && self.clock.borrow().timestamp_ns() < activation_ns
547                {
548                    self.generate_order_rejected(
549                        order,
550                        format!(
551                            "Contract {} is not yet active, activation {}",
552                            self.instrument.id(),
553                            self.instrument.activation_ns().unwrap()
554                        )
555                        .into(),
556                    );
557                    return;
558                }
559                if let Some(expiration_ns) = self.instrument.expiration_ns()
560                    && self.clock.borrow().timestamp_ns() >= expiration_ns
561                {
562                    self.generate_order_rejected(
563                        order,
564                        format!(
565                            "Contract {} has expired, expiration {}",
566                            self.instrument.id(),
567                            self.instrument.expiration_ns().unwrap()
568                        )
569                        .into(),
570                    );
571                    return;
572                }
573            }
574
575            // Contingent orders checks
576            if self.config.support_contingent_orders {
577                if let Some(parent_order_id) = order.parent_order_id() {
578                    let parent_order = cache_borrow.order(&parent_order_id);
579                    if parent_order.is_none()
580                        || parent_order.unwrap().contingency_type().unwrap() != ContingencyType::Oto
581                    {
582                        panic!("OTO parent not found");
583                    }
584                    if let Some(parent_order) = parent_order {
585                        let parent_order_status = parent_order.status();
586                        let order_is_open = order.is_open();
587                        if parent_order.status() == OrderStatus::Rejected && order.is_open() {
588                            self.generate_order_rejected(
589                                order,
590                                format!("Rejected OTO order from {parent_order_id}").into(),
591                            );
592                            return;
593                        } else if parent_order.status() == OrderStatus::Accepted
594                            && parent_order.status() == OrderStatus::Triggered
595                        {
596                            log::info!(
597                                "Pending OTO order {} triggers from {parent_order_id}",
598                                order.client_order_id(),
599                            );
600                            return;
601                        }
602                    }
603                }
604
605                if let Some(linked_order_ids) = order.linked_order_ids() {
606                    for client_order_id in linked_order_ids {
607                        match cache_borrow.order(client_order_id) {
608                            Some(contingent_order)
609                                if (order.contingency_type().unwrap() == ContingencyType::Oco
610                                    || order.contingency_type().unwrap()
611                                        == ContingencyType::Ouo)
612                                    && !order.is_closed()
613                                    && contingent_order.is_closed() =>
614                            {
615                                self.generate_order_rejected(
616                                    order,
617                                    format!("Contingent order {client_order_id} already closed")
618                                        .into(),
619                                );
620                                return;
621                            }
622                            None => panic!("Cannot find contingent order for {client_order_id}"),
623                            _ => {}
624                        }
625                    }
626                }
627            }
628
629            // Check for valid order quantity precision
630            if order.quantity().precision != self.instrument.size_precision() {
631                self.generate_order_rejected(
632                    order,
633                    format!(
634                        "Invalid order quantity precision for order {}, was {} when {} size precision is {}",
635                        order.client_order_id(),
636                        order.quantity().precision,
637                        self.instrument.id(),
638                        self.instrument.size_precision()
639                    )
640                        .into(),
641                );
642                return;
643            }
644
645            // Check for valid order price precision
646            if let Some(price) = order.price()
647                && price.precision != self.instrument.price_precision()
648            {
649                self.generate_order_rejected(
650                        order,
651                        format!(
652                            "Invalid order price precision for order {}, was {} when {} price precision is {}",
653                            order.client_order_id(),
654                            price.precision,
655                            self.instrument.id(),
656                            self.instrument.price_precision()
657                        )
658                            .into(),
659                    );
660                return;
661            }
662
663            // Check for valid order trigger price precision
664            if let Some(trigger_price) = order.trigger_price()
665                && trigger_price.precision != self.instrument.price_precision()
666            {
667                self.generate_order_rejected(
668                        order,
669                        format!(
670                            "Invalid order trigger price precision for order {}, was {} when {} price precision is {}",
671                            order.client_order_id(),
672                            trigger_price.precision,
673                            self.instrument.id(),
674                            self.instrument.price_precision()
675                        )
676                            .into(),
677                    );
678                return;
679            }
680
681            // Get position if exists
682            let position: Option<&Position> = cache_borrow
683                .position_for_order(&order.client_order_id())
684                .or_else(|| {
685                    if self.oms_type == OmsType::Netting {
686                        let position_id = PositionId::new(
687                            format!("{}-{}", order.instrument_id(), order.strategy_id()).as_str(),
688                        );
689                        cache_borrow.position(&position_id)
690                    } else {
691                        None
692                    }
693                });
694
695            // Check not shorting an equity without a MARGIN account
696            if order.order_side() == OrderSide::Sell
697                && self.account_type != AccountType::Margin
698                && matches!(self.instrument, InstrumentAny::Equity(_))
699                && (position.is_none()
700                    || !order.would_reduce_only(position.unwrap().side, position.unwrap().quantity))
701            {
702                let position_string = position.map_or("None".to_string(), |pos| pos.id.to_string());
703                self.generate_order_rejected(
704                    order,
705                    format!(
706                        "Short selling not permitted on a CASH account with position {position_string} and order {order}",
707                    )
708                        .into(),
709                );
710                return;
711            }
712
713            // Check reduce-only instruction
714            if self.config.use_reduce_only
715                && order.is_reduce_only()
716                && !order.is_closed()
717                && position.is_none_or(|pos| {
718                    pos.is_closed()
719                        || (order.is_buy() && pos.is_long())
720                        || (order.is_sell() && pos.is_short())
721                })
722            {
723                self.generate_order_rejected(
724                    order,
725                    format!(
726                        "Reduce-only order {} ({}-{}) would have increased position",
727                        order.client_order_id(),
728                        order.order_type().to_string().to_uppercase(),
729                        order.order_side().to_string().to_uppercase()
730                    )
731                    .into(),
732                );
733                return;
734            }
735        }
736
737        match order.order_type() {
738            OrderType::Market => self.process_market_order(order),
739            OrderType::Limit => self.process_limit_order(order),
740            OrderType::MarketToLimit => self.process_market_to_limit_order(order),
741            OrderType::StopMarket => self.process_stop_market_order(order),
742            OrderType::StopLimit => self.process_stop_limit_order(order),
743            OrderType::MarketIfTouched => self.process_market_if_touched_order(order),
744            OrderType::LimitIfTouched => self.process_limit_if_touched_order(order),
745            OrderType::TrailingStopMarket => self.process_trailing_stop_order(order),
746            OrderType::TrailingStopLimit => self.process_trailing_stop_order(order),
747        }
748    }
749
750    pub fn process_modify(&mut self, command: &ModifyOrder, account_id: AccountId) {
751        if let Some(order) = self.core.get_order(command.client_order_id) {
752            self.update_order(
753                &mut order.to_any(),
754                command.quantity,
755                command.price,
756                command.trigger_price,
757                None,
758            );
759        } else {
760            self.generate_order_modify_rejected(
761                command.trader_id,
762                command.strategy_id,
763                command.instrument_id,
764                command.client_order_id,
765                Ustr::from(format!("Order {} not found", command.client_order_id).as_str()),
766                Some(command.venue_order_id),
767                Some(account_id),
768            );
769        }
770    }
771
772    pub fn process_cancel(&mut self, command: &CancelOrder, account_id: AccountId) {
773        match self.core.get_order(command.client_order_id) {
774            Some(passive_order) => {
775                if passive_order.is_inflight() || passive_order.is_open() {
776                    self.cancel_order(&OrderAny::from(passive_order.to_owned()), None);
777                }
778            }
779            None => self.generate_order_cancel_rejected(
780                command.trader_id,
781                command.strategy_id,
782                account_id,
783                command.instrument_id,
784                command.client_order_id,
785                command.venue_order_id,
786                Ustr::from(format!("Order {} not found", command.client_order_id).as_str()),
787            ),
788        }
789    }
790
791    pub fn process_cancel_all(&mut self, command: &CancelAllOrders, account_id: AccountId) {
792        let instrument_id = command.instrument_id;
793        let open_orders = self
794            .cache
795            .borrow()
796            .orders_open(None, Some(&instrument_id), None, None)
797            .into_iter()
798            .cloned()
799            .collect::<Vec<OrderAny>>();
800        for order in open_orders {
801            if command.order_side != OrderSide::NoOrderSide
802                && command.order_side != order.order_side()
803            {
804                continue;
805            }
806            if order.is_inflight() || order.is_open() {
807                self.cancel_order(&order, None);
808            }
809        }
810    }
811
812    pub fn process_batch_cancel(&mut self, command: &BatchCancelOrders, account_id: AccountId) {
813        for order in &command.cancels {
814            self.process_cancel(order, account_id);
815        }
816    }
817
818    fn process_market_order(&mut self, order: &mut OrderAny) {
819        if order.time_in_force() == TimeInForce::AtTheOpen
820            || order.time_in_force() == TimeInForce::AtTheClose
821        {
822            log::error!(
823                "Market auction for the time in force {} is currently not supported",
824                order.time_in_force()
825            );
826            return;
827        }
828
829        // Check if market exists
830        let order_side = order.order_side();
831        let is_ask_initialized = self.core.is_ask_initialized;
832        let is_bid_initialized = self.core.is_bid_initialized;
833        if (order.order_side() == OrderSide::Buy && !self.core.is_ask_initialized)
834            || (order.order_side() == OrderSide::Sell && !self.core.is_bid_initialized)
835        {
836            self.generate_order_rejected(
837                order,
838                format!("No market for {}", order.instrument_id()).into(),
839            );
840            return;
841        }
842
843        self.fill_market_order(order);
844    }
845
846    fn process_limit_order(&mut self, order: &mut OrderAny) {
847        let limit_px = order.price().expect("Limit order must have a price");
848        if order.is_post_only()
849            && self
850                .core
851                .is_limit_matched(order.order_side_specified(), limit_px)
852        {
853            self.generate_order_rejected(
854                order,
855                format!(
856                    "POST_ONLY {} {} order limit px of {} would have been a TAKER: bid={}, ask={}",
857                    order.order_type(),
858                    order.order_side(),
859                    order.price().unwrap(),
860                    self.core
861                        .bid
862                        .map_or_else(|| "None".to_string(), |p| p.to_string()),
863                    self.core
864                        .ask
865                        .map_or_else(|| "None".to_string(), |p| p.to_string())
866                )
867                .into(),
868            );
869            return;
870        }
871
872        // Order is valid and accepted
873        self.accept_order(order);
874
875        // Check for immediate fill
876        if self
877            .core
878            .is_limit_matched(order.order_side_specified(), limit_px)
879        {
880            // Filling as liquidity taker
881            if order.liquidity_side().is_some()
882                && order.liquidity_side().unwrap() == LiquiditySide::NoLiquiditySide
883            {
884                order.set_liquidity_side(LiquiditySide::Taker);
885            }
886            self.fill_limit_order(order);
887        } else if matches!(order.time_in_force(), TimeInForce::Fok | TimeInForce::Ioc) {
888            self.cancel_order(order, None);
889        }
890    }
891
892    fn process_market_to_limit_order(&mut self, order: &mut OrderAny) {
893        // Check that market exists
894        if (order.order_side() == OrderSide::Buy && !self.core.is_ask_initialized)
895            || (order.order_side() == OrderSide::Sell && !self.core.is_bid_initialized)
896        {
897            self.generate_order_rejected(
898                order,
899                format!("No market for {}", order.instrument_id()).into(),
900            );
901            return;
902        }
903
904        // Immediately fill marketable order
905        self.fill_market_order(order);
906
907        if order.is_open() {
908            self.accept_order(order);
909        }
910    }
911
912    fn process_stop_market_order(&mut self, order: &mut OrderAny) {
913        let stop_px = order
914            .trigger_price()
915            .expect("Stop order must have a trigger price");
916        if self
917            .core
918            .is_stop_matched(order.order_side_specified(), stop_px)
919        {
920            if self.config.reject_stop_orders {
921                self.generate_order_rejected(
922                    order,
923                    format!(
924                        "{} {} order stop px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
925                        order.order_type(),
926                        order.order_side(),
927                        order.trigger_price().unwrap(),
928                        self.core
929                            .bid
930                            .map_or_else(|| "None".to_string(), |p| p.to_string()),
931                        self.core
932                            .ask
933                            .map_or_else(|| "None".to_string(), |p| p.to_string())
934                    ).into(),
935                );
936                return;
937            }
938            self.fill_market_order(order);
939            return;
940        }
941
942        // order is not matched but is valid and we accept it
943        self.accept_order(order);
944    }
945
946    fn process_stop_limit_order(&mut self, order: &mut OrderAny) {
947        let stop_px = order
948            .trigger_price()
949            .expect("Stop order must have a trigger price");
950        if self
951            .core
952            .is_stop_matched(order.order_side_specified(), stop_px)
953        {
954            if self.config.reject_stop_orders {
955                self.generate_order_rejected(
956                    order,
957                    format!(
958                        "{} {} order stop px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
959                        order.order_type(),
960                        order.order_side(),
961                        order.trigger_price().unwrap(),
962                        self.core
963                            .bid
964                            .map_or_else(|| "None".to_string(), |p| p.to_string()),
965                        self.core
966                            .ask
967                            .map_or_else(|| "None".to_string(), |p| p.to_string())
968                    ).into(),
969                );
970                return;
971            }
972
973            self.accept_order(order);
974            self.generate_order_triggered(order);
975
976            // Check for immediate fill
977            let limit_px = order.price().expect("Stop limit order must have a price");
978            if self
979                .core
980                .is_limit_matched(order.order_side_specified(), limit_px)
981            {
982                order.set_liquidity_side(LiquiditySide::Taker);
983                self.fill_limit_order(order);
984            }
985        }
986
987        // order is not matched but is valid and we accept it
988        self.accept_order(order);
989    }
990
991    fn process_market_if_touched_order(&mut self, order: &mut OrderAny) {
992        if self
993            .core
994            .is_touch_triggered(order.order_side_specified(), order.trigger_price().unwrap())
995        {
996            if self.config.reject_stop_orders {
997                self.generate_order_rejected(
998                    order,
999                    format!(
1000                        "{} {} order trigger px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
1001                        order.order_type(),
1002                        order.order_side(),
1003                        order.trigger_price().unwrap(),
1004                        self.core
1005                            .bid
1006                            .map_or_else(|| "None".to_string(), |p| p.to_string()),
1007                        self.core
1008                            .ask
1009                            .map_or_else(|| "None".to_string(), |p| p.to_string())
1010                    ).into(),
1011                );
1012                return;
1013            }
1014            self.fill_market_order(order);
1015            return;
1016        }
1017
1018        // Order is valid and accepted
1019        self.accept_order(order);
1020    }
1021
1022    fn process_limit_if_touched_order(&mut self, order: &mut OrderAny) {
1023        if self
1024            .core
1025            .is_touch_triggered(order.order_side_specified(), order.trigger_price().unwrap())
1026        {
1027            if self.config.reject_stop_orders {
1028                self.generate_order_rejected(
1029                    order,
1030                    format!(
1031                        "{} {} order trigger px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
1032                        order.order_type(),
1033                        order.order_side(),
1034                        order.trigger_price().unwrap(),
1035                        self.core
1036                            .bid
1037                            .map_or_else(|| "None".to_string(), |p| p.to_string()),
1038                        self.core
1039                            .ask
1040                            .map_or_else(|| "None".to_string(), |p| p.to_string())
1041                    ).into(),
1042                );
1043                return;
1044            }
1045            self.accept_order(order);
1046            self.generate_order_triggered(order);
1047
1048            // Check if immediate marketable
1049            if self
1050                .core
1051                .is_limit_matched(order.order_side_specified(), order.price().unwrap())
1052            {
1053                order.set_liquidity_side(LiquiditySide::Taker);
1054                self.fill_limit_order(order);
1055            }
1056            return;
1057        }
1058
1059        // Order is valid and accepted
1060        self.accept_order(order);
1061    }
1062
1063    fn process_trailing_stop_order(&mut self, order: &mut OrderAny) {
1064        if let Some(trigger_price) = order.trigger_price()
1065            && self
1066                .core
1067                .is_stop_matched(order.order_side_specified(), trigger_price)
1068        {
1069            self.generate_order_rejected(
1070                    order,
1071                    format!(
1072                        "{} {} order trigger px of {} was in the market: bid={}, ask={}, but rejected because of configuration",
1073                        order.order_type(),
1074                        order.order_side(),
1075                        trigger_price,
1076                        self.core
1077                            .bid
1078                            .map_or_else(|| "None".to_string(), |p| p.to_string()),
1079                        self.core
1080                            .ask
1081                            .map_or_else(|| "None".to_string(), |p| p.to_string())
1082                    ).into(),
1083                );
1084            return;
1085        }
1086
1087        // Order is valid and accepted
1088        self.accept_order(order);
1089    }
1090
1091    // -- ORDER PROCESSING ----------------------------------------------------
1092
1093    /// Iterate the matching engine by processing the bid and ask order sides
1094    /// and advancing time up to the given UNIX `timestamp_ns`.
1095    ///
1096    /// # Panics
1097    ///
1098    /// Panics if the best bid or ask price is unavailable when iterating.
1099    pub fn iterate(&mut self, timestamp_ns: UnixNanos) {
1100        // TODO implement correct clock fixed time setting self.clock.set_time(ts_now);
1101
1102        // Check for updates in orderbook and set bid and ask in order matching core and iterate
1103        if self.book.has_bid() {
1104            self.core.set_bid_raw(self.book.best_bid_price().unwrap());
1105        }
1106        if self.book.has_ask() {
1107            self.core.set_ask_raw(self.book.best_ask_price().unwrap());
1108        }
1109        self.core.iterate();
1110
1111        self.core.bid = self.book.best_bid_price();
1112        self.core.ask = self.book.best_ask_price();
1113
1114        let orders_bid = self.core.get_orders_bid().to_vec();
1115        let orders_ask = self.core.get_orders_ask().to_vec();
1116
1117        self.iterate_orders(timestamp_ns, &orders_bid);
1118        self.iterate_orders(timestamp_ns, &orders_ask);
1119    }
1120
1121    fn maybe_activate_trailing_stop(
1122        &mut self,
1123        order: &mut OrderAny,
1124        bid: Option<Price>,
1125        ask: Option<Price>,
1126    ) -> bool {
1127        match order {
1128            OrderAny::TrailingStopMarket(inner) => {
1129                if inner.is_activated {
1130                    return true;
1131                }
1132
1133                if inner.activation_price.is_none() {
1134                    let px = match inner.order_side() {
1135                        OrderSide::Buy => ask,
1136                        OrderSide::Sell => bid,
1137                        _ => None,
1138                    };
1139                    if let Some(p) = px {
1140                        inner.activation_price = Some(p);
1141                        inner.set_activated();
1142                        if let Err(e) = self.cache.borrow_mut().update_order(order) {
1143                            log::error!("Failed to update order: {e}");
1144                        }
1145                        return true;
1146                    }
1147                    return false;
1148                }
1149
1150                let activation_price = inner.activation_price.unwrap();
1151                let hit = match inner.order_side() {
1152                    OrderSide::Buy => ask.is_some_and(|a| a <= activation_price),
1153                    OrderSide::Sell => bid.is_some_and(|b| b >= activation_price),
1154                    _ => false,
1155                };
1156                if hit {
1157                    inner.set_activated();
1158                    if let Err(e) = self.cache.borrow_mut().update_order(order) {
1159                        log::error!("Failed to update order: {e}");
1160                    }
1161                }
1162                hit
1163            }
1164            OrderAny::TrailingStopLimit(inner) => {
1165                if inner.is_activated {
1166                    return true;
1167                }
1168
1169                if inner.activation_price.is_none() {
1170                    let px = match inner.order_side() {
1171                        OrderSide::Buy => ask,
1172                        OrderSide::Sell => bid,
1173                        _ => None,
1174                    };
1175                    if let Some(p) = px {
1176                        inner.activation_price = Some(p);
1177                        inner.set_activated();
1178                        if let Err(e) = self.cache.borrow_mut().update_order(order) {
1179                            log::error!("Failed to update order: {e}");
1180                        }
1181                        return true;
1182                    }
1183                    return false;
1184                }
1185
1186                let activation_price = inner.activation_price.unwrap();
1187                let hit = match inner.order_side() {
1188                    OrderSide::Buy => ask.is_some_and(|a| a <= activation_price),
1189                    OrderSide::Sell => bid.is_some_and(|b| b >= activation_price),
1190                    _ => false,
1191                };
1192                if hit {
1193                    inner.set_activated();
1194                    if let Err(e) = self.cache.borrow_mut().update_order(order) {
1195                        log::error!("Failed to update order: {e}");
1196                    }
1197                }
1198                hit
1199            }
1200            _ => true,
1201        }
1202    }
1203
1204    fn iterate_orders(&mut self, timestamp_ns: UnixNanos, orders: &[PassiveOrderAny]) {
1205        for order in orders {
1206            if order.is_closed() {
1207                continue;
1208            }
1209
1210            if self.config.support_gtd_orders
1211                && order
1212                    .expire_time()
1213                    .is_some_and(|expire_timestamp_ns| timestamp_ns >= expire_timestamp_ns)
1214            {
1215                let _ = self.core.delete_order(order);
1216                self.cached_filled_qty.remove(&order.client_order_id());
1217                self.expire_order(order);
1218                continue;
1219            }
1220
1221            if matches!(
1222                order,
1223                PassiveOrderAny::Stop(
1224                    StopOrderAny::TrailingStopMarket(_) | StopOrderAny::TrailingStopLimit(_)
1225                )
1226            ) {
1227                let mut any = OrderAny::from(order.clone());
1228
1229                if !self.maybe_activate_trailing_stop(&mut any, self.core.bid, self.core.ask) {
1230                    continue;
1231                }
1232
1233                self.update_trailing_stop_order(&mut any);
1234            }
1235
1236            // Move market back to targets
1237            if let Some(target_bid) = self.target_bid {
1238                self.core.bid = Some(target_bid);
1239                self.target_bid = None;
1240            }
1241            if let Some(target_bid) = self.target_bid.take() {
1242                self.core.bid = Some(target_bid);
1243                self.target_bid = None;
1244            }
1245            if let Some(target_ask) = self.target_ask.take() {
1246                self.core.ask = Some(target_ask);
1247                self.target_ask = None;
1248            }
1249            if let Some(target_last) = self.target_last.take() {
1250                self.core.last = Some(target_last);
1251                self.target_last = None;
1252            }
1253        }
1254
1255        // Reset any targets after iteration
1256        self.target_bid = None;
1257        self.target_ask = None;
1258        self.target_last = None;
1259    }
1260
1261    fn determine_limit_price_and_volume(&mut self, order: &OrderAny) -> Vec<(Price, Quantity)> {
1262        match order.price() {
1263            Some(order_price) => {
1264                // construct book order with price as passive with limit order price
1265                let book_order =
1266                    BookOrder::new(order.order_side(), order_price, order.quantity(), 1);
1267
1268                let mut fills = self.book.simulate_fills(&book_order);
1269
1270                // return immediately if no fills
1271                if fills.is_empty() {
1272                    return fills;
1273                }
1274
1275                // check if trigger price exists
1276                if let Some(triggered_price) = order.trigger_price() {
1277                    // Filling as TAKER from trigger
1278                    if order
1279                        .liquidity_side()
1280                        .is_some_and(|liquidity_side| liquidity_side == LiquiditySide::Taker)
1281                    {
1282                        if order.order_side() == OrderSide::Sell && order_price > triggered_price {
1283                            // manually change the fills index 0
1284                            let first_fill = fills.first().unwrap();
1285                            let triggered_qty = first_fill.1;
1286                            fills[0] = (triggered_price, triggered_qty);
1287                            self.target_bid = self.core.bid;
1288                            self.target_ask = self.core.ask;
1289                            self.target_last = self.core.last;
1290                            self.core.set_ask_raw(order_price);
1291                            self.core.set_last_raw(order_price);
1292                        } else if order.order_side() == OrderSide::Buy
1293                            && order_price < triggered_price
1294                        {
1295                            // manually change the fills index 0
1296                            let first_fill = fills.first().unwrap();
1297                            let triggered_qty = first_fill.1;
1298                            fills[0] = (triggered_price, triggered_qty);
1299                            self.target_bid = self.core.bid;
1300                            self.target_ask = self.core.ask;
1301                            self.target_last = self.core.last;
1302                            self.core.set_bid_raw(order_price);
1303                            self.core.set_last_raw(order_price);
1304                        }
1305                    }
1306                }
1307
1308                // Filling as MAKER from trigger
1309                if order
1310                    .liquidity_side()
1311                    .is_some_and(|liquidity_side| liquidity_side == LiquiditySide::Maker)
1312                {
1313                    match order.order_side().as_specified() {
1314                        OrderSideSpecified::Buy => {
1315                            let target_price = if order
1316                                .trigger_price()
1317                                .is_some_and(|trigger_price| order_price > trigger_price)
1318                            {
1319                                order.trigger_price().unwrap()
1320                            } else {
1321                                order_price
1322                            };
1323                            for fill in &fills {
1324                                let last_px = fill.0;
1325                                if last_px < order_price {
1326                                    // Marketable SELL would have filled at limit
1327                                    self.target_bid = self.core.bid;
1328                                    self.target_ask = self.core.ask;
1329                                    self.target_last = self.core.last;
1330                                    self.core.set_ask_raw(target_price);
1331                                    self.core.set_last_raw(target_price);
1332                                }
1333                            }
1334                        }
1335                        OrderSideSpecified::Sell => {
1336                            let target_price = if order
1337                                .trigger_price()
1338                                .is_some_and(|trigger_price| order_price < trigger_price)
1339                            {
1340                                order.trigger_price().unwrap()
1341                            } else {
1342                                order_price
1343                            };
1344                            for fill in &fills {
1345                                let last_px = fill.0;
1346                                if last_px > order_price {
1347                                    // Marketable BUY would have filled at limit
1348                                    self.target_bid = self.core.bid;
1349                                    self.target_ask = self.core.ask;
1350                                    self.target_last = self.core.last;
1351                                    self.core.set_bid_raw(target_price);
1352                                    self.core.set_last_raw(target_price);
1353                                }
1354                            }
1355                        }
1356                    }
1357                }
1358
1359                fills
1360            }
1361            None => panic!("Limit order must have a price"),
1362        }
1363    }
1364
1365    fn determine_market_price_and_volume(&self, order: &OrderAny) -> Vec<(Price, Quantity)> {
1366        // construct price
1367        let price = match order.order_side().as_specified() {
1368            OrderSideSpecified::Buy => Price::max(FIXED_PRECISION),
1369            OrderSideSpecified::Sell => Price::min(FIXED_PRECISION),
1370        };
1371
1372        // Construct BookOrder from order
1373        let book_order = BookOrder::new(order.order_side(), price, order.quantity(), 0);
1374        self.book.simulate_fills(&book_order)
1375    }
1376
1377    pub fn fill_market_order(&mut self, order: &mut OrderAny) {
1378        if let Some(filled_qty) = self.cached_filled_qty.get(&order.client_order_id())
1379            && filled_qty >= &order.quantity()
1380        {
1381            log::info!(
1382                "Ignoring fill as already filled pending application of events: {:?}, {:?}, {:?}, {:?}",
1383                filled_qty,
1384                order.quantity(),
1385                order.filled_qty(),
1386                order.quantity()
1387            );
1388            return;
1389        }
1390
1391        let venue_position_id = self.ids_generator.get_position_id(order, Some(true));
1392        let position: Option<Position> = if let Some(venue_position_id) = venue_position_id {
1393            let cache = self.cache.as_ref().borrow();
1394            cache.position(&venue_position_id).cloned()
1395        } else {
1396            None
1397        };
1398
1399        if self.config.use_reduce_only && order.is_reduce_only() && position.is_none() {
1400            log::warn!(
1401                "Canceling REDUCE_ONLY {} as would increase position",
1402                order.order_type()
1403            );
1404            self.cancel_order(order, None);
1405            return;
1406        }
1407        // set order side as taker
1408        order.set_liquidity_side(LiquiditySide::Taker);
1409        let fills = self.determine_market_price_and_volume(order);
1410        self.apply_fills(order, fills, LiquiditySide::Taker, None, position);
1411    }
1412
1413    /// # Panics
1414    ///
1415    /// Panics if the order has no price, or if fill price or quantity precision mismatches occur.
1416    pub fn fill_limit_order(&mut self, order: &mut OrderAny) {
1417        match order.price() {
1418            Some(order_price) => {
1419                let cached_filled_qty = self.cached_filled_qty.get(&order.client_order_id());
1420                if cached_filled_qty.is_some() && *cached_filled_qty.unwrap() >= order.quantity() {
1421                    log::debug!(
1422                        "Ignoring fill as already filled pending pending application of events: {}, {}, {}, {}",
1423                        cached_filled_qty.unwrap(),
1424                        order.quantity(),
1425                        order.filled_qty(),
1426                        order.leaves_qty(),
1427                    );
1428                    return;
1429                }
1430
1431                if order
1432                    .liquidity_side()
1433                    .is_some_and(|liquidity_side| liquidity_side == LiquiditySide::Maker)
1434                {
1435                    if order.order_side() == OrderSide::Buy
1436                        && self.core.bid.is_some_and(|bid| bid == order_price)
1437                        && !self.fill_model.is_limit_filled()
1438                    {
1439                        // no filled
1440                        return;
1441                    }
1442                    if order.order_side() == OrderSide::Sell
1443                        && self.core.ask.is_some_and(|ask| ask == order_price)
1444                        && !self.fill_model.is_limit_filled()
1445                    {
1446                        // no filled
1447                        return;
1448                    }
1449                }
1450
1451                let venue_position_id = self.ids_generator.get_position_id(order, None);
1452                let position = if let Some(venue_position_id) = venue_position_id {
1453                    let cache = self.cache.as_ref().borrow();
1454                    cache.position(&venue_position_id).cloned()
1455                } else {
1456                    None
1457                };
1458
1459                if self.config.use_reduce_only && order.is_reduce_only() && position.is_none() {
1460                    log::warn!(
1461                        "Canceling REDUCE_ONLY {} as would increase position",
1462                        order.order_type()
1463                    );
1464                    self.cancel_order(order, None);
1465                    return;
1466                }
1467
1468                let fills = self.determine_limit_price_and_volume(order);
1469
1470                self.apply_fills(
1471                    order,
1472                    fills,
1473                    order.liquidity_side().unwrap(),
1474                    venue_position_id,
1475                    position,
1476                );
1477            }
1478            None => panic!("Limit order must have a price"),
1479        }
1480    }
1481
1482    fn apply_fills(
1483        &mut self,
1484        order: &mut OrderAny,
1485        fills: Vec<(Price, Quantity)>,
1486        liquidity_side: LiquiditySide,
1487        venue_position_id: Option<PositionId>,
1488        position: Option<Position>,
1489    ) {
1490        if order.time_in_force() == TimeInForce::Fok {
1491            let mut total_size = Quantity::zero(order.quantity().precision);
1492            for (fill_px, fill_qty) in &fills {
1493                total_size = total_size.add(*fill_qty);
1494            }
1495
1496            if order.leaves_qty() > total_size {
1497                self.cancel_order(order, None);
1498                return;
1499            }
1500        }
1501
1502        if fills.is_empty() {
1503            if order.status() == OrderStatus::Submitted {
1504                self.generate_order_rejected(
1505                    order,
1506                    format!("No market for {}", order.instrument_id()).into(),
1507                );
1508            } else {
1509                log::error!(
1510                    "Cannot fill order: no fills from book when fills were expected (check size in data)"
1511                );
1512                return;
1513            }
1514        }
1515
1516        if self.oms_type == OmsType::Netting {
1517            let venue_position_id: Option<PositionId> = None;
1518        }
1519
1520        let mut initial_market_to_limit_fill = false;
1521        for &(mut fill_px, ref fill_qty) in &fills {
1522            // Validate price precision
1523            assert!(
1524                (fill_px.precision == self.instrument.price_precision()),
1525                "Invalid price precision for fill price {} when instrument price precision is {}.\
1526                     Check that the data price precision matches the {} instrument",
1527                fill_px.precision,
1528                self.instrument.price_precision(),
1529                self.instrument.id()
1530            );
1531
1532            // Validate quantity precision
1533            assert!(
1534                (fill_qty.precision == self.instrument.size_precision()),
1535                "Invalid quantity precision for fill quantity {} when instrument size precision is {}.\
1536                     Check that the data quantity precision matches the {} instrument",
1537                fill_qty.precision,
1538                self.instrument.size_precision(),
1539                self.instrument.id()
1540            );
1541
1542            if order.filled_qty() == Quantity::zero(order.filled_qty().precision)
1543                && order.order_type() == OrderType::MarketToLimit
1544            {
1545                self.generate_order_updated(order, order.quantity(), Some(fill_px), None);
1546                initial_market_to_limit_fill = true;
1547            }
1548
1549            if self.book_type == BookType::L1_MBP && self.fill_model.is_slipped() {
1550                fill_px = match order.order_side().as_specified() {
1551                    OrderSideSpecified::Buy => fill_px.add(self.instrument.price_increment()),
1552                    OrderSideSpecified::Sell => fill_px.sub(self.instrument.price_increment()),
1553                }
1554            }
1555
1556            // Check reduce only order
1557            // If the incoming simulated fill would exceed the position when reduce-only is honored,
1558            // clamp the effective fill size to the adjusted (remaining position) quantity.
1559            let mut effective_fill_qty = *fill_qty;
1560
1561            if self.config.use_reduce_only
1562                && order.is_reduce_only()
1563                && let Some(position) = &position
1564                && *fill_qty > position.quantity
1565            {
1566                if position.quantity == Quantity::zero(position.quantity.precision) {
1567                    // Done
1568                    return;
1569                }
1570
1571                // Adjusted target quantity equals the remaining position size
1572                let adjusted_fill_qty =
1573                    Quantity::from_raw(position.quantity.raw, fill_qty.precision);
1574
1575                // Determine the effective fill size for this iteration first
1576                effective_fill_qty = std::cmp::min(effective_fill_qty, adjusted_fill_qty);
1577
1578                // Only emit an update if the order quantity actually changes
1579                if order.quantity() != adjusted_fill_qty {
1580                    self.generate_order_updated(order, adjusted_fill_qty, None, None);
1581                }
1582            }
1583
1584            if fill_qty.is_zero() {
1585                if fills.len() == 1 && order.status() == OrderStatus::Submitted {
1586                    self.generate_order_rejected(
1587                        order,
1588                        format!("No market for {}", order.instrument_id()).into(),
1589                    );
1590                }
1591                return;
1592            }
1593
1594            self.fill_order(
1595                order,
1596                fill_px,
1597                effective_fill_qty,
1598                liquidity_side,
1599                venue_position_id,
1600                position.clone(),
1601            );
1602
1603            if order.order_type() == OrderType::MarketToLimit && initial_market_to_limit_fill {
1604                // filled initial level
1605                return;
1606            }
1607        }
1608
1609        if order.time_in_force() == TimeInForce::Ioc && order.is_open() {
1610            // IOC order has filled all available size
1611            self.cancel_order(order, None);
1612            return;
1613        }
1614
1615        if order.is_open()
1616            && self.book_type == BookType::L1_MBP
1617            && matches!(
1618                order.order_type(),
1619                OrderType::Market
1620                    | OrderType::MarketIfTouched
1621                    | OrderType::StopMarket
1622                    | OrderType::TrailingStopMarket
1623            )
1624        {
1625            // Exhausted simulated book volume (continue aggressive filling into next level)
1626            // This is a very basic implementation of slipping by a single tick, in the future
1627            // we will implement more detailed fill modeling.
1628            todo!("Exhausted simulated book volume")
1629        }
1630    }
1631
1632    fn fill_order(
1633        &mut self,
1634        order: &mut OrderAny,
1635        last_px: Price,
1636        last_qty: Quantity,
1637        liquidity_side: LiquiditySide,
1638        venue_position_id: Option<PositionId>,
1639        position: Option<Position>,
1640    ) {
1641        match self.cached_filled_qty.get(&order.client_order_id()) {
1642            Some(filled_qty) => {
1643                let leaves_qty = order.quantity() - *filled_qty;
1644                let last_qty = min(last_qty, leaves_qty);
1645                let new_filled_qty = *filled_qty + last_qty;
1646                // update cached filled qty
1647                self.cached_filled_qty
1648                    .insert(order.client_order_id(), new_filled_qty);
1649            }
1650            None => {
1651                self.cached_filled_qty
1652                    .insert(order.client_order_id(), last_qty);
1653            }
1654        }
1655
1656        // calculate commission
1657        let commission = self
1658            .fee_model
1659            .get_commission(order, last_qty, last_px, &self.instrument)
1660            .unwrap();
1661
1662        let venue_order_id = self.ids_generator.get_venue_order_id(order).unwrap();
1663        self.generate_order_filled(
1664            order,
1665            venue_order_id,
1666            venue_position_id,
1667            last_qty,
1668            last_px,
1669            self.instrument.quote_currency(),
1670            commission,
1671            liquidity_side,
1672        );
1673
1674        if order.is_passive() && order.is_closed() {
1675            // Check if order exists in OrderMatching core, and delete it if it does
1676            if self.core.order_exists(order.client_order_id()) {
1677                let _ = self.core.delete_order(
1678                    &PassiveOrderAny::try_from(order.clone()).expect("passive order conversion"),
1679                );
1680            }
1681            self.cached_filled_qty.remove(&order.client_order_id());
1682        }
1683
1684        if !self.config.support_contingent_orders {
1685            return;
1686        }
1687
1688        if let Some(contingency_type) = order.contingency_type() {
1689            match contingency_type {
1690                ContingencyType::Oto => {
1691                    if let Some(linked_orders_ids) = order.linked_order_ids() {
1692                        for client_order_id in linked_orders_ids {
1693                            let mut child_order = match self.cache.borrow().order(client_order_id) {
1694                                Some(child_order) => child_order.clone(),
1695                                None => panic!("Order {client_order_id} not found in cache"),
1696                            };
1697
1698                            if child_order.is_closed() || child_order.is_active_local() {
1699                                continue;
1700                            }
1701
1702                            // Check if we need to index position id
1703                            if let (None, Some(position_id)) =
1704                                (child_order.position_id(), order.position_id())
1705                            {
1706                                self.cache
1707                                    .borrow_mut()
1708                                    .add_position_id(
1709                                        &position_id,
1710                                        &self.venue,
1711                                        client_order_id,
1712                                        &child_order.strategy_id(),
1713                                    )
1714                                    .unwrap();
1715                                log::debug!(
1716                                    "Added position id {position_id} to cache for order {client_order_id}"
1717                                );
1718                            }
1719
1720                            if (!child_order.is_open())
1721                                || (matches!(child_order.status(), OrderStatus::PendingUpdate)
1722                                    && child_order
1723                                        .previous_status()
1724                                        .is_some_and(|s| matches!(s, OrderStatus::Submitted)))
1725                            {
1726                                let account_id = order.account_id().unwrap_or_else(|| {
1727                                    *self.account_ids.get(&order.trader_id()).unwrap_or_else(|| {
1728                                        panic!(
1729                                            "Account ID not found for trader {}",
1730                                            order.trader_id()
1731                                        )
1732                                    })
1733                                });
1734                                self.process_order(&mut child_order, account_id);
1735                            }
1736                        }
1737                    } else {
1738                        log::error!(
1739                            "OTO order {} does not have linked orders",
1740                            order.client_order_id()
1741                        );
1742                    }
1743                }
1744                ContingencyType::Oco => {
1745                    if let Some(linked_orders_ids) = order.linked_order_ids() {
1746                        for client_order_id in linked_orders_ids {
1747                            let 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_closed() || child_order.is_active_local() {
1753                                continue;
1754                            }
1755
1756                            self.cancel_order(&child_order, None);
1757                        }
1758                    } else {
1759                        log::error!(
1760                            "OCO order {} does not have linked orders",
1761                            order.client_order_id()
1762                        );
1763                    }
1764                }
1765                ContingencyType::Ouo => {
1766                    if let Some(linked_orders_ids) = order.linked_order_ids() {
1767                        for client_order_id in linked_orders_ids {
1768                            let mut child_order = match self.cache.borrow().order(client_order_id) {
1769                                Some(child_order) => child_order.clone(),
1770                                None => panic!("Order {client_order_id} not found in cache"),
1771                            };
1772
1773                            if child_order.is_active_local() {
1774                                continue;
1775                            }
1776
1777                            if order.is_closed() && child_order.is_open() {
1778                                self.cancel_order(&child_order, None);
1779                            } else if !order.leaves_qty().is_zero()
1780                                && order.leaves_qty() != child_order.leaves_qty()
1781                            {
1782                                let price = child_order.price();
1783                                let trigger_price = child_order.trigger_price();
1784                                self.update_order(
1785                                    &mut child_order,
1786                                    Some(order.leaves_qty()),
1787                                    price,
1788                                    trigger_price,
1789                                    Some(false),
1790                                );
1791                            }
1792                        }
1793                    } else {
1794                        log::error!(
1795                            "OUO order {} does not have linked orders",
1796                            order.client_order_id()
1797                        );
1798                    }
1799                }
1800                _ => {}
1801            }
1802        }
1803    }
1804
1805    fn update_limit_order(&mut self, order: &mut OrderAny, quantity: Quantity, price: Price) {
1806        if self
1807            .core
1808            .is_limit_matched(order.order_side_specified(), price)
1809        {
1810            if order.is_post_only() {
1811                self.generate_order_modify_rejected(
1812                    order.trader_id(),
1813                    order.strategy_id(),
1814                    order.instrument_id(),
1815                    order.client_order_id(),
1816                    Ustr::from(format!(
1817                        "POST_ONLY {} {} order with new limit px of {} would have been a TAKER: bid={}, ask={}",
1818                        order.order_type(),
1819                        order.order_side(),
1820                        price,
1821                        self.core.bid.map_or_else(|| "None".to_string(), |p| p.to_string()),
1822                        self.core.ask.map_or_else(|| "None".to_string(), |p| p.to_string())
1823                    ).as_str()),
1824                    order.venue_order_id(),
1825                    order.account_id(),
1826                );
1827                return;
1828            }
1829
1830            self.generate_order_updated(order, quantity, Some(price), None);
1831            order.set_liquidity_side(LiquiditySide::Taker);
1832            self.fill_limit_order(order);
1833            return;
1834        }
1835        self.generate_order_updated(order, quantity, Some(price), None);
1836    }
1837
1838    fn update_stop_market_order(
1839        &mut self,
1840        order: &mut OrderAny,
1841        quantity: Quantity,
1842        trigger_price: Price,
1843    ) {
1844        if self
1845            .core
1846            .is_stop_matched(order.order_side_specified(), trigger_price)
1847        {
1848            self.generate_order_modify_rejected(
1849                order.trader_id(),
1850                order.strategy_id(),
1851                order.instrument_id(),
1852                order.client_order_id(),
1853                Ustr::from(
1854                    format!(
1855                        "{} {} order new stop px of {} was in the market: bid={}, ask={}",
1856                        order.order_type(),
1857                        order.order_side(),
1858                        trigger_price,
1859                        self.core
1860                            .bid
1861                            .map_or_else(|| "None".to_string(), |p| p.to_string()),
1862                        self.core
1863                            .ask
1864                            .map_or_else(|| "None".to_string(), |p| p.to_string())
1865                    )
1866                    .as_str(),
1867                ),
1868                order.venue_order_id(),
1869                order.account_id(),
1870            );
1871            return;
1872        }
1873
1874        self.generate_order_updated(order, quantity, None, Some(trigger_price));
1875    }
1876
1877    fn update_stop_limit_order(
1878        &mut self,
1879        order: &mut OrderAny,
1880        quantity: Quantity,
1881        price: Price,
1882        trigger_price: Price,
1883    ) {
1884        if order.is_triggered().is_some_and(|t| t) {
1885            // Update limit price
1886            if self
1887                .core
1888                .is_limit_matched(order.order_side_specified(), price)
1889            {
1890                if order.is_post_only() {
1891                    self.generate_order_modify_rejected(
1892                        order.trader_id(),
1893                        order.strategy_id(),
1894                        order.instrument_id(),
1895                        order.client_order_id(),
1896                        Ustr::from(format!(
1897                            "POST_ONLY {} {} order with new limit px of {} would have been a TAKER: bid={}, ask={}",
1898                            order.order_type(),
1899                            order.order_side(),
1900                            price,
1901                            self.core.bid.map_or_else(|| "None".to_string(), |p| p.to_string()),
1902                            self.core.ask.map_or_else(|| "None".to_string(), |p| p.to_string())
1903                        ).as_str()),
1904                        order.venue_order_id(),
1905                        order.account_id(),
1906                    );
1907                    return;
1908                }
1909                self.generate_order_updated(order, quantity, Some(price), None);
1910                order.set_liquidity_side(LiquiditySide::Taker);
1911                self.fill_limit_order(order);
1912                return; // Filled
1913            }
1914        } else {
1915            // Update stop price
1916            if self
1917                .core
1918                .is_stop_matched(order.order_side_specified(), trigger_price)
1919            {
1920                self.generate_order_modify_rejected(
1921                    order.trader_id(),
1922                    order.strategy_id(),
1923                    order.instrument_id(),
1924                    order.client_order_id(),
1925                    Ustr::from(
1926                        format!(
1927                            "{} {} order new stop px of {} was in the market: bid={}, ask={}",
1928                            order.order_type(),
1929                            order.order_side(),
1930                            trigger_price,
1931                            self.core
1932                                .bid
1933                                .map_or_else(|| "None".to_string(), |p| p.to_string()),
1934                            self.core
1935                                .ask
1936                                .map_or_else(|| "None".to_string(), |p| p.to_string())
1937                        )
1938                        .as_str(),
1939                    ),
1940                    order.venue_order_id(),
1941                    order.account_id(),
1942                );
1943                return;
1944            }
1945        }
1946
1947        self.generate_order_updated(order, quantity, Some(price), Some(trigger_price));
1948    }
1949
1950    fn update_market_if_touched_order(
1951        &mut self,
1952        order: &mut OrderAny,
1953        quantity: Quantity,
1954        trigger_price: Price,
1955    ) {
1956        if self
1957            .core
1958            .is_touch_triggered(order.order_side_specified(), trigger_price)
1959        {
1960            self.generate_order_modify_rejected(
1961                order.trader_id(),
1962                order.strategy_id(),
1963                order.instrument_id(),
1964                order.client_order_id(),
1965                Ustr::from(
1966                    format!(
1967                        "{} {} order new trigger px of {} was in the market: bid={}, ask={}",
1968                        order.order_type(),
1969                        order.order_side(),
1970                        trigger_price,
1971                        self.core
1972                            .bid
1973                            .map_or_else(|| "None".to_string(), |p| p.to_string()),
1974                        self.core
1975                            .ask
1976                            .map_or_else(|| "None".to_string(), |p| p.to_string())
1977                    )
1978                    .as_str(),
1979                ),
1980                order.venue_order_id(),
1981                order.account_id(),
1982            );
1983            // Cannot update order
1984            return;
1985        }
1986
1987        self.generate_order_updated(order, quantity, None, Some(trigger_price));
1988    }
1989
1990    fn update_limit_if_touched_order(
1991        &mut self,
1992        order: &mut OrderAny,
1993        quantity: Quantity,
1994        price: Price,
1995        trigger_price: Price,
1996    ) {
1997        if order.is_triggered().is_some_and(|t| t) {
1998            // Update limit price
1999            if self
2000                .core
2001                .is_limit_matched(order.order_side_specified(), price)
2002            {
2003                if order.is_post_only() {
2004                    self.generate_order_modify_rejected(
2005                        order.trader_id(),
2006                        order.strategy_id(),
2007                        order.instrument_id(),
2008                        order.client_order_id(),
2009                        Ustr::from(format!(
2010                            "POST_ONLY {} {} order with new limit px of {} would have been a TAKER: bid={}, ask={}",
2011                            order.order_type(),
2012                            order.order_side(),
2013                            price,
2014                            self.core.bid.map_or_else(|| "None".to_string(), |p| p.to_string()),
2015                            self.core.ask.map_or_else(|| "None".to_string(), |p| p.to_string())
2016                        ).as_str()),
2017                        order.venue_order_id(),
2018                        order.account_id(),
2019                    );
2020                    // Cannot update order
2021                    return;
2022                }
2023                self.generate_order_updated(order, quantity, Some(price), None);
2024                order.set_liquidity_side(LiquiditySide::Taker);
2025                self.fill_limit_order(order);
2026                return;
2027            }
2028        } else {
2029            // Update trigger price
2030            if self
2031                .core
2032                .is_touch_triggered(order.order_side_specified(), trigger_price)
2033            {
2034                self.generate_order_modify_rejected(
2035                    order.trader_id(),
2036                    order.strategy_id(),
2037                    order.instrument_id(),
2038                    order.client_order_id(),
2039                    Ustr::from(
2040                        format!(
2041                            "{} {} order new trigger px of {} was in the market: bid={}, ask={}",
2042                            order.order_type(),
2043                            order.order_side(),
2044                            trigger_price,
2045                            self.core
2046                                .bid
2047                                .map_or_else(|| "None".to_string(), |p| p.to_string()),
2048                            self.core
2049                                .ask
2050                                .map_or_else(|| "None".to_string(), |p| p.to_string())
2051                        )
2052                        .as_str(),
2053                    ),
2054                    order.venue_order_id(),
2055                    order.account_id(),
2056                );
2057                return;
2058            }
2059        }
2060
2061        self.generate_order_updated(order, quantity, Some(price), Some(trigger_price));
2062    }
2063
2064    fn update_trailing_stop_order(&mut self, order: &mut OrderAny) {
2065        let (new_trigger_price, new_price) = trailing_stop_calculate(
2066            self.instrument.price_increment(),
2067            order.trigger_price(),
2068            order.activation_price(),
2069            order,
2070            self.core.bid,
2071            self.core.ask,
2072            self.core.last,
2073        )
2074        .unwrap();
2075
2076        if new_trigger_price.is_none() && new_price.is_none() {
2077            return;
2078        }
2079
2080        self.generate_order_updated(order, order.quantity(), new_price, new_trigger_price);
2081    }
2082
2083    // -- EVENT HANDLING -----------------------------------------------------
2084
2085    fn accept_order(&mut self, order: &mut OrderAny) {
2086        if order.is_closed() {
2087            // Temporary guard to prevent invalid processing
2088            return;
2089        }
2090        if order.status() != OrderStatus::Accepted {
2091            let venue_order_id = self.ids_generator.get_venue_order_id(order).unwrap();
2092            self.generate_order_accepted(order, venue_order_id);
2093
2094            if matches!(
2095                order.order_type(),
2096                OrderType::TrailingStopLimit | OrderType::TrailingStopMarket
2097            ) && order.trigger_price().is_none()
2098            {
2099                self.update_trailing_stop_order(order);
2100            }
2101        }
2102
2103        let _ = self.core.add_order(
2104            PassiveOrderAny::try_from(order.to_owned()).expect("passive order conversion"),
2105        );
2106    }
2107
2108    fn expire_order(&mut self, order: &PassiveOrderAny) {
2109        if self.config.support_contingent_orders
2110            && order
2111                .contingency_type()
2112                .is_some_and(|c| c != ContingencyType::NoContingency)
2113        {
2114            self.cancel_contingent_orders(&OrderAny::from(order.clone()));
2115        }
2116
2117        self.generate_order_expired(&order.to_any());
2118    }
2119
2120    fn cancel_order(&mut self, order: &OrderAny, cancel_contingencies: Option<bool>) {
2121        let cancel_contingencies = cancel_contingencies.unwrap_or(true);
2122        if order.is_active_local() {
2123            log::error!(
2124                "Cannot cancel an order with {} from the matching engine",
2125                order.status()
2126            );
2127            return;
2128        }
2129
2130        // Check if order exists in OrderMatching core, and delete it if it does
2131        if self.core.order_exists(order.client_order_id()) {
2132            let _ = self.core.delete_order(
2133                &PassiveOrderAny::try_from(order.clone()).expect("passive order conversion"),
2134            );
2135        }
2136        self.cached_filled_qty.remove(&order.client_order_id());
2137
2138        let venue_order_id = self.ids_generator.get_venue_order_id(order).unwrap();
2139        self.generate_order_canceled(order, venue_order_id);
2140
2141        if self.config.support_contingent_orders
2142            && order.contingency_type().is_some()
2143            && order.contingency_type().unwrap() != ContingencyType::NoContingency
2144            && cancel_contingencies
2145        {
2146            self.cancel_contingent_orders(order);
2147        }
2148    }
2149
2150    fn update_order(
2151        &mut self,
2152        order: &mut OrderAny,
2153        quantity: Option<Quantity>,
2154        price: Option<Price>,
2155        trigger_price: Option<Price>,
2156        update_contingencies: Option<bool>,
2157    ) {
2158        let update_contingencies = update_contingencies.unwrap_or(true);
2159        let quantity = quantity.unwrap_or(order.quantity());
2160
2161        match order {
2162            OrderAny::Limit(_) | OrderAny::MarketToLimit(_) => {
2163                let price = price.unwrap_or(order.price().unwrap());
2164                self.update_limit_order(order, quantity, price);
2165            }
2166            OrderAny::StopMarket(_) => {
2167                let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2168                self.update_stop_market_order(order, quantity, trigger_price);
2169            }
2170            OrderAny::StopLimit(_) => {
2171                let price = price.unwrap_or(order.price().unwrap());
2172                let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2173                self.update_stop_limit_order(order, quantity, price, trigger_price);
2174            }
2175            OrderAny::MarketIfTouched(_) => {
2176                let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2177                self.update_market_if_touched_order(order, quantity, trigger_price);
2178            }
2179            OrderAny::LimitIfTouched(_) => {
2180                let price = price.unwrap_or(order.price().unwrap());
2181                let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2182                self.update_limit_if_touched_order(order, quantity, price, trigger_price);
2183            }
2184            OrderAny::TrailingStopMarket(_) => {
2185                let trigger_price = trigger_price.unwrap_or(order.trigger_price().unwrap());
2186                self.update_market_if_touched_order(order, quantity, trigger_price);
2187            }
2188            OrderAny::TrailingStopLimit(trailing_stop_limit_order) => {
2189                let price = price.unwrap_or(trailing_stop_limit_order.price().unwrap());
2190                let trigger_price =
2191                    trigger_price.unwrap_or(trailing_stop_limit_order.trigger_price().unwrap());
2192                self.update_limit_if_touched_order(order, quantity, price, trigger_price);
2193            }
2194            _ => {
2195                panic!(
2196                    "Unsupported order type {} for update_order",
2197                    order.order_type()
2198                );
2199            }
2200        }
2201
2202        if self.config.support_contingent_orders
2203            && order
2204                .contingency_type()
2205                .is_some_and(|c| c != ContingencyType::NoContingency)
2206            && update_contingencies
2207        {
2208            self.update_contingent_order(order);
2209        }
2210    }
2211
2212    pub fn trigger_stop_order(&mut self, order: &mut OrderAny) {
2213        todo!("trigger_stop_order")
2214    }
2215
2216    fn update_contingent_order(&mut self, order: &OrderAny) {
2217        log::debug!("Updating OUO orders from {}", order.client_order_id());
2218        if let Some(linked_order_ids) = order.linked_order_ids() {
2219            for client_order_id in linked_order_ids {
2220                let mut child_order = match self.cache.borrow().order(client_order_id) {
2221                    Some(order) => order.clone(),
2222                    None => panic!("Order {client_order_id} not found in cache."),
2223                };
2224
2225                if child_order.is_active_local() {
2226                    continue;
2227                }
2228
2229                if order.leaves_qty().is_zero() {
2230                    self.cancel_order(&child_order, None);
2231                } else if child_order.leaves_qty() != order.leaves_qty() {
2232                    let price = child_order.price();
2233                    let trigger_price = child_order.trigger_price();
2234                    self.update_order(
2235                        &mut child_order,
2236                        Some(order.leaves_qty()),
2237                        price,
2238                        trigger_price,
2239                        Some(false),
2240                    );
2241                }
2242            }
2243        }
2244    }
2245
2246    fn cancel_contingent_orders(&mut self, order: &OrderAny) {
2247        if let Some(linked_order_ids) = order.linked_order_ids() {
2248            for client_order_id in linked_order_ids {
2249                let contingent_order = match self.cache.borrow().order(client_order_id) {
2250                    Some(order) => order.clone(),
2251                    None => panic!("Cannot find contingent order for {client_order_id}"),
2252                };
2253                if contingent_order.is_active_local() {
2254                    // order is not on the exchange yet
2255                    continue;
2256                }
2257                if !contingent_order.is_closed() {
2258                    self.cancel_order(&contingent_order, Some(false));
2259                }
2260            }
2261        }
2262    }
2263
2264    // -- EVENT GENERATORS -----------------------------------------------------
2265
2266    fn generate_order_rejected(&self, order: &OrderAny, reason: Ustr) {
2267        let ts_now = self.clock.borrow().timestamp_ns();
2268        let account_id = order
2269            .account_id()
2270            .unwrap_or(self.account_ids.get(&order.trader_id()).unwrap().to_owned());
2271
2272        // Check if rejection is due to post-only
2273        let due_post_only = reason.as_str().starts_with("POST_ONLY");
2274
2275        let event = OrderEventAny::Rejected(OrderRejected::new(
2276            order.trader_id(),
2277            order.strategy_id(),
2278            order.instrument_id(),
2279            order.client_order_id(),
2280            account_id,
2281            reason,
2282            UUID4::new(),
2283            ts_now,
2284            ts_now,
2285            false,
2286            due_post_only,
2287        ));
2288        msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2289    }
2290
2291    fn generate_order_accepted(&self, order: &mut OrderAny, venue_order_id: VenueOrderId) {
2292        let ts_now = self.clock.borrow().timestamp_ns();
2293        let account_id = order
2294            .account_id()
2295            .unwrap_or(self.account_ids.get(&order.trader_id()).unwrap().to_owned());
2296        let event = OrderEventAny::Accepted(OrderAccepted::new(
2297            order.trader_id(),
2298            order.strategy_id(),
2299            order.instrument_id(),
2300            order.client_order_id(),
2301            venue_order_id,
2302            account_id,
2303            UUID4::new(),
2304            ts_now,
2305            ts_now,
2306            false,
2307        ));
2308        msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2309
2310        // TODO remove this when execution engine msgbus handlers are correctly set
2311        order.apply(event).expect("Failed to apply order event");
2312    }
2313
2314    #[allow(clippy::too_many_arguments)]
2315    fn generate_order_modify_rejected(
2316        &self,
2317        trader_id: TraderId,
2318        strategy_id: StrategyId,
2319        instrument_id: InstrumentId,
2320        client_order_id: ClientOrderId,
2321        reason: Ustr,
2322        venue_order_id: Option<VenueOrderId>,
2323        account_id: Option<AccountId>,
2324    ) {
2325        let ts_now = self.clock.borrow().timestamp_ns();
2326        let event = OrderEventAny::ModifyRejected(OrderModifyRejected::new(
2327            trader_id,
2328            strategy_id,
2329            instrument_id,
2330            client_order_id,
2331            reason,
2332            UUID4::new(),
2333            ts_now,
2334            ts_now,
2335            false,
2336            venue_order_id,
2337            account_id,
2338        ));
2339        msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2340    }
2341
2342    #[allow(clippy::too_many_arguments)]
2343    fn generate_order_cancel_rejected(
2344        &self,
2345        trader_id: TraderId,
2346        strategy_id: StrategyId,
2347        account_id: AccountId,
2348        instrument_id: InstrumentId,
2349        client_order_id: ClientOrderId,
2350        venue_order_id: VenueOrderId,
2351        reason: Ustr,
2352    ) {
2353        let ts_now = self.clock.borrow().timestamp_ns();
2354        let event = OrderEventAny::CancelRejected(OrderCancelRejected::new(
2355            trader_id,
2356            strategy_id,
2357            instrument_id,
2358            client_order_id,
2359            reason,
2360            UUID4::new(),
2361            ts_now,
2362            ts_now,
2363            false,
2364            Some(venue_order_id),
2365            Some(account_id),
2366        ));
2367        msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2368    }
2369
2370    fn generate_order_updated(
2371        &self,
2372        order: &mut OrderAny,
2373        quantity: Quantity,
2374        price: Option<Price>,
2375        trigger_price: Option<Price>,
2376    ) {
2377        let ts_now = self.clock.borrow().timestamp_ns();
2378        let event = OrderEventAny::Updated(OrderUpdated::new(
2379            order.trader_id(),
2380            order.strategy_id(),
2381            order.instrument_id(),
2382            order.client_order_id(),
2383            quantity,
2384            UUID4::new(),
2385            ts_now,
2386            ts_now,
2387            false,
2388            order.venue_order_id(),
2389            order.account_id(),
2390            price,
2391            trigger_price,
2392        ));
2393        msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2394
2395        // TODO remove this when execution engine msgbus handlers are correctly set
2396        order.apply(event).expect("Failed to apply order event");
2397    }
2398
2399    fn generate_order_canceled(&self, order: &OrderAny, venue_order_id: VenueOrderId) {
2400        let ts_now = self.clock.borrow().timestamp_ns();
2401        let event = OrderEventAny::Canceled(OrderCanceled::new(
2402            order.trader_id(),
2403            order.strategy_id(),
2404            order.instrument_id(),
2405            order.client_order_id(),
2406            UUID4::new(),
2407            ts_now,
2408            ts_now,
2409            false,
2410            Some(venue_order_id),
2411            order.account_id(),
2412        ));
2413        msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2414    }
2415
2416    fn generate_order_triggered(&self, order: &OrderAny) {
2417        let ts_now = self.clock.borrow().timestamp_ns();
2418        let event = OrderEventAny::Triggered(OrderTriggered::new(
2419            order.trader_id(),
2420            order.strategy_id(),
2421            order.instrument_id(),
2422            order.client_order_id(),
2423            UUID4::new(),
2424            ts_now,
2425            ts_now,
2426            false,
2427            order.venue_order_id(),
2428            order.account_id(),
2429        ));
2430        msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2431    }
2432
2433    fn generate_order_expired(&self, order: &OrderAny) {
2434        let ts_now = self.clock.borrow().timestamp_ns();
2435        let event = OrderEventAny::Expired(OrderExpired::new(
2436            order.trader_id(),
2437            order.strategy_id(),
2438            order.instrument_id(),
2439            order.client_order_id(),
2440            UUID4::new(),
2441            ts_now,
2442            ts_now,
2443            false,
2444            order.venue_order_id(),
2445            order.account_id(),
2446        ));
2447        msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2448    }
2449
2450    #[allow(clippy::too_many_arguments)]
2451    fn generate_order_filled(
2452        &mut self,
2453        order: &mut OrderAny,
2454        venue_order_id: VenueOrderId,
2455        venue_position_id: Option<PositionId>,
2456        last_qty: Quantity,
2457        last_px: Price,
2458        quote_currency: Currency,
2459        commission: Money,
2460        liquidity_side: LiquiditySide,
2461    ) {
2462        let ts_now = self.clock.borrow().timestamp_ns();
2463        let account_id = order
2464            .account_id()
2465            .unwrap_or(self.account_ids.get(&order.trader_id()).unwrap().to_owned());
2466        let event = OrderEventAny::Filled(OrderFilled::new(
2467            order.trader_id(),
2468            order.strategy_id(),
2469            order.instrument_id(),
2470            order.client_order_id(),
2471            venue_order_id,
2472            account_id,
2473            self.ids_generator.generate_trade_id(),
2474            order.order_side(),
2475            order.order_type(),
2476            last_qty,
2477            last_px,
2478            quote_currency,
2479            liquidity_side,
2480            UUID4::new(),
2481            ts_now,
2482            ts_now,
2483            false,
2484            venue_position_id,
2485            Some(commission),
2486        ));
2487        msgbus::send_any("ExecEngine.process".into(), &event as &dyn Any);
2488
2489        // TODO remove this when execution engine msgbus handlers are correctly set
2490        order.apply(event).expect("Failed to apply order event");
2491    }
2492}