// -------------------------------------------------------------------------------------------------
//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
//  https://nautechsystems.io
//
//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
//  You may not use this file except in compliance with the License.
//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// -------------------------------------------------------------------------------------------------

use std::{cell::RefCell, rc::Rc};

use chrono::{DateTime, TimeZone, Utc};
use nautilus_common::{
    cache::Cache,
    clock::TestClock,
    messages::execution::{BatchCancelOrders, CancelAllOrders, CancelOrder, ModifyOrder},
    msgbus::{
        self, MessagingSwitchboard, TypedIntoHandler,
        stubs::{
            TypedIntoMessageSavingHandler, TypedMessageSavingHandler,
            get_typed_into_message_saving_handler, get_typed_message_saving_handler,
        },
    },
};
use nautilus_core::{UUID4, UnixNanos};
use nautilus_execution::{
    matching_engine::{config::OrderMatchingEngineConfig, engine::OrderMatchingEngine},
    models::{
        fee::{CappedOptionFeeModel, FeeModelAny, FixedFeeModel},
        fill::{BestPriceFillModel, DefaultFillModel, FillModelAny, FillModelHandle},
    },
};
use nautilus_model::{
    data::{
        Bar, BarType, BookOrder, InstrumentClose, OptionGreeks, QuoteTick, TradeTick,
        stubs::OrderBookDeltaTestBuilder,
    },
    enums::{
        AccountType, AggressorSide, AssetClass, BookAction, BookType, ContingencyType,
        InstrumentCloseType, LiquiditySide, MarketStatus, MarketStatusAction, OmsType, OptionKind,
        OrderSide, OrderStatus, OrderType, TimeInForce, TrailingOffsetType, TriggerType,
    },
    events::{
        OrderEmulated, OrderEventAny, OrderEventType, OrderFilled, OrderRejected, OrderReleased,
        order::spec::{OrderEmulatedSpec, OrderFilledSpec, OrderRejectedSpec, OrderReleasedSpec},
    },
    identifiers::{
        AccountId, ClientId, ClientOrderId, InstrumentId, PositionId, StrategyId, Symbol, TradeId,
        TraderId, VenueOrderId, stubs::account_id,
    },
    instruments::{
        CryptoOption, CryptoPerpetual, Equity, IndexInstrument, Instrument, InstrumentAny,
        OptionContract,
        stubs::{binary_option, crypto_perpetual_ethusdt, equity_aapl, futures_contract_es},
    },
    orders::{
        Order, OrderAny, OrderTestBuilder,
        stubs::{TestOrderEventStubs, TestOrderStubs},
    },
    position::Position,
    stubs::TestDefault,
    types::{Currency, Money, Price, Quantity},
};
use rstest::{fixture, rstest};
use rust_decimal_macros::dec;
use ustr::Ustr;

#[fixture]
pub fn test_clock() -> Rc<RefCell<TestClock>> {
    Rc::new(RefCell::new(TestClock::new()))
}

#[expect(clippy::too_many_arguments)]
fn build_order_filled(
    trader_id: TraderId,
    strategy_id: StrategyId,
    instrument_id: InstrumentId,
    client_order_id: ClientOrderId,
    venue_order_id: VenueOrderId,
    account_id: AccountId,
    trade_id: TradeId,
    order_side: OrderSide,
    order_type: OrderType,
    last_qty: Quantity,
    last_px: Price,
    currency: Currency,
    liquidity_side: LiquiditySide,
    position_id: Option<PositionId>,
    commission: Option<Money>,
) -> OrderFilled {
    OrderFilledSpec::builder()
        .trader_id(trader_id)
        .strategy_id(strategy_id)
        .instrument_id(instrument_id)
        .client_order_id(client_order_id)
        .venue_order_id(venue_order_id)
        .account_id(account_id)
        .trade_id(trade_id)
        .order_side(order_side)
        .order_type(order_type)
        .last_qty(last_qty)
        .last_px(last_px)
        .currency(currency)
        .liquidity_side(liquidity_side)
        .maybe_position_id(position_id)
        .maybe_commission(commission)
        .build()
}

fn build_order_emulated(
    trader_id: TraderId,
    strategy_id: StrategyId,
    instrument_id: InstrumentId,
    client_order_id: ClientOrderId,
) -> OrderEmulated {
    OrderEmulatedSpec::builder()
        .trader_id(trader_id)
        .strategy_id(strategy_id)
        .instrument_id(instrument_id)
        .client_order_id(client_order_id)
        .build()
}

fn build_order_released(
    trader_id: TraderId,
    strategy_id: StrategyId,
    instrument_id: InstrumentId,
    client_order_id: ClientOrderId,
    released_price: Price,
) -> OrderReleased {
    OrderReleasedSpec::builder()
        .trader_id(trader_id)
        .strategy_id(strategy_id)
        .instrument_id(instrument_id)
        .client_order_id(client_order_id)
        .released_price(released_price)
        .build()
}

#[fixture]
fn order_event_handler() -> TypedIntoMessageSavingHandler<OrderEventAny> {
    let (handler, saving_handler) = get_typed_into_message_saving_handler::<OrderEventAny>(Some(
        Ustr::from("ExecEngine.process"),
    ));
    msgbus::register_order_event_endpoint(MessagingSwitchboard::exec_engine_process(), handler);
    saving_handler
}

#[fixture]
pub fn instrument_eth_usdt(crypto_perpetual_ethusdt: CryptoPerpetual) -> InstrumentAny {
    InstrumentAny::CryptoPerpetual(crypto_perpetual_ethusdt)
}

// Market buy order with corresponding fill
#[fixture]
pub fn market_order_buy(instrument_eth_usdt: InstrumentAny) -> OrderAny {
    OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build()
}

#[fixture]
pub fn market_order_fill(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
    market_order_buy: OrderAny,
) -> OrderFilled {
    build_order_filled(
        market_order_buy.trader_id(),
        market_order_buy.strategy_id(),
        market_order_buy.instrument_id(),
        market_order_buy.client_order_id(),
        VenueOrderId::new("BINANCE-1"),
        account_id,
        TradeId::new("1"),
        market_order_buy.order_side(),
        market_order_buy.order_type(),
        Quantity::from("1"),
        Price::from("1000.000"),
        instrument_eth_usdt.quote_currency(),
        LiquiditySide::Taker,
        Some(PositionId::new("P-1")),
        None,
    )
}

// Market sell order
#[fixture]
pub fn market_order_sell(instrument_eth_usdt: InstrumentAny) -> OrderAny {
    OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build()
}

// For valid ES futures contract currently active
#[fixture]
fn instrument_es() -> InstrumentAny {
    let activation = UnixNanos::from(
        Utc.with_ymd_and_hms(2022, 4, 8, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    let expiration = UnixNanos::from(
        Utc.with_ymd_and_hms(2100, 7, 8, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    InstrumentAny::FuturesContract(futures_contract_es(Some(activation), Some(expiration)))
}

#[fixture]
fn engine_config() -> OrderMatchingEngineConfig {
    OrderMatchingEngineConfig {
        support_contingent_orders: true,
        use_reduce_only: true,
        ..Default::default()
    }
}
// -- HELPERS ---------------------------------------------------------------------------

fn get_order_matching_engine(
    instrument: InstrumentAny,
    cache: Option<Rc<RefCell<Cache>>>,
    account_type: Option<AccountType>,
    config: Option<OrderMatchingEngineConfig>,
    clock: Option<Rc<RefCell<TestClock>>>,
) -> OrderMatchingEngine {
    let cache = cache.unwrap_or(Rc::new(RefCell::new(Cache::default())));
    let clock = clock.unwrap_or(Rc::new(RefCell::new(TestClock::new())));
    let config = config.unwrap_or_default();
    OrderMatchingEngine::new(
        instrument,
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        account_type.unwrap_or(AccountType::Cash),
        clock,
        cache,
        config,
    )
}

fn get_order_matching_engine_l2(
    instrument: InstrumentAny,
    cache: Option<Rc<RefCell<Cache>>>,
    account_type: Option<AccountType>,
    config: Option<OrderMatchingEngineConfig>,
    clock: Option<Rc<RefCell<TestClock>>>,
) -> OrderMatchingEngine {
    let cache = cache.unwrap_or(Rc::new(RefCell::new(Cache::default())));
    let config = config.unwrap_or_default();
    let clock = clock.unwrap_or(Rc::new(RefCell::new(TestClock::new())));
    OrderMatchingEngine::new(
        instrument,
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L2_MBP,
        OmsType::Netting,
        account_type.unwrap_or(AccountType::Cash),
        clock,
        cache,
        config,
    )
}

fn crypto_perpetual_with_price_precision(
    instrument: InstrumentAny,
    price_precision: u8,
    price_increment: &str,
) -> InstrumentAny {
    match instrument {
        InstrumentAny::CryptoPerpetual(mut crypto_perp) => {
            crypto_perp.price_precision = price_precision;
            crypto_perp.price_increment = Price::from(price_increment);
            InstrumentAny::CryptoPerpetual(crypto_perp)
        }
        _ => panic!("Test fixture expected CryptoPerpetual instrument"),
    }
}

fn crypto_perpetual_with_size_precision(
    instrument: InstrumentAny,
    size_precision: u8,
    size_increment: &str,
) -> InstrumentAny {
    match instrument {
        InstrumentAny::CryptoPerpetual(mut crypto_perp) => {
            crypto_perp.size_precision = size_precision;
            crypto_perp.size_increment = Quantity::from(size_increment);
            InstrumentAny::CryptoPerpetual(crypto_perp)
        }
        _ => panic!("Test fixture expected CryptoPerpetual instrument"),
    }
}

fn order_event_handler_with_cache(
    cache: Rc<RefCell<Cache>>,
) -> TypedIntoMessageSavingHandler<OrderEventAny> {
    use nautilus_common::msgbus::typed_handler::TypedIntoHandler;

    let messages: Rc<RefCell<Vec<OrderEventAny>>> = Rc::new(RefCell::new(Vec::new()));
    let messages_for_handler = messages.clone();

    msgbus::register_order_event_endpoint(
        MessagingSwitchboard::exec_engine_process(),
        TypedIntoHandler::from(move |event: OrderEventAny| {
            if let Ok(mut cache_ref) = cache.try_borrow_mut() {
                let _ = cache_ref.update_order(&event);
            }

            // Save the event for test assertions
            messages_for_handler.borrow_mut().push(event);
        }),
    );

    TypedIntoMessageSavingHandler::new_with_messages(
        Some(Ustr::from("ExecEngine.process")),
        messages,
    )
}

fn get_order_event_handler_messages(
    event_handler: &TypedIntoMessageSavingHandler<OrderEventAny>,
) -> Vec<OrderEventAny> {
    event_handler.get_messages()
}

fn clear_order_event_handler_messages(
    event_handler: &TypedIntoMessageSavingHandler<OrderEventAny>,
) {
    event_handler.clear();
}

// -- TESTS -----------------------------------------------------------------------------------

#[rstest]
fn test_process_order_when_instrument_already_expired(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    mut market_order_buy: OrderAny,
    test_clock: Rc<RefCell<TestClock>>,
) {
    let instrument = InstrumentAny::FuturesContract(futures_contract_es(None, None));

    // Set current timestamp ns to be higher than es instrument activation (1.1.2024)
    test_clock
        .borrow_mut()
        .set_time(UnixNanos::from(1704067200000000000));
    // Create engine and process order
    let mut engine = get_order_matching_engine(instrument, None, None, None, Some(test_clock));

    engine.process_order(&mut market_order_buy, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from("Contract ESZ21.GLBX has expired, expiration 1639699200000000000")
    );
}

#[rstest]
fn test_process_order_when_instrument_not_active(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    mut market_order_buy: OrderAny,
) {
    let activation = UnixNanos::from(
        Utc.with_ymd_and_hms(2222, 4, 8, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    let expiration = UnixNanos::from(
        Utc.with_ymd_and_hms(2223, 7, 8, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    let instrument =
        InstrumentAny::FuturesContract(futures_contract_es(Some(activation), Some(expiration)));

    // Create engine and process order
    let mut engine = get_order_matching_engine(instrument, None, None, None, None);

    engine.process_order(&mut market_order_buy, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from("Contract ESZ21.GLBX is not yet active, activation 7960723200000000000")
    );
}

#[rstest]
fn test_process_order_when_invalid_quantity_precision(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Create market order with invalid quantity precision 0 for eth/usdt precision of 3
    let mut market_order_invalid_precision = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1"))
        .submit(true)
        .build();

    // Create engine and process order
    let mut engine = get_order_matching_engine(instrument_eth_usdt, None, None, None, None);

    engine.process_order(&mut market_order_invalid_precision, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from(
            "Invalid order quantity precision for order O-19700101-000000-001-001-1, was 0 when ETHUSDT-PERP.BINANCE size precision is 3"
        )
    );
}

#[rstest]
fn test_process_order_when_invalid_display_qty_precision(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .display_qty(Quantity::from("0.0001"))
        .submit(true)
        .build();
    let mut engine = get_order_matching_engine(instrument_eth_usdt, None, None, None, None);

    engine.process_order(&mut limit_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from(
            "Invalid order display quantity precision for order O-19700101-000000-001-001-1, was 4 when ETHUSDT-PERP.BINANCE size precision is 3"
        )
    );
}

#[rstest]
fn test_process_order_when_invalid_price_precision(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_es: InstrumentAny,
    test_clock: Rc<RefCell<TestClock>>,
) {
    // Create engine and process order
    // Set current timestamp ns to be higher than es instrument activation (1.1.2024)
    test_clock
        .borrow_mut()
        .set_time(UnixNanos::from(1704067200000000000));
    let mut engine =
        get_order_matching_engine(instrument_es.clone(), None, None, None, Some(test_clock));

    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_es.id())
        .side(OrderSide::Sell)
        .price(Price::from("100.12333")) // <-- Wrong price precision for es futures contract (which is 2)
        .quantity(Quantity::from("1"))
        .submit(true)
        .build();

    engine.process_order(&mut limit_order, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from(
            "Invalid order price precision for order O-19700101-000000-001-001-1, was 5 when ESZ21.GLBX price precision is 2"
        )
    );
}

#[rstest]
fn test_process_order_when_invalid_trigger_price_precision(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_es: InstrumentAny,
    test_clock: Rc<RefCell<TestClock>>,
) {
    // Create engine and process order
    // Set current timestamp ns to be higher than es instrument activation (1.1.2024)
    test_clock
        .borrow_mut()
        .set_time(UnixNanos::from(1704067200000000000));
    let mut engine =
        get_order_matching_engine(instrument_es.clone(), None, None, None, Some(test_clock));
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_es.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("100.12333")) // <-- Wrong trigger price precision for es futures contract (which is 2)
        .quantity(Quantity::from("1"))
        .submit(true)
        .build();

    engine.process_order(&mut stop_order, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from(
            "Invalid order trigger price precision for order O-19700101-000000-001-001-1, was 5 when ESZ21.GLBX price precision is 2"
        )
    );
}

#[rstest]
fn test_process_order_when_shorting_equity_without_margin_account(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    equity_aapl: Equity,
) {
    let instrument = InstrumentAny::Equity(equity_aapl);

    let mut market_order_sell = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("1"))
        .submit(true)
        .build();

    // Create engine and process order
    let mut engine = get_order_matching_engine(instrument, None, None, None, None);

    engine.process_order(&mut market_order_sell, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from(
            "Short selling not permitted on a CASH account with position None and \
            order MarketOrder(SELL 1 AAPL.XNAS @ MARKET GTC, status=SUBMITTED, \
            client_order_id=O-19700101-000000-001-001-1, venue_order_id=None, position_id=None, \
            exec_algorithm_id=None, exec_spawn_id=None, tags=None)"
        )
    );
}

#[rstest]
fn test_process_order_when_invalid_reduce_only(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    engine_config: OrderMatchingEngineConfig,
) {
    let mut engine = get_order_matching_engine(
        instrument_eth_usdt.clone(),
        None,
        None,
        Some(engine_config),
        None,
    );
    let mut market_order_reduce = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .reduce_only(true)
        .submit(true)
        .build();

    engine.process_order(&mut market_order_reduce, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from(
            "Reduce-only order O-19700101-000000-001-001-1 (MARKET-BUY) would have increased position"
        )
    );
}

#[rstest]
#[case::paused(MarketStatusAction::Pause, MarketStatus::Paused)]
#[case::suspended(MarketStatusAction::Suspend, MarketStatus::Suspended)]
#[case::closed(MarketStatusAction::Close, MarketStatus::Closed)]
fn test_process_order_rejects_when_market_not_open_and_accepts_after_reopen(
    #[case] close_action: MarketStatusAction,
    #[case] expected_status: MarketStatus,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);
    engine.process_status(close_action);
    assert_eq!(engine.market_status, expected_status);

    let rejected_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut rejected_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(rejected_client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut rejected_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let rejected = match saved_messages.first().unwrap() {
        OrderEventAny::Rejected(rejected) => rejected,
        event => panic!("Expected OrderRejected event, was {event:?}"),
    };
    assert_eq!(rejected.client_order_id, rejected_client_order_id);
    assert!(rejected.reason.as_str().contains(expected_status.as_ref()));
    assert!(!engine.order_exists(rejected_client_order_id));

    clear_order_event_handler_messages(&order_event_handler);
    engine.process_status(MarketStatusAction::Trading);
    assert_eq!(engine.market_status, MarketStatus::Open);

    let accepted_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut accepted_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(accepted_client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut accepted_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let accepted = match saved_messages.first().unwrap() {
        OrderEventAny::Accepted(accepted) => accepted,
        event => panic!("Expected OrderAccepted event, was {event:?}"),
    };
    assert_eq!(accepted.client_order_id, accepted_client_order_id);
    assert!(engine.order_exists(accepted_client_order_id));
}

#[rstest]
fn test_market_status_pause_blocks_matching_until_trading_resumes(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let resting_bid_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut resting_bid = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1499.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(resting_bid_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut resting_bid, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    engine_l2.process_status(MarketStatusAction::Pause);
    assert_eq!(engine_l2.market_status, MarketStatus::Paused);

    let matching_ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1498.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&matching_ask).unwrap();

    let paused_messages = get_order_event_handler_messages(&order_event_handler);
    assert!(
        paused_messages
            .iter()
            .all(|event| !matches!(event, OrderEventAny::Filled(_))),
        "paused market must not fill resting orders, was {paused_messages:?}",
    );
    assert!(engine_l2.order_exists(resting_bid_id));

    engine_l2.process_status(MarketStatusAction::Trading);
    engine_l2.iterate(UnixNanos::from(2_u64), AggressorSide::NoAggressor);

    let resumed_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = resumed_messages
        .iter()
        .find_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .expect("resting order should fill after market reopens");
    assert_eq!(fill.client_order_id, resting_bid_id);
}

#[rstest]
fn test_process_order_when_invalid_contingent_orders(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_es: InstrumentAny,
    engine_config: OrderMatchingEngineConfig,
    test_clock: Rc<RefCell<TestClock>>,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    // Set current timestamp ns to be higher than es instrument activation (1.1.2024)
    test_clock
        .borrow_mut()
        .set_time(UnixNanos::from(1704067200000000000));
    let mut engine = get_order_matching_engine(
        instrument_es.clone(),
        Some(cache.clone()),
        None,
        Some(engine_config),
        Some(test_clock),
    );

    let entry_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let stop_loss_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-2");

    // Create entry market order
    let mut entry_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_es.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from(1))
        .contingency_type(ContingencyType::Oto)
        .client_order_id(entry_client_order_id)
        .submit(true)
        .build();
    // Set entry order status to Rejected with proper event
    let rejected_event = OrderRejected::default();
    entry_order
        .apply(OrderEventAny::Rejected(rejected_event))
        .unwrap();

    // Create stop loss order
    let stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_es.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("0.95"))
        .quantity(Quantity::from(1))
        .contingency_type(ContingencyType::Oto)
        .client_order_id(stop_loss_client_order_id)
        .parent_order_id(entry_client_order_id)
        .submit(true)
        .build();
    // Make it Accepted
    let mut accepted_stop_order = TestOrderStubs::make_accepted_order(&stop_order);

    // 1. Save entry order in the cache as it will be loaded by the matching engine
    // 2. Send the stop loss order which has parent of entry order
    cache
        .as_ref()
        .borrow_mut()
        .add_order(entry_order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut accepted_stop_order, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from(format!("Rejected OTO order from {entry_client_order_id}").as_str())
    );
}

#[rstest]
fn test_process_order_when_closed_linked_order(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_es: InstrumentAny,
    engine_config: OrderMatchingEngineConfig,
    test_clock: Rc<RefCell<TestClock>>,
) {
    // Set current timestamp ns to be higher than es instrument activation (1.1.2024)
    test_clock
        .borrow_mut()
        .set_time(UnixNanos::from(1704067200000000000));
    let cache = Rc::new(RefCell::new(Cache::default()));
    let mut engine = get_order_matching_engine(
        instrument_es.clone(),
        Some(cache.clone()),
        None,
        Some(engine_config),
        Some(test_clock),
    );
    // Set current timestamp ns to be higher than es instrument activation

    let stop_loss_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-2");
    let take_profit_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-3");
    // Create two linked orders: stop loss and take profit
    let mut stop_loss_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_es.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("0.95"))
        .quantity(Quantity::from(1))
        .contingency_type(ContingencyType::Oco)
        .client_order_id(stop_loss_client_order_id)
        .linked_order_ids(vec![take_profit_client_order_id])
        .submit(true)
        .build();
    let take_profit_order = OrderTestBuilder::new(OrderType::MarketIfTouched)
        .instrument_id(instrument_es.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("1.1"))
        .quantity(Quantity::from(1))
        .contingency_type(ContingencyType::Oco)
        .client_order_id(take_profit_client_order_id)
        .linked_order_ids(vec![stop_loss_client_order_id])
        .submit(true)
        .build();
    // Set stop loss order status to Rejected with proper event
    let rejected_event: OrderRejected = OrderRejectedSpec::builder()
        .client_order_id(stop_loss_client_order_id)
        .reason(Ustr::from("Rejected"))
        .build();
    stop_loss_order
        .apply(OrderEventAny::Rejected(rejected_event))
        .unwrap();

    // Make take profit order Accepted
    let mut accepted_take_profit = TestOrderStubs::make_accepted_order(&take_profit_order);

    // 1. Save stop loss order in cache which is rejected and closed is set to true
    // 2. Send the take profit order which has linked the stop loss order
    cache
        .as_ref()
        .borrow_mut()
        .add_order(stop_loss_order.clone(), None, None, false)
        .unwrap();
    let _stop_loss_closed_after = stop_loss_order.is_closed();
    engine.process_order(&mut accepted_take_profit, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first_message.message().unwrap(),
        Ustr::from(format!("Contingent order {stop_loss_client_order_id} already closed").as_str())
    );
}

#[rstest]
fn test_process_bracket_order_list_does_not_double_submit_children(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
    engine_config: OrderMatchingEngineConfig,
) {
    // Regression for discussion #4040: duplicate process_order on OTO children
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        Some(engine_config),
        None,
    );

    // Ask side only: market BUY fills, SELL SL/TP have no bid to match
    let ask = BookOrder::new(
        OrderSide::Sell,
        Price::from("1500.00"),
        Quantity::from("10.000"),
        1,
    );
    engine
        .process_order_book_delta(
            &OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
                .book_action(BookAction::Add)
                .book_order(ask)
                .build(),
        )
        .unwrap();

    let entry_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let sl_id = ClientOrderId::from("O-19700101-000000-001-001-2");
    let tp_id = ClientOrderId::from("O-19700101-000000-001-001-3");

    let mut entry = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(entry_id)
        .contingency_type(ContingencyType::Oto)
        .linked_order_ids(vec![sl_id, tp_id])
        .submit(true)
        .build();
    let mut sl = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("100.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(sl_id)
        .parent_order_id(entry_id)
        .contingency_type(ContingencyType::Oco)
        .linked_order_ids(vec![tp_id])
        .submit(true)
        .build();
    let mut tp = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .price(Price::from("3000.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(tp_id)
        .parent_order_id(entry_id)
        .contingency_type(ContingencyType::Oco)
        .linked_order_ids(vec![sl_id])
        .submit(true)
        .build();

    {
        let mut cache_mut = cache.borrow_mut();
        cache_mut
            .add_order(entry.clone(), None, None, false)
            .unwrap();
        cache_mut.add_order(sl.clone(), None, None, false).unwrap();
        cache_mut.add_order(tp.clone(), None, None, false).unwrap();
    }

    // Mirror `SimulatedExchange::process_trading_command` for SubmitOrderList
    engine.process_order(&mut entry, account_id);
    engine.process_order(&mut sl, account_id);
    engine.process_order(&mut tp, account_id);

    let events = get_order_event_handler_messages(&order_event_handler);
    let already_exists = events
        .iter()
        .filter(|e| {
            e.event_type() == OrderEventType::Rejected
                && e.message()
                    .is_some_and(|m| m.as_str().contains("already exists"))
        })
        .count();
    assert_eq!(
        already_exists, 0,
        "No bracket child should be rejected with 'Order already exists'; events: {events:?}",
    );

    let filled = events
        .iter()
        .filter(|e| e.event_type() == OrderEventType::Filled)
        .count();
    let accepted = events
        .iter()
        .filter(|e| e.event_type() == OrderEventType::Accepted)
        .count();
    assert_eq!(filled, 1, "entry should fill exactly once");
    assert_eq!(
        accepted, 2,
        "SL and TP should each be accepted exactly once"
    );
}

#[rstest]
fn test_process_market_order_no_market_rejected(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    mut market_order_buy: OrderAny,
    mut market_order_sell: OrderAny,
) {
    // Create engine and process order
    let mut engine = get_order_matching_engine(instrument_eth_usdt, None, None, None, None);

    engine.process_order(&mut market_order_buy, account_id);
    engine.process_order(&mut market_order_sell, account_id);

    // Get messages and test
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let first = saved_messages.first().unwrap();
    let second = saved_messages.get(1).unwrap();
    assert_eq!(first.event_type(), OrderEventType::Rejected);
    assert_eq!(second.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first.message().unwrap(),
        Ustr::from("No market for ETHUSDT-PERP.BINANCE")
    );
    assert_eq!(
        second.message().unwrap(),
        Ustr::from("No market for ETHUSDT-PERP.BINANCE")
    );
}

#[rstest]
#[case::at_the_open(TimeInForce::AtTheOpen, "AT_THE_OPEN")]
#[case::at_the_close(TimeInForce::AtTheClose, "AT_THE_CLOSE")]
fn test_process_market_order_at_the_open_or_close_rejected(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] time_in_force: TimeInForce,
    #[case] tif_str: &str,
) {
    let mut engine = get_order_matching_engine(instrument_eth_usdt.clone(), None, None, None, None);

    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .time_in_force(time_in_force)
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();

    engine.process_order(&mut market_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first = saved_messages.first().unwrap();
    assert_eq!(first.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first.message().unwrap(),
        Ustr::from(format!("time in force {tif_str} is not currently supported").as_str())
    );
}

#[rstest]
#[case::at_the_open(TimeInForce::AtTheOpen, "AT_THE_OPEN")]
#[case::at_the_close(TimeInForce::AtTheClose, "AT_THE_CLOSE")]
fn test_process_limit_order_at_the_open_or_close_rejected(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] time_in_force: TimeInForce,
    #[case] tif_str: &str,
) {
    let mut engine = get_order_matching_engine(instrument_eth_usdt.clone(), None, None, None, None);

    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1500.00"))
        .quantity(Quantity::from("1.000"))
        .time_in_force(time_in_force)
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();

    engine.process_order(&mut limit_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first = saved_messages.first().unwrap();
    assert_eq!(first.event_type(), OrderEventType::Rejected);
    assert_eq!(
        first.message().unwrap(),
        Ustr::from(format!("time in force {tif_str} is not currently supported").as_str())
    );
}

#[rstest]
fn test_bid_ask_initialized(instrument_es: InstrumentAny) {
    let mut engine_l2 = get_order_matching_engine_l2(instrument_es.clone(), None, None, None, None);
    // Create bid and ask orderbook delta and check if
    // bid and ask are initialized in order matching core
    let book_order_buy = BookOrder::new(
        OrderSide::Buy,
        Price::from("100.00"),
        Quantity::from("1"),
        0,
    );
    let book_order_sell = BookOrder::new(
        OrderSide::Sell,
        Price::from("101.00"),
        Quantity::from("1"),
        0,
    );
    let orderbook_delta_buy = OrderBookDeltaTestBuilder::new(instrument_es.id())
        .book_action(BookAction::Add)
        .book_order(book_order_buy)
        .build();
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_es.id())
        .book_action(BookAction::Add)
        .book_order(book_order_sell)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_buy)
        .unwrap();
    assert_eq!(engine_l2.get_core().bid, Some(Price::from("100.00")));
    assert_eq!(engine_l2.get_core().ask, None);

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    assert_eq!(engine_l2.get_core().bid, Some(Price::from("100.00")));
    assert_eq!(engine_l2.get_core().ask, Some(Price::from("101.00")));
}

#[rstest]
fn test_not_enough_quantity_filled_fok_order(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();

    // Create FOK market order with quantity 2 which won't be enough to fill the order
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("2.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .time_in_force(TimeInForce::Fok)
        .submit(true)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut market_order, account_id);

    // We need to test that one OrderCanceled event was generated
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let first_message = saved_messages.first().unwrap();
    assert_eq!(first_message.event_type(), OrderEventType::Canceled);
    let canceled = match first_message {
        OrderEventAny::Canceled(canceled) => canceled,
        _ => panic!("Expected OrderCanceled event in first message"),
    };
    assert_eq!(canceled.client_order_id, market_order.client_order_id());
}

#[rstest]
fn test_valid_market_buy(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Create 2 orderbook deltas and appropriate market order
    let book_order_1 = BookOrder::new(
        OrderSide::Sell,
        Price::from("1500.00"),
        Quantity::from("1.000"),
        1,
    );
    let book_order_2 = BookOrder::new(
        OrderSide::Sell,
        Price::from("1510.00"),
        Quantity::from("1.000"),
        1,
    );
    let orderbook_delta_sell_1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(book_order_1)
        .build();
    let orderbook_delta_sell_2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(book_order_2)
        .build();

    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("2.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();

    // Process orderbook deltas to add liquidity then process market order
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell_1)
        .unwrap();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell_2)
        .unwrap();
    engine_l2.process_order(&mut market_order, account_id);

    // We need to test that two Order filled events were generated where with correct prices and quantities
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let fill1 = match event1 {
        OrderEventAny::Filled(fill) => fill,
        _ => panic!("Expected OrderFilled event in first message"),
    };
    let event2 = saved_messages.get(1).unwrap();
    let fill2 = match event2 {
        OrderEventAny::Filled(fill) => fill,
        _ => panic!("Expected OrderFilled event in second message"),
    };
    // Check correct prices and quantities
    assert_eq!(fill1.last_px, Price::from("1500.00"));
    assert_eq!(fill1.last_qty, Quantity::from("1.000"));
    assert_eq!(fill2.last_px, Price::from("1510.00"));
    assert_eq!(fill2.last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_market_fill_caps_pending_cached_quantity_before_commission(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.500"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&ask).unwrap();

    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("2.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-CAP"))
        .submit(true)
        .build();

    engine_l2.process_order(&mut market_order, account_id);
    engine_l2.fill_market_order(market_order.client_order_id());

    let fills: Vec<OrderFilled> = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();

    assert_eq!(fills.len(), 2);
    assert_eq!(fills[0].last_qty, Quantity::from("1.500"));
    assert_eq!(fills[1].last_qty, Quantity::from("0.500"));

    let commission = fills[1].commission.expect("expected commission");
    let expected_commission = fills[1].last_qty.as_decimal()
        * fills[1].last_px.as_decimal()
        * instrument_eth_usdt.taker_fee();
    assert_eq!(commission.currency, instrument_eth_usdt.quote_currency());
    assert_eq!(commission.as_decimal(), expected_commission);
}

#[rstest]
fn test_market_order_with_acks_generates_accepted_then_filled(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        use_market_order_acks: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let orderbook_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&orderbook_delta).unwrap();

    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    // Verify OrderAccepted is generated before OrderFilled
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);

    let accepted = match saved_messages.first().unwrap() {
        OrderEventAny::Accepted(a) => a,
        other => panic!("Expected OrderAccepted, was {other:?}"),
    };
    assert_eq!(accepted.client_order_id, market_order.client_order_id());

    let filled = match saved_messages.get(1).unwrap() {
        OrderEventAny::Filled(f) => f,
        other => panic!("Expected OrderFilled, was {other:?}"),
    };
    assert_eq!(filled.client_order_id, market_order.client_order_id());
    assert_eq!(filled.last_px, Price::from("1500.00"));
    assert_eq!(filled.last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_market_order_with_protection_and_acks_generates_accepted_then_filled(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig::builder()
        .use_market_order_acks(true)
        .price_protection_points(600u32)
        .build();

    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let orderbook_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&orderbook_delta).unwrap();

    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    // Protection is computed at fill time (trigger-time semantics), so no OrderUpdated event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);

    let accepted = match saved_messages.first().unwrap() {
        OrderEventAny::Accepted(a) => a,
        other => panic!("Expected OrderAccepted, was {other:?}"),
    };
    assert_eq!(accepted.client_order_id, market_order.client_order_id());

    let filled = match saved_messages.get(1).unwrap() {
        OrderEventAny::Filled(f) => f,
        other => panic!("Expected OrderFilled, was {other:?}"),
    };
    assert_eq!(filled.client_order_id, market_order.client_order_id());
}

#[rstest]
fn test_process_limit_post_only_order_that_would_be_a_taker(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();

    // Create a post-only limit buy order with price above 1500.00
    // that would match the existing sell order and be a taker
    let mut post_only_limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1501.00"))
        .quantity(Quantity::from("1.000"))
        .post_only(true)
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut post_only_limit_order, account_id);

    // Test that one Order rejected event was generated
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let first_message = saved_messages.first().unwrap();
    let rejected = match first_message {
        OrderEventAny::Rejected(rejected) => rejected,
        _ => panic!("Expected OrderRejected event in first message"),
    };

    assert_eq!(saved_messages.len(), 1);
    assert_eq!(first_message.event_type(), OrderEventType::Rejected);
    assert_eq!(
        rejected.reason,
        Ustr::from(
            "POST_ONLY LIMIT BUY order limit px of 1501.00 would have been a TAKER: bid=None, ask=1500.00"
        )
    );
    assert!(
        rejected.due_post_only,
        "due_post_only should be set for post-only rejections"
    );
}

#[rstest]
fn test_process_limit_order_not_matched_and_canceled_fok_order(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    // Create limit order which is below currently supplied liquidity and ask
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .time_in_force(TimeInForce::Fok)
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut limit_order, account_id);

    // Check we have received OrderAccepted and then OrderCanceled event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    let event2 = saved_messages.get(1).unwrap();
    let rejected = match event2 {
        OrderEventAny::Canceled(canceled) => canceled,
        _ => panic!("Expected OrderCanceled event in second message"),
    };

    assert_eq!(saved_messages.len(), 2);
    assert_eq!(accepted.client_order_id, client_order_id);
    assert_eq!(rejected.client_order_id, client_order_id);
}

#[rstest]
#[case(TimeInForce::Ioc)]
#[case(TimeInForce::Fok)]
fn test_process_limit_order_without_immediate_match_cancels(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    #[case] time_in_force: TimeInForce,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .time_in_force(time_in_force)
        .client_order_id(client_order_id)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut limit_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let accepted = match saved_messages.first().unwrap() {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    let canceled = match saved_messages.get(1).unwrap() {
        OrderEventAny::Canceled(canceled) => canceled,
        _ => panic!("Expected OrderCanceled event in second message"),
    };

    assert_eq!(saved_messages.len(), 2);
    assert_eq!(accepted.client_order_id, client_order_id);
    assert_eq!(canceled.client_order_id, client_order_id);
    assert_eq!(canceled.venue_order_id, Some(accepted.venue_order_id));
}

#[rstest]
fn test_accept_order_released_dispatches_and_registers(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .emulation_trigger(TriggerType::Default)
        .client_order_id(client_order_id)
        .build();

    // Drive the order through `Emulated` -> `Released` so it arrives at the
    // matching engine in a status that has no transition into `Accepted`.
    let trader_id = limit_order.trader_id();
    let strategy_id = limit_order.strategy_id();
    let instrument_id = limit_order.instrument_id();
    limit_order
        .apply(OrderEventAny::Emulated(build_order_emulated(
            trader_id,
            strategy_id,
            instrument_id,
            client_order_id,
        )))
        .unwrap();
    limit_order
        .apply(OrderEventAny::Released(build_order_released(
            trader_id,
            strategy_id,
            instrument_id,
            client_order_id,
            Price::from("1495.00"),
        )))
        .unwrap();
    assert_eq!(limit_order.status(), OrderStatus::Released);

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut limit_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let accepted_count = saved_messages
        .iter()
        .filter(|e| matches!(e, OrderEventAny::Accepted(_)))
        .count();
    assert_eq!(
        accepted_count, 1,
        "Released order should still dispatch OrderAccepted",
    );
    assert!(
        engine_l2.order_exists(client_order_id),
        "Released order should register with the matching core",
    );
}

#[rstest]
fn test_process_limit_order_matched_immediate_fill(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1501.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut limit_order, account_id);

    // Check we have received first OrderAccepted and then OrderFilled event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    let event2 = saved_messages.get(1).unwrap();
    let fill = match event2 {
        OrderEventAny::Filled(fill) => fill,
        _ => panic!("Expected OrderFilled event in second message"),
    };

    assert_eq!(saved_messages.len(), 2);
    assert_eq!(accepted.client_order_id, client_order_id);
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_px, Price::from("1500.00"));
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_process_stop_market_order_triggered_rejected(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    // Create order matching engine which rejects stop orders
    let engine_config = OrderMatchingEngineConfig {
        reject_stop_orders: true,
        ..Default::default()
    };
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        None,
        None,
        Some(engine_config),
        None,
    );

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    // Create but stop market order, which is triggered (price of 1495 is below current ask of 1500)
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut stop_order, account_id);

    // Check we have received OrderRejected event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let event = saved_messages.first().unwrap();
    let rejected = match event {
        OrderEventAny::Rejected(rejected) => rejected,
        _ => panic!("Expected OrderRejected event in first message"),
    };

    assert_eq!(saved_messages.len(), 1);
    assert_eq!(rejected.client_order_id, client_order_id);
    assert_eq!(
        rejected.reason,
        Ustr::from(
            "STOP_MARKET BUY order stop px of 1495.00 was in the market: bid=None, ask=1500.00, but rejected because of configuration"
        )
    );
}

