nautechsystems/nautilus_trader · error

`{name}` must be greater than or equal to zero

Error message

`{name}` must be greater than or equal to zero

What it means

The internal `check_fee_rate` helper validates that an optional fee rate is not negative, formatting the error with the parameter name (`{name}` must be >= 0). Fee rates (maker/taker percentages) must be non-negative; negative values are rejected at model construction or rate resolution.

Source

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

fn option_fee_rate(
    order: &OrderAny,
    instrument: &InstrumentAny,
    maker_rate: Option<Decimal>,
    taker_rate: Option<Decimal>,
) -> anyhow::Result<Decimal> {
    let rate = match order.liquidity_side() {
        Some(LiquiditySide::Maker) => maker_rate.unwrap_or_else(|| instrument.maker_fee()),
        Some(LiquiditySide::Taker) => taker_rate.unwrap_or_else(|| instrument.taker_fee()),
        Some(LiquiditySide::NoLiquiditySide) | None => anyhow::bail!("Liquidity side not set"),
    };
    check_fee_rate(Some(rate), "fee_rate")?;
    Ok(rate)
}

fn check_fee_rate(rate: Option<Decimal>, name: &str) -> anyhow::Result<()> {
    if rate.is_some_and(|rate| rate < Decimal::ZERO) {
        anyhow::bail!("`{name}` must be greater than or equal to zero");
    }
    Ok(())
}

fn check_option_instrument(instrument: &InstrumentAny, model_name: &str) -> anyhow::Result<()> {
    if !matches!(
        instrument,
        InstrumentAny::CryptoOption(_) | InstrumentAny::OptionContract(_)
    ) {
        anyhow::bail!("{model_name} requires an option instrument");
    }
    Ok(())
}

fn commission_currency(instrument: &InstrumentAny) -> Currency {
    if instrument.is_inverse() {
        instrument.settlement_currency()
    } else {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Correct the sign of the maker/taker rate passed to the model constructor or override
  2. If a maker rebate is intended, verify the model/venue supports rebates and use the proper mechanism
  3. Validate fee-rate config (reject negatives) at load time with a clear message

Example fix

// before
let model = OptionFeeModel::new(Some(Decimal::new(-2, 4)), None)?; // -0.0002
// after
let maker_rate = Decimal::new(2, 4); // 0.0002
assert!(maker_rate >= Decimal::ZERO);
let model = OptionFeeModel::new(Some(maker_rate), None)?;
Defensive patterns

Strategy: validation

Validate before calling

for (name, rate) in [("maker_fee", maker_fee), ("taker_fee", taker_fee)] {
    if let Some(r) = rate {
        if r < Decimal::ZERO { return Err(anyhow::anyhow!("{name} must be >= 0")); }
    }
}

Type guard

fn valid_rate(r: Option<Decimal>) -> bool { r.map_or(true, |v| v >= Decimal::ZERO) }

Try / catch

let model = OptionFeeModel::new(maker_fee, taker_fee)
    .context("invalid option fee rates")?;

Prevention

When it happens

Trigger: Constructing an option fee model via `new` with a negative maker_fee or taker_fee, or calling `option_fee_rate` with an override rate < 0.

Common situations: Configuring maker rebates as plain negative rates instead of using a rebate mechanism; sign typo in config (e.g. -0.0002 instead of 0.0002); converting a signed spread/basis into a rate.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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