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