#[rstest]
fn test_process_stop_market_order_valid_trigger_filled(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    // Create but stop market order, which is triggered (price of 1495 is below current ask of 1500)
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut stop_order, account_id);

    // Check we have received OrderFilled event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = saved_messages.first().unwrap();
    let fill = match fill {
        OrderEventAny::Filled(fill) => fill,
        _ => panic!("Expected OrderFilled event in first message"),
    };

    assert_eq!(saved_messages.len(), 1);
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_px, Price::from("1500.00"));
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_process_stop_market_order_valid_not_triggered_accepted(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    // Create but stop market order, which is not triggered (price of 1505 is above current ask of 1500)
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1505.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut stop_order, account_id);

    // Check we have received OrderAccepted event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let event = saved_messages.first().unwrap();
    let accepted = match event {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };

    assert_eq!(saved_messages.len(), 1);
    assert_eq!(accepted.client_order_id, client_order_id);
}

#[rstest]
fn test_process_stop_limit_order_triggered_not_filled(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    // Create but stop limit order, which is triggered (price of 1495 is below current ask of 1500)
    // but price of 1490 it's not immediately filled.
    let mut stop_order = OrderTestBuilder::new(OrderType::StopLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1495.00"))
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut stop_order, account_id);

    // Check we have received OrderAccepted and OrderTriggered
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    let event2 = saved_messages.get(1).unwrap();
    let triggered = match event2 {
        OrderEventAny::Triggered(triggered) => triggered,
        _ => panic!("Expected OrderTriggered event in second message"),
    };

    assert_eq!(saved_messages.len(), 2);
    assert_eq!(accepted.client_order_id, client_order_id);
    assert_eq!(triggered.client_order_id, client_order_id);

    let resting = engine_l2
        .get_core()
        .get_order(client_order_id)
        .copied()
        .expect("triggered stop-limit should rest in the core");
    assert_eq!(resting.trigger_price, None);
    assert_eq!(resting.limit_price, Some(Price::from("1490.00")));

    clear_order_event_handler_messages(&order_event_handler);
    let matching_ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1489.00"),
            Quantity::from("1.000"),
            2,
        ))
        .build();
    engine_l2.process_order_book_delta(&matching_ask).unwrap();

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = saved_messages
        .iter()
        .find_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .expect("triggered stop-limit should fill as a limit after the ask crosses");
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_process_stop_limit_order_triggered_filled(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    // Create but stop limit order, which is triggered (price of 1505 is above current ask of 1500)
    // and price 1502 is also above current ask of 1500 so it's immediately filled.
    let mut stop_order = OrderTestBuilder::new(OrderType::StopLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1495.00"))
        .price(Price::from("1502.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut stop_order, account_id);

    // Check we have received OrderAccepted, OrderTriggered and finally OrderFilled event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 3);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let order_triggered = match event2 {
        OrderEventAny::Triggered(triggered) => triggered,
        _ => panic!("Expected OrderTriggered event in second message"),
    };
    assert_eq!(order_triggered.client_order_id, client_order_id);
    let event3 = saved_messages.get(2).unwrap();
    let fill = match event3 {
        OrderEventAny::Filled(fill) => fill,
        _ => panic!("Expected OrderFilled event in third message"),
    };
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_px, Price::from("1500.00"));
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_passive_stop_limit_trigger_emits_triggered_before_fill(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("2.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&ask).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_limit = OrderTestBuilder::new(OrderType::StopLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1505.00"))
        .price(Price::from("1506.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_limit, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1505.00"),
        Quantity::from("1.000"),
        AggressorSide::Buyer,
        TradeId::new("trigger-1"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine_l2.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);

    let triggered = match saved_messages.first().unwrap() {
        OrderEventAny::Triggered(triggered) => triggered,
        other => panic!("Expected OrderTriggered event first, was {other:?}"),
    };
    let filled = match saved_messages.get(1).unwrap() {
        OrderEventAny::Filled(filled) => filled,
        other => panic!("Expected OrderFilled event second, was {other:?}"),
    };

    assert_eq!(triggered.client_order_id, client_order_id);
    assert_eq!(filled.client_order_id, client_order_id);
    assert_eq!(filled.liquidity_side, LiquiditySide::Taker);
}

#[rstest]
fn test_passive_post_only_stop_limit_rejected_when_triggered_as_taker(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("2.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&ask).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_limit = OrderTestBuilder::new(OrderType::StopLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1505.00"))
        .price(Price::from("1506.00"))
        .quantity(Quantity::from("1.000"))
        .post_only(true)
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_limit, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1505.00"),
        Quantity::from("1.000"),
        AggressorSide::Buyer,
        TradeId::new("trigger-1"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine_l2.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);

    let triggered = match saved_messages.first().unwrap() {
        OrderEventAny::Triggered(triggered) => triggered,
        other => panic!("Expected OrderTriggered event first, was {other:?}"),
    };
    let rejected = match saved_messages.get(1).unwrap() {
        OrderEventAny::Rejected(rejected) => rejected,
        other => panic!("Expected OrderRejected event second, was {other:?}"),
    };

    assert_eq!(triggered.client_order_id, client_order_id);
    assert_eq!(rejected.client_order_id, client_order_id);
    assert!(rejected.due_post_only);
    assert!(!engine_l2.order_exists(client_order_id));
}

#[rstest]
#[case::stop_limit(OrderType::StopLimit, "1495.00")]
#[case::limit_if_touched(OrderType::LimitIfTouched, "1500.00")]
fn test_immediate_post_only_limit_style_trigger_rejected_as_taker(
    #[case] order_type: OrderType,
    #[case] trigger_price: &str,
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("2.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&ask).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut order = OrderTestBuilder::new(order_type)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from(trigger_price))
        .price(Price::from("1505.00"))
        .quantity(Quantity::from("1.000"))
        .post_only(true)
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 3);

    let accepted = match saved_messages.first().unwrap() {
        OrderEventAny::Accepted(accepted) => accepted,
        event => panic!("Expected OrderAccepted event first, was {event:?}"),
    };
    let triggered = match saved_messages.get(1).unwrap() {
        OrderEventAny::Triggered(triggered) => triggered,
        event => panic!("Expected OrderTriggered event second, was {event:?}"),
    };
    let rejected = match saved_messages.get(2).unwrap() {
        OrderEventAny::Rejected(rejected) => rejected,
        event => panic!("Expected OrderRejected event third, was {event:?}"),
    };

    assert_eq!(accepted.client_order_id, client_order_id);
    assert_eq!(triggered.client_order_id, client_order_id);
    assert_eq!(rejected.client_order_id, client_order_id);
    assert!(rejected.due_post_only);
    assert!(!engine_l2.order_exists(client_order_id));
}

#[rstest]
fn test_passive_stop_limit_rekeys_to_limit_after_trigger(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("2.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&ask).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_limit = OrderTestBuilder::new(OrderType::StopLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1505.00"))
        .price(Price::from("1502.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_limit, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1505.00"),
        Quantity::from("1.000"),
        AggressorSide::Buyer,
        TradeId::new("trigger-1"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine_l2.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    assert!(matches!(
        saved_messages.first().unwrap(),
        OrderEventAny::Triggered(_)
    ));
    assert!(engine_l2.order_exists(client_order_id));
    clear_order_event_handler_messages(&order_event_handler);

    let bid = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("1499.00"),
            Quantity::from("1.000"),
            2,
        ))
        .ts_init(UnixNanos::from(3u64))
        .build();
    engine_l2.process_order_book_delta(&bid).unwrap();

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let filled = match saved_messages.first().unwrap() {
        OrderEventAny::Filled(filled) => filled,
        other => panic!("Expected OrderFilled event after re-key, was {other:?}"),
    };

    assert_eq!(filled.client_order_id, client_order_id);
    assert_eq!(filled.liquidity_side, LiquiditySide::Maker);
}

#[rstest]
fn test_process_cancel_command_valid(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    // Create normal l2 engine without reject_stop_orders config param
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    // Create BUY LIMIT order below current ask, so it won't be filled
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    // Create cancel command for limit order above
    let cancel_command = CancelOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );

    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();
    engine_l2.process_order(&mut limit_order, account_id);
    engine_l2.process_cancel(&cancel_command, account_id);

    // Check we have received OrderAccepted and then OrderCanceled event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let canceled = match event2 {
        OrderEventAny::Canceled(canceled) => canceled,
        _ => panic!("Expected OrderCanceled event in second message"),
    };
    assert_eq!(canceled.client_order_id, client_order_id);
}

#[rstest]
fn test_process_cancel_command_order_not_found(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
) {
    // Create normal l2 engine without reject_stop_orders config param
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let account_id = AccountId::from("ACCOUNT-001");
    let cancel_command = CancelOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );

    // Process cancel command for order which doesn't exists
    engine_l2.process_cancel(&cancel_command, account_id);

    // Check we have received OrderCancelRejected event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let event = saved_messages.first().unwrap();
    let rejected = match event {
        OrderEventAny::CancelRejected(rejected) => rejected,
        _ => panic!("Expected OrderRejected event in first message"),
    };
    assert_eq!(rejected.client_order_id, client_order_id);
    assert_eq!(
        rejected.reason,
        Ustr::from(format!("Order {client_order_id} not found").as_str())
    );
}

#[rstest]
fn test_process_cancel_all_command(instrument_eth_usdt: InstrumentAny, account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create 3 limit orders which are not filled (2 from the same instrument and 1 from different instrument)
    // and update the cache
    // as orders opened from cancel_all command will be read from open_orders cache function
    let client_order_id_1 = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order_1 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_1)
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(limit_order_1.clone(), None, None, false)
        .unwrap();
    engine_l2.process_order(&mut limit_order_1, account_id);

    let client_order_id_2 = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut limit_order_2 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1496.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_2)
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(limit_order_2.clone(), None, None, false)
        .unwrap();
    engine_l2.process_order(&mut limit_order_2, account_id);

    let client_order_id_3 = ClientOrderId::from("O-19700101-000000-001-001-3");
    let mut limit_order_3 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(InstrumentId::from("BTCUSDT-PERP.BINANCE")) // <-- Different instrument
        .side(OrderSide::Buy)
        .price(Price::from("1497.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_3)
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(limit_order_3.clone(), None, None, false)
        .unwrap();
    engine_l2.process_order(&mut limit_order_3, account_id);

    // Create cancel all order which related to only ETHUSDT-PERP.BINANCE instrument
    let cancel_all_command = CancelAllOrders::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        OrderSide::Buy,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_cancel_all(&cancel_all_command, account_id);

    // Check we have received 3 OrderAccepted and 2 OrderCanceled events
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 5);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id_1);
    let event2 = saved_messages.get(1).unwrap();
    let accepted = match event2 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in second message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id_2);
    let event3 = saved_messages.get(2).unwrap();
    let accepted = match event3 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in third message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id_3);
    let event4 = saved_messages.get(3).unwrap();
    let canceled1 = match event4 {
        OrderEventAny::Canceled(canceled) => canceled,
        _ => panic!("Expected OrderCanceled event in fourth message"),
    };
    let event5 = saved_messages.get(4).unwrap();
    let canceled2 = match event5 {
        OrderEventAny::Canceled(canceled) => canceled,
        _ => panic!("Expected OrderCanceled event in fifth message"),
    };
    // Because of nondeterministic order of events we need to check both client order ids
    let ids = [canceled1.client_order_id, canceled2.client_order_id];
    assert!(ids.contains(&client_order_id_1));
    assert_eq!(canceled1.instrument_id, instrument_eth_usdt.id());
    assert!(ids.contains(&client_order_id_2));
    assert_eq!(canceled2.instrument_id, instrument_eth_usdt.id());
}

#[rstest]
fn test_process_batch_cancel_command(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), Some(cache), None, None, None);

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create 2 limits order which will be canceled with batch cancel command
    let client_order_id_1 = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order_1 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_1)
        .submit(true)
        .build();
    let client_order_id_2 = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut limit_order_2 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1496.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_2)
        .submit(true)
        .build();

    engine_l2.process_order(&mut limit_order_1, account_id);
    engine_l2.process_order(&mut limit_order_2, account_id);

    let cancel_1 = CancelOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id_1,
        Some(VenueOrderId::from("V1")),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    let cancel_2 = CancelOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id_2,
        Some(VenueOrderId::from("V2")),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    let batch_cancel_command = BatchCancelOrders::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        vec![cancel_1, cancel_2],
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );

    engine_l2.process_batch_cancel(&batch_cancel_command, account_id);

    // Check we have received 2 OrderAccepted and 2 OrderCanceled events
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 4);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id_1);
    let event2 = saved_messages.get(1).unwrap();
    let accepted = match event2 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in second message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id_2);
    let event3 = saved_messages.get(2).unwrap();
    let canceled1 = match event3 {
        OrderEventAny::Canceled(canceled) => canceled,
        _ => panic!("Expected OrderCanceled event in third message"),
    };
    let event4 = saved_messages.get(3).unwrap();
    let _canceled2 = match event4 {
        OrderEventAny::Canceled(canceled) => canceled,
        _ => panic!("Expected OrderCanceled event in fourth message"),
    };
    assert_eq!(canceled1.client_order_id, client_order_id_1);
}

#[rstest]
fn test_process_cancel_skips_already_canceled_order(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    // The matching core still tracks the order (no cancel has gone through the
    // engine yet) but the cached order is already terminal because an external
    // cancel event was applied. process_cancel must drop the stale command
    // instead of emitting a duplicate OrderCanceled.
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2.process_order(&mut limit_order, account_id);

    let cached_order = cache.borrow().order(&client_order_id).unwrap().clone();
    let canceled_event =
        TestOrderEventStubs::canceled(&cached_order, account_id, Some(VenueOrderId::from("V1")));
    cache
        .borrow_mut()
        .order_mut(&client_order_id)
        .unwrap()
        .apply(canceled_event)
        .unwrap();

    clear_order_event_handler_messages(&order_event_handler);

    let cancel_command = CancelOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    assert!(
        engine_l2.order_exists(client_order_id),
        "matching core should still hold the order before process_cancel runs",
    );

    engine_l2.process_cancel(&cancel_command, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert!(
        saved_messages.is_empty(),
        "expected no events for cancel of already-terminal order, found {saved_messages:?}",
    );
    assert!(
        !engine_l2.order_exists(client_order_id),
        "stale matching-core entry should be purged when the gate fires",
    );
}

#[rstest]
fn test_iterate_purges_already_canceled_order_from_core(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2.process_order(&mut limit_order, account_id);

    let cached_order = cache.borrow().order(&client_order_id).unwrap().clone();
    let canceled_event =
        TestOrderEventStubs::canceled(&cached_order, account_id, Some(VenueOrderId::from("V1")));
    cache
        .borrow_mut()
        .order_mut(&client_order_id)
        .unwrap()
        .apply(canceled_event)
        .unwrap();

    clear_order_event_handler_messages(&order_event_handler);

    assert!(
        engine_l2.order_exists(client_order_id),
        "matching core should still hold the order before iterate runs",
    );

    engine_l2.iterate(UnixNanos::from(1), AggressorSide::NoAggressor);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert!(
        saved_messages.is_empty(),
        "expected no events while purging stale closed order, found {saved_messages:?}",
    );
    assert!(
        !engine_l2.order_exists(client_order_id),
        "stale closed order should be purged during matching iteration",
    );
}

#[rstest]
fn test_process_cancel_all_skips_orders_closed_by_contingent_cascade(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
    engine_config: OrderMatchingEngineConfig,
) {
    // Two OUO-linked limit orders. Cancelling the first leg cascades through
    // cancel_contingent_orders, closing the second leg before the loop reaches
    // it. The per-iteration re-fetch must observe that and skip cancelling it
    // a second time.
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        Some(engine_config),
        None,
    );

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let client_order_id_1 = ClientOrderId::from("O-19700101-000000-001-001-1");
    let client_order_id_2 = ClientOrderId::from("O-19700101-000000-001-001-2");

    let mut limit_order_1 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_1)
        .contingency_type(ContingencyType::Ouo)
        .linked_order_ids(vec![client_order_id_2])
        .submit(true)
        .build();

    let mut limit_order_2 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1496.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_2)
        .contingency_type(ContingencyType::Ouo)
        .linked_order_ids(vec![client_order_id_1])
        .submit(true)
        .build();

    cache
        .borrow_mut()
        .add_order(limit_order_1.clone(), None, None, false)
        .unwrap();
    cache
        .borrow_mut()
        .add_order(limit_order_2.clone(), None, None, false)
        .unwrap();

    engine_l2.process_order(&mut limit_order_1, account_id);
    engine_l2.process_order(&mut limit_order_2, account_id);

    clear_order_event_handler_messages(&order_event_handler);

    let cancel_all_command = CancelAllOrders::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        OrderSide::Buy,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_cancel_all(&cancel_all_command, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let canceled_ids: Vec<_> = saved_messages
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Canceled(c) => Some(c.client_order_id),
            _ => None,
        })
        .collect();

    assert_eq!(
        canceled_ids.len(),
        2,
        "expected 2 canceled events (one per leg), found {saved_messages:?}",
    );
    assert!(canceled_ids.contains(&client_order_id_1));
    assert!(canceled_ids.contains(&client_order_id_2));
}

#[rstest]
fn test_expire_order(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    // Create order matching engine with gtd support
    let engine_config = OrderMatchingEngineConfig {
        support_gtd_orders: true,
        ..Default::default()
    };
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        None,
        None,
        Some(engine_config),
        None,
    );

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create GTD LIMIT order which will expire after we process tick
    // that has higher timestamp than expire_time.
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let expire_time = DateTime::parse_from_rfc3339("2019-10-23T10:32:49.669Z")
        .unwrap()
        .with_timezone(&Utc)
        .timestamp_nanos_opt()
        .unwrap();
    let tick_time = DateTime::parse_from_rfc3339("2025-10-23T10:32:50.000Z")
        .unwrap()
        .with_timezone(&Utc)
        .timestamp_nanos_opt()
        .unwrap();

    let mut limit_order_expire = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .expire_time(UnixNanos::from(expire_time as u64))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    let tick = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1500.00"),
        Quantity::from("1.000"),
        AggressorSide::Buyer,
        TradeId::new("1"),
        UnixNanos::from(tick_time as u64),
        UnixNanos::from(tick_time as u64),
    );
    engine_l2.process_order(&mut limit_order_expire, account_id);
    engine_l2.process_trade_tick(&tick);

    // Check we have received OrderAccepted and then OrderExpired event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let expired = match event2 {
        OrderEventAny::Expired(expired) => expired,
        _ => panic!("Expected OrderExpired event in second message"),
    };
    assert_eq!(expired.client_order_id, client_order_id);
}

#[rstest]
fn test_process_modify_order_rejected_not_found(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Create modify order command with client order id that didn't pass through the engine
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        None,
        None,
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    // Check if we have received OrderModifyRejected event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let event = saved_messages.first().unwrap();
    let rejected = match event {
        OrderEventAny::ModifyRejected(rejected) => rejected,
        _ => panic!("Expected OrderRejected event in first message"),
    };
    assert_eq!(rejected.client_order_id, client_order_id);
}

// Rejected post-only modify must keep FIFO; helper must not re-key.
#[rstest]
fn test_rejected_post_only_modify_preserves_fifo_priority(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Seed ask so the post-only would-cross check has a value
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let id_first = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_first = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(id_first)
        .post_only(true)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit_first, account_id);

    let id_second = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut limit_second = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(id_second)
        .post_only(true)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit_second, account_id);

    // Modify into ask: post_only rejects without dispatching OrderUpdated
    let modify_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        id_first,
        Some(VenueOrderId::from("V1")),
        None,
        Some(Price::from("1500.00")),
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_command, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let rejected_count = saved_messages
        .iter()
        .filter(|e| matches!(e, OrderEventAny::ModifyRejected(_)))
        .count();
    assert_eq!(rejected_count, 1, "post-only modify should be rejected");
    assert!(
        !saved_messages
            .iter()
            .any(|e| matches!(e, OrderEventAny::Updated(_))),
        "rejected modify must not dispatch OrderUpdated, was {saved_messages:?}",
    );

    let bids = engine_l2.get_core().get_orders_bid();
    assert_eq!(bids.len(), 2, "both orders should remain in core");
    assert_eq!(
        bids[0].client_order_id, id_first,
        "first-inserted order must keep FIFO priority after rejected modify",
    );
    assert_eq!(bids[1].client_order_id, id_second);
}

// Rejected modify must not call `snapshot_queue_position`; queue_ahead
// would otherwise reset and erase the queue progress from prior trades.
#[rstest]
fn test_rejected_post_only_modify_preserves_queue_position(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let (mut engine, cache, handler) = get_l1_queue_position_engine(instrument_eth_usdt.clone());

    // BBO bid=100 size=10, ask=101 size=5
    let quote_initial = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("10.000"),
        Quantity::from("5.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote_initial);

    // Pre-add to cache so OrderAccepted applies and the order reaches
    // is_open=true; otherwise process_modify short-circuits before price_changed.
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("100.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .post_only(true)
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut order, account_id);
    clear_order_event_handler_messages(&handler);

    // Seller aggressor consumes the full visible bid depth: queue_ahead 10 -> 0
    let trade_drain = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Quantity::from("10.000"),
        AggressorSide::Seller,
        TradeId::new("DRAIN"),
        UnixNanos::from(1u64),
        UnixNanos::from(1u64),
    );
    engine.process_trade_tick(&trade_drain);
    assert!(
        get_order_event_handler_messages(&handler)
            .iter()
            .all(|e| !matches!(e, OrderEventAny::Filled(_))),
        "drain trade exactly consumes the queue, must not fill the order yet",
    );

    // Replenish bid_size to 10; price unchanged, so queue_ahead stays at zero
    let quote_replenish = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("10.000"),
        Quantity::from("5.000"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine.process_quote_tick(&quote_replenish);

    // Post-only modify into ask=101: rejected without OrderUpdated; the
    // price_changed gate must skip snapshot_queue_position
    let modify_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        None,
        Some(Price::from("101.00")),
        None,
        UUID4::new(),
        UnixNanos::from(3u64),
        None,
        None, // correlation_id
    );
    engine.process_modify(&modify_command, account_id);
    let after_modify = get_order_event_handler_messages(&handler);
    assert!(
        after_modify
            .iter()
            .any(|e| matches!(e, OrderEventAny::ModifyRejected(_))),
        "post-only modify into ask must be rejected, was {after_modify:?}",
    );
    clear_order_event_handler_messages(&handler);

    // With queue_ahead preserved at zero, this single-unit trade fills.
    // A reset to bid_size=10 would have left it queued.
    let trade_fill = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Quantity::from("1.000"),
        AggressorSide::Seller,
        TradeId::new("FILL"),
        UnixNanos::from(4u64),
        UnixNanos::from(4u64),
    );
    engine.process_trade_tick(&trade_fill);

    let messages = get_order_event_handler_messages(&handler);
    let fill = messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .unwrap_or_else(|| {
            panic!(
                "queue_ahead must have stayed at zero across the rejected modify; \
             single-unit trade should fill the order, was events {messages:?}",
            )
        });
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
}

// Same shape as the post-only FIFO test for a stop-market trigger
// modified into the market.
#[rstest]
fn test_rejected_stop_market_modify_preserves_fifo_priority(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Seed ask so BUY stop trigger checks have a value
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Two BUY StopMarket at the same trigger above ask: both rest in the
    // stop bucket in insertion order
    let id_first = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_first = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1505.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(id_first)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_first, account_id);

    let id_second = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut stop_second = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1505.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(id_second)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_second, account_id);

    // Modify trigger to 1495 (below ask 1500, in the market): rejected
    // without dispatching OrderUpdated
    let modify_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        id_first,
        Some(VenueOrderId::from("V1")),
        None,
        None,
        Some(Price::from("1495.00")),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_command, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let rejected_count = saved_messages
        .iter()
        .filter(|e| matches!(e, OrderEventAny::ModifyRejected(_)))
        .count();
    assert_eq!(
        rejected_count, 1,
        "in-market stop modify should be rejected"
    );
    assert!(
        !saved_messages
            .iter()
            .any(|e| matches!(e, OrderEventAny::Updated(_))),
        "rejected stop modify must not dispatch OrderUpdated, was {saved_messages:?}",
    );

    let bids = engine_l2.get_core().get_orders_bid();
    assert_eq!(bids.len(), 2, "both stops should remain in core");
    assert_eq!(
        bids[0].client_order_id, id_first,
        "first-inserted stop must keep FIFO priority after rejected modify",
    );
    assert_eq!(bids[1].client_order_id, id_second);
}

#[rstest]
fn test_update_limit_order_post_only_matched(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create BUY LIMIT order post only which will won't be filled
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .post_only(true)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit_order, account_id);

    // Create ModifyOrder command to update price of the order to be matched
    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        None,
        Some(Price::from("1500.00")), // Set price which will be matched
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    // Check that we have received OrderAccepted and then OrderModifyRejected event because of post only
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let rejected = match event2 {
        OrderEventAny::ModifyRejected(rejected) => rejected,
        _ => panic!("Expected OrderModifyRejected event in second message"),
    };
    assert_eq!(rejected.client_order_id, client_order_id);
    assert_eq!(
        rejected.reason,
        Ustr::from(
            "POST_ONLY LIMIT BUY order with new limit px of 1500.00 would have been a TAKER: bid=None, ask=1500.00"
        )
    );
}

