nautechsystems/nautilus_trader · error

Liquidity side not set

Error message

Liquidity side not set

What it means

MakerTakerFeeModel::get_commission computes the fee rate by matching the order's liquidity_side. If the order has `NoLiquiditySide` or `None`, no maker/taker rate can be selected, so the call fails with 'Liquidity side not set'. The model requires the venue/emulator to have stamped the liquidity side on the order before fee calculation.

Source

Thrown at crates/execution/src/models/fee.rs:411

        skip_from_py_object
    )
)]
pub struct MakerTakerFeeModel;

impl FeeModel for MakerTakerFeeModel {
    fn get_commission(
        &self,
        order: &OrderAny,
        fill_quantity: Quantity,
        fill_px: Price,
        instrument: &InstrumentAny,
    ) -> anyhow::Result<Money> {
        let notional =
            instrument.try_calculate_notional_value(fill_quantity, fill_px, Some(false))?;
        let rate = match order.liquidity_side() {
            Some(LiquiditySide::Maker) => instrument.maker_fee(),
            Some(LiquiditySide::Taker) => instrument.taker_fee(),
            Some(LiquiditySide::NoLiquiditySide) | None => anyhow::bail!("Liquidity side not set"),
        };
        let commission = mul_checked(notional.as_decimal(), rate)?;

        Money::from_decimal(commission, notional.currency).map_err(Into::into)
    }
}

/// Fee model for probability-priced outcome shares.
///
/// Applies `qty * fee_rate * p * (1 - p)` using the instrument's maker or
/// taker fee rate. This matches venues that represent outcome shares as
/// [`InstrumentAny::BinaryOption`] instruments quoted on a `[0, 1]`
/// probability scale.
///
/// This model covers quote-currency match-time exchange fees only.
/// Venue-specific rebate programs or non-quote fee assets remain outside the
/// core execution layer.
#[derive(Debug, Clone)]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the fill/execution report sets `liquidity_side` (Maker/Taker) before fee calculation
  2. In custom fill models or simulators, call set liquidity side explicitly when generating fills
  3. If the side is genuinely unknown, skip maker/taker fee modeling and use a fixed-fee model instead

Example fix

// before
let fee = maker_taker_model.get_commission(&order, qty, px, &instrument)?; // liquidity_side None
// after
order.set_liquidity_side(LiquiditySide::Taker);
let fee = maker_taker_model.get_commission(&order, qty, px, &instrument)?;
Defensive patterns

Strategy: type-guard

Validate before calling

if !matches!(order.liquidity_side(), Some(LiquiditySide::Maker) | Some(LiquiditySide::Taker)) {
    // set the side or use a fallback fee model before calling get_commission
}

Type guard

fn has_liquidity_side(order: &OrderAny) -> bool {
    matches!(order.liquidity_side(), Some(LiquiditySide::Maker) | Some(LiquiditySide::Taker))
}

Try / catch

match model.get_commission(&order, qty, px, &instrument) {
    Ok(fee) => fee,
    Err(e) if e.to_string().contains("Liquidity side not set") => fallback_fixed_fee(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling get_commission with an order whose `liquidity_side()` is None or NoLiquiditySide — typically an order filled outside the normal matching pipeline (simulated fills, custom fill models, or fees computed before the fill report sets the side).

Common situations: Backtesting with a custom FillModel that doesn't set liquidity_side; calculating fees on unfilled or partially processed orders; venue adapter not populating liquidity side on fill reports.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/263542ce1407bc93. Report an issue: GitHub.