nautechsystems/nautilus_trader · error

Commission must be greater than or equal to zero

Error message

Commission must be greater than or equal to zero

What it means

PerpetualFeeModel::new (or similar fee model constructor) validates that the commission amount passed in is non-negative. A negative Money value would produce nonsensical (negative) fees, so the constructor rejects it with anyhow::bail. This is a fail-fast guard at model construction time.

Source

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

        extends = PyFeeModel,
        skip_from_py_object
    )
)]
pub struct FixedFeeModel {
    commission: Money,
    zero_commission: Money,
    charge_commission_once: bool,
}

impl FixedFeeModel {
    /// Creates a new [`FixedFeeModel`] instance.
    ///
    /// # Errors
    ///
    /// Returns an error if `commission` is negative.
    pub fn new(commission: Money, charge_commission_once: Option<bool>) -> anyhow::Result<Self> {
        if commission.raw < 0 {
            anyhow::bail!("Commission must be greater than or equal to zero")
        }
        let zero_commission = Money::zero(commission.currency);
        Ok(Self {
            commission,
            zero_commission,
            charge_commission_once: charge_commission_once.unwrap_or(true),
        })
    }
}

impl FeeModel for FixedFeeModel {
    fn get_commission(
        &self,
        order: &OrderAny,
        _fill_quantity: Quantity,
        _fill_px: Price,
        _instrument: &InstrumentAny,
    ) -> anyhow::Result<Money> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the Money value passed as `commission` and correct its sign before constructing the model
  2. Clamp or validate at the config boundary: reject negative commission inputs when parsing user config
  3. If the rate is legitimately signed, apply the sign to the notional calculation instead of the commission

Example fix

// before
let model = PerpetualFeeModel::new(Money::new(-Decimal::new(15, 1), Currency::USD()), None)?;
// after
let commission = Money::new(Decimal::new(15, 1), Currency::USD());
assert!(commission.raw >= Decimal::ZERO);
let model = PerpetualFeeModel::new(commission, None)?;
Defensive patterns

Strategy: validation

Validate before calling

if commission.raw < Decimal::ZERO {
    return Err(anyhow::anyhow!("commission must be >= 0, got {}", commission));
}

Type guard

fn is_non_negative(m: &Money) -> bool { m.raw >= Decimal::ZERO }

Try / catch

match FeeModel::new(commission, once) {
    Ok(model) => model,
    Err(e) => { log::error!("fee model init failed: {e}"); return Err(e); }
}

Prevention

When it happens

Trigger: Calling the fee model's `new(commission, charge_commission_once)` with a `Money` whose `raw` decimal is < 0, e.g. `Money::new(-1.5, Currency::USD())` or a commission computed from a negative rate.

Common situations: Loading commission from config where a sign was mistyped, computing commission as a signed delta instead of an absolute value, or a currency conversion/rounding pipeline that flipped the sign.

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/0dbf884e41c6f87e. Report an issue: GitHub.