#[rstest]
fn test_update_limit_order_valid(instrument_eth_usdt: InstrumentAny, account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create BUY LIMIT order post only which will won't be filled
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(limit_order.clone(), None, None, false)
        .unwrap();
    engine_l2.process_order(&mut limit_order, account_id);

    // Create ModifyOrder command to update price to 1500.00 where it will be matched immediately
    let new_limit_price = Price::from("1500.00");
    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        None,
        Some(new_limit_price),
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    // Check that we have received OrderAccepted and then OrderUpdated
    // and then OrderFilled as limit order was matched immediately
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 3);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let updated = match event2 {
        OrderEventAny::Updated(updated) => updated,
        _ => panic!("Expected OrderUpdated event in second message"),
    };
    assert_eq!(updated.client_order_id, client_order_id);
    assert_eq!(updated.price.unwrap(), new_limit_price);
    let event3 = saved_messages.get(2).unwrap();
    let fill = match event3 {
        OrderEventAny::Filled(fill) => fill,
        _ => panic!("Expected OrderFilled event in third message"),
    };
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_px, new_limit_price);
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_update_stop_market_order_valid(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create BUY STOP MARKET order which is not activated as trigger price of 1505.00 is above current ask of 1500.00
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_market_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1505.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_market_order, account_id);

    // Create modify command which moves trigger price to 1501.00 which won't trigger the stop price
    //  as ask is at 1500.00 and order will be correctly updated
    let new_trigger_price = Price::from("1501.00");
    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        None,
        None,
        Some(new_trigger_price),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    // Check that we have received OrderAccepted and then OrderUpdated
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let updated = match event2 {
        OrderEventAny::Updated(updated) => updated,
        _ => panic!("Expected OrderUpdated event in second message"),
    };
    assert_eq!(updated.client_order_id, client_order_id);
    assert_eq!(updated.trigger_price.unwrap(), new_trigger_price);
}

#[rstest]
fn test_update_stop_limit_order_valid_update_not_triggered(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Create BUY STOP LIMIT order which is not activated as trigger price of 1505.00 is above current ask of 1500.00
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_market_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1502.00"))
        .trigger_price(Price::from("1505.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_market_order, account_id);

    // Create modify command which moves trigger price to 1501.00 which won't trigger the stop price again
    // as ask is at 1500.00 and order will be correctly updated
    let new_trigger_price = Price::from("1501.00");
    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        None,
        None,
        Some(new_trigger_price),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    // Check that we have received OrderAccepted and then OrderUpdated
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let updated = match event2 {
        OrderEventAny::Updated(updated) => updated,
        _ => panic!("Expected OrderUpdated event in second message"),
    };
    assert_eq!(updated.client_order_id, client_order_id);
    assert_eq!(updated.trigger_price.unwrap(), new_trigger_price);
}

#[rstest]
fn test_process_market_if_touched_order_already_triggered(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create MARKET IF TOUCHED order which is already activated as trigger price of 1500.00 is equal to current ask of 1500.00
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_market_order = OrderTestBuilder::new(OrderType::MarketIfTouched)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1500.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_market_order, account_id);

    // Check that order was filled immediately with correct price and quantity
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let event = saved_messages.first().unwrap();
    let fill = match event {
        OrderEventAny::Filled(fill) => fill,
        _ => panic!("Expected OrderFilled event in first message"),
    };
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_px, Price::from("1500.00"));
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_update_market_if_touched_order_valid(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Create MARKET IF TOUCHED order which is not activated as trigger price of 1505.00 is above current ask of 1500.00
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_market_order = OrderTestBuilder::new(OrderType::MarketIfTouched)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1505.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_market_order, account_id);

    // Create modify command which moves trigger price to 1501.00 which won't trigger the stop price again
    // as ask is at 1500.00 and order will be correctly updated
    let new_trigger_price = Price::from("1501.00");
    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        None,
        None,
        Some(new_trigger_price),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    // Check that we have received OrderAccepted and then OrderUpdated
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let updated = match event2 {
        OrderEventAny::Updated(updated) => updated,
        _ => panic!("Expected OrderUpdated event in second message"),
    };
    assert_eq!(updated.client_order_id, client_order_id);
    assert_eq!(updated.trigger_price.unwrap(), new_trigger_price);
}

#[rstest]
fn test_process_limit_if_touched_order_immediate_trigger_and_fill(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create LIMIT IF TOUCHED order which is already activated as
    // trigger price of 1500.00 is equal to current ask of 1500.00
    // and limit price of 1505.00 is above current ask of 1500.00
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_if_touched_order = OrderTestBuilder::new(OrderType::LimitIfTouched)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1500.00"))
        .price(Price::from("1505.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit_if_touched_order, account_id);

    // Check that order was filled immediately with correct price and quantity
    // We should receive OrderAccepted, OrderTriggered and OrderFilled event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 3);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let triggered = match event2 {
        OrderEventAny::Triggered(triggered) => triggered,
        _ => panic!("Expected OrderTriggered event in second message"),
    };
    assert_eq!(triggered.client_order_id, client_order_id);
    let event3 = saved_messages.get(2).unwrap();
    let fill = match event3 {
        OrderEventAny::Filled(fill) => fill,
        _ => panic!("Expected OrderFilled event in third message"),
    };
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_px, Price::from("1500.00"));
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_update_limit_if_touched_order_valid(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create LIMIT IF TOUCHED order which is not activated as trigger price of 1498.00 is below current ask of 1500.00
    let client_order_id = ClientOrderId::new("O-19700101-000000-001-001-1");
    let mut limit_if_touched_order = OrderTestBuilder::new(OrderType::LimitIfTouched)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1498.00"))
        .price(Price::from("1505.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit_if_touched_order, account_id);

    // Create modify command which moves trigger price to 1499.00 which won't trigger the trigger price again
    // as ask is at 1500.00 and order will be correctly updated
    let new_trigger_price = Price::from("1499.00");
    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        None,
        None,
        Some(new_trigger_price),
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    // Check that we have received OrderAccepted and then OrderUpdated
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let updated = match event2 {
        OrderEventAny::Updated(updated) => updated,
        _ => panic!("Expected OrderUpdated event in second message"),
    };
    assert_eq!(updated.client_order_id, client_order_id);
    assert_eq!(updated.trigger_price.unwrap(), new_trigger_price);
}

#[rstest]
fn test_process_market_to_limit_orders_not_fully_filled(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Add SELL limit orderbook delta to have ask initialized
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create MARKET TO LIMIT order with quantity of 2 which will be half filled
    // and order half will be accepted as limit order
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut market_to_limit_order = OrderTestBuilder::new(OrderType::MarketToLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("2.000"))
        .client_order_id(client_order_id)
        .build();
    // Make order submitted
    let submitted = TestOrderEventStubs::submitted(&market_to_limit_order, account_id);
    market_to_limit_order.apply(submitted).unwrap();
    engine_l2.process_order(&mut market_to_limit_order, account_id);

    // Check sequence of events for MARKET-TO-LIMIT order being not fully filled
    // 1. OrderUpdated - order was updated to new limix price where market order stopped filling
    // 2. OrderFilled - market order which was filled emits filled event
    // 3. OrderAccepted - remaining quantity of market order is accepted as limit order
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 3);
    let event1 = saved_messages.first().unwrap();
    let updated = match event1 {
        OrderEventAny::Updated(order) => order,
        _ => panic!("Expected OrderUpdated event in first message"),
    };
    assert_eq!(updated.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let fill = match event2 {
        OrderEventAny::Filled(order) => order,
        _ => panic!("Expected OrderFilled event in second message"),
    };
    assert_eq!(fill.client_order_id, client_order_id);
    let event3 = saved_messages.get(2).unwrap();
    let accepted = match event3 {
        OrderEventAny::Accepted(order) => order,
        _ => panic!("Expected OrderFilled event in third message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_px, Price::from("1500.00"));
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
    // Check that we have one resting limit order in the matching core
    let resting_orders = engine_l2.get_core().get_orders();
    assert_eq!(resting_orders.len(), 1);
    let first_order = resting_orders.first().unwrap();
    assert_eq!(first_order.client_order_id, client_order_id);
}

#[rstest]
fn test_process_trailing_stop_orders_rejeceted_and_valid(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create two trailing stop orders
    // 1. TrailingStopMarket BUY order with trigger price of 1498.00 which will be rejected as trigger price is below current ask
    // 2. TrailingStopLimit BUY order with trigger price of 1502.00 which will be accepted as trigger price is above current ask
    let client_order_id_trailing_stop_market = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut trailing_stop_market = OrderTestBuilder::new(OrderType::TrailingStopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1498.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_trailing_stop_market)
        .trailing_offset(dec!(0.1))
        .limit_offset(dec!(0.1))
        .trailing_offset_type(TrailingOffsetType::Price)
        .submit(true)
        .build();
    let client_order_id_trailing_stop_limit = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut trailing_stop_limit = OrderTestBuilder::new(OrderType::TrailingStopLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1502.00"))
        .price(Price::from("1500.00"))
        .quantity(Quantity::from("1.000"))
        .trailing_offset(dec!(0.1))
        .limit_offset(dec!(0.1))
        .trailing_offset_type(TrailingOffsetType::Price)
        .client_order_id(client_order_id_trailing_stop_limit)
        .submit(true)
        .build();
    engine_l2.process_order(&mut trailing_stop_market, account_id);
    engine_l2.process_order(&mut trailing_stop_limit, account_id);

    // Check that we have received OrderRejected for TrailingStopMarket order
    // and OrderAccepted for TrailingStopLimit order
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let rejected = match event1 {
        OrderEventAny::Rejected(rejected) => rejected,
        _ => panic!("Expected OrderRejected event in first message"),
    };
    assert_eq!(
        rejected.client_order_id,
        client_order_id_trailing_stop_market
    );
    let event2 = saved_messages.get(1).unwrap();
    let accepted = match event2 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in second message"),
    };
    assert_eq!(
        accepted.client_order_id,
        client_order_id_trailing_stop_limit
    );
}

#[rstest]
fn test_updating_of_trailing_stop_market_order_with_no_trigger_price_set(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create TrailingStopMarket BUY order which is not triggered
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut trailing_stop_market = OrderTestBuilder::new(OrderType::TrailingStopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .trigger_price(Price::from("1505.00"))
        // Explicitly use LAST_PRICE here because this test drives the trailing update via TradeTicks.
        // TriggerType::Default is documented to behave like BID_ASK (quote-based) and will not update
        // from last-trade-only data.
        .trigger_type(TriggerType::LastPrice)
        .trailing_offset(dec!(1))
        .limit_offset(dec!(1))
        .trailing_offset_type(TrailingOffsetType::Price)
        .submit(true)
        .build();
    engine_l2.process_order(&mut trailing_stop_market, account_id);

    // Move the market down to 1480.00 so that Trailing buy order is recalculated
    let tick = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1480.00"),
        Quantity::from("1.000"),
        AggressorSide::Seller,
        TradeId::from("1"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine_l2.process_trade_tick(&tick);

    // Check that we have received OrderAccepted and then OrderUpdated
    // with new trigger price of 1481.00 because of trailing offset of 1
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id);
    let event2 = saved_messages.get(1).unwrap();
    let updated = match event2 {
        OrderEventAny::Updated(updated) => updated,
        _ => panic!("Expected OrderUpdated event in second message"),
    };
    assert_eq!(updated.client_order_id, client_order_id);
    assert_eq!(updated.trigger_price.unwrap(), Price::from("1481.00"));
}

#[rstest]
#[case(ContingencyType::Oco)]
#[case(ContingencyType::Ouo)]
fn test_updating_of_contingent_orders(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
    #[case] contingency_type: ContingencyType,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let engine_config = OrderMatchingEngineConfig {
        support_contingent_orders: true,
        ..Default::default()
    };
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        Some(engine_config),
        None,
    );

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let client_order_id_primary = ClientOrderId::from("O-19700101-000000-001-001-1");
    let client_order_id_contingent = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut primary_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_primary)
        .contingency_type(contingency_type)
        .linked_order_ids(vec![client_order_id_contingent])
        .submit(true)
        .build();
    let mut contingent_stop_market_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("1500.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_contingent)
        .linked_order_ids(vec![client_order_id_primary])
        .contingency_type(contingency_type)
        .submit(true)
        .build();

    cache
        .borrow_mut()
        .add_order(primary_order.clone(), None, None, false)
        .unwrap();
    cache
        .borrow_mut()
        .add_order(contingent_stop_market_order.clone(), None, None, false)
        .unwrap();
    engine_l2.process_order(&mut primary_order, account_id);

    engine_l2.process_order(&mut contingent_stop_market_order, account_id);

    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id_primary,
        Some(VenueOrderId::from("V1")),
        Some(Quantity::from("2.000")),
        None,
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 4);
    let event1 = saved_messages.first().unwrap();
    let accepted = match event1 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in first message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id_primary);
    let event2 = saved_messages.get(1).unwrap();
    let accepted = match event2 {
        OrderEventAny::Accepted(accepted) => accepted,
        _ => panic!("Expected OrderAccepted event in second message"),
    };
    assert_eq!(accepted.client_order_id, client_order_id_contingent);
    let event3 = saved_messages.get(2).unwrap();
    let updated = match event3 {
        OrderEventAny::Updated(updated) => updated,
        _ => panic!("Expected OrderUpdated event in third message"),
    };
    assert_eq!(updated.client_order_id, client_order_id_primary);
    assert_eq!(updated.quantity, Quantity::from("2.000"));
    let event4 = saved_messages.get(3).unwrap();
    let updated = match event4 {
        OrderEventAny::Updated(updated) => updated,
        _ => panic!("Expected OrderUpdated event in fourth message"),
    };
    assert_eq!(updated.client_order_id, client_order_id_contingent);
    assert_eq!(updated.quantity, Quantity::from("2.000"));
}

#[rstest]
fn test_reduce_only_order_exceeding_position_quantity(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    engine_config: OrderMatchingEngineConfig,
) {
    // Reproduces bug where reduce-only order exceeding position causes panic

    let mut engine = get_order_matching_engine(
        instrument_eth_usdt.clone(),
        None,
        None,
        Some(engine_config),
        None,
    );

    let mut buy_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("79.000"))
        .submit(true)
        .build();

    engine.process_order(&mut buy_order, account_id);

    let mut reduce_only_sell = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("80.000")) // Exceeds position quantity
        .price(Price::from("1500.00"))
        .reduce_only(true)
        .submit(true)
        .build();

    engine.process_order(&mut reduce_only_sell, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert!(saved_messages.len() >= 2, "Should have at least 2 events");

    let has_quantity_update = saved_messages.iter().any(|event| {
        matches!(event, OrderEventAny::Updated(updated) if updated.quantity == Quantity::from("79.000"))
    });

    assert!(
        has_quantity_update || !saved_messages.is_empty(),
        "Order should be processed without panic"
    );
}

#[rstest]
fn test_reduce_only_stop_market_caps_cumulative_multi_level_fill(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    cache
        .borrow_mut()
        .add_instrument(instrument_eth_usdt.clone())
        .unwrap();

    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    let position_id = PositionId::new(
        format!(
            "{}-{}",
            instrument_eth_usdt.id(),
            StrategyId::test_default()
        )
        .as_str(),
    );
    let opening_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("2382.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-OPEN"))
        .build();
    let opening_fill = build_order_filled(
        opening_order.trader_id(),
        opening_order.strategy_id(),
        opening_order.instrument_id(),
        opening_order.client_order_id(),
        VenueOrderId::from("V-OPEN"),
        account_id,
        TradeId::new("E-OPEN"),
        opening_order.order_side(),
        opening_order.order_type(),
        opening_order.quantity(),
        Price::from("1495.00"),
        instrument_eth_usdt.quote_currency(),
        LiquiditySide::Taker,
        Some(position_id),
        None,
    );
    let position = Position::new(&instrument_eth_usdt, opening_fill);
    cache
        .borrow_mut()
        .add_position(&position, OmsType::Netting)
        .unwrap();

    let pre_trigger_bid = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("1491.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&pre_trigger_bid)
        .unwrap();

    for (order_id, price, quantity) in [
        (2, "1490.00", "1000.000"),
        (3, "1489.00", "1000.000"),
        (4, "1488.00", "1000.000"),
    ] {
        let bid = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from(price),
                Quantity::from(quantity),
                order_id,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid).unwrap();
    }

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-RO");
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("1490.00"))
        .quantity(Quantity::from("7142.000"))
        .client_order_id(client_order_id)
        .reduce_only(true)
        .submit(true)
        .build();
    engine_l2.process_order(&mut stop_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    let trigger_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Delete)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("1491.00"),
            Quantity::from("0.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&trigger_delta).unwrap();

    let messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) if fill.client_order_id == client_order_id => Some(fill),
            _ => None,
        })
        .collect();
    let updated = messages
        .iter()
        .find_map(|event| match event {
            OrderEventAny::Updated(updated) if updated.client_order_id == client_order_id => {
                Some(updated)
            }
            _ => None,
        })
        .expect("Expected reduce-only order quantity update");

    assert_eq!(fills.len(), 3);
    assert_eq!(fills[0].last_qty, Quantity::from("1000.000"));
    assert_eq!(fills[1].last_qty, Quantity::from("1000.000"));
    assert_eq!(fills[2].last_qty, Quantity::from("382.000"));
    assert_eq!(updated.quantity, Quantity::from("2382.000"));

    let cache_ref = cache.borrow();
    let order = cache_ref
        .order(&client_order_id)
        .expect("Expected reduce-only order in cache");
    assert_eq!(order.quantity(), Quantity::from("2382.000"));
    assert_eq!(order.filled_qty(), Quantity::from("2382.000"));
    assert_eq!(order.status(), OrderStatus::Filled);
}

#[rstest]
fn test_process_market_orders_with_protection_rejeceted_and_valid(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig::builder()
        .trade_execution(false)
        .price_protection_points(600u32)
        .build();

    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let _ = engine_l2.process_order_book_delta(&orderbook_delta_sell);

    // Create two market orders with Protection
    // 1. Market SELL order will be rejected as the Bid Side of the book is empty
    // 2. Market BUY order will be accepted as there is an order on the Ask side of the book
    let client_order_id_market_buy = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut market_buy_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_market_buy)
        .submit(true)
        .build();

    let client_order_id_market_sell = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut market_sell_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_market_sell)
        .submit(true)
        .build();

    engine_l2.process_order(&mut market_sell_order, account_id);
    engine_l2.process_order(&mut market_buy_order, account_id);

    // Protection is computed at fill time (trigger-time semantics), so no OrderUpdated event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);

    let rejected = match saved_messages.first().unwrap() {
        OrderEventAny::Rejected(rejected) => rejected,
        _ => panic!("Expected OrderRejected event in first message"),
    };
    assert_eq!(rejected.client_order_id, client_order_id_market_sell);
    assert_eq!(rejected.reason, "No market for ETHUSDT-PERP.BINANCE");

    let filled = match saved_messages.get(1).unwrap() {
        OrderEventAny::Filled(filled) => filled,
        _ => panic!("Expected Filled event in second message"),
    };
    assert_eq!(filled.client_order_id, client_order_id_market_buy);
}

#[rstest]
fn test_process_stop_orders_with_protection_both_accepted(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    // With trigger-time semantics, stop orders don't require bid/ask at submission
    // Protection is computed when the stop triggers
    let config = OrderMatchingEngineConfig::builder()
        .trade_execution(false)
        .price_protection_points(600u32)
        .build();

    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    let _ = engine_l2.process_order_book_delta(&orderbook_delta_sell);

    // Create two Stop Market orders with Protection
    // Both are accepted because stops not triggered - protection computed at fill time
    let client_order_id_market_buy = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut market_buy_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .trigger_price(Price::new(1501.0, 2))
        .client_order_id(client_order_id_market_buy)
        .submit(true)
        .build();

    let client_order_id_market_sell = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut market_sell_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("1.000"))
        .trigger_price(Price::new(1499.0, 2))
        .client_order_id(client_order_id_market_sell)
        .submit(true)
        .build();

    engine_l2.process_order(&mut market_sell_order, account_id);
    engine_l2.process_order(&mut market_buy_order, account_id);

    // Both stops accepted (not triggered), protection computed at fill time
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);

    let accepted1 = match saved_messages.first().unwrap() {
        OrderEventAny::Accepted(accepted) => accepted,
        other => panic!("Expected OrderAccepted event in first message, was {other:?}"),
    };
    assert_eq!(accepted1.client_order_id, client_order_id_market_sell);

    let accepted2 = match saved_messages.get(1).unwrap() {
        OrderEventAny::Accepted(accepted) => accepted,
        other => panic!("Expected OrderAccepted event in second message, was {other:?}"),
    };
    assert_eq!(accepted2.client_order_id, client_order_id_market_buy);
}

#[rstest]
fn test_protection_filtered_fills_do_not_consume_liquidity(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    // Protection filters out fills beyond the boundary.
    // Those filtered fills should NOT count toward liquidity consumption.
    // We verify this by: order1 filters 1003, then order2 can still fill all 5 at 1003
    let config = OrderMatchingEngineConfig {
        liquidity_consumption: true,
        price_protection_points: Some(100), // 100 points = 1.00 offset
        ..Default::default()
    };

    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("1000.00"),
            Quantity::from("100.000"),
            0,
        ))
        .build();
    let _ = engine_l2.process_order_book_delta(&bid_delta);

    // Protection = 1001 + 1.00 = 1002, so 1001/1002 pass, 1003 filtered
    let ask_delta_1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1001.00"),
            Quantity::from("2.000"),
            1,
        ))
        .build();
    let _ = engine_l2.process_order_book_delta(&ask_delta_1);

    let ask_delta_2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1002.00"),
            Quantity::from("5.000"),
            2,
        ))
        .build();
    let _ = engine_l2.process_order_book_delta(&ask_delta_2);

    let ask_delta_3 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1003.00"),
            Quantity::from("5.000"),
            3,
        ))
        .build();
    let _ = engine_l2.process_order_book_delta(&ask_delta_3);

    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("10.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order1, account_id);

    let messages = get_order_event_handler_messages(&order_event_handler);
    let fills1: Vec<_> = messages
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();
    let total_filled_1: f64 = fills1.iter().map(|f| f.last_qty.as_f64()).sum();
    assert_eq!(
        total_filled_1, 7.0,
        "Order1 should fill 7 units (2 at 1001 + 5 at 1002)"
    );

    clear_order_event_handler_messages(&order_event_handler);

    // Order2 SELL uses bid-side (unaffected by ask-side consumption from order1)
    let mut order2 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order2, account_id);

    let messages = get_order_event_handler_messages(&order_event_handler);
    let fill2 = messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Expected fill for order2");

    // Sell should fill at bid 1000, protection = 1000 - 1 = 999, so 1000 is within protection
    assert_eq!(fill2.last_qty, Quantity::from("5.000"));
    assert_eq!(fill2.last_px, Price::from("1000.00"));
}

#[rstest]
fn test_process_mark_bar_skipped_without_panic(instrument_eth_usdt: InstrumentAny) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine = get_order_matching_engine(instrument_eth_usdt, None, None, Some(config), None);

    // Mark price bars are not supported for bar execution, they must be skipped rather than panic
    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-MARK-EXTERNAL");
    let mark_bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("100000.000"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };

    engine.process_bar(&mark_bar);

    assert!(
        engine.get_core().last.is_none(),
        "Mark price bar should be skipped and not update market state"
    );
}

#[rstest]
fn test_process_mark_bar_does_not_replace_selected_execution_bar(
    instrument_eth_usdt: InstrumentAny,
) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine = get_order_matching_engine(instrument_eth_usdt, None, None, Some(config), None);

    let hourly_bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-HOUR-LAST-EXTERNAL");
    let first_hourly_bar = Bar {
        bar_type: hourly_bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("100000.000"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };
    let mark_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-MARK-EXTERNAL"),
        open: Price::from("1400.00"),
        high: Price::from("1410.00"),
        low: Price::from("1390.00"),
        close: Price::from("1405.00"),
        volume: Quantity::from("100000.000"),
        ts_event: UnixNanos::from(2_000_000_000),
        ts_init: UnixNanos::from(2_000_000_000),
    };
    let second_hourly_bar = Bar {
        bar_type: hourly_bar_type,
        open: Price::from("1600.00"),
        high: Price::from("1610.00"),
        low: Price::from("1590.00"),
        close: Price::from("1605.00"),
        volume: Quantity::from("100000.000"),
        ts_event: UnixNanos::from(3_000_000_000),
        ts_init: UnixNanos::from(3_000_000_000),
    };

    engine.process_bar(&first_hourly_bar);
    engine.process_bar(&mark_bar);
    engine.process_bar(&second_hourly_bar);

    assert_eq!(engine.get_core().last, Some(Price::from("1605.00")));
}

#[rstest]
fn test_process_monthly_bar_not_skipped(instrument_eth_usdt: InstrumentAny) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine = get_order_matching_engine(instrument_eth_usdt, None, None, Some(config), None);

    // Create a monthly bar (EXTERNAL source to ensure it's processed for execution)
    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-MONTH-LAST-EXTERNAL");
    let monthly_bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("100000.000"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };

    // Process the monthly bar
    engine.process_bar(&monthly_bar);

    // Monthly bars should now be processed for execution (LAST price type bars update last price)
    assert!(
        engine.get_core().last.is_some(),
        "Monthly bar should be processed and update market state"
    );
}

#[rstest]
fn test_process_yearly_bar_not_skipped(instrument_eth_usdt: InstrumentAny) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine = get_order_matching_engine(instrument_eth_usdt, None, None, Some(config), None);

    // Create a yearly bar (EXTERNAL source to ensure it's processed for execution)
    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-YEAR-LAST-EXTERNAL");
    let yearly_bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("100000.000"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };

    // Process the yearly bar
    engine.process_bar(&yearly_bar);

    // Yearly bars should now be processed for execution (LAST price type bars update last price)
    assert!(
        engine.get_core().last.is_some(),
        "Yearly bar should be processed and update market state"
    );
}

// Regression: quartering bar volume at `FIXED_PRECISION` scale produced synthesized
// tick sizes whose raw values were not multiples of `10^(FIXED_PRECISION - precision)`.
// Those fills were skipped by `normalize_fill_quantity`.
#[rstest]
fn test_process_trade_bar_fills_order_with_volume_not_divisible_by_four(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 2, "0.01");
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument.clone(), Some(cache), None, Some(config), None);

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("0.01"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Volume = 0.05 at size_precision=2: 5 units, quarter=1, remainder=1.
    // Pre-fix the synthesized close size would have raw = 5*FIXED_SCALAR/4, not
    // aligned to 10^(FIXED_PRECISION-2), and any submitted order would have its
    // fill silently dropped by `normalize_fill_quantity`.
    let bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL"),
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("0.05"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };

    engine.process_bar(&bar);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();

    assert_eq!(fills.len(), 1);
    assert_eq!(fills[0].client_order_id, client_order_id);
    assert_eq!(fills[0].last_qty, Quantity::from("0.01"));
    assert_eq!(fills[0].last_px, Price::from("1490.00"));
    assert_eq!(
        engine.get_core().last,
        Some(Price::from("1505.00")),
        "Bar should advance market state through to the close tick",
    );
}

#[rstest]
fn test_process_trade_bar_with_units_less_than_four_does_not_overfill(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 0, "1");
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument.clone(), Some(cache), None, Some(config), None);

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .price(Price::from("1510.00"))
        .quantity(Quantity::from("6"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Volume = 3 at size_precision=0: one unit funds each open/high/low path tick.
    let bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL"),
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("3"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };

    engine.process_bar(&bar);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();
    let total_qty = fills
        .iter()
        .map(|fill| fill.last_qty.as_decimal())
        .sum::<rust_decimal::Decimal>();

    assert_eq!(total_qty, dec!(3));
    assert!(
        fills
            .iter()
            .all(|fill| fill.client_order_id == client_order_id)
    );
    assert_eq!(
        engine.get_core().last,
        Some(Price::from("1505.00")),
        "Low-volume bar should still advance market state through to the close tick",
    );
}

#[rstest]
fn test_process_trade_bar_with_two_units_fills_orders_at_high_and_low(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 0, "1");
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument.clone(), Some(cache), None, Some(config), None);

    let sell_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let buy_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut sell_limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Sell)
        .price(Price::from("1510.00"))
        .quantity(Quantity::from("1"))
        .client_order_id(sell_client_order_id)
        .submit(true)
        .build();
    let mut buy_limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("1"))
        .client_order_id(buy_client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut sell_limit_order, account_id);
    engine.process_order(&mut buy_limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    let bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL"),
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("2"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };

    engine.process_bar(&bar);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();
    let sell_fill = fills
        .iter()
        .find(|fill| fill.client_order_id == sell_client_order_id)
        .expect("expected sell order fill at bar high");
    let buy_fill = fills
        .iter()
        .find(|fill| fill.client_order_id == buy_client_order_id)
        .expect("expected buy order fill at bar low");

    assert_eq!(fills.len(), 2);
    assert_eq!(sell_fill.last_qty, Quantity::from("1"));
    assert_eq!(sell_fill.last_px, Price::from("1510.00"));
    assert_eq!(buy_fill.last_qty, Quantity::from("1"));
    assert_eq!(buy_fill.last_px, Price::from("1490.00"));
}

#[rstest]
fn test_process_trade_bar_with_three_units_fills_order_at_high(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 0, "1");
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument.clone(), Some(cache), None, Some(config), None);

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Sell)
        .price(Price::from("1508.00"))
        .quantity(Quantity::from("1"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    let bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL"),
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("3"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };

    engine.process_bar(&bar);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();

    assert_eq!(fills.len(), 1);
    assert_eq!(fills[0].client_order_id, client_order_id);
    assert_eq!(fills[0].last_qty, Quantity::from("1"));
    assert_eq!(fills[0].last_px, Price::from("1508.00"));
}

#[rstest]
fn test_process_quote_bar_with_volume_not_divisible_by_four(instrument_eth_usdt: InstrumentAny) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 2, "0.01");
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine = get_order_matching_engine(instrument, None, None, Some(config), None);

    let ts = UnixNanos::from(1_000_000_000);
    // Bid and ask bars at matching ts_init with volumes that have non-zero remainder
    // mod 4 at size_precision=2: bid=0.07 (7 units), ask=0.09 (9 units).
    let bid_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-BID-EXTERNAL"),
        open: Price::from("1499.00"),
        high: Price::from("1500.00"),
        low: Price::from("1498.00"),
        close: Price::from("1499.50"),
        volume: Quantity::from("0.07"),
        ts_event: ts,
        ts_init: ts,
    };
    let ask_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-ASK-EXTERNAL"),
        open: Price::from("1500.00"),
        high: Price::from("1501.00"),
        low: Price::from("1499.00"),
        close: Price::from("1500.50"),
        volume: Quantity::from("0.09"),
        ts_event: ts,
        ts_init: ts,
    };

    // The bug pattern: pre-fix, the synthesized bid/ask quote sizes had unaligned
    // raw values that could fail downstream precision checks.
    engine.process_bar(&bid_bar);
    engine.process_bar(&ask_bar);
}

#[rstest]
fn test_process_quote_bar_with_one_unit_updates_close(instrument_eth_usdt: InstrumentAny) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 0, "1");
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine = get_order_matching_engine(instrument, None, None, Some(config), None);

    let ts = UnixNanos::from(1_000_000_000);
    let bid_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-BID-EXTERNAL"),
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("1"),
        ts_event: ts,
        ts_init: ts,
    };
    let ask_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-ASK-EXTERNAL"),
        open: Price::from("1501.00"),
        high: Price::from("1511.00"),
        low: Price::from("1491.00"),
        close: Price::from("1506.00"),
        volume: Quantity::from("1"),
        ts_event: ts,
        ts_init: ts,
    };

    engine.process_bar(&bid_bar);
    engine.process_bar(&ask_bar);

    assert_eq!(engine.get_core().bid, Some(Price::from("1505.00")));
    assert_eq!(engine.get_core().ask, Some(Price::from("1506.00")));
}

