nautechsystems/nautilus_trader · error

Polymarket fee rate must be greater than or equal to zero

Error message

Polymarket fee rate must be greater than or equal to zero

What it means

validate_schedule rejects fee schedules with a negative rate, since a fee rate below zero is meaningless for this model (it would turn commission into a payout). The rate comes from the market's fee schedule in the binary option's instrument info.

Solutions

  1. Inspect the fee schedule source (instrument info) and correct the negative rate value.
  2. Validate fee schedule data at instrument-build time so bad schedules are rejected earlier.
  3. Check whether the API response was misparsed (e.g. a rebate misread as a rate).
  4. Fix test fixtures to use non-negative rates.

Example fix

// before
let schedule = FeeSchedule { rate: Decimal::new(-5, 3), .. };
// after
let schedule = FeeSchedule { rate: Decimal::new(5, 3), .. }; // 0.005
Defensive patterns

Strategy: validation

Validate before calling

if schedule.rate < Decimal::ZERO {
    return Err("negative fee rate in schedule".into());
}

Type guard

fn has_valid_rate(s: &FeeSchedule) -> bool {
    s.rate >= Decimal::ZERO
}

Try / catch

match fee_model.get_commission(&order, qty, px, &instrument) {
    Ok(fee) => fee,
    Err(e) if e.to_string().contains("greater than or equal to zero") => {
        log::error!("corrupt fee schedule (negative rate); refusing to trade");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: get_commission encounters a FeeSchedule whose rate < 0 — from a malformed CLOB API response, bad manual fixture, or sign error when constructing the schedule.

Common situations: Hand-written test fixtures with negative rates; API/schema changes altering how the rate is encoded; data pipelines mangling 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/fd5508bed899a1e0. Report an issue: GitHub.

Appendix: source

Thrown at crates/adapters/polymarket/src/models.rs:118

                .round_dp(5),
            LiquiditySide::Taker => fee_equivalent.round_dp(5),
            LiquiditySide::NoLiquiditySide => unreachable!(),
        };

        Money::from_decimal(commission, instrument.quote_currency()).map_err(Into::into)
    }
}

fn validate_schedule(schedule: &FeeSchedule) -> anyhow::Result<()> {
    if schedule.exponent != Decimal::ONE {
        anyhow::bail!(
            "PolymarketFeeModel requires fee schedule exponent 1, was {}",
            schedule.exponent
        );
    }

    if schedule.rate < Decimal::ZERO {
        anyhow::bail!("Polymarket fee rate must be greater than or equal to zero");
    }

    if !(Decimal::ZERO..=Decimal::ONE).contains(&schedule.rebate_rate) {
        anyhow::bail!("Polymarket rebate rate must be in [0, 1]");
    }

    if !schedule.taker_only {
        anyhow::bail!("PolymarketFeeModel requires a taker-only fee schedule");
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use nautilus_core::UnixNanos;
    use nautilus_execution::models::fee::{FeeModel, FeeModelHandle};
    use nautilus_model::{
        enums::{LiquiditySide, OrderSide, OrderType},

View on GitHub (pinned to 18893faf8b)