#[rstest]
fn test_process_quote_bar_with_asymmetric_units_updates_positive_sides(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 0, "1");
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument.clone(), Some(cache), None, Some(config), None);

    let sell_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let buy_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut sell_limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Sell)
        .price(Price::from("1510.00"))
        .quantity(Quantity::from("1"))
        .client_order_id(sell_client_order_id)
        .submit(true)
        .build();
    let mut buy_limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .price(Price::from("1506.00"))
        .quantity(Quantity::from("1"))
        .client_order_id(buy_client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut sell_limit_order, account_id);
    engine.process_order(&mut buy_limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    let ts = UnixNanos::from(1_000_000_000);
    let bid_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-BID-EXTERNAL"),
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("3"),
        ts_event: ts,
        ts_init: ts,
    };
    let ask_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-ASK-EXTERNAL"),
        open: Price::from("1501.00"),
        high: Price::from("1511.00"),
        low: Price::from("1491.00"),
        close: Price::from("1506.00"),
        volume: Quantity::from("1"),
        ts_event: ts,
        ts_init: ts,
    };

    engine.process_bar(&bid_bar);
    engine.process_bar(&ask_bar);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();
    let sell_fill = fills
        .iter()
        .find(|fill| fill.client_order_id == sell_client_order_id)
        .expect("expected sell order fill at bar high bid");
    let buy_fill = fills
        .iter()
        .find(|fill| fill.client_order_id == buy_client_order_id)
        .expect("expected buy order fill at bar close ask");

    assert_eq!(fills.len(), 2);
    assert_eq!(sell_fill.last_qty, Quantity::from("1"));
    assert_eq!(sell_fill.last_px, Price::from("1510.00"));
    assert_eq!(buy_fill.last_qty, Quantity::from("1"));
    assert_eq!(buy_fill.last_px, Price::from("1506.00"));
    assert_eq!(engine.get_core().bid, Some(Price::from("1490.00")));
    assert_eq!(engine.get_core().ask, Some(Price::from("1506.00")));
}

#[rstest]
fn test_process_quote_bar_with_zero_ask_volume_clears_previous_ask(
    instrument_eth_usdt: InstrumentAny,
) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 0, "1");
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine = get_order_matching_engine(instrument, None, None, Some(config), None);

    let first_ts = UnixNanos::from(1_000_000_000);
    let first_bid_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-BID-EXTERNAL"),
        open: Price::from("1498.00"),
        high: Price::from("1500.00"),
        low: Price::from("1497.00"),
        close: Price::from("1499.00"),
        volume: Quantity::from("1"),
        ts_event: first_ts,
        ts_init: first_ts,
    };
    let first_ask_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-ASK-EXTERNAL"),
        open: Price::from("1499.00"),
        high: Price::from("1501.00"),
        low: Price::from("1498.00"),
        close: Price::from("1500.00"),
        volume: Quantity::from("1"),
        ts_event: first_ts,
        ts_init: first_ts,
    };

    engine.process_bar(&first_bid_bar);
    engine.process_bar(&first_ask_bar);

    assert_eq!(engine.best_bid_price(), Some(Price::from("1499.00")));
    assert_eq!(engine.best_ask_price(), Some(Price::from("1500.00")));

    let second_ts = UnixNanos::from(2_000_000_000);
    let second_bid_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-BID-EXTERNAL"),
        open: Price::from("1600.00"),
        high: Price::from("1610.00"),
        low: Price::from("1590.00"),
        close: Price::from("1605.00"),
        volume: Quantity::from("3"),
        ts_event: second_ts,
        ts_init: second_ts,
    };
    let second_ask_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-ASK-EXTERNAL"),
        open: Price::from("1601.00"),
        high: Price::from("1611.00"),
        low: Price::from("1591.00"),
        close: Price::from("1606.00"),
        volume: Quantity::from("0"),
        ts_event: second_ts,
        ts_init: second_ts,
    };

    engine.process_bar(&second_bid_bar);
    engine.process_bar(&second_ask_bar);

    assert_eq!(engine.best_bid_price(), Some(Price::from("1590.00")));
    assert_eq!(engine.best_ask_price(), None);
    assert_eq!(engine.get_core().bid, Some(Price::from("1590.00")));
    assert_eq!(engine.get_core().ask, None);
}

#[rstest]
fn test_process_quote_bar_with_zero_close_side_does_not_cross_book(
    instrument_eth_usdt: InstrumentAny,
) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 0, "1");
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine = get_order_matching_engine(instrument, None, None, Some(config), None);

    let ts = UnixNanos::from(1_000_000_000);
    let bid_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-BID-EXTERNAL"),
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("8"),
        ts_event: ts,
        ts_init: ts,
    };
    let ask_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-ASK-EXTERNAL"),
        open: Price::from("1501.00"),
        high: Price::from("1511.00"),
        low: Price::from("1491.00"),
        close: Price::from("1506.00"),
        volume: Quantity::from("3"),
        ts_event: ts,
        ts_init: ts,
    };

    engine.process_bar(&bid_bar);
    engine.process_bar(&ask_bar);

    assert_eq!(engine.best_bid_price(), Some(Price::from("1505.00")));
    assert_eq!(engine.best_ask_price(), None);
    assert_eq!(engine.get_core().bid, Some(Price::from("1505.00")));
    assert_eq!(engine.get_core().ask, None);
}

#[rstest]
fn test_process_quote_bar_with_two_units_fills_orders_at_high_and_low(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 0, "1");
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument.clone(), Some(cache), None, Some(config), None);

    let sell_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let buy_client_order_id = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut sell_limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Sell)
        .price(Price::from("1510.00"))
        .quantity(Quantity::from("1"))
        .client_order_id(sell_client_order_id)
        .submit(true)
        .build();
    let mut buy_limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .price(Price::from("1491.00"))
        .quantity(Quantity::from("1"))
        .client_order_id(buy_client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut sell_limit_order, account_id);
    engine.process_order(&mut buy_limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    let ts = UnixNanos::from(1_000_000_000);
    let bid_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-BID-EXTERNAL"),
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1505.00"),
        volume: Quantity::from("2"),
        ts_event: ts,
        ts_init: ts,
    };
    let ask_bar = Bar {
        bar_type: BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-ASK-EXTERNAL"),
        open: Price::from("1501.00"),
        high: Price::from("1511.00"),
        low: Price::from("1491.00"),
        close: Price::from("1506.00"),
        volume: Quantity::from("2"),
        ts_event: ts,
        ts_init: ts,
    };

    engine.process_bar(&bid_bar);
    engine.process_bar(&ask_bar);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();
    let sell_fill = fills
        .iter()
        .find(|fill| fill.client_order_id == sell_client_order_id)
        .expect("expected sell order fill at bar high bid");
    let buy_fill = fills
        .iter()
        .find(|fill| fill.client_order_id == buy_client_order_id)
        .expect("expected buy order fill at bar low ask");

    assert_eq!(fills.len(), 2);
    assert_eq!(sell_fill.last_qty, Quantity::from("1"));
    assert_eq!(sell_fill.last_px, Price::from("1510.00"));
    assert_eq!(buy_fill.last_qty, Quantity::from("1"));
    assert_eq!(buy_fill.last_px, Price::from("1491.00"));
    assert_eq!(engine.get_core().bid, Some(Price::from("1490.00")));
    assert_eq!(engine.get_core().ask, Some(Price::from("1491.00")));
}

#[rstest]
fn test_modify_partially_filled_order_quantity_below_filled_rejected(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    // Tests that modifying a partially filled order to a quantity below filled_qty is rejected

    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Add SELL limit orderbook delta with partial liquidity
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("0.500"), // Only 0.5 available
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Create BUY LIMIT order that will be partially filled
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1500.00")) // Match at ask to get partial fill
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit_order, account_id);

    // Order should be accepted and partially filled (0.5 of 1.0)
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2);
    let event1 = saved_messages.first().unwrap();
    assert!(
        matches!(event1, OrderEventAny::Accepted(_)),
        "Expected OrderAccepted"
    );
    let event2 = saved_messages.get(1).unwrap();
    let fill = match event2 {
        OrderEventAny::Filled(filled) => filled,
        _ => panic!("Expected OrderFilled event"),
    };
    assert_eq!(fill.last_qty, Quantity::from("0.500"));

    // Clear messages before modify
    clear_order_event_handler_messages(&order_event_handler);

    // Attempt to modify quantity to 0.4, which is below filled_qty of 0.5
    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        Some(Quantity::from("0.400")), // Below filled quantity
        None,
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    // Should receive OrderModifyRejected
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 1);
    let event = saved_messages.first().unwrap();
    let rejected = match event {
        OrderEventAny::ModifyRejected(rejected) => rejected,
        _ => panic!("Expected OrderModifyRejected event, was {event:?}"),
    };
    assert_eq!(rejected.client_order_id, client_order_id);
    assert!(rejected.reason.contains("below filled quantity"));
}

#[rstest]
#[case("0.600", None, Some("0.400"))]
#[case("1.000", None, None)]
#[case("0.600", Some("0.400"), None)]
#[case("0.600", Some("0.600"), None)]
fn test_ouo_sibling_adjusted_after_resolving_order_fill(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
    #[case] fill_qty: &str,
    #[case] sibling_filled_qty: Option<&str>,
    #[case] expected_sibling_quantity: Option<&str>,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let engine_config = OrderMatchingEngineConfig {
        support_contingent_orders: true,
        ..Default::default()
    };
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        Some(engine_config),
        None,
    );

    let client_order_id_resolving = ClientOrderId::from("O-19700101-000000-001-001-1");
    let client_order_id_sibling = ClientOrderId::from("O-19700101-000000-001-001-2");

    let mut resolving_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1500.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_resolving)
        .contingency_type(ContingencyType::Ouo)
        .linked_order_ids(vec![client_order_id_sibling])
        .submit(true)
        .build();

    let mut sibling_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1495.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_sibling)
        .contingency_type(ContingencyType::Ouo)
        .linked_order_ids(vec![client_order_id_resolving])
        .submit(true)
        .build();

    cache
        .borrow_mut()
        .add_order(resolving_order.clone(), None, None, false)
        .unwrap();
    cache
        .borrow_mut()
        .add_order(sibling_order.clone(), None, None, false)
        .unwrap();

    engine_l2.process_order(&mut resolving_order, account_id);
    engine_l2.process_order(&mut sibling_order, account_id);

    if let Some(sibling_filled_qty) = sibling_filled_qty {
        let sibling_order = cache
            .borrow()
            .order(&client_order_id_sibling)
            .map(|order| order.clone())
            .unwrap();
        let sibling_filled = build_order_filled(
            sibling_order.trader_id(),
            sibling_order.strategy_id(),
            sibling_order.instrument_id(),
            sibling_order.client_order_id(),
            sibling_order.venue_order_id().unwrap(),
            sibling_order.account_id().unwrap(),
            TradeId::from("T-SIBLING-001"),
            sibling_order.order_side(),
            sibling_order.order_type(),
            Quantity::from(sibling_filled_qty),
            sibling_order.price().unwrap(),
            instrument_eth_usdt.quote_currency(),
            LiquiditySide::Maker,
            None,
            None,
        );
        cache
            .borrow_mut()
            .update_order(&OrderEventAny::Filled(sibling_filled))
            .unwrap();
    }

    clear_order_event_handler_messages(&order_event_handler);

    let fill_qty = Quantity::from(fill_qty);
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            fill_qty,
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert_eq!(saved_messages.len(), 2, "events: {saved_messages:?}");

    let fill = match saved_messages.first().unwrap() {
        OrderEventAny::Filled(filled) => filled,
        event => panic!("Expected OrderFilled event, was {event:?}"),
    };
    assert_eq!(fill.client_order_id, client_order_id_resolving);
    assert_eq!(fill.last_qty, fill_qty);

    match expected_sibling_quantity {
        Some(quantity) => {
            let updated = match saved_messages.get(1).unwrap() {
                OrderEventAny::Updated(updated) => updated,
                event => panic!("Expected OrderUpdated event, was {event:?}"),
            };
            assert_eq!(updated.client_order_id, client_order_id_sibling);
            assert_eq!(updated.quantity, Quantity::from(quantity));
        }
        None => {
            let canceled = match saved_messages.get(1).unwrap() {
                OrderEventAny::Canceled(canceled) => canceled,
                event => panic!("Expected OrderCanceled event, was {event:?}"),
            };
            assert_eq!(canceled.client_order_id, client_order_id_sibling);
        }
    }
}

#[rstest]
#[case("0.600", None, None, true)]
#[case("1.000", Some("1495.00"), Some("0.400"), false)]
fn test_ouo_child_cancelled_after_parent_modify(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
    #[case] modify_quantity: &str,
    #[case] modify_price: Option<&str>,
    #[case] contingent_filled_qty: Option<&str>,
    #[case] expect_primary_cancel: bool,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let engine_config = OrderMatchingEngineConfig {
        support_contingent_orders: true,
        ..Default::default()
    };
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        Some(engine_config),
        None,
    );

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("0.600"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let orderbook_delta_buy = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("1400.00"),
            Quantity::from("1.000"),
            2,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_buy)
        .unwrap();

    let client_order_id_primary = ClientOrderId::from("O-19700101-000000-001-001-1");
    let client_order_id_contingent = ClientOrderId::from("O-19700101-000000-001-001-2");

    let mut primary_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1500.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_primary)
        .contingency_type(ContingencyType::Ouo)
        .linked_order_ids(vec![client_order_id_contingent])
        .submit(true)
        .build();

    // Use a stop price far below bid to avoid "in the market" rejection
    let mut contingent_stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("1380.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id_contingent)
        .linked_order_ids(vec![client_order_id_primary])
        .contingency_type(ContingencyType::Ouo)
        .submit(true)
        .build();

    cache
        .borrow_mut()
        .add_order(primary_order.clone(), None, None, false)
        .unwrap();
    cache
        .borrow_mut()
        .add_order(contingent_stop_order.clone(), None, None, false)
        .unwrap();

    engine_l2.process_order(&mut primary_order, account_id);
    engine_l2.process_order(&mut contingent_stop_order, account_id);

    if let Some(contingent_filled_qty) = contingent_filled_qty {
        let contingent_stop_order = cache
            .borrow()
            .order(&client_order_id_contingent)
            .map(|order| order.clone())
            .unwrap();
        let contingent_filled = build_order_filled(
            contingent_stop_order.trader_id(),
            contingent_stop_order.strategy_id(),
            contingent_stop_order.instrument_id(),
            contingent_stop_order.client_order_id(),
            contingent_stop_order.venue_order_id().unwrap(),
            contingent_stop_order.account_id().unwrap(),
            TradeId::from("T-CONTINGENT-001"),
            contingent_stop_order.order_side(),
            contingent_stop_order.order_type(),
            Quantity::from(contingent_filled_qty),
            contingent_stop_order.trigger_price().unwrap(),
            instrument_eth_usdt.quote_currency(),
            LiquiditySide::Maker,
            None,
            None,
        );
        cache
            .borrow_mut()
            .update_order(&OrderEventAny::Filled(contingent_filled))
            .unwrap();
    }

    clear_order_event_handler_messages(&order_event_handler);

    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id_primary,
        Some(VenueOrderId::from("V1")),
        Some(Quantity::from(modify_quantity)),
        modify_price.map(Price::from),
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let expected_message_count = if expect_primary_cancel { 3 } else { 2 };
    assert_eq!(
        saved_messages.len(),
        expected_message_count,
        "events: {saved_messages:?}",
    );

    let event1 = saved_messages.first().unwrap();
    let updated_primary = match event1 {
        OrderEventAny::Updated(updated) => updated,
        _ => panic!("Expected OrderUpdated event for primary"),
    };
    assert_eq!(updated_primary.client_order_id, client_order_id_primary);
    assert_eq!(updated_primary.quantity, Quantity::from(modify_quantity));

    let event2 = saved_messages.get(1).unwrap();
    let cancelled_child = match event2 {
        OrderEventAny::Canceled(cancelled) => cancelled,
        _ => panic!("Expected OrderCanceled event for contingent, was {event2:?}"),
    };
    assert_eq!(cancelled_child.client_order_id, client_order_id_contingent);

    if expect_primary_cancel {
        let event3 = saved_messages.get(2).unwrap();
        let cancelled_primary = match event3 {
            OrderEventAny::Canceled(cancelled) => cancelled,
            _ => panic!("Expected OrderCanceled event for primary, was {event3:?}"),
        };
        assert_eq!(cancelled_primary.client_order_id, client_order_id_primary);
    }
}

#[rstest]
#[case(OrderSide::Buy)]
#[case(OrderSide::Sell)]
fn test_liquidity_consumption_tracks_fills_at_price_level(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
) {
    let config = OrderMatchingEngineConfig {
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let orderbook_delta_buy = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("900.00"),
            Quantity::from("100.000"),
            100,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_buy)
        .unwrap();

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("100.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // 3 orders request 130 total but only 100 available
    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("30.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order1, account_id);

    let mut order2 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order2, account_id);

    let mut order3 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-3"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order3, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let filled_events: Vec<_> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();

    assert_eq!(filled_events.len(), 3);
    assert_eq!(filled_events[0].last_qty, Quantity::from("30.000"));
    assert_eq!(filled_events[1].last_qty, Quantity::from("50.000"));
    assert_eq!(filled_events[2].last_qty, Quantity::from("20.000"));
}

#[rstest]
#[case(OrderSide::Buy, OrderSide::Sell)]
#[case(OrderSide::Sell, OrderSide::Buy)]
fn test_liquidity_consumption_resets_on_fresh_data(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
    #[case] book_side: OrderSide,
) {
    let config = OrderMatchingEngineConfig {
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let liquidity_price = if book_side == OrderSide::Sell {
        Price::from("1000.00")
    } else {
        Price::from("900.00")
    };

    // Opposite side liquidity for valid spread
    let opposite_price = if book_side == OrderSide::Sell {
        Price::from("900.00")
    } else {
        Price::from("1000.00")
    };

    let orderbook_delta_opposite = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            if book_side == OrderSide::Sell {
                OrderSide::Buy
            } else {
                OrderSide::Sell
            },
            opposite_price,
            Quantity::from("100.000"),
            100,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_opposite)
        .unwrap();

    let orderbook_delta_target = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            book_side,
            liquidity_price,
            Quantity::from("50.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_target)
        .unwrap();

    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order1, account_id);

    // Update triggers consumption reset
    let orderbook_delta_update = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Update)
        .book_order(BookOrder::new(
            book_side,
            liquidity_price,
            Quantity::from("80.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_update)
        .unwrap();

    let mut order2 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("60.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order2, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let filled_events: Vec<_> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();

    assert_eq!(filled_events.len(), 2);
    assert_eq!(filled_events[0].last_qty, Quantity::from("50.000"));
    assert_eq!(filled_events[1].last_qty, Quantity::from("60.000"));
}

#[rstest]
#[case(OrderSide::Buy)]
#[case(OrderSide::Sell)]
fn test_liquidity_consumption_off_allows_repeated_fills(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
) {
    let config = OrderMatchingEngineConfig {
        liquidity_consumption: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let orderbook_delta_buy = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("900.00"),
            Quantity::from("50.000"),
            100,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_buy)
        .unwrap();

    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("50.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    // Both orders fill 50 each despite only 50 displayed (no consumption tracking)
    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order1, account_id);

    let mut order2 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order2, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let filled_events: Vec<_> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();

    assert_eq!(filled_events.len(), 2);
    assert_eq!(filled_events[0].last_qty, Quantity::from("50.000"));
    assert_eq!(filled_events[1].last_qty, Quantity::from("50.000"));
}

/// Regression test for liquidity consumption not resetting when unrelated levels change.
///
/// Bug scenario: When multiple levels exist, consuming all liquidity at those levels,
/// then DELETING a "better" price level should NOT reset consumption at "worse" levels.
/// Previously, consumption tracking used cumulative quantity which caused incorrect
/// resets when any better-priced level changed.
///
/// For BUY: deleting lower ask shouldn't reset higher asks
/// For SELL: deleting higher bid shouldn't reset lower bids
#[rstest]
#[case(OrderSide::Buy, OrderSide::Sell)]
#[case(OrderSide::Sell, OrderSide::Buy)]
fn test_liquidity_consumption_unchanged_levels_stay_depleted(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
    #[case] book_side: OrderSide,
) {
    let config = OrderMatchingEngineConfig {
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Price levels depend on which side we're testing
    // BUY order crosses asks: 1000 (best) -> 1001 -> 1002 (worst)
    // SELL order crosses bids: 1002 (best) -> 1001 -> 1000 (worst)
    let (prices, opposite_price, best_price) = if order_side == OrderSide::Buy {
        (
            [
                Price::from("1000.00"),
                Price::from("1001.00"),
                Price::from("1002.00"),
            ],
            Price::from("900.00"),
            Price::from("1000.00"),
        )
    } else {
        (
            [
                Price::from("1002.00"),
                Price::from("1001.00"),
                Price::from("1000.00"),
            ],
            Price::from("1100.00"),
            Price::from("1002.00"),
        )
    };

    let opposite_side_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            order_side,
            opposite_price,
            Quantity::from("1000.000"),
            100,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&opposite_side_delta)
        .unwrap();

    let delta1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            book_side,
            prices[0],
            Quantity::from("200.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&delta1).unwrap();

    let delta2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            book_side,
            prices[1],
            Quantity::from("812.000"),
            2,
        ))
        .build();
    engine_l2.process_order_book_delta(&delta2).unwrap();

    let delta3 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            book_side,
            prices[2],
            Quantity::from("1012.000"),
            3,
        ))
        .build();
    engine_l2.process_order_book_delta(&delta3).unwrap();

    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("2024.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order1, account_id);

    let delete_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Delete)
        .book_order(BookOrder::new(
            book_side,
            best_price,
            Quantity::from("0.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&delete_delta).unwrap();

    let mut order2 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("100.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order2, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let filled_events: Vec<_> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .collect();

    let order1_fills: Vec<_> = filled_events
        .iter()
        .filter(|f| f.client_order_id.as_str().ends_with("-1"))
        .collect();
    let order2_fill_count = filled_events
        .iter()
        .filter(|f| f.client_order_id.as_str().ends_with("-2"))
        .count();

    assert_eq!(
        order1_fills.len(),
        3,
        "First order should fill at all 3 levels"
    );
    let total_fill_qty: f64 = order1_fills.iter().map(|f| f.last_qty.as_f64()).sum();
    assert!(
        (total_fill_qty - 2024.0).abs() < 0.001,
        "First order should consume total 2024 units"
    );

    assert_eq!(
        order2_fill_count, 0,
        "Second order should not fill - unchanged levels should stay depleted"
    );
}

/// Regression test for stop-limit double-accept bug.
/// When a stop-limit order triggers but doesn't immediately fill, it should only be
/// accepted once, not twice.
#[rstest]
fn test_stop_limit_triggered_not_filled_single_accept(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Add sell order at 1500
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");

    // Create stop-limit order: triggers at 1495 (below ask 1500), limit 1490 (not immediately filled)
    let mut stop_order = OrderTestBuilder::new(OrderType::StopLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1495.00"))
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2.process_order(&mut stop_order, account_id);

    // Verify: should have exactly 1 Accepted event and 1 Triggered event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let accepted_count = saved_messages
        .iter()
        .filter(|e| matches!(e, OrderEventAny::Accepted(_)))
        .count();

    assert_eq!(
        accepted_count, 1,
        "Expected exactly 1 Accepted event, was {accepted_count} (double-accept bug)"
    );
    assert_eq!(
        saved_messages.len(),
        2,
        "Expected Accepted + Triggered events"
    );

    // Verify order exists in core exactly once
    let orders = engine_l2.get_core().get_orders();
    let matching_order_count = orders
        .iter()
        .filter(|o| o.client_order_id == client_order_id)
        .count();
    assert_eq!(
        matching_order_count, 1,
        "Order should exist exactly once in core"
    );
}

/// Regression test for order modify persistence bug.
/// When an order is modified, the new price should persist to the core
/// and be used for subsequent matching.
#[rstest]
fn test_modify_limit_order_price_persists_to_core(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), Some(cache), None, None, None);

    // Add sell order at 1500
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");

    // Create limit buy at 1490 (below ask, won't fill)
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2.process_order(&mut limit_order, account_id);

    // Verify order is in core at original price
    let order_in_core = engine_l2.get_core().get_order(client_order_id).unwrap();
    assert_eq!(order_in_core.limit_price, Some(Price::from("1490.00")));

    clear_order_event_handler_messages(&order_event_handler);

    // Modify order to new price 1495 (still below ask, won't fill)
    let modify_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        Some(Quantity::from("1.000")),
        Some(Price::from("1495.00")),
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );

    engine_l2.process_modify(&modify_command, account_id);

    // Verify: order in core should have new price
    let order_in_core = engine_l2.get_core().get_order(client_order_id).unwrap();
    assert_eq!(
        order_in_core.limit_price,
        Some(Price::from("1495.00")),
        "Modified price should persist to core"
    );
}

/// Regression test for rejected modify not changing book priority.
/// When a modify is rejected (e.g., invalid precision), the order should remain
/// unchanged in the book without affecting its time priority.
#[rstest]
fn test_rejected_modify_does_not_change_book_priority(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Add sell order at 1500
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");

    // Create limit buy at 1490 (below ask, won't fill)
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2.process_order(&mut limit_order, account_id);

    // Get initial order count in core
    let initial_order_count = engine_l2.get_core().get_orders().len();

    clear_order_event_handler_messages(&order_event_handler);

    // Try to modify with invalid precision (instrument has 2 decimal places for price)
    let modify_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        Some(Quantity::from("1.000")),
        Some(Price::from("1495.12345")), // Invalid precision
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );

    engine_l2.process_modify(&modify_command, account_id);

    // Verify: modify should be rejected
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let rejected_count = saved_messages
        .iter()
        .filter(|e| matches!(e, OrderEventAny::ModifyRejected(_)))
        .count();
    assert_eq!(rejected_count, 1, "Modify should be rejected");

    // Verify: order should still be in core with original price
    let order_in_core = engine_l2.get_core().get_order(client_order_id).unwrap();
    assert_eq!(
        order_in_core.limit_price,
        Some(Price::from("1490.00")),
        "Order price should be unchanged after rejected modify"
    );

    // Verify: order count unchanged (not deleted and re-added)
    let final_order_count = engine_l2.get_core().get_orders().len();
    assert_eq!(
        initial_order_count, final_order_count,
        "Order count should be unchanged"
    );
}

/// Regression test for netting mode position ID.
/// In netting mode, fills should not have a venue_position_id.
#[rstest]
fn test_netting_mode_fills_have_no_position_id(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    // Create engine in netting mode (default)
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Add sell order at 1500
    let orderbook_delta_sell = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2
        .process_order_book_delta(&orderbook_delta_sell)
        .unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");

    // Create market buy that will fill
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine_l2.process_order(&mut market_order, account_id);

    // Get fill event
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill_event = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .expect("Expected a fill event");

    // In netting mode, position_id should be None
    assert!(
        fill_event.position_id.is_none(),
        "Netting mode fills should have position_id = None"
    );
}

// NOTE: Trade consumption tests are in Python (test_matching_engine.py) because the Rust
// matching engine's handler mechanism for filling resting orders via iteration is not yet
// implemented (marked TBD in engine.rs). The trade consumption tracking code in
// determine_limit_price_and_volume is ready but can't be exercised until handlers are wired up.

/// Tests BUY MarketIfTouchedOrder fills at trigger price (not bar extreme) during bar processing.
///
/// Note: Manually triggers the stop order because the handler mechanism is TBD.
#[rstest]
fn test_market_if_touched_buy_fills_at_trigger_price(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL");
    let init_bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1500.00"),
        volume: Quantity::from("100.000"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };
    engine.process_bar(&init_bar);
    clear_order_event_handler_messages(&order_event_handler);

    // BUY MIT at 1480 triggers when price touches from above
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut mit_order = OrderTestBuilder::new(OrderType::MarketIfTouched)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1480.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut mit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Bar low at 1470 crosses trigger at 1480
    let _trigger_bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1470.00"),
        close: Price::from("1490.00"),
        volume: Quantity::from("100.000"),
        ts_event: UnixNanos::from(2_000_000_000),
        ts_init: UnixNanos::from(2_000_000_000),
    };

    // Simulate bar H/L/C processing (handler not yet wired)
    engine.set_fill_at_market(false);
    engine.trigger_stop_order(client_order_id);
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill_event = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .expect("Expected a fill event");

    assert_eq!(
        fill_event.last_px,
        Price::from("1480.00"),
        "BUY MIT should fill at trigger price 1480.00, not bar low 1470.00"
    );
}

/// Tests SELL MarketIfTouchedOrder fills at trigger price (not bar extreme) during bar processing.
///
/// Note: Manually triggers the stop order because the handler mechanism is TBD.
#[rstest]
fn test_market_if_touched_sell_fills_at_trigger_price(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL");
    let init_bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1500.00"),
        volume: Quantity::from("100.000"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };
    engine.process_bar(&init_bar);
    clear_order_event_handler_messages(&order_event_handler);

    // SELL MIT at 1520 triggers when price touches from below
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut mit_order = OrderTestBuilder::new(OrderType::MarketIfTouched)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("1520.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut mit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Bar high at 1530 crosses trigger at 1520
    let _trigger_bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1530.00"),
        low: Price::from("1490.00"),
        close: Price::from("1510.00"),
        volume: Quantity::from("100.000"),
        ts_event: UnixNanos::from(2_000_000_000),
        ts_init: UnixNanos::from(2_000_000_000),
    };

    // Simulate bar H/L/C processing (handler not yet wired)
    engine.set_fill_at_market(false);
    engine.trigger_stop_order(client_order_id);
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill_event = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .expect("Expected a fill event");

    assert_eq!(
        fill_event.last_px,
        Price::from("1520.00"),
        "SELL MIT should fill at trigger price 1520.00, not bar high 1530.00"
    );
}

/// Tests BUY MIT fills at trigger price with liquidity consumption enabled.
///
/// Regression: Liquidity consumption must not discard fills at trigger price
/// where no book liquidity exists (gap scenario).
#[rstest]
fn test_market_if_touched_buy_fills_at_trigger_price_with_liquidity_consumption(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL");
    let init_bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1500.00"),
        volume: Quantity::from("100.000"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };
    engine.process_bar(&init_bar);
    clear_order_event_handler_messages(&order_event_handler);

    // BUY MIT at 1480 triggers when price touches from above
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut mit_order = OrderTestBuilder::new(OrderType::MarketIfTouched)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1480.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut mit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Simulate bar H/L/C processing (handler not yet wired)
    engine.set_fill_at_market(false);
    engine.trigger_stop_order(client_order_id);
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill_event = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .expect("Expected a fill event");

    assert_eq!(
        fill_event.last_px,
        Price::from("1480.00"),
        "BUY MIT should fill at trigger price with liquidity consumption enabled"
    );
}

/// Tests SELL MIT fills at trigger price with liquidity consumption enabled.
///
/// Regression: Liquidity consumption must not discard fills at trigger price
/// where no book liquidity exists (gap scenario).
#[rstest]
fn test_market_if_touched_sell_fills_at_trigger_price_with_liquidity_consumption(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL");
    let init_bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1490.00"),
        close: Price::from("1500.00"),
        volume: Quantity::from("100.000"),
        ts_event: UnixNanos::from(1_000_000_000),
        ts_init: UnixNanos::from(1_000_000_000),
    };
    engine.process_bar(&init_bar);
    clear_order_event_handler_messages(&order_event_handler);

    // SELL MIT at 1520 triggers when price touches from below
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut mit_order = OrderTestBuilder::new(OrderType::MarketIfTouched)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("1520.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut mit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Simulate bar H/L/C processing (handler not yet wired)
    engine.set_fill_at_market(false);
    engine.trigger_stop_order(client_order_id);
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill_event = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .expect("Expected a fill event");

    assert_eq!(
        fill_event.last_px,
        Price::from("1520.00"),
        "SELL MIT should fill at trigger price with liquidity consumption enabled"
    );
}

/// Regression test for liquidity consumption tracking at multiple price levels.
///
/// When an order fills across multiple price levels, consumption must be tracked
/// separately at each original book price level. This test verifies that after
/// consuming liquidity at multiple levels, subsequent orders cannot access that
/// consumed liquidity.
#[rstest]
#[case(OrderSide::Buy)]
#[case(OrderSide::Sell)]
fn test_liquidity_consumption_tracks_fills_at_multiple_price_levels(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
) {
    let config = OrderMatchingEngineConfig {
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    if order_side == OrderSide::Buy {
        let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("900.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid_delta).unwrap();

        let ask1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("999.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask1).unwrap();

        let ask2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1000.00"),
                Quantity::from("50.000"),
                2,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask2).unwrap();

        let ask3 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1001.00"),
                Quantity::from("100.000"),
                3,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask3).unwrap();
    } else {
        let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1100.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask_delta).unwrap();

        let bid1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("1001.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid1).unwrap();

        let bid2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("1000.00"),
                Quantity::from("50.000"),
                2,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid2).unwrap();

        let bid3 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("999.00"),
                Quantity::from("100.000"),
                3,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid3).unwrap();
    }

    let limit_price = Price::from("1000.00");

    let mut order1 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(limit_price)
        .quantity(Quantity::from("100.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order1, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let order1_fills: Vec<_> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) if fill.client_order_id.as_str().ends_with("-1") => {
                Some(fill)
            }
            _ => None,
        })
        .collect();

    let total_order1: f64 = order1_fills.iter().map(|f| f.last_qty.as_f64()).sum();
    assert!(
        (total_order1 - 100.0).abs() < 0.001,
        "First order should fill 100, was {total_order1}"
    );

    clear_order_event_handler_messages(&order_event_handler);

    let mut order2 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(limit_price)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order2, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let order2_fills: Vec<_> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) if fill.client_order_id.as_str().ends_with("-2") => {
                Some(fill)
            }
            _ => None,
        })
        .collect();

    let total_order2: f64 = order2_fills.iter().map(|f| f.last_qty.as_f64()).sum();
    assert!(
        total_order2 < 0.001,
        "Second order should NOT fill - both price levels should be consumed. \
         Got fill of {total_order2}. If this fails, consumption was incorrectly \
         tracked at wrong price levels."
    );
}

#[rstest]
#[case(OrderSide::Buy)]
#[case(OrderSide::Sell)]
fn test_fok_order_canceled_when_liquidity_consumption_exhausts_fills(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
) {
    let config = OrderMatchingEngineConfig {
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Set up book with one side having 50 units of liquidity
    if order_side == OrderSide::Buy {
        let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("900.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid_delta).unwrap();

        let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1000.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask_delta).unwrap();
    } else {
        let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1100.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask_delta).unwrap();

        let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("1000.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid_delta).unwrap();
    }

    // First order consumes all liquidity
    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order1, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // FOK order should be canceled since no liquidity remains
    let limit_price = Price::from("1000.00");
    let mut fok_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(limit_price)
        .quantity(Quantity::from("30.000"))
        .time_in_force(TimeInForce::Fok)
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut fok_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let canceled_count = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Canceled(_)))
        .count();

    assert_eq!(
        canceled_count, 1,
        "FOK order should be canceled when liquidity is exhausted"
    );
}

#[rstest]
#[case(OrderSide::Buy)]
#[case(OrderSide::Sell)]
fn test_ioc_order_canceled_when_liquidity_consumption_exhausts_fills(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
) {
    let config = OrderMatchingEngineConfig {
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Set up book with one side having 50 units of liquidity
    if order_side == OrderSide::Buy {
        let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("900.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid_delta).unwrap();

        let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1000.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask_delta).unwrap();
    } else {
        let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1100.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask_delta).unwrap();

        let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("1000.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid_delta).unwrap();
    }

    // First order consumes all liquidity
    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order1, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // IOC order should be canceled since no liquidity remains
    let limit_price = Price::from("1000.00");
    let mut ioc_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(limit_price)
        .quantity(Quantity::from("30.000"))
        .time_in_force(TimeInForce::Ioc)
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut ioc_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let canceled_count = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Canceled(_)))
        .count();

    assert_eq!(
        canceled_count, 1,
        "IOC order should be canceled when liquidity is exhausted"
    );
}

#[rstest]
#[case(OrderSide::Buy)]
#[case(OrderSide::Sell)]
fn test_gtc_order_not_canceled_when_liquidity_consumption_exhausts_fills(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
) {
    let config = OrderMatchingEngineConfig {
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Set up book with one side having 50 units of liquidity
    if order_side == OrderSide::Buy {
        let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("900.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid_delta).unwrap();

        let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1000.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask_delta).unwrap();
    } else {
        let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1100.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine_l2.process_order_book_delta(&ask_delta).unwrap();

        let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("1000.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine_l2.process_order_book_delta(&bid_delta).unwrap();
    }

    // First order consumes all liquidity
    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut order1, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // GTC order should NOT be canceled - it should remain open
    let limit_price = Price::from("1000.00");
    let mut gtc_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(limit_price)
        .quantity(Quantity::from("30.000"))
        .time_in_force(TimeInForce::Gtc)
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine_l2.process_order(&mut gtc_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let canceled_count = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Canceled(_)))
        .count();
    let filled_count = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Filled(_)))
        .count();

    assert_eq!(
        canceled_count, 0,
        "GTC order should NOT be canceled when liquidity is exhausted"
    );
    assert_eq!(
        filled_count, 0,
        "GTC order should NOT be filled when liquidity is exhausted"
    );
}

#[rstest]
#[case(OrderSide::Buy, AggressorSide::Seller)]
#[case(OrderSide::Sell, AggressorSide::Buyer)]
fn test_trade_execution_fill_model_at_limit_with_prob_zero_does_not_fill(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
    #[case] aggressor_side: AggressorSide,
) {
    // Test that when trade price equals limit price exactly, the fill model
    // probability check is used. With prob_fill_on_limit=0.0, the order should
    // not fill from trade execution (simulates being at back of queue).

    let fill_model = FillModelAny::Default(DefaultFillModel::new(0.0, 0.0, Some(42)).unwrap());
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        ..Default::default()
    };

    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        instrument_eth_usdt.clone(),
        1,
        fill_model.into(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        config,
    );

    // Set initial market state
    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1010.00"),
        Quantity::from("100.000"),
        Quantity::from("100.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    // Place limit order at 1005.00 (in the spread)
    let limit_price = Price::from("1005.00");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(limit_price)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Trade at exactly the limit price
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        limit_price,
        Quantity::from("10.000"),
        aggressor_side,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let filled_count = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Filled(_)))
        .count();

    assert_eq!(
        filled_count, 0,
        "Order should NOT fill when trade at limit price with prob_fill_on_limit=0.0"
    );
}

#[rstest]
#[case(OrderSide::Buy, AggressorSide::Seller)]
#[case(OrderSide::Sell, AggressorSide::Buyer)]
fn test_trade_execution_fill_model_at_limit_with_prob_one_fills(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
    #[case] aggressor_side: AggressorSide,
) {
    // Test that when trade price equals limit price exactly, with
    // prob_fill_on_limit=1.0 the order fills deterministically.

    let fill_model = FillModelAny::Default(DefaultFillModel::new(1.0, 0.0, Some(42)).unwrap());
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        ..Default::default()
    };

    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        instrument_eth_usdt.clone(),
        1,
        fill_model.into(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        config,
    );

    // Set initial market state
    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1010.00"),
        Quantity::from("100.000"),
        Quantity::from("100.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    // Place limit order at 1005.00 (in the spread)
    let limit_price = Price::from("1005.00");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(limit_price)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Trade at exactly the limit price
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        limit_price,
        Quantity::from("10.000"),
        aggressor_side,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let filled_events: Vec<_> = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Filled(_)))
        .collect();

    assert_eq!(
        filled_events.len(),
        1,
        "Order should fill when trade at limit price with prob_fill_on_limit=1.0"
    );

    // Verify fill price
    if let OrderEventAny::Filled(filled) = &filled_events[0] {
        assert_eq!(filled.last_px, limit_price);
    }
}

#[rstest]
#[case(OrderSide::Buy, AggressorSide::Seller, "1005.00", "1000.00")]
#[case(OrderSide::Sell, AggressorSide::Buyer, "1005.00", "1010.00")]
fn test_trade_execution_crossing_limit_fills_regardless_of_fill_model(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
    #[case] aggressor_side: AggressorSide,
    #[case] limit_price_str: &str,
    #[case] trade_price_str: &str,
) {
    // Test that when trade price crosses the limit (better price), the fill
    // model is NOT consulted and the order fills.

    let fill_model = FillModelAny::Default(DefaultFillModel::new(0.0, 0.0, Some(42)).unwrap());
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        ..Default::default()
    };

    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        instrument_eth_usdt.clone(),
        1,
        fill_model.into(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        config,
    );

    // Set initial market state with wider spread
    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("990.00"),
        Price::from("1020.00"),
        Quantity::from("100.000"),
        Quantity::from("100.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    // Place limit order
    let limit_price = Price::from(limit_price_str);
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(limit_price)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Trade crosses the limit price (better price for the order)
    let trade_price = Price::from(trade_price_str);
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        trade_price,
        Quantity::from("10.000"),
        aggressor_side,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let filled_events: Vec<_> = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Filled(_)))
        .collect();

    assert_eq!(
        filled_events.len(),
        1,
        "Order should fill when trade crosses limit regardless of fill model"
    );

    // Verify fill at limit price (conservative)
    if let OrderEventAny::Filled(filled) = &filled_events[0] {
        assert_eq!(filled.last_px, limit_price);
    }
}

// A no-aggressor trade tick overrides both core sides to the trade price;
// `iterate` must not reset the override from the book mid-call. The
// `last_trade_size` gate keeps the override in place so a resting limit at
// the trade price matches in the same iterate.
#[rstest]
fn test_no_aggressor_trade_fills_resting_limit_at_trade_price(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Wide spread: bid 95, ask 105
    let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("95.00"),
            Quantity::from("10.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&bid_delta).unwrap();
    let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("105.00"),
            Quantity::from("10.000"),
            2,
        ))
        .build();
    engine_l2.process_order_book_delta(&ask_delta).unwrap();

    // Resting BUY at 100 sits in core (Maker), inside the spread
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("100.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // No-aggressor trade at 100. process_trade_tick overrides both core sides
    // to 100 before calling iterate; the gate must hold that override.
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Quantity::from("1.000"),
        AggressorSide::NoAggressor,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine_l2.process_trade_tick(&trade);

    let fills: Vec<_> = get_order_event_handler_messages(&order_event_handler)
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(*f),
            _ => None,
        })
        .collect();
    assert_eq!(
        fills.len(),
        1,
        "no-aggressor trade at the limit price must fill the resting BUY",
    );
    assert_eq!(fills[0].client_order_id, client_order_id);
}

#[rstest]
#[case(OrderSide::Buy, AggressorSide::Seller)]
#[case(OrderSide::Sell, AggressorSide::Buyer)]
fn test_trade_execution_fill_model_rejection_still_applies_liquidity_consumption(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
    #[case] aggressor_side: AggressorSide,
) {
    // Test that when trade execution fill is skipped due to fill model rejection,
    // liquidity consumption tracking from the trade is still applied.
    //
    // Setup: No book liquidity at the limit/trade price, so trade execution path
    // is exercised. Fill model rejects (prob=0), verifying the skip-trade-fill branch.

    let fill_model = FillModelAny::Default(DefaultFillModel::new(0.0, 0.0, Some(42)).unwrap());
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        liquidity_consumption: true,
        ..Default::default()
    };

    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        instrument_eth_usdt.clone(),
        1,
        fill_model.into(),
        FeeModelAny::default().into(),
        BookType::L2_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        config,
    );

    // Set up book with liquidity AWAY from the limit/trade price (1010.00).
    // This ensures fills_at_trade_price=False so trade execution path is exercised.
    if order_side == OrderSide::Buy {
        let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("900.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine.process_order_book_delta(&bid_delta).unwrap();

        // Ask at 1005.00 - NOT at 1010.00 where limit/trade will be
        let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1005.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine.process_order_book_delta(&ask_delta).unwrap();
    } else {
        let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Sell,
                Price::from("1100.00"),
                Quantity::from("1000.000"),
                100,
            ))
            .build();
        engine.process_order_book_delta(&ask_delta).unwrap();

        // Bid at 1015.00 - NOT at 1010.00 where limit/trade will be
        let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
            .book_action(BookAction::Add)
            .book_order(BookOrder::new(
                OrderSide::Buy,
                Price::from("1015.00"),
                Quantity::from("50.000"),
                1,
            ))
            .build();
        engine.process_order_book_delta(&bid_delta).unwrap();
    }

    // First order consumes book liquidity (at 1005.00 or 1015.00)
    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .quantity(Quantity::from("50.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order1, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let filled_count = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Filled(_)))
        .count();
    assert_eq!(filled_count, 1);
    clear_order_event_handler_messages(&order_event_handler);

    // Place limit order at 1010.00 (in the spread, no book liquidity here)
    let limit_price = Price::from("1010.00");
    let mut order2 = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(limit_price)
        .quantity(Quantity::from("30.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine.process_order(&mut order2, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Trade tick at the limit price.
    // fills_at_trade_price=False (no book at 1010.00) → trade execution path entered
    // Fill model rejects (prob=0) → no fill
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        limit_price,
        Quantity::from("100.000"),
        aggressor_side,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let filled_count = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Filled(_)))
        .count();

    assert_eq!(
        filled_count, 0,
        "Order should NOT fill when fill model rejects at limit price"
    );
}

#[rstest]
fn test_bar_execution_fills_stop_order(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Set initial market with a quote tick so bid/ask are initialized
    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1499.00"),
        Price::from("1501.00"),
        Quantity::from("1.000"),
        Quantity::from("1.000"),
        UnixNanos::from(1u64),
        UnixNanos::from(1u64),
    );
    engine.process_quote_tick(&quote);

    // Place SELL stop at 1490 (not triggered: bid=1499 > 1490)
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut stop_order, account_id);

    clear_order_event_handler_messages(&order_event_handler);

    // Process a bar that sweeps through the stop trigger via the low tick
    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL");
    let bar = Bar {
        bar_type,
        open: Price::from("1500.00"),
        high: Price::from("1510.00"),
        low: Price::from("1480.00"),
        close: Price::from("1495.00"),
        volume: Quantity::from("100.000"),
        ts_event: UnixNanos::from(2u64),
        ts_init: UnixNanos::from(2u64),
    };
    engine.process_bar(&bar);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Expected stop order to fill during bar execution");

    // Fill should be at trigger price (L1 stop market fills at trigger, not book price)
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_px, Price::from("1490.00"));
}

#[rstest]
fn test_bar_adaptive_ordering_fills_low_side_first(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        bar_adaptive_high_low_ordering: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Set initial market
    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1500.00"),
        Price::from("1500.00"),
        Quantity::from("1.000"),
        Quantity::from("1.000"),
        UnixNanos::from(1u64),
        UnixNanos::from(1u64),
    );
    engine.process_quote_tick(&quote);

    // Place BUY stop at 1510 and SELL stop at 1490 (both not triggered)
    let buy_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut buy_stop = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1510.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(buy_id)
        .submit(true)
        .build();
    engine.process_order(&mut buy_stop, account_id);

    let sell_id = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut sell_stop = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(sell_id)
        .submit(true)
        .build();
    engine.process_order(&mut sell_stop, account_id);

    clear_order_event_handler_messages(&order_event_handler);

    // Bar with open closer to low: |H-O|=22 > |L-O|=18 → adaptive → L processed first
    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL");
    let bar = Bar {
        bar_type,
        open: Price::from("1498.00"),
        high: Price::from("1520.00"),
        low: Price::from("1480.00"),
        close: Price::from("1500.00"),
        volume: Quantity::from("100.000"),
        ts_event: UnixNanos::from(2u64),
        ts_init: UnixNanos::from(2u64),
    };
    engine.process_bar(&bar);

    // With adaptive ordering, low is processed before high.
    // SELL stop triggers on low tick, BUY stop triggers on high tick.
    // Verify SELL fill comes before BUY fill.
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = saved_messages
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert_eq!(fills.len(), 2, "Both stops should fill");
    assert_eq!(
        fills[0].client_order_id, sell_id,
        "SELL should fill first (low processed before high)"
    );
    assert_eq!(fills[1].client_order_id, buy_id, "BUY should fill second");
}

// L2 engine with trade_execution=false does not iterate on trade ticks
#[ignore]
#[rstest]
fn test_trailing_stop_market_updated_then_triggered(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Add sell-side liquidity
    let delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("10.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&delta).unwrap();

    // Submit trailing stop market BUY at trigger 1510 with offset 5
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut trailing_stop = OrderTestBuilder::new(OrderType::TrailingStopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .trigger_price(Price::from("1510.00"))
        .trigger_type(TriggerType::LastPrice)
        .trailing_offset(dec!(5))
        .trailing_offset_type(TrailingOffsetType::Price)
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut trailing_stop, account_id);

    // Market drops to 1480 → trailing trigger updates to 1485 (1480 + 5)
    let tick1 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1480.00"),
        Quantity::from("1.000"),
        AggressorSide::Seller,
        TradeId::new("1"),
        UnixNanos::from(1u64),
        UnixNanos::from(1u64),
    );
    engine_l2.process_trade_tick(&tick1);

    // Market recovers to 1490 → ask(1500) >= trigger(1485) → stop triggers
    let tick2 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1490.00"),
        Quantity::from("1.000"),
        AggressorSide::Buyer,
        TradeId::new("2"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine_l2.process_trade_tick(&tick2);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);

    // Verify the full lifecycle: Accepted → Updated → Filled
    let accepted_count = saved_messages
        .iter()
        .filter(|e| matches!(e, OrderEventAny::Accepted(_)))
        .count();
    let updated_count = saved_messages
        .iter()
        .filter(|e| matches!(e, OrderEventAny::Updated(_)))
        .count();
    let fill = saved_messages.iter().find_map(|e| match e {
        OrderEventAny::Filled(f) => Some(f),
        _ => None,
    });

    assert_eq!(accepted_count, 1, "Should have 1 accepted event");
    assert!(updated_count >= 1, "Should have at least 1 trailing update");
    assert!(
        fill.is_some(),
        "Trailing stop should have triggered and filled"
    );
    assert_eq!(fill.unwrap().client_order_id, client_order_id);
}

#[rstest]
fn test_gtd_order_partially_filled_then_expired(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        support_gtd_orders: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Add partial sell-side liquidity (0.500 available but order wants 1.000)
    let delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("0.500"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&delta).unwrap();

    // Submit GTD limit buy at 1500 for 1.000 — will partially fill 0.500
    let expire_ns: u64 = 1_500_000_000_000_000_000;
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1500.00"))
        .quantity(Quantity::from("1.000"))
        .time_in_force(TimeInForce::Gtd)
        .expire_time(UnixNanos::from(expire_ns))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit_order, account_id);

    // Verify partial fill
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill_count = saved_messages
        .iter()
        .filter(|e| matches!(e, OrderEventAny::Filled(_)))
        .count();
    assert_eq!(fill_count, 1, "Should have 1 partial fill");

    let fill = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .unwrap();
    assert_eq!(fill.last_qty, Quantity::from("0.500"));

    clear_order_event_handler_messages(&order_event_handler);

    // The engine doesn't deplete book liquidity on fills, so explicitly
    // remove the consumed ask level before driving the boundary tick.
    // Without this, the matching pass would re-fill the resting buy.
    let delete_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Delete)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("0.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&delete_delta).unwrap();

    clear_order_event_handler_messages(&order_event_handler);

    // Drive iterate past expire_time. With no matching liquidity, the
    // resting buy cannot fill and GTD expiry fires.
    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1490.00"),
        Price::from("1510.00"),
        Quantity::from("1.000"),
        Quantity::from("1.000"),
        UnixNanos::from(expire_ns + 1),
        UnixNanos::from(expire_ns + 1),
    );
    engine_l2.process_quote_tick(&quote);

    // Remaining quantity should expire
    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let expired = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Expired(exp) => Some(exp),
            _ => None,
        })
        .expect("Expected OrderExpired event for GTD order");
    assert_eq!(expired.client_order_id, client_order_id);
}

#[rstest]
fn test_gtd_order_at_boundary_tick_fills_before_expiry(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    // A GTD order whose expire_time matches the tick that would fill it
    // should fill, not expire. Matching runs before GTD-expire in iterate.
    let config = OrderMatchingEngineConfig {
        support_gtd_orders: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let initial_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1505.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&initial_delta).unwrap();

    let expire_ns: u64 = 1_500_000_000_000_000_000;
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1500.00"))
        .quantity(Quantity::from("1.000"))
        .time_in_force(TimeInForce::Gtd)
        .expire_time(UnixNanos::from(expire_ns))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit_order, account_id);

    clear_order_event_handler_messages(&order_event_handler);

    // Cross the spread at the exact expire timestamp: ask drops to 1500
    let crossing_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            2,
        ))
        .ts_init(UnixNanos::from(expire_ns))
        .build();
    engine_l2.process_order_book_delta(&crossing_delta).unwrap();

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Expected OrderFilled event at boundary tick");
    assert_eq!(fill.client_order_id, client_order_id);
    assert_eq!(fill.last_qty, Quantity::from("1.000"));

    let expired_count = saved_messages
        .iter()
        .filter(|e| matches!(e, OrderEventAny::Expired(_)))
        .count();
    assert_eq!(expired_count, 0, "Order should not expire after filling");
}

#[rstest]
fn test_price_protection_exact_boundary_fills(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    // Protection of 100 points on ETHUSDT (price_increment=0.01) means
    // max BUY price = ask + 100 * 0.01 = ask + 1.00
    let config = OrderMatchingEngineConfig::builder()
        .price_protection_points(100u32)
        .build();

    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    // Add two ask levels: one at boundary, one beyond
    let delta1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    // 1500 + 100*0.01 = 1501.00 is the boundary
    let delta2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1501.00"),
            Quantity::from("1.000"),
            2,
        ))
        .build();
    engine_l2.process_order_book_delta(&delta1).unwrap();
    engine_l2.process_order_book_delta(&delta2).unwrap();

    // Market BUY for 2.000 — should fill both levels (1500 + 1501 are both <= 1501 boundary)
    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("2.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut market_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = saved_messages
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert_eq!(fills.len(), 2, "Both levels at/within boundary should fill");
    assert_eq!(fills[0].last_px, Price::from("1500.00"));
    assert_eq!(fills[1].last_px, Price::from("1501.00"));
}

#[rstest]
fn test_settlement_price_used_on_contract_expiration(
    instrument_es: InstrumentAny,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    // Set clock past instrument activation (2022-04-08)
    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock
        .borrow_mut()
        .set_time(UnixNanos::from(1_680_000_000_000_000_000u64));

    let config = OrderMatchingEngineConfig {
        use_position_ids: true,
        ..Default::default()
    };

    let fee_model =
        FeeModelAny::Fixed(FixedFeeModel::new(Money::new(0.0, Currency::USD()), None).unwrap());

    let mut engine = OrderMatchingEngine::new(
        instrument_es.clone(),
        1,
        FillModelHandle::default(),
        fee_model.into(),
        BookType::L2_MBP,
        OmsType::Netting,
        AccountType::Margin,
        clock,
        cache.clone(),
        config,
    );

    let delta = OrderBookDeltaTestBuilder::new(instrument_es.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("4501.00"),
            Quantity::from(10),
            1,
        ))
        .build();
    engine.process_order_book_delta(&delta).unwrap();

    let mut buy_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_es.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from(1))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();

    cache
        .borrow_mut()
        .add_order(buy_order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut buy_order, account_id);

    let messages = get_order_event_handler_messages(&order_event_handler);
    let mut fill = messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(*f),
            _ => None,
        })
        .expect("Expected a fill event from the market order");

    fill.position_id = Some(PositionId::new("P-001"));
    let position = Position::new(&instrument_es, fill);
    let strategy_id = position.strategy_id;
    cache
        .borrow_mut()
        .add_position(&position, OmsType::Netting)
        .unwrap();

    clear_order_event_handler_messages(&order_event_handler);
    let topic = format!("events.order.{strategy_id}");
    let (published_handler, published_events): (_, TypedMessageSavingHandler<OrderEventAny>) =
        get_typed_message_saving_handler(None);
    msgbus::subscribe_order_events(topic.clone().into(), published_handler.clone(), None);

    // Set settlement price (different from close price and market price)
    let settlement = Price::from("4600.00");
    engine.set_settlement_price(settlement);

    // Process contract expiration with a different close price
    let close = InstrumentClose::new(
        instrument_es.id(),
        Price::from("4550.00"),
        InstrumentCloseType::ContractExpired,
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_instrument_close(close);

    msgbus::unsubscribe_order_events(topic.into(), &published_handler);

    let published_messages = published_events.get_messages();
    assert_eq!(published_messages.len(), 1);
    assert!(matches!(
        published_messages.first(),
        Some(OrderEventAny::Initialized(init))
            if init.client_order_id.as_str().starts_with("EXPIRATION-")
    ));

    let messages = get_order_event_handler_messages(&order_event_handler);
    let expiration_fill = messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(*f),
            _ => None,
        })
        .expect("Expected a fill event from contract expiration");

    assert_eq!(expiration_fill.last_px, settlement);
}

#[rstest]
fn test_trade_tick_seeds_liquidity_consumption_for_stop_market_fill(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Scenario from issue #3596: a BUY trade consumes ask-side liquidity,
    // then a resting stop market BUY order triggers. The stop order should
    // fill against post-trade liquidity (reduced book), not the full pre-trade book.
    //
    // Book asks: 1000.00×10, 1001.00×10
    // Stop BUY trigger=1001.00 (above best ask, won't trigger on submission)
    // Trade: BUY 8 @ 1001.00 (consumes 8 from best ask at 1000.00, triggers stop)
    // Stop BUY qty=5 fills → should get 2 @ 1000.00 + 3 @ 1001.00
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("900.00"),
            Quantity::from("100.000"),
            100,
        ))
        .build();
    engine.process_order_book_delta(&bid_delta).unwrap();

    let ask1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("10.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&ask1).unwrap();

    let ask2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1001.00"),
            Quantity::from("10.000"),
            2,
        ))
        .build();
    engine.process_order_book_delta(&ask2).unwrap();

    // Trigger at 1001.00, above current best ask of 1000.00, so not triggered on submission
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1001.00"))
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut stop_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // BUY trade at 1001.00 size=8 triggers the stop and consumes 8 from best ask.
    // ts_event=2 > book.ts_last=1, so seeding proceeds (trade is newer than book).
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1001.00"),
        Quantity::from("8.000"),
        AggressorSide::Buyer,
        TradeId::new("1"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<_> = saved_messages
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert!(
        !fills.is_empty(),
        "Stop order should have triggered and filled"
    );

    let total_qty: f64 = fills.iter().map(|f| f.last_qty.as_f64()).sum();
    assert!(
        (total_qty - 5.0).abs() < 0.001,
        "Total fill qty should be 5.0, was {total_qty}"
    );

    // Trade consumed 8 of 10 at 1000.00 → only 2 remain at best ask.
    // The stop order should NOT get all 5 at 1000.00, it should spill to 1001.00.
    let got_fill_at_1001 = fills.iter().any(|f| f.last_px == Price::from("1001.00"));
    assert!(
        got_fill_at_1001,
        "Expected fill at 1001.00 due to consumed liquidity at 1000.00, \
         but all fills were at 1000.00. Trade consumption was not applied."
    );
}

#[rstest]
fn test_trade_tick_seeds_consumption_with_stale_entry_reconciles(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Verifies that seed_trade_consumption reconciles stale consumption entries.
    // If the book level size changed since the last consumption entry was created,
    // the seeded consumption must update original_size to prevent
    // apply_liquidity_consumption from resetting consumed to 0.
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("900.00"),
            Quantity::from("100.000"),
            100,
        ))
        .build();
    engine.process_order_book_delta(&bid_delta).unwrap();

    let ask1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("10.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&ask1).unwrap();

    let ask2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1001.00"),
            Quantity::from("20.000"),
            2,
        ))
        .build();
    engine.process_order_book_delta(&ask2).unwrap();

    // Fill a market BUY to populate consumption at 1000.00 with original_size=10
    let mut order1 = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("3.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order1, account_id);

    // Update ask level to a new size (simulating fresh book data)
    let ask_update = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Update)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("8.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&ask_update).unwrap();

    // Consumption entry for 1000.00 now has original_size=10 but book shows 8 (stale).
    // Stop trigger at 1001.00 (above best ask 1000.00, won't trigger on submission).
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1001.00"))
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-2"))
        .submit(true)
        .build();
    engine.process_order(&mut stop_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Trade consumes 6 of 8 at 1000.00 and triggers the stop
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1001.00"),
        Quantity::from("6.000"),
        AggressorSide::Buyer,
        TradeId::new("1"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<_> = saved_messages
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert!(
        !fills.is_empty(),
        "Stop order should have triggered and filled"
    );

    // Trade consumed 6 of 8 at 1000.00 → only 2 remain.
    // Stop BUY qty=5 should spill to next level if consumption was correctly reconciled.
    let got_fill_at_1001 = fills.iter().any(|f| f.last_px == Price::from("1001.00"));
    assert!(
        got_fill_at_1001,
        "Expected fill at 1001.00 due to consumed liquidity at 1000.00 with stale entry. \
         Stale original_size was not reconciled, causing consumption to be discarded."
    );
}

#[rstest]
fn test_trade_tick_skips_seeding_when_book_already_updated(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // When deltas arrive before the trade tick (e.g., Binance), the book already
    // reflects the trade's consumed volume. Seeding would double-count consumption,
    // producing systematically pessimistic fills. Verify we skip seeding in this case.
    //
    // Book: asks 1000.00×10, 1001.00×20
    // Delta arrives with ts_event=3 reducing 1000.00→2 (trade consumed 8)
    // Trade arrives with ts_event=2 → book.ts_last(3) > trade.ts_event(2) → skip
    // Then a market BUY gets full book liquidity (not phantom-consumed)
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("900.00"),
            Quantity::from("100.000"),
            100,
        ))
        .build();
    engine.process_order_book_delta(&bid_delta).unwrap();

    let ask1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("10.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&ask1).unwrap();

    let ask2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1001.00"),
            Quantity::from("20.000"),
            2,
        ))
        .build();
    engine.process_order_book_delta(&ask2).unwrap();

    // Delta arrives with ts_event=3 (after trade), reducing 1000.00→2
    let ask_update = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Update)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("2.000"),
            1,
        ))
        .ts_event(UnixNanos::from(3))
        .build();
    engine.process_order_book_delta(&ask_update).unwrap();

    // Trade tick with ts_event=2 → book.ts_last(3) > trade.ts_event(2) → skip seeding
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Quantity::from("8.000"),
        AggressorSide::Buyer,
        TradeId::new("1"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine.process_trade_tick(&trade);

    // Now submit a market BUY. If seeding was incorrectly applied, it would have
    // consumed 2 at 1000.00 (phantom), leaving 0 available at best ask.
    // With correct skip, the full 2 at 1000.00 should be available.
    clear_order_event_handler_messages(&order_event_handler);
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<_> = saved_messages
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    // With seeding skipped, fills should use actual book: 2 at 1000.00, 3 at 1001.00
    // If seeding had NOT been skipped, 1000.00 would be fully consumed → 0+5 at 1001.00
    let got_fill_at_1000 = fills.iter().any(|f| f.last_px == Price::from("1000.00"));
    assert!(
        got_fill_at_1000,
        "Should fill at 1000.00 from actual book (seeding should be skipped for \
         already-updated book). If all fills are at 1001.00, trade seeding double-counted."
    );
}

#[rstest]
fn test_trade_tick_seeds_consumption_when_book_ts_equals_trade_ts(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Verifies that seeding proceeds when book.ts_last == trade.ts_event.
    // Equal timestamps don't prove deltas already reflect the trade, only
    // strictly newer book timestamps skip seeding. This pins the > guard.
    //
    // Book deltas: ts_event=1 → book.ts_last=1
    // Trade: ts_event=1 → book.ts_last(1) > trade.ts_event(1) is false → seed
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let bid_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("900.00"),
            Quantity::from("100.000"),
            100,
        ))
        .build();
    engine.process_order_book_delta(&bid_delta).unwrap();

    let ask1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("10.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&ask1).unwrap();

    let ask2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1001.00"),
            Quantity::from("10.000"),
            2,
        ))
        .build();
    engine.process_order_book_delta(&ask2).unwrap();

    // Stop trigger above best ask so it doesn't trigger on submission
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1001.00"))
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut stop_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // Trade with ts_event=1 (same as book deltas) → seeding should proceed
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1001.00"),
        Quantity::from("8.000"),
        AggressorSide::Buyer,
        TradeId::new("1"),
        UnixNanos::from(1u64),
        UnixNanos::from(1u64),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<_> = saved_messages
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert!(
        !fills.is_empty(),
        "Stop order should have triggered and filled"
    );

    // Trade consumed 8 of 10 at 1000.00 → only 2 remain.
    // If seeding was incorrectly skipped (>= guard), all 5 would fill at 1000.00.
    let got_fill_at_1001 = fills.iter().any(|f| f.last_px == Price::from("1001.00"));
    assert!(
        got_fill_at_1001,
        "Expected fill at 1001.00, seeding should proceed when book.ts_last == trade.ts_event. \
         If all fills at 1000.00, the guard incorrectly skipped seeding on equal timestamps."
    );
}

#[rstest]
fn test_trade_tick_seeds_consumption_for_seller_side(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Verifies that seller-side trades correctly seed bid_consumption.
    // A SELL trade consumes bid-side liquidity, then a resting stop market SELL
    // order triggers and fills against the reduced bid book.
    //
    // Book bids: 1000.00×10, 999.00×10
    // Stop SELL trigger=999.00 (below best bid, won't trigger on submission)
    // Trade: SELL 8 @ 999.00 (consumes 8 from best bid at 1000.00, triggers stop)
    // Stop SELL qty=5 fills → should get 2 @ 1000.00 + 3 @ 999.00
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        liquidity_consumption: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let ask_delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1100.00"),
            Quantity::from("100.000"),
            100,
        ))
        .build();
    engine.process_order_book_delta(&ask_delta).unwrap();

    let bid1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("1000.00"),
            Quantity::from("10.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&bid1).unwrap();

    let bid2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("999.00"),
            Quantity::from("10.000"),
            2,
        ))
        .build();
    engine.process_order_book_delta(&bid2).unwrap();

    // Stop SELL trigger below best bid so it doesn't trigger on submission
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .trigger_price(Price::from("999.00"))
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut stop_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    // SELL trade at 999.00 size=8 triggers the stop and consumes 8 from best bid.
    // ts_event=2 > book.ts_last=1, so seeding proceeds.
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("999.00"),
        Quantity::from("8.000"),
        AggressorSide::Seller,
        TradeId::new("1"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<_> = saved_messages
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert!(
        !fills.is_empty(),
        "Stop order should have triggered and filled"
    );

    let total_qty: f64 = fills.iter().map(|f| f.last_qty.as_f64()).sum();
    assert!(
        (total_qty - 5.0).abs() < 0.001,
        "Total fill qty should be 5.0, was {total_qty}"
    );

    // Trade consumed 8 of 10 at 1000.00 → only 2 remain at best bid.
    // The stop SELL should spill to 999.00.
    let got_fill_at_999 = fills.iter().any(|f| f.last_px == Price::from("999.00"));
    assert!(
        got_fill_at_999,
        "Expected fill at 999.00 due to consumed bid liquidity at 1000.00, \
         but all fills were at 1000.00. Seller-side trade consumption was not applied."
    );
}

// Regression: order rejection inside process_order must not panic when the
// event handler borrows the same cache (RefCell re-entrancy).
#[rstest]
fn test_process_order_rejection_no_refcell_reentrant_panic(
    account_id: AccountId,
    equity_aapl: Equity,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let instrument = InstrumentAny::Equity(equity_aapl);

    // Register handler that borrows the cache, mimicking ExecutionEngine::process
    let cache_clone = Rc::clone(&cache);
    let handler = TypedIntoHandler::from(move |_event: OrderEventAny| {
        let _guard = cache_clone.borrow_mut();
    });
    msgbus::register_order_event_endpoint(MessagingSwitchboard::exec_engine_process(), handler);

    let mut engine = get_order_matching_engine(instrument, Some(cache), None, None, None);

    // Sell on a Cash equity account triggers short-sell rejection
    let mut sell_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(engine.instrument.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("1"))
        .submit(true)
        .build();

    // Should not panic with "RefCell already borrowed"
    engine.process_order(&mut sell_order, account_id);
}

fn get_l1_queue_position_engine(
    instrument: InstrumentAny,
) -> (
    OrderMatchingEngine,
    Rc<RefCell<Cache>>,
    TypedIntoMessageSavingHandler<OrderEventAny>,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));

    let handler = order_event_handler_with_cache(Rc::clone(&cache));

    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        queue_position: true,
        ..Default::default()
    };

    let engine = OrderMatchingEngine::new(
        instrument,
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Margin,
        clock,
        Rc::clone(&cache),
        config,
    );

    (engine, cache, handler)
}

#[rstest]
fn test_l1_queue_position_deep_order_deferred_snapshot(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Regression (paulbir): exact replay of deep-order scenario.
    // BUY LIMIT placed 2 ticks below BBO. Old code gave queue_ahead=0
    // so the order filled instantly when BBO dropped to its level.
    let (mut engine, _cache, handler) = get_l1_queue_position_engine(instrument_eth_usdt.clone());

    // BBO bid=54.59@600
    let quote0 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Price::from("54.62"),
        Quantity::from("600.000"),
        Quantity::from("3617.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote0);

    // BUY LIMIT at 54.57, 2 ticks below BBO
    let mut order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("54.57"))
        .quantity(Quantity::from("1000.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);
    clear_order_event_handler_messages(&handler);

    // Trade at BBO, not at order level
    let trade1 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Quantity::from("37.000"),
        AggressorSide::Seller,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade1);

    // Quote confirms consumed liquidity
    let quote1 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Price::from("54.62"),
        Quantity::from("563.000"),
        Quantity::from("3617.000"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_quote_tick(&quote1);

    // More trades at BBO
    let trade2 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Quantity::from("200.000"),
        AggressorSide::Seller,
        TradeId::new("2"),
        UnixNanos::from(3),
        UnixNanos::from(3),
    );
    engine.process_trade_tick(&trade2);

    let trade3 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Quantity::from("363.000"),
        AggressorSide::Seller,
        TradeId::new("3"),
        UnixNanos::from(4),
        UnixNanos::from(4),
    );
    engine.process_trade_tick(&trade3);

    // BBO drops to the order's level with size=1895
    // Old code: queue=0 -> fills immediately here (wrong)
    // New code: pending resolved to 1895 -> blocks fill
    let quote2 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.57"),
        Price::from("54.59"),
        Quantity::from("1895.000"),
        Quantity::from("200.000"),
        UnixNanos::from(5),
        UnixNanos::from(5),
    );
    engine.process_quote_tick(&quote2);

    assert_eq!(
        get_order_event_handler_messages(&handler)
            .iter()
            .filter(|e| matches!(e, OrderEventAny::Filled(_)))
            .count(),
        0,
        "Should not fill when BBO first reaches order level"
    );

    // Trade at the order's level: queue 1895->1695, must NOT fill yet
    let trade4 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.57"),
        Quantity::from("200.000"),
        AggressorSide::Seller,
        TradeId::new("4"),
        UnixNanos::from(6),
        UnixNanos::from(6),
    );
    engine.process_trade_tick(&trade4);

    assert_eq!(
        get_order_event_handler_messages(&handler)
            .iter()
            .filter(|e| matches!(e, OrderEventAny::Filled(_)))
            .count(),
        0,
        "Should not fill, queue still has 1695 ahead"
    );
}

#[rstest]
fn test_l1_queue_position_at_bbo_trades_decrement_queue(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Regression (paulbir): replay of at-the-top scenario.
    // BUY LIMIT at BBO with queue=600. Three trades (37+200+363=600)
    // between quotes. Old code skipped trade decrements for L1.
    let (mut engine, _cache, handler) = get_l1_queue_position_engine(instrument_eth_usdt.clone());

    // BBO bid=54.59@600
    let quote0 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Price::from("54.62"),
        Quantity::from("600.000"),
        Quantity::from("3617.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote0);

    // BUY LIMIT at BBO
    let mut order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("54.59"))
        .quantity(Quantity::from("100.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);
    clear_order_event_handler_messages(&handler);

    // Trade 37 @ BBO -> queue 600->563
    let trade1 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Quantity::from("37.000"),
        AggressorSide::Seller,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade1);
    assert!(
        get_order_event_handler_messages(&handler)
            .iter()
            .all(|e| !matches!(e, OrderEventAny::Filled(_))),
        "Should not fill after first trade"
    );

    // Quote confirms consumed liquidity
    let quote1 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Price::from("54.62"),
        Quantity::from("563.000"),
        Quantity::from("3617.000"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_quote_tick(&quote1);

    // Trade 200 @ BBO -> queue 563->363
    let trade2 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Quantity::from("200.000"),
        AggressorSide::Seller,
        TradeId::new("2"),
        UnixNanos::from(3),
        UnixNanos::from(3),
    );
    engine.process_trade_tick(&trade2);
    assert!(
        get_order_event_handler_messages(&handler)
            .iter()
            .all(|e| !matches!(e, OrderEventAny::Filled(_))),
        "Should not fill after second trade"
    );

    // Trade 363 @ BBO -> queue 363->0, excess=0 (exact match)
    let trade3 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Quantity::from("363.000"),
        AggressorSide::Seller,
        TradeId::new("3"),
        UnixNanos::from(4),
        UnixNanos::from(4),
    );
    engine.process_trade_tick(&trade3);
    assert!(
        get_order_event_handler_messages(&handler)
            .iter()
            .all(|e| !matches!(e, OrderEventAny::Filled(_))),
        "Should not fill when trade exactly depletes queue (zero excess)"
    );

    // Queue exhausted. Next trade through fills the order.
    let trade4 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("54.59"),
        Quantity::from("100.000"),
        AggressorSide::Seller,
        TradeId::new("4"),
        UnixNanos::from(5),
        UnixNanos::from(5),
    );
    engine.process_trade_tick(&trade4);

    let fills: Vec<_> = get_order_event_handler_messages(&handler)
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(*f),
            _ => None,
        })
        .collect();
    assert_eq!(fills.len(), 1, "Should fill after queue fully exhausted");
    assert_eq!(fills[0].last_qty, Quantity::from("100.000"));
}

// A trade printing through the order's price must not bypass an active
// queue. With `queue_ahead > 0` the order does not fill even when the
// trade price crosses through.
#[rstest]
#[case::buy(OrderSide::Buy, "99.00", "98.00", AggressorSide::Seller)]
#[case::sell(OrderSide::Sell, "101.00", "102.00", AggressorSide::Buyer)]
fn test_l1_queue_position_cross_through_with_queue_ahead_does_not_fill(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] order_side: OrderSide,
    #[case] order_price: &str,
    #[case] trade_price: &str,
    #[case] aggressor_side: AggressorSide,
) {
    let (mut engine, _cache, handler) = get_l1_queue_position_engine(instrument_eth_usdt.clone());

    let quote0 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("99.00"),
        Price::from("101.00"),
        Quantity::from("10.000"),
        Quantity::from("10.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote0);

    // Limit at the BBO: tracked queue_ahead = 10
    let mut order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(order_side)
        .price(Price::from(order_price))
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);
    clear_order_event_handler_messages(&handler);

    // Trade prints through the order price.
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from(trade_price),
        Quantity::from("5.000"),
        aggressor_side,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade);

    assert_eq!(
        get_order_event_handler_messages(&handler)
            .iter()
            .filter(|e| matches!(e, OrderEventAny::Filled(_)))
            .count(),
        0,
        "cross-through trade must not fill while queue_ahead > 0",
    );
}

#[rstest]
fn test_l1_queue_position_trade_partial_does_not_fill(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // A trade smaller than the queue does not trigger a fill.
    let (mut engine, _cache, handler) = get_l1_queue_position_engine(instrument_eth_usdt.clone());

    let quote0 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("10.000"),
        Quantity::from("10.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote0);

    let mut order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("100.00"))
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);
    clear_order_event_handler_messages(&handler);

    // Trade of 5 partially consumes queue (10->5)
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Quantity::from("5.000"),
        AggressorSide::Seller,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade);

    assert_eq!(
        get_order_event_handler_messages(&handler)
            .iter()
            .filter(|e| matches!(e, OrderEventAny::Filled(_)))
            .count(),
        0,
        "Should not fill when queue only partially consumed"
    );
}

#[rstest]
fn test_l1_queue_position_full_example(account_id: AccountId, instrument_eth_usdt: InstrumentAny) {
    // Trades at the order's price consume queue ahead. Quote size
    // changes do not affect queue. Fill only when a trade exhausts queue.
    let (mut engine, _cache, handler) = get_l1_queue_position_engine(instrument_eth_usdt.clone());

    let quote0 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("10.000"),
        Quantity::from("10.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote0);

    let mut order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("100.00"))
        .quantity(Quantity::from("2.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);
    clear_order_event_handler_messages(&handler);

    // Trade 3 at 100 -> ahead: 10->7
    let trade1 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Quantity::from("3.000"),
        AggressorSide::Seller,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade1);
    assert!(
        get_order_event_handler_messages(&handler)
            .iter()
            .all(|e| !matches!(e, OrderEventAny::Filled(_)))
    );

    // Quote size decrease does not affect queue (trade-driven model)
    let quote1 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("8.000"),
        Quantity::from("10.000"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_quote_tick(&quote1);

    // Trade 4 at 100 -> ahead: 7->3
    let trade2 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Quantity::from("4.000"),
        AggressorSide::Seller,
        TradeId::new("2"),
        UnixNanos::from(3),
        UnixNanos::from(3),
    );
    engine.process_trade_tick(&trade2);
    assert!(
        get_order_event_handler_messages(&handler)
            .iter()
            .all(|e| !matches!(e, OrderEventAny::Filled(_)))
    );

    // Quote size increase does not affect queue
    let quote2 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("15.000"),
        Quantity::from("10.000"),
        UnixNanos::from(4),
        UnixNanos::from(4),
    );
    engine.process_quote_tick(&quote2);

    // Trade 2 at 100 -> ahead: 3->1
    let trade3 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Quantity::from("2.000"),
        AggressorSide::Seller,
        TradeId::new("3"),
        UnixNanos::from(5),
        UnixNanos::from(5),
    );
    engine.process_trade_tick(&trade3);
    assert!(
        get_order_event_handler_messages(&handler)
            .iter()
            .all(|e| !matches!(e, OrderEventAny::Filled(_)))
    );

    // Trade 3 at 100 -> ahead: 1->0, excess=2, fill=min(2,2)=2
    let trade4 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Quantity::from("3.000"),
        AggressorSide::Seller,
        TradeId::new("4"),
        UnixNanos::from(6),
        UnixNanos::from(6),
    );
    engine.process_trade_tick(&trade4);

    let fills: Vec<_> = get_order_event_handler_messages(&handler)
        .iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(*f),
            _ => None,
        })
        .collect();
    assert_eq!(fills.len(), 1);
    assert_eq!(fills[0].last_qty, Quantity::from("2.000"));
}

#[rstest]
fn test_l1_queue_position_bid_price_drop_clears_queue(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // When bid drops below order price, queue is cleared and next trade fills.
    let (mut engine, _cache, handler) = get_l1_queue_position_engine(instrument_eth_usdt.clone());

    let quote0 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("10.000"),
        Quantity::from("10.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote0);

    let mut order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("100.00"))
        .quantity(Quantity::from("2.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);
    clear_order_event_handler_messages(&handler);

    // Bid drops below order price -> queue cleared
    let quote1 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("99.00"),
        Price::from("101.00"),
        Quantity::from("5.000"),
        Quantity::from("10.000"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_quote_tick(&quote1);

    // Trade at order level should fill (queue was cleared)
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Quantity::from("2.000"),
        AggressorSide::Seller,
        TradeId::new("1"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_trade_tick(&trade);

    assert_eq!(
        get_order_event_handler_messages(&handler)
            .iter()
            .filter(|e| matches!(e, OrderEventAny::Filled(_)))
            .count(),
        1,
    );
}

#[rstest]
fn test_l1_queue_position_pending_not_stuck_after_zero_size_quote(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Zero-size L1 quote before a price drop must not stall the pending order.
    let (mut engine, _cache, handler) = get_l1_queue_position_engine(instrument_eth_usdt.clone());

    // BBO at 100/101
    let quote0 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("500.000"),
        Quantity::from("500.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote0);

    // BUY LIMIT at 99.00, behind BBO -> pending
    let mut order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("99.00"))
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);
    clear_order_event_handler_messages(&handler);

    // Zero-size bid update (venue clears the level)
    let quote1 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("0.000"),
        Quantity::from("500.000"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_quote_tick(&quote1);

    // Bid drops past order level (100 -> 98, skipping 99)
    let quote2 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("98.00"),
        Price::from("99.00"),
        Quantity::from("300.000"),
        Quantity::from("200.000"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_quote_tick(&quote2);

    // Trade at order level should fill (queue cleared by price drop)
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("99.00"),
        Quantity::from("5.000"),
        AggressorSide::Seller,
        TradeId::new("1"),
        UnixNanos::from(3),
        UnixNanos::from(3),
    );
    engine.process_trade_tick(&trade);

    assert_eq!(
        get_order_event_handler_messages(&handler)
            .iter()
            .filter(|e| matches!(e, OrderEventAny::Filled(_)))
            .count(),
        1,
    );
}

#[rstest]
fn test_l1_queue_position_pending_resolved_by_any_side_trade(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // A trade through a pending level resolves it regardless of aggressor
    // side. A buyer trade at 98 still proves the market reached 98, so a
    // BUY pending at 99 is crossed even though the trade is buyer-initiated.
    // The resolve moves it to queue_ahead(0); a follow-up seller trade at
    // the order level then fills it immediately.
    let (mut engine, _cache, handler) = get_l1_queue_position_engine(instrument_eth_usdt.clone());

    let quote0 = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("100.00"),
        Price::from("101.00"),
        Quantity::from("500.000"),
        Quantity::from("500.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote0);

    // BUY LIMIT at 99.00, behind BBO -> pending
    let mut order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("99.00"))
        .quantity(Quantity::from("5.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);
    clear_order_event_handler_messages(&handler);

    // Buyer trade at 98 crosses through 99 (opposite aggressor side).
    // This resolves the pending → queue_ahead with 0 ahead, but does NOT
    // fill because the ask is still at 101.
    let trade1 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("98.00"),
        Quantity::from("5.000"),
        AggressorSide::Buyer,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade1);

    assert_eq!(
        get_order_event_handler_messages(&handler)
            .iter()
            .filter(|e| matches!(e, OrderEventAny::Filled(_)))
            .count(),
        0,
    );

    // Seller trade at 99 now fills because queue was already resolved
    let trade2 = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("99.00"),
        Quantity::from("5.000"),
        AggressorSide::Seller,
        TradeId::new("2"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_trade_tick(&trade2);

    assert_eq!(
        get_order_event_handler_messages(&handler)
            .iter()
            .filter(|e| matches!(e, OrderEventAny::Filled(_)))
            .count(),
        1,
    );
}

#[rstest]
fn test_triggered_stop_market_removed_from_matching_core_after_trade_trigger(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let ask1 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("10.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&ask1).unwrap();

    let ask2 = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1001.00"),
            Quantity::from("10.000"),
            2,
        ))
        .build();
    engine.process_order_book_delta(&ask2).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-9");
    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1001.00"))
        .quantity(Quantity::from("5.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine.process_order(&mut stop_order, account_id);
    assert!(engine.order_exists(client_order_id));
    clear_order_event_handler_messages(&order_event_handler);

    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1001.00"),
        Quantity::from("8.000"),
        AggressorSide::Buyer,
        TradeId::new("repro-1"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert!(
        saved_messages
            .iter()
            .any(|event| matches!(event, OrderEventAny::Filled(_))),
        "Stop order should have triggered and filled",
    );
    assert!(
        !engine.order_exists(client_order_id),
        "Filled stop order should be removed from matching core"
    );
}

#[rstest]
fn test_fully_filled_limit_order_removed_from_core(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("5.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&delta).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1501.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine.process_order(&mut limit_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert!(
        saved_messages
            .iter()
            .any(|event| matches!(event, OrderEventAny::Filled(_))),
        "Limit order should have filled immediately",
    );
    assert!(
        !engine.order_exists(client_order_id),
        "Filled limit order should be removed from matching core",
    );
    assert_eq!(
        engine.cached_filled_qty_len(),
        1,
        "cached_filled_qty should stay until the fill event closes the cached order",
    );
}

#[rstest]
fn test_closed_filled_order_purges_cached_fill_qty_after_cache_update(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let mut engine = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    let delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("5.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&delta).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1501.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine.process_order(&mut limit_order, account_id);
    assert_eq!(engine.cached_filled_qty_len(), 1);

    let fill_event = get_order_event_handler_messages(&order_event_handler)
        .iter()
        .find(|event| matches!(event, OrderEventAny::Filled(_)))
        .unwrap()
        .clone();
    cache.borrow_mut().update_order(&fill_event).unwrap();

    engine.iterate(UnixNanos::from(1), AggressorSide::NoAggressor);

    assert_eq!(
        engine.cached_filled_qty_len(),
        0,
        "closed cached orders should purge filled-quantity guards without core membership",
    );

    clear_order_event_handler_messages(&order_event_handler);
    engine.fill_limit_order(client_order_id);

    assert!(
        get_order_event_handler_messages(&order_event_handler)
            .iter()
            .all(|event| !matches!(event, OrderEventAny::Filled(_))),
        "closed orders should not fill again after their cached guard is purged",
    );
}

#[rstest]
fn test_closed_filled_market_order_does_not_fill_after_cache_update(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let mut engine = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    let delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("5.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&delta).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine.process_order(&mut market_order, account_id);
    assert_eq!(engine.cached_filled_qty_len(), 1);

    let fill_event = get_order_event_handler_messages(&order_event_handler)
        .iter()
        .find(|event| matches!(event, OrderEventAny::Filled(_)))
        .unwrap()
        .clone();
    cache.borrow_mut().update_order(&fill_event).unwrap();

    engine.iterate(UnixNanos::from(1), AggressorSide::NoAggressor);

    assert_eq!(
        engine.cached_filled_qty_len(),
        0,
        "closed cached market orders should purge filled-quantity guards without core membership",
    );

    clear_order_event_handler_messages(&order_event_handler);
    engine.fill_market_order(client_order_id);

    assert!(
        get_order_event_handler_messages(&order_event_handler)
            .iter()
            .all(|event| !matches!(event, OrderEventAny::Filled(_))),
        "closed market orders should not fill again after their cached guard is purged",
    );
}

#[rstest]
fn test_triggered_stop_limit_removed_from_core_after_fill(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1000.00"),
            Quantity::from("10.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&ask).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut stop_limit = OrderTestBuilder::new(OrderType::StopLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1005.00"))
        .price(Price::from("1010.00"))
        .quantity(Quantity::from("2.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    engine.process_order(&mut stop_limit, account_id);
    assert!(engine.order_exists(client_order_id));
    clear_order_event_handler_messages(&order_event_handler);

    // Trade triggers the stop (ask crosses trigger price)
    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1005.00"),
        Quantity::from("10.000"),
        AggressorSide::Buyer,
        TradeId::new("trigger-1"),
        UnixNanos::from(2u64),
        UnixNanos::from(2u64),
    );
    engine.process_trade_tick(&trade);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert!(
        saved_messages
            .iter()
            .any(|event| matches!(event, OrderEventAny::Filled(_))),
        "Stop limit order should have triggered and filled",
    );
    assert!(
        !engine.order_exists(client_order_id),
        "Filled stop limit order should be removed from matching core",
    );
}

#[rstest]
fn test_fully_filled_market_to_limit_not_in_core(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    let delta = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("5.000"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&delta).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut mtl_order = OrderTestBuilder::new(OrderType::MarketToLimit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .build();

    let submitted = TestOrderEventStubs::submitted(&mtl_order, account_id);
    mtl_order.apply(submitted).unwrap();
    engine.process_order(&mut mtl_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    assert!(
        saved_messages
            .iter()
            .any(|event| matches!(event, OrderEventAny::Filled(_))),
        "MarketToLimit order should have filled",
    );
    assert!(
        !engine.order_exists(client_order_id),
        "Fully filled MarketToLimit should not rest in matching core",
    );
    assert_eq!(
        engine.cached_filled_qty_len(),
        1,
        "cached_filled_qty should stay until the fill event closes the cached order",
    );
}

#[rstest]
fn test_l1_ask_tracks_decreasing_seller_trade_prices(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Previously the ask only monotonically increased and the restore block
    // overwrote the correct book-synced value with the stale high-water mark.

    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("990.00"),
        Price::from("1010.00"),
        Quantity::from("100.000"),
        Quantity::from("100.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    // Three seller trades: 1000 -> 1050 -> 900
    for (i, price_str) in ["1000.00", "1050.00", "900.00"].iter().enumerate() {
        let trade = TradeTick::new(
            instrument_eth_usdt.id(),
            Price::from(*price_str),
            Quantity::from("10.000"),
            AggressorSide::Seller,
            TradeId::new(format!("{}", i + 1)),
            UnixNanos::from((i + 1) as u64),
            UnixNanos::from((i + 1) as u64),
        );
        engine.process_trade_tick(&trade);
    }

    // Submit market BUY: should fill at 900 (latest trade), not stale 1050
    clear_order_event_handler_messages(&order_event_handler);
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Market BUY should have filled");

    assert_eq!(
        fill.last_px,
        Price::from("900.00"),
        "Fill price should match latest trade, not stale ask high-water mark"
    );
}

#[rstest]
fn test_l1_bid_tracks_increasing_buyer_trade_prices(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Symmetric: L1_MBP bid must not become stale after trades at higher prices

    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("990.00"),
        Price::from("1010.00"),
        Quantity::from("100.000"),
        Quantity::from("100.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    // Three buyer trades: 1000 -> 950 -> 1100
    for (i, price_str) in ["1000.00", "950.00", "1100.00"].iter().enumerate() {
        let trade = TradeTick::new(
            instrument_eth_usdt.id(),
            Price::from(*price_str),
            Quantity::from("10.000"),
            AggressorSide::Buyer,
            TradeId::new(format!("{}", i + 1)),
            UnixNanos::from((i + 1) as u64),
            UnixNanos::from((i + 1) as u64),
        );
        engine.process_trade_tick(&trade);
    }

    // Submit market SELL: should fill at 1100 (latest trade), not stale 950
    clear_order_event_handler_messages(&order_event_handler);
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Market SELL should have filled");

    assert_eq!(
        fill.last_px,
        Price::from("1100.00"),
        "Fill price should match latest trade, not stale bid low-water mark"
    );
}

#[rstest]
fn test_l1_sequential_trades_alternating_aggressors(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Mixed aggressor sides: verify fill prices match the latest trade

    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("990.00"),
        Price::from("1010.00"),
        Quantity::from("100.000"),
        Quantity::from("100.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    let trades: Vec<(&str, AggressorSide)> = vec![
        ("1000.00", AggressorSide::Seller),
        ("1020.00", AggressorSide::Buyer),
        ("980.00", AggressorSide::Seller),
        ("950.00", AggressorSide::Buyer),
    ];

    for (i, (price_str, side)) in trades.iter().enumerate() {
        let trade = TradeTick::new(
            instrument_eth_usdt.id(),
            Price::from(*price_str),
            Quantity::from("10.000"),
            *side,
            TradeId::new(format!("{}", i + 1)),
            UnixNanos::from((i + 1) as u64),
            UnixNanos::from((i + 1) as u64),
        );
        engine.process_trade_tick(&trade);
    }

    clear_order_event_handler_messages(&order_event_handler);
    let mut buy_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut buy_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Market BUY should have filled");

    assert_eq!(
        fill.last_px,
        Price::from("950.00"),
        "BUY fill price should match latest trade price"
    );
}

#[rstest]
fn test_l1_trade_only_no_initial_quote_ask_tracks_price(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // The exact scenario from the bug report: trade-only data with no quotes.
    // With the BestPriceFillModel the fill price comes from core.bid/core.ask
    // via a synthetic L2 book, so stale core values produce wrong fill prices.

    let fill_model = FillModelAny::BestPrice(BestPriceFillModel::new(1.0, 0.0, None).unwrap());
    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        ..Default::default()
    };
    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        instrument_eth_usdt.clone(),
        1,
        fill_model.into(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        config,
    );

    // No quote: only trade ticks, seller trades at 100 -> 105 -> 90
    for (i, price_str) in ["100.00", "105.00", "90.00"].iter().enumerate() {
        let trade = TradeTick::new(
            instrument_eth_usdt.id(),
            Price::from(*price_str),
            Quantity::from("10.000"),
            AggressorSide::Seller,
            TradeId::new(format!("{}", i + 1)),
            UnixNanos::from((i + 1) as u64),
            UnixNanos::from((i + 1) as u64),
        );
        engine.process_trade_tick(&trade);
    }

    clear_order_event_handler_messages(&order_event_handler);
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Market BUY should have filled");

    assert_eq!(
        fill.last_px,
        Price::from("90.00"),
        "BestPriceFillModel should use latest trade price, not stale ask"
    );
}

#[rstest]
fn test_l1_no_aggressor_trades_track_price(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // NoAggressor trades (common in some data feeds) must also track price

    let config = OrderMatchingEngineConfig {
        trade_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("990.00"),
        Price::from("1010.00"),
        Quantity::from("100.000"),
        Quantity::from("100.000"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    for (i, price_str) in ["1000.00", "1050.00", "900.00"].iter().enumerate() {
        let trade = TradeTick::new(
            instrument_eth_usdt.id(),
            Price::from(*price_str),
            Quantity::from("10.000"),
            AggressorSide::NoAggressor,
            TradeId::new(format!("{}", i + 1)),
            UnixNanos::from((i + 1) as u64),
            UnixNanos::from((i + 1) as u64),
        );
        engine.process_trade_tick(&trade);
    }

    clear_order_event_handler_messages(&order_event_handler);
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fill = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Market BUY should have filled");

    assert_eq!(
        fill.last_px,
        Price::from("900.00"),
        "NoAggressor trades should track price like other aggressor sides"
    );
}

#[rstest]
fn test_stale_trade_tick_does_not_mutate_book(instrument_eth_usdt: InstrumentAny) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        instrument_eth_usdt.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig {
            trade_execution: true,
            ..Default::default()
        },
    );

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1000.00"),
        Quantity::from("1.000"),
        Quantity::from("1.000"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_quote_tick(&quote);

    let stale_trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1100.00"),
        Quantity::from("1.000"),
        AggressorSide::Buyer,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&stale_trade);

    assert_eq!(engine.best_bid_price(), Some(Price::from("1000.00")));
    assert_eq!(engine.best_ask_price(), Some(Price::from("1000.00")));
}

#[rstest]
fn test_stale_quote_tick_does_not_mutate_book(instrument_eth_usdt: InstrumentAny) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        instrument_eth_usdt.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    let trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Quantity::from("1.000"),
        AggressorSide::Buyer,
        TradeId::new("1"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_trade_tick(&trade);

    let stale_quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1100.00"),
        Price::from("1200.00"),
        Quantity::from("1.000"),
        Quantity::from("1.000"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_quote_tick(&stale_quote);

    assert_eq!(engine.best_bid_price(), Some(Price::from("1000.00")));
    assert_eq!(engine.best_ask_price(), Some(Price::from("1000.00")));
}

// Cython-vs-Rust parity: a non-crossing modify must keep the core's
// `RestingOrder` snapshot in sync so a later quote fills at the new price.
#[rstest]
fn test_modify_then_iterate_fills_at_new_limit_price(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine_l2 = get_order_matching_engine_l2(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    // Ask far above the BUY limit so neither submit nor modify cross
    let initial_ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("110.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&initial_ask).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("99.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(limit_order.clone(), None, None, false)
        .unwrap();
    engine_l2.process_order(&mut limit_order, account_id);

    // Modify to 100.00; still below ask 110, so does not cross at modify time
    let modified_limit_price = Price::from("100.00");
    let modify_order_command = ModifyOrder::new(
        TraderId::test_default(),
        Some(ClientId::from("CLIENT-001")),
        StrategyId::test_default(),
        instrument_eth_usdt.id(),
        client_order_id,
        Some(VenueOrderId::from("V1")),
        None,
        Some(modified_limit_price),
        None,
        UUID4::new(),
        UnixNanos::default(),
        None,
        None, // correlation_id
    );
    engine_l2.process_modify(&modify_order_command, account_id);

    // Drop ask to 99.50: above the stale 99.00 but below the new 100.00
    let crossing_ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("99.50"),
            Quantity::from("1.000"),
            2,
        ))
        .build();
    engine_l2.process_order_book_delta(&crossing_ask).unwrap();

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let event_types: Vec<OrderEventType> = saved_messages.iter().map(|e| e.event_type()).collect();
    assert!(
        event_types.contains(&OrderEventType::Filled),
        "expected fill at the modified limit price, was events {event_types:?}",
    );

    let fill = saved_messages
        .iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("OrderFilled event present");
    assert_eq!(fill.client_order_id, client_order_id);
    // Fill at the limit price (resting MAKER); the parity point is that any
    // fill happens (stale 99.00 snapshot would not cross ask 99.50)
    assert_eq!(fill.last_px, Price::from("100.00"));
    assert_eq!(fill.last_qty, Quantity::from("1.000"));
}

// Trailing recompute that yields no new prices must not dispatch a
// duplicate `OrderUpdated`; the helper's `unchanged` skip handles this.
// Uses the cache-applying handler so each event mutates the cached order.
#[rstest]
fn test_trailing_stop_no_recompute_skips_resync(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), Some(cache), None, None, None);

    // Quote-driven trailing recompute uses ask for BUY trailing stops
    let initial_ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&initial_ask).unwrap();
    let initial_bid = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("1499.00"),
            Quantity::from("1.000"),
            2,
        ))
        .build();
    engine_l2.process_order_book_delta(&initial_bid).unwrap();

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut trailing = OrderTestBuilder::new(OrderType::TrailingStopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .trigger_price(Price::from("1505.00"))
        .trigger_type(TriggerType::BidAsk)
        .trailing_offset(dec!(1))
        .trailing_offset_type(TrailingOffsetType::Price)
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut trailing, account_id);

    // Add better ask at 1490; trailing recompute lowers trigger to 1491
    let new_ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1490.00"),
            Quantity::from("1.000"),
            3,
        ))
        .build();
    engine_l2.process_order_book_delta(&new_ask).unwrap();

    // Sanity: recompute emitted at least one OrderUpdated
    let after_recompute = get_order_event_handler_messages(&order_event_handler);
    let updated_after_recompute = after_recompute
        .iter()
        .filter(|e| matches!(e, OrderEventAny::Updated(_)))
        .count();
    assert!(
        updated_after_recompute >= 1,
        "trailing recompute should dispatch at least one OrderUpdated, was {updated_after_recompute}",
    );
    let stop_after_recompute = engine_l2
        .get_core()
        .get_order(client_order_id)
        .copied()
        .expect("trailing stop should still be in the core");

    clear_order_event_handler_messages(&order_event_handler);

    // Add a sell at 1495 above best_ask 1490; best_ask is unchanged, so
    // the trailing recompute returns (None, None) and dispatches nothing
    let no_change_ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1495.00"),
            Quantity::from("1.000"),
            4,
        ))
        .build();
    engine_l2.process_order_book_delta(&no_change_ask).unwrap();

    let after_no_change = get_order_event_handler_messages(&order_event_handler);
    assert!(
        !after_no_change
            .iter()
            .any(|e| matches!(e, OrderEventAny::Updated(_))),
        "no-recompute quote must not dispatch OrderUpdated, was {after_no_change:?}",
    );
    let stop_after_no_change = engine_l2
        .get_core()
        .get_order(client_order_id)
        .copied()
        .expect("trailing stop should still be in the core");
    assert_eq!(
        stop_after_recompute, stop_after_no_change,
        "core RestingOrder should be byte-identical when nothing changed",
    );
}

// GTD expiry and trail recompute must fire in the same `iterate` call.
#[rstest]
fn test_gtd_expiry_and_trailing_recompute_in_same_iterate(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let config = OrderMatchingEngineConfig {
        support_gtd_orders: true,
        ..Default::default()
    };
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let initial_ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1500.00"),
            Quantity::from("1.000"),
            1,
        ))
        .build();
    engine_l2.process_order_book_delta(&initial_ask).unwrap();
    let initial_bid = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("1485.00"),
            Quantity::from("1.000"),
            2,
        ))
        .build();
    engine_l2.process_order_book_delta(&initial_bid).unwrap();

    let expire_ns: u64 = 1_500_000_000_000_000_000;

    let gtd_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut gtd_limit = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .time_in_force(TimeInForce::Gtd)
        .expire_time(UnixNanos::from(expire_ns))
        .client_order_id(gtd_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut gtd_limit, account_id);

    let trail_id = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut trailing = OrderTestBuilder::new(OrderType::TrailingStopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .trigger_price(Price::from("1510.00"))
        .trigger_type(TriggerType::BidAsk)
        .trailing_offset(dec!(5))
        .trailing_offset_type(TrailingOffsetType::Price)
        .client_order_id(trail_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut trailing, account_id);

    clear_order_event_handler_messages(&order_event_handler);

    // Better ask at the GTD expire timestamp triggers iterate at the boundary
    let new_ask = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1495.00"),
            Quantity::from("1.000"),
            3,
        ))
        .ts_init(UnixNanos::from(expire_ns))
        .build();
    engine_l2.process_order_book_delta(&new_ask).unwrap();

    let saved = get_order_event_handler_messages(&order_event_handler);

    let expired = saved
        .iter()
        .find(|e| matches!(e, OrderEventAny::Expired(ev) if ev.client_order_id == gtd_id));
    assert!(
        expired.is_some(),
        "GTD limit should expire at boundary tick within the per-order loop",
    );

    let trail_update = saved.iter().rev().find_map(|e| match e {
        OrderEventAny::Updated(ev) if ev.client_order_id == trail_id => Some(ev),
        _ => None,
    });
    let upd = trail_update
        .expect("Trailing-stop should emit OrderUpdated in the same iterate as the GTD expiry");
    assert_eq!(
        upd.trigger_price.unwrap(),
        Price::from("1500.00"),
        "Inline trail recompute must use ask(1495) + offset(5)",
    );
}

// When a Maker fill closes the only resting order, the per-order loop has
// nothing eligible for the inline restore; the end-of-iterate fallback must
// pull `core.last` back so the fill override doesn't leak.
#[rstest]
fn test_fallback_target_restore_preserves_core_last_after_maker_fill(
    instrument_eth_usdt: InstrumentAny,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Seed `core.last` via a trade tick (quote ticks don't set last)
    let seed_trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1500.00"),
        Quantity::from("1.000"),
        AggressorSide::Buyer,
        TradeId::from("seed"),
        UnixNanos::from(1u64),
        UnixNanos::from(1u64),
    );
    engine_l2.process_trade_tick(&seed_trade);

    let pre_fill_last = engine_l2
        .get_core()
        .last
        .expect("trade tick should seed core.last");
    assert_eq!(pre_fill_last, Price::from("1500.00"));

    let limit_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(limit_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit, account_id);

    // Crossing SELL at 1480 fires a Maker fill on the BUY at 1490, which
    // closes the only order. Inline restore is skipped (closed); only the
    // fallback can pull `core.last` back.
    let crossing = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1480.00"),
            Quantity::from("1.000"),
            2,
        ))
        .build();
    engine_l2.process_order_book_delta(&crossing).unwrap();

    assert_eq!(
        engine_l2.get_core().last,
        Some(pre_fill_last),
        "fallback restore should return core.last to the pre-fill value",
    );
}

// A trailing-stop visited after a Maker-trigger fill in the same per-order
// loop must recompute against the mutated `core.last`, not the pre-fill
// value.
#[rstest]
fn test_trailing_stop_recompute_after_maker_fill_uses_mutated_core(
    instrument_eth_usdt: InstrumentAny,
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let mut engine_l2 =
        get_order_matching_engine_l2(instrument_eth_usdt.clone(), None, None, None, None);

    // Seed `core.last` so the Maker fill saves a non-trivial `target_last`.
    let seed_trade = TradeTick::new(
        instrument_eth_usdt.id(),
        Price::from("1500.00"),
        Quantity::from("1.000"),
        AggressorSide::Buyer,
        TradeId::from("seed"),
        UnixNanos::from(1u64),
        UnixNanos::from(1u64),
    );
    engine_l2.process_trade_tick(&seed_trade);

    let limit_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1490.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(limit_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut limit, account_id);

    let trail_id = ClientOrderId::from("O-19700101-000000-001-001-2");
    let mut trail = OrderTestBuilder::new(OrderType::TrailingStopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("1.000"))
        .trigger_price(Price::from("1485.00"))
        .trigger_type(TriggerType::LastPrice)
        .trailing_offset(dec!(3))
        .trailing_offset_type(TrailingOffsetType::Price)
        .client_order_id(trail_id)
        .submit(true)
        .build();
    engine_l2.process_order(&mut trail, account_id);

    clear_order_event_handler_messages(&order_event_handler);

    // Maker fill on the BUY at 1490 mutates `core.last` to 1490; the trail
    // SELL is visited next and recomputes against the mutated last.
    // Expected new trigger: 1490 - 3 = 1487.
    let crossing = OrderBookDeltaTestBuilder::new(instrument_eth_usdt.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("1480.00"),
            Quantity::from("1.000"),
            2,
        ))
        .build();
    engine_l2.process_order_book_delta(&crossing).unwrap();

    let saved = get_order_event_handler_messages(&order_event_handler);

    let trail_update = saved
        .iter()
        .rev()
        .find_map(|e| match e {
            OrderEventAny::Updated(ev) if ev.client_order_id == trail_id => Some(ev),
            _ => None,
        })
        .expect("trailing-stop should recompute trigger after the Maker fill");
    assert_eq!(
        trail_update.trigger_price.unwrap(),
        Price::from("1487.00"),
        "trail recompute must use mutated `core.last` (1490) minus offset(3)",
    );
}

#[rstest]
fn test_update_instrument_resets_market_state(instrument_eth_usdt: InstrumentAny) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        instrument_eth_usdt.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1001.00"),
        Quantity::from("1.000"),
        Quantity::from("1.000"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_quote_tick(&quote);

    let updated_instrument = match instrument_eth_usdt {
        InstrumentAny::CryptoPerpetual(mut crypto_perp) => {
            crypto_perp.price_precision = 3;
            crypto_perp.price_increment = Price::from("0.001");
            InstrumentAny::CryptoPerpetual(crypto_perp)
        }
        _ => panic!("Test fixture expected CryptoPerpetual instrument"),
    };

    engine.update_instrument(updated_instrument).unwrap();

    assert!(engine.best_bid_price().is_none());
    assert!(engine.best_ask_price().is_none());
    assert_eq!(engine.get_core().price_increment, Price::from("0.001"));
}

#[rstest]
fn test_update_instrument_without_precision_change_keeps_market_state(
    instrument_eth_usdt: InstrumentAny,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        instrument_eth_usdt.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1001.00"),
        Quantity::from("1.000"),
        Quantity::from("1.000"),
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_quote_tick(&quote);

    let best_bid_before = engine.best_bid_price();
    let best_ask_before = engine.best_ask_price();
    let increment_before = engine.get_core().price_increment;

    let updated_instrument = match instrument_eth_usdt {
        InstrumentAny::CryptoPerpetual(mut crypto_perp) => {
            crypto_perp.ts_event = UnixNanos::from(3);
            InstrumentAny::CryptoPerpetual(crypto_perp)
        }
        _ => panic!("Test fixture expected CryptoPerpetual instrument"),
    };

    engine.update_instrument(updated_instrument).unwrap();

    assert_eq!(engine.best_bid_price(), best_bid_before);
    assert_eq!(engine.best_ask_price(), best_ask_before);
    assert_eq!(engine.get_core().price_increment, increment_before);
}

#[rstest]
fn test_update_instrument_normalizes_tick_compatible_resting_order_fill(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), Some(cache), None, None, None);

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1000.00"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    clear_order_event_handler_messages(&order_event_handler);

    let updated_instrument = crypto_perpetual_with_price_precision(instrument_eth_usdt, 3, "0.001");
    engine
        .update_instrument(updated_instrument.clone())
        .unwrap();

    let trade = TradeTick::new(
        updated_instrument.id(),
        Price::from("999.000"),
        Quantity::from("10.000"),
        AggressorSide::Seller,
        TradeId::new("1"),
        UnixNanos::from(1),
        UnixNanos::from(1),
    );
    engine.process_trade_tick(&trade);

    let filled_events: Vec<_> = get_order_event_handler_messages(&order_event_handler)
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(filled) => Some(*filled),
            _ => None,
        })
        .collect();

    assert_eq!(filled_events.len(), 1);
    assert_eq!(filled_events[0].client_order_id, client_order_id);
    assert_eq!(filled_events[0].last_px, Price::from("1000.000"));
    assert_eq!(filled_events[0].last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_update_instrument_removes_incompatible_resting_order_from_core(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    let initial_instrument = crypto_perpetual_with_price_precision(instrument_eth_usdt, 3, "0.001");
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine = get_order_matching_engine(
        initial_instrument.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(initial_instrument.id())
        .side(OrderSide::Buy)
        .price(Price::from("1000.005"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    assert!(engine.order_exists(client_order_id));
    clear_order_event_handler_messages(&order_event_handler);

    let updated_instrument = crypto_perpetual_with_price_precision(initial_instrument, 2, "0.01");
    engine.update_instrument(updated_instrument).unwrap();

    assert!(!engine.order_exists(client_order_id));
    assert_eq!(
        cache.borrow().order(&client_order_id).unwrap().status(),
        OrderStatus::Canceled
    );
    assert!(
        get_order_event_handler_messages(&order_event_handler)
            .iter()
            .any(|event| matches!(event, OrderEventAny::Canceled(canceled) if canceled.client_order_id == client_order_id))
    );
}

#[rstest]
fn test_update_instrument_cancels_tick_incompatible_resting_order(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    let initial_instrument = crypto_perpetual_with_price_precision(instrument_eth_usdt, 3, "0.001");
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine = get_order_matching_engine(
        initial_instrument.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(initial_instrument.id())
        .side(OrderSide::Buy)
        .price(Price::from("1000.005"))
        .quantity(Quantity::from("1.000"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    assert!(engine.order_exists(client_order_id));
    clear_order_event_handler_messages(&order_event_handler);

    let updated_instrument = crypto_perpetual_with_price_precision(initial_instrument, 3, "0.01");
    engine.update_instrument(updated_instrument).unwrap();

    assert!(!engine.order_exists(client_order_id));
    assert_eq!(
        cache.borrow().order(&client_order_id).unwrap().status(),
        OrderStatus::Canceled
    );
    assert!(
        get_order_event_handler_messages(&order_event_handler)
            .iter()
            .any(|event| matches!(event, OrderEventAny::Canceled(canceled) if canceled.client_order_id == client_order_id))
    );
}

#[rstest]
fn test_update_instrument_cancels_quantity_incompatible_resting_order(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    let mut engine = get_order_matching_engine(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-1");
    let mut limit_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .price(Price::from("1000.00"))
        .quantity(Quantity::from("1.001"))
        .client_order_id(client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut limit_order, account_id);
    assert!(engine.order_exists(client_order_id));
    clear_order_event_handler_messages(&order_event_handler);

    let updated_instrument = crypto_perpetual_with_size_precision(instrument_eth_usdt, 2, "0.01");
    engine.update_instrument(updated_instrument).unwrap();

    assert!(!engine.order_exists(client_order_id));
    assert_eq!(
        cache.borrow().order(&client_order_id).unwrap().status(),
        OrderStatus::Canceled
    );
    assert!(
        get_order_event_handler_messages(&order_event_handler)
            .iter()
            .any(|event| matches!(event, OrderEventAny::Canceled(canceled) if canceled.client_order_id == client_order_id))
    );
}

#[rstest]
fn test_process_bar_drops_precision_mismatch_after_instrument_update(
    instrument_eth_usdt: InstrumentAny,
) {
    let config = OrderMatchingEngineConfig {
        bar_execution: true,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let updated_instrument = crypto_perpetual_with_price_precision(instrument_eth_usdt, 3, "0.001");
    engine.update_instrument(updated_instrument).unwrap();

    let bar_type = BarType::from("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL");
    let stale_bar = Bar {
        bar_type,
        open: Price::from("1000.00"),
        high: Price::from("1001.00"),
        low: Price::from("999.00"),
        close: Price::from("1000.50"),
        volume: Quantity::from("100.000"),
        ts_event: UnixNanos::from(1),
        ts_init: UnixNanos::from(1),
    };

    engine.process_bar(&stale_bar);

    assert!(engine.get_core().last.is_none());
}

fn option_contract(
    underlying: &str,
    venue: &str,
    expiration_ns: UnixNanos,
    kind: OptionKind,
) -> OptionContract {
    let symbol = match kind {
        OptionKind::Call => "AAPL211217C00150000",
        OptionKind::Put => "AAPL211217P00150000",
    };
    OptionContract::new(
        InstrumentId::from(format!("{symbol}.{venue}").as_str()),
        Symbol::from(symbol),
        AssetClass::Equity,
        Some(Ustr::from(venue)),
        Ustr::from(underlying),
        kind,
        Price::from("149.00"),
        Currency::USD(),
        UnixNanos::from(0),
        expiration_ns,
        2,
        Price::from("0.01"),
        Quantity::from(1),
        Quantity::from(1),
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        UnixNanos::default(),
        UnixNanos::default(),
    )
}

fn crypto_option_call_btc(venue: &str, expiration_ns: UnixNanos, strike: Price) -> CryptoOption {
    CryptoOption::new(
        InstrumentId::from(format!("BTC-OPT-CALL.{venue}").as_str()),
        Symbol::from("BTC-OPT-CALL"),
        Currency::from("BTC"),
        Currency::from("USD"),
        Currency::from("BTC"),
        false,
        OptionKind::Call,
        strike,
        UnixNanos::from(0),
        expiration_ns,
        2,
        1,
        Price::from("0.01"),
        Quantity::from("0.1"),
        Some(Quantity::from(1)),
        Some(Quantity::from(1)),
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        UnixNanos::default(),
        UnixNanos::default(),
    )
}

fn underlying_index(venue: &str) -> IndexInstrument {
    IndexInstrument::new(
        InstrumentId::from(format!("AAPL.{venue}").as_str()),
        Symbol::from("AAPL"),
        Currency::USD(),
        2,
        0,
        Price::from("0.01"),
        Quantity::from(1),
        None,
        None,
        UnixNanos::default(),
        UnixNanos::default(),
    )
}

fn underlying_equity(venue: &str) -> Equity {
    Equity::new(
        InstrumentId::from(format!("AAPL.{venue}").as_str()),
        Symbol::from("AAPL"),
        None,
        Currency::USD(),
        2,
        Price::from("0.01"),
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        UnixNanos::default(),
        UnixNanos::default(),
    )
}

fn open_long_option_position(
    cache: &Rc<RefCell<Cache>>,
    instrument: &InstrumentAny,
    account_id: AccountId,
    quantity: Quantity,
    open_price: Price,
) -> Position {
    let trader_id = TraderId::from("TRADER-001");
    let strategy_id = StrategyId::from("S-001");
    let client_order_id = ClientOrderId::from("OPT-OPEN-1");
    let venue_order_id = VenueOrderId::from("OPT-OPEN-1");
    let position_id = PositionId::from("P-OPT-1");

    let order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .quantity(quantity)
        .client_order_id(client_order_id)
        .submit(true)
        .build();

    cache
        .borrow_mut()
        .add_order(order, Some(position_id), None, false)
        .unwrap();

    let fill = build_order_filled(
        trader_id,
        strategy_id,
        instrument.id(),
        client_order_id,
        venue_order_id,
        account_id,
        TradeId::from("OPT-OPEN-1"),
        OrderSide::Buy,
        OrderType::Market,
        quantity,
        open_price,
        instrument.quote_currency(),
        LiquiditySide::Taker,
        Some(position_id),
        Some(Money::new(0.0, instrument.quote_currency())),
    );

    let position = Position::new(instrument, fill);
    cache
        .borrow_mut()
        .add_position(&position, OmsType::Netting)
        .unwrap();
    position
}

#[rstest]
fn test_option_cash_settlement_at_intrinsic_value(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract(
        "AAPL",
        venue,
        expiration_ns,
        OptionKind::Call,
    ));
    let underlying = InstrumentAny::IndexInstrument(underlying_index(venue));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache
        .borrow_mut()
        .add_instrument(underlying.clone())
        .unwrap();

    // ITM call: spot 160 > strike 149 -> cash payout 11.00
    cache
        .borrow_mut()
        .add_trade(TradeTick::new(
            underlying.id(),
            Price::from("160.00"),
            Quantity::from(1),
            AggressorSide::NoAggressor,
            TradeId::from("U-1"),
            UnixNanos::from(1),
            UnixNanos::from(1),
        ))
        .unwrap();

    let position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from(1),
        Price::from("5.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let fills: Vec<OrderFilled> = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    let settlement_fill = fills
        .iter()
        .find(|f| {
            f.client_order_id
                .as_str()
                .starts_with(&format!("{venue}-LEG-CASH-"))
        })
        .expect("Expected cash settlement fill");

    assert_eq!(settlement_fill.instrument_id, option.id());
    assert_eq!(settlement_fill.order_side, OrderSide::Sell);
    assert_eq!(settlement_fill.last_qty, position.quantity);
    assert_eq!(settlement_fill.last_px, Price::from("11.00"));
    assert_eq!(settlement_fill.position_id, Some(position.id));
}

#[rstest]
fn test_option_physical_settlement_delivers_underlying(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract(
        "AAPL",
        venue,
        expiration_ns,
        OptionKind::Call,
    ));
    let underlying = InstrumentAny::Equity(underlying_equity(venue));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache
        .borrow_mut()
        .add_instrument(underlying.clone())
        .unwrap();

    // ITM call: spot 160 > strike 149 -> exercise into long underlying at strike
    cache
        .borrow_mut()
        .add_trade(TradeTick::new(
            underlying.id(),
            Price::from("160.00"),
            Quantity::from(1),
            AggressorSide::NoAggressor,
            TradeId::from("U-1"),
            UnixNanos::from(1),
            UnixNanos::from(1),
        ))
        .unwrap();

    let position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from(1),
        Price::from("5.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let fills: Vec<OrderFilled> = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    let close_fill = fills
        .iter()
        .find(|f| {
            f.client_order_id
                .as_str()
                .starts_with(&format!("{venue}-LEG-EX-"))
                && f.client_order_id.as_str().ends_with("-CLOSE")
        })
        .expect("Expected option close fill");
    let underlying_fill = fills
        .iter()
        .find(|f| {
            f.client_order_id
                .as_str()
                .starts_with(&format!("{venue}-LEG-EX-"))
                && f.client_order_id.as_str().ends_with("-OPEN")
        })
        .expect("Expected underlying open fill");

    // Option leg closes worthless on physical settlement (no custom price set).
    assert_eq!(close_fill.instrument_id, option.id());
    assert_eq!(close_fill.order_side, OrderSide::Sell);
    assert_eq!(close_fill.last_qty, position.quantity);
    assert_eq!(close_fill.last_px, Price::from("0.00"));
    assert_eq!(close_fill.position_id, Some(position.id));

    // Underlying leg buys at strike (long call exercise: receive underlying).
    assert_eq!(underlying_fill.instrument_id, underlying.id());
    assert_eq!(underlying_fill.order_side, OrderSide::Buy);
    assert_eq!(underlying_fill.last_px, Price::from("149.00"));
    assert_eq!(underlying_fill.position_id, None);
}

#[rstest]
fn test_marketable_resting_limit_at_expiration_boundary_fills_before_close(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let activation = UnixNanos::from(
        Utc.with_ymd_and_hms(2021, 9, 10, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    let expiration_ns = UnixNanos::from(
        Utc.with_ymd_and_hms(2099, 12, 17, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    let instrument =
        InstrumentAny::FuturesContract(futures_contract_es(Some(activation), Some(expiration_ns)));

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock
        .borrow_mut()
        .set_time(UnixNanos::from(activation.as_u64() + 1));

    let mut engine = OrderMatchingEngine::new(
        instrument.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L2_MBP,
        OmsType::Netting,
        AccountType::Margin,
        clock,
        cache.clone(),
        OrderMatchingEngineConfig {
            use_position_ids: true,
            ..Default::default()
        },
    );

    // Seed an ask so the opening market BUY fills.
    let opening_ask = OrderBookDeltaTestBuilder::new(instrument.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("4501.00"),
            Quantity::from(10),
            1,
        ))
        .build();
    engine.process_order_book_delta(&opening_ask).unwrap();

    let mut buy_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from(1))
        .client_order_id(ClientOrderId::from("OPEN-1"))
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(buy_order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut buy_order, account_id);

    // Materialize the position from the opening fill.
    let mut opening_fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Expected an opening fill");
    opening_fill.position_id = Some(PositionId::from("P-001"));
    let position = Position::new(&instrument, opening_fill);
    cache
        .borrow_mut()
        .add_position(&position, OmsType::Netting)
        .unwrap();

    clear_order_event_handler_messages(&order_event_handler);

    // Submit a passive SELL limit at 4502 (above current best ask, so it rests).
    let close_client_order_id = ClientOrderId::from("CLOSE-LIMIT-1");
    let mut close_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Sell)
        .price(Price::from("4502.00"))
        .quantity(Quantity::from(1))
        .client_order_id(close_client_order_id)
        .submit(true)
        .build();
    engine.process_order(&mut close_order, account_id);

    // Drive iterate exactly at expiration_ns with a buy delta crossing 4502.
    // Matching must fill the resting limit before check_instrument_expiration.
    let crossing_bid = OrderBookDeltaTestBuilder::new(instrument.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("4503.00"),
            Quantity::from(1),
            2,
        ))
        .ts_init(expiration_ns)
        .build();
    engine.process_order_book_delta(&crossing_bid).unwrap();

    let messages = get_order_event_handler_messages(&order_event_handler);
    let limit_fill_idx = messages
        .iter()
        .position(|e| {
            matches!(
                e,
                OrderEventAny::Filled(f) if f.client_order_id == close_client_order_id
            )
        })
        .expect("Expected resting limit to fill at boundary tick");

    let limit_fill = match &messages[limit_fill_idx] {
        OrderEventAny::Filled(f) => f,
        _ => unreachable!(),
    };
    assert_eq!(limit_fill.last_qty, Quantity::from(1));

    // Matching must run before instrument-expiration handling so the resting
    // limit fills before any EXPIRATION-* close order is generated.
    let expiration_event_idx = messages.iter().position(|e| {
        matches!(
            e,
            OrderEventAny::Filled(f) if f.client_order_id.as_str().starts_with("EXPIRATION-")
        )
    });

    if let Some(exp_idx) = expiration_event_idx {
        assert!(
            limit_fill_idx < exp_idx,
            "Resting limit must fill before EXPIRATION close fires"
        );
    }
}

fn run_otm_expiry_case(kind: OptionKind, spot: Price, account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract("AAPL", venue, expiration_ns, kind));
    let underlying = InstrumentAny::Equity(underlying_equity(venue));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache
        .borrow_mut()
        .add_instrument(underlying.clone())
        .unwrap();

    cache
        .borrow_mut()
        .add_trade(TradeTick::new(
            underlying.id(),
            spot,
            Quantity::from(1),
            AggressorSide::NoAggressor,
            TradeId::from("U-1"),
            UnixNanos::from(1),
            UnixNanos::from(1),
        ))
        .unwrap();

    let position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from(1),
        Price::from("5.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let fills: Vec<OrderFilled> = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    let otm_fill = fills
        .iter()
        .find(|f| {
            f.client_order_id
                .as_str()
                .starts_with(&format!("{venue}-LEG-OTM-"))
        })
        .expect("Expected OTM expiry fill");

    assert_eq!(otm_fill.instrument_id, option.id());
    assert_eq!(otm_fill.order_side, OrderSide::Sell);
    assert_eq!(otm_fill.last_qty, position.quantity);
    assert_eq!(otm_fill.last_px, Price::from("0.00"));
    assert_eq!(otm_fill.position_id, Some(position.id));

    assert!(
        !fills
            .iter()
            .any(|f| f.client_order_id.as_str().contains("-LEG-CASH-")
                || f.client_order_id.as_str().contains("-LEG-EX-")),
        "OTM path must not emit cash or physical settlement fills"
    );
}

#[rstest]
#[case::call_otm(OptionKind::Call, Price::from("140.00"))]
#[case::put_otm(OptionKind::Put, Price::from("160.00"))]
fn test_option_otm_expiry_closes_at_zero(
    #[case] kind: OptionKind,
    #[case] spot: Price,
    account_id: AccountId,
) {
    run_otm_expiry_case(kind, spot, account_id);
}

#[rstest]
fn test_option_cash_settlement_put_pays_strike_minus_spot(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract(
        "AAPL",
        venue,
        expiration_ns,
        OptionKind::Put,
    ));
    let underlying = InstrumentAny::IndexInstrument(underlying_index(venue));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache
        .borrow_mut()
        .add_instrument(underlying.clone())
        .unwrap();

    // ITM put: spot 140 < strike 149 -> cash payout 9.00
    cache
        .borrow_mut()
        .add_trade(TradeTick::new(
            underlying.id(),
            Price::from("140.00"),
            Quantity::from(1),
            AggressorSide::NoAggressor,
            TradeId::from("U-1"),
            UnixNanos::from(1),
            UnixNanos::from(1),
        ))
        .unwrap();

    let position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from(1),
        Price::from("5.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let settlement_fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f)
                if f.client_order_id
                    .as_str()
                    .starts_with(&format!("{venue}-LEG-CASH-")) =>
            {
                Some(f)
            }
            _ => None,
        })
        .expect("Expected cash settlement fill");

    assert_eq!(settlement_fill.instrument_id, option.id());
    assert_eq!(settlement_fill.order_side, OrderSide::Sell);
    assert_eq!(settlement_fill.last_qty, position.quantity);
    assert_eq!(settlement_fill.last_px, Price::from("9.00"));
    assert_eq!(settlement_fill.position_id, Some(position.id));
}

#[rstest]
fn test_option_physical_settlement_put_flips_underlying_side(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract(
        "AAPL",
        venue,
        expiration_ns,
        OptionKind::Put,
    ));
    let underlying = InstrumentAny::Equity(underlying_equity(venue));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache
        .borrow_mut()
        .add_instrument(underlying.clone())
        .unwrap();

    // ITM put: spot 140 < strike 149 -> long Put exercise sells underlying at strike
    cache
        .borrow_mut()
        .add_trade(TradeTick::new(
            underlying.id(),
            Price::from("140.00"),
            Quantity::from(1),
            AggressorSide::NoAggressor,
            TradeId::from("U-1"),
            UnixNanos::from(1),
            UnixNanos::from(1),
        ))
        .unwrap();

    let position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from(1),
        Price::from("5.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option,
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let fills: Vec<OrderFilled> = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    let underlying_fill = fills
        .iter()
        .find(|f| {
            f.client_order_id
                .as_str()
                .starts_with(&format!("{venue}-LEG-EX-"))
                && f.client_order_id.as_str().ends_with("-OPEN")
        })
        .expect("Expected underlying open fill");

    // Long Put exercise -> sell underlying at strike (flipped from Long position side).
    assert_eq!(underlying_fill.instrument_id, underlying.id());
    assert_eq!(underlying_fill.order_side, OrderSide::Sell);
    assert_eq!(underlying_fill.last_px, Price::from("149.00"));
    assert_eq!(underlying_fill.last_qty, position.quantity);
    assert_eq!(underlying_fill.position_id, None);
}

#[rstest]
fn test_check_instrument_expiration_fallback_uses_book(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let activation = UnixNanos::from(
        Utc.with_ymd_and_hms(2021, 9, 10, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    let expiration_ns = UnixNanos::from(
        Utc.with_ymd_and_hms(2099, 12, 17, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    let instrument =
        InstrumentAny::FuturesContract(futures_contract_es(Some(activation), Some(expiration_ns)));

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock
        .borrow_mut()
        .set_time(UnixNanos::from(activation.as_u64() + 1));

    let fee_model =
        FeeModelAny::Fixed(FixedFeeModel::new(Money::new(0.0, Currency::USD()), None).unwrap());
    let mut engine = OrderMatchingEngine::new(
        instrument.clone(),
        1,
        FillModelHandle::default(),
        fee_model.into(),
        BookType::L2_MBP,
        OmsType::Netting,
        AccountType::Margin,
        clock,
        cache.clone(),
        OrderMatchingEngineConfig {
            use_position_ids: true,
            ..Default::default()
        },
    );

    let opening_ask = OrderBookDeltaTestBuilder::new(instrument.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("4501.00"),
            Quantity::from(10),
            1,
        ))
        .build();
    engine.process_order_book_delta(&opening_ask).unwrap();

    let opening_bid = OrderBookDeltaTestBuilder::new(instrument.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("4499.00"),
            Quantity::from(10),
            2,
        ))
        .build();
    engine.process_order_book_delta(&opening_bid).unwrap();

    let mut buy_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from(1))
        .client_order_id(ClientOrderId::from("OPEN-1"))
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(buy_order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut buy_order, account_id);

    let mut opening_fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Expected an opening fill");
    opening_fill.position_id = Some(PositionId::from("P-001"));
    let position = Position::new(&instrument, opening_fill);
    cache
        .borrow_mut()
        .add_position(&position, OmsType::Netting)
        .unwrap();

    clear_order_event_handler_messages(&order_event_handler);

    // Expire via timestamp with no settlement_price and no InstrumentClose.
    // The fallback goes through fill_market_order -> book bid 4499.
    let trigger_delta = OrderBookDeltaTestBuilder::new(instrument.id())
        .book_action(BookAction::Update)
        .book_order(BookOrder::new(
            OrderSide::Buy,
            Price::from("4499.00"),
            Quantity::from(10),
            2,
        ))
        .ts_init(expiration_ns)
        .build();
    engine.process_order_book_delta(&trigger_delta).unwrap();

    let messages = get_order_event_handler_messages(&order_event_handler);
    let accepted_idx = messages
        .iter()
        .position(|e| {
            matches!(
                e,
                OrderEventAny::Accepted(a) if a.client_order_id.as_str().starts_with("EXPIRATION-")
            )
        })
        .expect("Expected OrderAccepted for the EXPIRATION close order");
    let fill_idx = messages
        .iter()
        .position(|e| {
            matches!(
                e,
                OrderEventAny::Filled(f) if f.client_order_id.as_str().starts_with("EXPIRATION-")
            )
        })
        .expect("Expected OrderFilled for the EXPIRATION close order");
    assert!(
        accepted_idx < fill_idx,
        "OrderAccepted must precede OrderFilled for fallback fill_market_order path"
    );

    let fill = match &messages[fill_idx] {
        OrderEventAny::Filled(f) => f,
        _ => unreachable!(),
    };
    assert_eq!(fill.order_side, OrderSide::Sell);
    assert_eq!(fill.last_qty, Quantity::from(1));
    assert_eq!(fill.last_px, Price::from("4499.00"));
    // Netting OMS nulls position_id in apply_fills, matching the existing
    // settlement-price test; only the fill price proves the book was consulted.
    let _ = position;
}

#[rstest]
fn test_process_option_expiry_no_positions_is_noop(account_id: AccountId) {
    let _ = account_id;
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract(
        "AAPL",
        venue,
        expiration_ns,
        OptionKind::Call,
    ));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option,
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let messages = get_order_event_handler_messages(&order_event_handler);
    assert!(
        messages
            .iter()
            .all(|e| !matches!(e, OrderEventAny::Filled(_))),
        "No fills should be emitted when no positions exist"
    );
}

#[rstest]
fn test_process_option_expiry_missing_underlying_instrument_is_noop(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract(
        "AAPL",
        venue,
        expiration_ns,
        OptionKind::Call,
    ));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    // NB: underlying instrument intentionally NOT added to the cache.

    let _position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from(1),
        Price::from("5.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option,
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let settlement_fills = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter(|e| {
            matches!(
                e,
                OrderEventAny::Filled(f)
                    if f.client_order_id.as_str().contains("-LEG-")
            )
        })
        .count();
    assert_eq!(
        settlement_fills, 0,
        "No settlement fills should be emitted when underlying instrument is absent"
    );
}

#[rstest]
fn test_process_option_expiry_missing_underlying_price_is_noop(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract(
        "AAPL",
        venue,
        expiration_ns,
        OptionKind::Call,
    ));
    let underlying = InstrumentAny::Equity(underlying_equity(venue));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache.borrow_mut().add_instrument(underlying).unwrap();
    // NB: no TradeTick added, so cache.price(LAST) is None.

    let _position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from(1),
        Price::from("5.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option,
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let settlement_fills = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter(|e| {
            matches!(
                e,
                OrderEventAny::Filled(f)
                    if f.client_order_id.as_str().contains("-LEG-")
            )
        })
        .count();
    assert_eq!(
        settlement_fills, 0,
        "No settlement fills should be emitted without an underlying last price"
    );
}

#[rstest]
fn test_check_instrument_expiration_idempotent_after_processed(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract(
        "AAPL",
        venue,
        expiration_ns,
        OptionKind::Call,
    ));
    let underlying = InstrumentAny::IndexInstrument(underlying_index(venue));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache
        .borrow_mut()
        .add_instrument(underlying.clone())
        .unwrap();
    cache
        .borrow_mut()
        .add_trade(TradeTick::new(
            underlying.id(),
            Price::from("160.00"),
            Quantity::from(1),
            AggressorSide::NoAggressor,
            TradeId::from("U-1"),
            UnixNanos::from(1),
            UnixNanos::from(1),
        ))
        .unwrap();

    let _position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from(1),
        Price::from("5.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option,
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let fills_first: usize = get_order_event_handler_messages(&order_event_handler)
        .iter()
        .filter(|e| matches!(e, OrderEventAny::Filled(_)))
        .count();
    assert!(fills_first >= 1, "First iterate must emit settlement fill");

    clear_order_event_handler_messages(&order_event_handler);

    // Drive iterate again past the expiration; expiration_processed should gate it.
    engine.iterate(
        UnixNanos::from(expiration_ns.as_u64() + 1),
        AggressorSide::NoAggressor,
    );

    let fills_second: usize = get_order_event_handler_messages(&order_event_handler)
        .iter()
        .filter(|e| matches!(e, OrderEventAny::Filled(_)))
        .count();
    assert_eq!(
        fills_second, 0,
        "Second iterate must be a no-op once expiration_processed is set"
    );
}

#[rstest]
fn test_check_instrument_expiration_uses_close_price_fallback(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let activation = UnixNanos::from(
        Utc.with_ymd_and_hms(2021, 9, 10, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    let expiration_ns = UnixNanos::from(
        Utc.with_ymd_and_hms(2099, 12, 17, 0, 0, 0)
            .unwrap()
            .timestamp_nanos_opt()
            .unwrap() as u64,
    );
    let instrument =
        InstrumentAny::FuturesContract(futures_contract_es(Some(activation), Some(expiration_ns)));

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock
        .borrow_mut()
        .set_time(UnixNanos::from(activation.as_u64() + 1));

    let fee_model =
        FeeModelAny::Fixed(FixedFeeModel::new(Money::new(0.0, Currency::USD()), None).unwrap());
    let mut engine = OrderMatchingEngine::new(
        instrument.clone(),
        1,
        FillModelHandle::default(),
        fee_model.into(),
        BookType::L2_MBP,
        OmsType::Netting,
        AccountType::Margin,
        clock,
        cache.clone(),
        OrderMatchingEngineConfig {
            use_position_ids: true,
            ..Default::default()
        },
    );

    let opening_ask = OrderBookDeltaTestBuilder::new(instrument.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("4501.00"),
            Quantity::from(10),
            1,
        ))
        .build();
    engine.process_order_book_delta(&opening_ask).unwrap();

    let mut buy_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from(1))
        .client_order_id(ClientOrderId::from("OPEN-1"))
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(buy_order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut buy_order, account_id);

    let mut opening_fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .expect("Expected opening fill");
    opening_fill.position_id = Some(PositionId::from("P-001"));
    let position = Position::new(&instrument, opening_fill);
    cache
        .borrow_mut()
        .add_position(&position, OmsType::Netting)
        .unwrap();

    clear_order_event_handler_messages(&order_event_handler);

    // No settlement_price set; the InstrumentClose carries the close_price fallback.
    let close_price = Price::from("4550.00");
    let close = InstrumentClose::new(
        instrument.id(),
        close_price,
        InstrumentCloseType::ContractExpired,
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_instrument_close(close);

    let fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f) if f.client_order_id.as_str().starts_with("EXPIRATION-") => {
                Some(f)
            }
            _ => None,
        })
        .expect("Expected EXPIRATION close fill from close_price fallback");
    assert_eq!(fill.last_px, close_price);
    let _ = position;
}

#[rstest]
fn test_binary_option_pending_resolution_then_instrument_close_settles_position(
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let instrument = InstrumentAny::BinaryOption(binary_option());
    cache
        .borrow_mut()
        .add_instrument(instrument.clone())
        .unwrap();

    let activation_ns = instrument.activation_ns().unwrap();
    let expiration_ns = instrument.expiration_ns().unwrap();
    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock
        .borrow_mut()
        .set_time(UnixNanos::from(activation_ns.as_u64() + 1));

    let mut engine = OrderMatchingEngine::new(
        instrument.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L2_MBP,
        OmsType::Netting,
        AccountType::Margin,
        clock.clone(),
        cache.clone(),
        OrderMatchingEngineConfig {
            use_position_ids: true,
            ..Default::default()
        },
    );

    let opening_ask = OrderBookDeltaTestBuilder::new(instrument.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("0.400"),
            Quantity::from("10.00"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&opening_ask).unwrap();

    let mut opening_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.00"))
        .client_order_id(ClientOrderId::from("OPEN-BO-1"))
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(opening_order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut opening_order, account_id);

    let mut opening_fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|event| match event {
            OrderEventAny::Filled(fill) => Some(fill),
            _ => None,
        })
        .expect("expected opening fill");
    opening_fill.position_id = Some(PositionId::from("P-BO-1"));
    let position = Position::new(&instrument, opening_fill);
    cache
        .borrow_mut()
        .add_position(&position, OmsType::Netting)
        .unwrap();

    clear_order_event_handler_messages(&order_event_handler);

    let mut resting_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .price(Price::from("0.050"))
        .quantity(Quantity::from("1.00"))
        .client_order_id(ClientOrderId::from("REST-BO-1"))
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(resting_order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut resting_order, account_id);

    clear_order_event_handler_messages(&order_event_handler);

    clock
        .borrow_mut()
        .set_time(UnixNanos::from(expiration_ns.as_u64() + 1));

    let mut probe_order = OrderTestBuilder::new(OrderType::Limit)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .price(Price::from("0.050"))
        .quantity(Quantity::from("1.00"))
        .client_order_id(ClientOrderId::from("PROBE-BO-1"))
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(probe_order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut probe_order, account_id);

    let events = get_order_event_handler_messages(&order_event_handler);
    assert!(
        events.iter().any(|event| matches!(
            event,
            OrderEventAny::Canceled(c) if c.client_order_id == ClientOrderId::from("REST-BO-1")
        )),
        "expected resting order cancellation when entering pending resolution"
    );
    let reject = events
        .iter()
        .find_map(|event| match event {
            OrderEventAny::Rejected(rejected)
                if rejected.client_order_id == ClientOrderId::from("PROBE-BO-1") =>
            {
                Some(rejected)
            }
            _ => None,
        })
        .expect("expected rejection for probe order submitted after expiry");
    assert!(
        reject.reason.as_str().contains("pending resolution"),
        "expected pending resolution rejection, found: {}",
        reject.reason,
    );

    clear_order_event_handler_messages(&order_event_handler);
    let close = InstrumentClose::new(
        instrument.id(),
        Price::from("1.000"),
        InstrumentCloseType::ContractExpired,
        UnixNanos::from(2),
        UnixNanos::from(2),
    );
    engine.process_instrument_close(close);

    let settlement_fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|event| match event {
            OrderEventAny::Filled(fill)
                if fill.client_order_id.as_str().starts_with("EXPIRATION-") =>
            {
                Some(fill)
            }
            _ => None,
        })
        .expect("expected settlement fill from instrument close");
    assert_eq!(settlement_fill.last_px, Price::from("1.000"));
    let _ = position;
}

#[rstest]
fn test_binary_option_expiration_check_uses_engine_clock_not_order_ts_init(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let instrument = InstrumentAny::BinaryOption(binary_option());
    cache
        .borrow_mut()
        .add_instrument(instrument.clone())
        .unwrap();

    let activation_ns = instrument.activation_ns().unwrap();
    let expiration_ns = instrument.expiration_ns().unwrap();
    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock
        .borrow_mut()
        .set_time(UnixNanos::from(activation_ns.as_u64() + 1));

    let mut engine = OrderMatchingEngine::new(
        instrument.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L2_MBP,
        OmsType::Netting,
        AccountType::Margin,
        clock,
        cache.clone(),
        OrderMatchingEngineConfig {
            use_position_ids: true,
            ..Default::default()
        },
    );

    let opening_ask = OrderBookDeltaTestBuilder::new(instrument.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("0.400"),
            Quantity::from("10.00"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&opening_ask).unwrap();

    let mut order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.00"))
        .client_order_id(ClientOrderId::from("CLOCK-NOT-TSINIT"))
        .ts_init(UnixNanos::from(expiration_ns.as_u64() + 100))
        .submit(true)
        .build();
    cache
        .borrow_mut()
        .add_order(order.clone(), None, None, false)
        .unwrap();
    engine.process_order(&mut order, account_id);

    let events = get_order_event_handler_messages(&order_event_handler);
    assert!(
        events.iter().any(|event| matches!(
            event,
            OrderEventAny::Filled(fill)
                if fill.client_order_id == ClientOrderId::from("CLOCK-NOT-TSINIT")
        )),
        "order.ts_init should not cause expiry rejection when engine clock is still pre-expiry",
    );
    assert!(
        !events.iter().any(|event| matches!(
            event,
            OrderEventAny::Rejected(rejected)
                if rejected.client_order_id == ClientOrderId::from("CLOCK-NOT-TSINIT")
        )),
        "pre-expiry submission should not be rejected solely because order.ts_init is later",
    );
}

#[rstest]
fn test_crypto_option_cash_settlement(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "DERIBIT";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let strike = Price::from("50000.00");
    let option = InstrumentAny::CryptoOption(crypto_option_call_btc(venue, expiration_ns, strike));

    let underlying = InstrumentAny::IndexInstrument(IndexInstrument::new(
        InstrumentId::from(format!("BTC.{venue}").as_str()),
        Symbol::from("BTC"),
        Currency::USD(),
        2,
        0,
        Price::from("0.01"),
        Quantity::from(1),
        None,
        None,
        UnixNanos::default(),
        UnixNanos::default(),
    ));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache
        .borrow_mut()
        .add_instrument(underlying.clone())
        .unwrap();

    // ITM call: spot 51000 > strike 50000 -> cash payout 1000.00
    cache
        .borrow_mut()
        .add_trade(TradeTick::new(
            underlying.id(),
            Price::from("51000.00"),
            Quantity::from(1),
            AggressorSide::NoAggressor,
            TradeId::from("U-1"),
            UnixNanos::from(1),
            UnixNanos::from(1),
        ))
        .unwrap();

    let position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from("1"),
        Price::from("100.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option.clone(),
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let settlement_fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f)
                if f.client_order_id
                    .as_str()
                    .starts_with(&format!("{venue}-LEG-CASH-")) =>
            {
                Some(f)
            }
            _ => None,
        })
        .expect("Expected CryptoOption cash settlement fill");

    assert_eq!(settlement_fill.instrument_id, option.id());
    assert_eq!(settlement_fill.order_side, OrderSide::Sell);
    assert_eq!(settlement_fill.last_qty, position.quantity);
    assert_eq!(settlement_fill.last_px, Price::from("1000.00"));
    assert_eq!(settlement_fill.position_id, Some(position.id));
}

#[rstest]
fn test_capped_option_fee_uses_underlying_mid_quote(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));

    let venue = "DERIBIT";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::CryptoOption(crypto_option_call_btc(
        venue,
        expiration_ns,
        Price::from("50000.00"),
    ));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache
        .borrow_mut()
        .add_quote(QuoteTick::new(
            InstrumentId::from(format!("BTC.{venue}").as_str()),
            Price::from("49990.00"),
            Price::from("50010.00"),
            Quantity::from(1),
            Quantity::from(1),
            UnixNanos::from(1),
            UnixNanos::from(1),
        ))
        .unwrap();

    let fee_model = FeeModelAny::CappedOption(
        CappedOptionFeeModel::new(Some(dec!(0.0001)), Some(dec!(0.0003)), None).unwrap(),
    );
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        option.clone(),
        1,
        FillModelHandle::default(),
        fee_model.into(),
        BookType::L2_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    let ask = OrderBookDeltaTestBuilder::new(option.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("200.00"),
            Quantity::from("1.0"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&ask).unwrap();

    let mut order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(option.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.0"))
        .client_order_id(ClientOrderId::from("DERIBIT-FEE-MID-QUOTE"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);

    let fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|event| match event {
            OrderEventAny::Filled(fill)
                if fill.client_order_id == ClientOrderId::from("DERIBIT-FEE-MID-QUOTE") =>
            {
                Some(fill)
            }
            _ => None,
        })
        .expect("expected option fill");
    let commission = fill.commission.expect("expected commission");

    assert_eq!(commission.currency, Currency::USD());
    assert_eq!(commission.as_decimal(), dec!(15.00));
}

#[rstest]
fn test_capped_option_fee_uses_option_greeks_underlying_price(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));

    let venue = "DERIBIT";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::CryptoOption(crypto_option_call_btc(
        venue,
        expiration_ns,
        Price::from("50000.00"),
    ));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache.borrow_mut().add_option_greeks(OptionGreeks {
        instrument_id: option.id(),
        underlying_price: Some(50000.0),
        ts_event: UnixNanos::from(1),
        ts_init: UnixNanos::from(1),
        ..Default::default()
    });

    let fee_model = FeeModelAny::CappedOption(
        CappedOptionFeeModel::new(Some(dec!(0.0001)), Some(dec!(0.0003)), None).unwrap(),
    );
    let clock = Rc::new(RefCell::new(TestClock::new()));
    let mut engine = OrderMatchingEngine::new(
        option.clone(),
        1,
        FillModelHandle::default(),
        fee_model.into(),
        BookType::L2_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    let ask = OrderBookDeltaTestBuilder::new(option.id())
        .book_action(BookAction::Add)
        .book_order(BookOrder::new(
            OrderSide::Sell,
            Price::from("200.00"),
            Quantity::from("1.0"),
            1,
        ))
        .build();
    engine.process_order_book_delta(&ask).unwrap();

    let mut order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(option.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.0"))
        .client_order_id(ClientOrderId::from("DERIBIT-FEE-OPTION-GREEKS"))
        .submit(true)
        .build();
    engine.process_order(&mut order, account_id);

    let fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|event| match event {
            OrderEventAny::Filled(fill)
                if fill.client_order_id == ClientOrderId::from("DERIBIT-FEE-OPTION-GREEKS") =>
            {
                Some(fill)
            }
            _ => None,
        })
        .expect("expected option fill");
    let commission = fill.commission.expect("expected commission");

    assert_eq!(commission.currency, Currency::USD());
    assert_eq!(commission.as_decimal(), dec!(15.00));
}

#[rstest]
fn test_option_cash_settlement_with_custom_settlement_price(account_id: AccountId) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());

    let venue = "OPRA";
    let expiration_ns = UnixNanos::from(2_000_000_000_000_000_000u64);
    let option = InstrumentAny::OptionContract(option_contract(
        "AAPL",
        venue,
        expiration_ns,
        OptionKind::Call,
    ));
    let underlying = InstrumentAny::IndexInstrument(underlying_index(venue));

    cache.borrow_mut().add_instrument(option.clone()).unwrap();
    cache
        .borrow_mut()
        .add_instrument(underlying.clone())
        .unwrap();

    cache
        .borrow_mut()
        .add_trade(TradeTick::new(
            underlying.id(),
            Price::from("160.00"),
            Quantity::from(1),
            AggressorSide::NoAggressor,
            TradeId::from("U-1"),
            UnixNanos::from(1),
            UnixNanos::from(1),
        ))
        .unwrap();

    let _position = open_long_option_position(
        &cache,
        &option,
        account_id,
        Quantity::from(1),
        Price::from("5.00"),
    );

    let clock = Rc::new(RefCell::new(TestClock::new()));
    clock.borrow_mut().set_time(expiration_ns);
    let mut engine = OrderMatchingEngine::new(
        option,
        1,
        FillModelHandle::default(),
        FeeModelAny::default().into(),
        BookType::L1_MBP,
        OmsType::Netting,
        AccountType::Cash,
        clock,
        cache,
        OrderMatchingEngineConfig::default(),
    );

    // Override the intrinsic value (would be 11.00) with an explicit settlement price.
    let custom = Price::from("7.50");
    engine.set_settlement_price(custom);

    engine.iterate(expiration_ns, AggressorSide::NoAggressor);

    let settlement_fill = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .find_map(|e| match e {
            OrderEventAny::Filled(f)
                if f.client_order_id
                    .as_str()
                    .starts_with(&format!("{venue}-LEG-CASH-")) =>
            {
                Some(f)
            }
            _ => None,
        })
        .expect("Expected cash settlement fill");
    assert_eq!(settlement_fill.last_px, custom);
}

#[rstest]
#[case(OrderSide::Buy, "1010.00", "1010.01")]
#[case(OrderSide::Sell, "1000.00", "999.99")]
fn test_l1_market_order_slips_remainder_through_next_tick(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] side: OrderSide,
    #[case] top_of_book_px: &str,
    #[case] slip_px: &str,
) {
    let mut engine = get_order_matching_engine(instrument_eth_usdt.clone(), None, None, None, None);

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1010.00"),
        Quantity::from("0.500"),
        Quantity::from("0.500"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(side)
        .quantity(Quantity::from("1.500"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let fills: Vec<OrderFilled> = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert_eq!(fills.len(), 2);
    assert_eq!(fills[0].last_px, Price::from(top_of_book_px));
    assert_eq!(fills[0].last_qty, Quantity::from("0.500"));
    assert_eq!(fills[1].last_px, Price::from(slip_px));
    assert_eq!(fills[1].last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_reduce_only_l1_market_order_slip_caps_remaining_position(
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    let cache = Rc::new(RefCell::new(Cache::default()));
    let order_event_handler = order_event_handler_with_cache(cache.clone());
    cache
        .borrow_mut()
        .add_instrument(instrument_eth_usdt.clone())
        .unwrap();
    let mut engine = get_order_matching_engine(
        instrument_eth_usdt.clone(),
        Some(cache.clone()),
        None,
        None,
        None,
    );

    let position_id = PositionId::new(
        format!(
            "{}-{}",
            instrument_eth_usdt.id(),
            StrategyId::test_default()
        )
        .as_str(),
    );
    let opening_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.000"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-OPEN"))
        .build();
    let opening_fill = build_order_filled(
        opening_order.trader_id(),
        opening_order.strategy_id(),
        opening_order.instrument_id(),
        opening_order.client_order_id(),
        VenueOrderId::from("V-OPEN"),
        account_id,
        TradeId::new("E-OPEN"),
        opening_order.order_side(),
        opening_order.order_type(),
        opening_order.quantity(),
        Price::from("1000.00"),
        instrument_eth_usdt.quote_currency(),
        LiquiditySide::Taker,
        Some(position_id),
        None,
    );
    let position = Position::new(&instrument_eth_usdt, opening_fill);
    cache
        .borrow_mut()
        .add_position(&position, OmsType::Netting)
        .unwrap();

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1010.00"),
        Quantity::from("0.500"),
        Quantity::from("0.500"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    let client_order_id = ClientOrderId::from("O-19700101-000000-001-001-RO");
    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Sell)
        .quantity(Quantity::from("1.500"))
        .client_order_id(client_order_id)
        .reduce_only(true)
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<OrderFilled> = messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(fill) if fill.client_order_id == client_order_id => Some(*fill),
            _ => None,
        })
        .collect();
    let updated = messages
        .iter()
        .find_map(|event| match event {
            OrderEventAny::Updated(updated) if updated.client_order_id == client_order_id => {
                Some(updated)
            }
            _ => None,
        })
        .expect("Expected reduce-only order quantity update");

    assert_eq!(fills.len(), 2);
    assert_eq!(fills[0].last_px, Price::from("1000.00"));
    assert_eq!(fills[0].last_qty, Quantity::from("0.500"));
    assert_eq!(fills[1].last_px, Price::from("999.99"));
    assert_eq!(fills[1].last_qty, Quantity::from("0.500"));
    assert_eq!(updated.quantity, Quantity::from("1.000"));

    let cache_ref = cache.borrow();
    let order = cache_ref
        .order(&client_order_id)
        .expect("Expected reduce-only order in cache");
    assert_eq!(order.quantity(), Quantity::from("1.000"));
    assert_eq!(order.filled_qty(), Quantity::from("1.000"));
    assert_eq!(order.status(), OrderStatus::Filled);
}

#[rstest]
#[case(OrderSide::Buy, "1010.00")]
#[case(OrderSide::Sell, "1000.00")]
fn test_l1_market_order_slip_respects_protection_boundary(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] side: OrderSide,
    #[case] top_of_book_px: &str,
) {
    // Protection points = 0 puts the boundary at top-of-book; the slipped tick exceeds it
    let config = OrderMatchingEngineConfig::builder()
        .price_protection_points(0u32)
        .build();
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1010.00"),
        Quantity::from("0.500"),
        Quantity::from("0.500"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(side)
        .quantity(Quantity::from("1.500"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let fills: Vec<OrderFilled> = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert_eq!(fills.len(), 1);
    assert_eq!(fills[0].last_px, Price::from(top_of_book_px));
    assert_eq!(fills[0].last_qty, Quantity::from("0.500"));
}

#[rstest]
#[case(OrderSide::Buy, "1010.00", "1010.01")]
#[case(OrderSide::Sell, "1000.00", "999.99")]
fn test_l1_market_order_slip_allowed_at_protection_boundary(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
    #[case] side: OrderSide,
    #[case] top_of_book_px: &str,
    #[case] slip_px: &str,
) {
    // Protection points = 1 puts the boundary exactly at the slipped tick;
    // the strict-inequality check must allow the fill at equality.
    let config = OrderMatchingEngineConfig::builder()
        .price_protection_points(1u32)
        .build();
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1010.00"),
        Quantity::from("0.500"),
        Quantity::from("0.500"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(side)
        .quantity(Quantity::from("1.500"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let fills: Vec<OrderFilled> = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert_eq!(fills.len(), 2);
    assert_eq!(fills[0].last_px, Price::from(top_of_book_px));
    assert_eq!(fills[0].last_qty, Quantity::from("0.500"));
    assert_eq!(fills[1].last_px, Price::from(slip_px));
    assert_eq!(fills[1].last_qty, Quantity::from("1.000"));
}

#[rstest]
fn test_l1_ioc_market_order_cancels_remainder_instead_of_slipping(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // IOC must cancel the remainder before the slip path runs
    let mut engine = get_order_matching_engine(instrument_eth_usdt.clone(), None, None, None, None);

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1010.00"),
        Quantity::from("0.500"),
        Quantity::from("0.500"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    let mut market_order = OrderTestBuilder::new(OrderType::Market)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .quantity(Quantity::from("1.500"))
        .time_in_force(TimeInForce::Ioc)
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut market_order, account_id);

    let saved_messages = get_order_event_handler_messages(&order_event_handler);
    let fills: Vec<&OrderFilled> = saved_messages
        .iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();
    let canceled_count = saved_messages
        .iter()
        .filter(|event| matches!(event, OrderEventAny::Canceled(_)))
        .count();

    assert_eq!(fills.len(), 1);
    assert_eq!(fills[0].last_px, Price::from("1010.00"));
    assert_eq!(fills[0].last_qty, Quantity::from("0.500"));
    assert_eq!(canceled_count, 1);
}

#[rstest]
fn test_l1_stop_market_order_slips_remainder_after_trigger(
    order_event_handler: TypedIntoMessageSavingHandler<OrderEventAny>,
    account_id: AccountId,
    instrument_eth_usdt: InstrumentAny,
) {
    // Trigger price below ask triggers immediately; remainder slips by one tick
    let config = OrderMatchingEngineConfig {
        reject_stop_orders: false,
        ..Default::default()
    };
    let mut engine =
        get_order_matching_engine(instrument_eth_usdt.clone(), None, None, Some(config), None);

    let quote = QuoteTick::new(
        instrument_eth_usdt.id(),
        Price::from("1000.00"),
        Price::from("1010.00"),
        Quantity::from("0.500"),
        Quantity::from("0.500"),
        UnixNanos::default(),
        UnixNanos::default(),
    );
    engine.process_quote_tick(&quote);

    let mut stop_order = OrderTestBuilder::new(OrderType::StopMarket)
        .instrument_id(instrument_eth_usdt.id())
        .side(OrderSide::Buy)
        .trigger_price(Price::from("1005.00"))
        .quantity(Quantity::from("1.500"))
        .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
        .submit(true)
        .build();
    engine.process_order(&mut stop_order, account_id);

    let fills: Vec<OrderFilled> = get_order_event_handler_messages(&order_event_handler)
        .into_iter()
        .filter_map(|event| match event {
            OrderEventAny::Filled(f) => Some(f),
            _ => None,
        })
        .collect();

    assert_eq!(fills.len(), 2);
    assert_eq!(fills[0].last_px, Price::from("1010.00"));
    assert_eq!(fills[0].last_qty, Quantity::from("0.500"));
    assert_eq!(fills[1].last_px, Price::from("1010.01"));
    assert_eq!(fills[1].last_qty, Quantity::from("1.000"));
}
