nautechsystems/nautilus_trader · error

Polymarket rebate rate must be in [0, 1]

Error message

Polymarket rebate rate must be in [0, 1]

What it means

validate_schedule requires the schedule's rebate_rate to be within [0, 1] because it is treated as a proportion. A rebate rate outside this interval is invalid input and would corrupt fee/rebate math, so validation bails.

Source

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

        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},
        instruments::{Instrument, InstrumentAny, stubs::audusd_sim},
        orders::{OrderAny, builder::OrderTestBuilder, stubs::TestOrderStubs},
        types::{Price, Quantity},
    };

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Normalize rebate_rate to a 0–1 fraction (divide percent values by 100) before constructing FeeSchedule.
  2. Verify how the CLOB API encodes rebate_rate and fix the deserialization accordingly.
  3. Validate schedules at instrument construction time to fail fast before trading.
  4. Correct any fixture with rebate_rate outside [0, 1].

Example fix

// before
let schedule = FeeSchedule { rebate_rate: Decimal::from(25), .. }; // percent
// after
let schedule = FeeSchedule { rebate_rate: Decimal::new(25, 2), .. }; // 0.25
Defensive patterns

Strategy: validation

Validate before calling

if !(Decimal::ZERO..=Decimal::ONE).contains(&schedule.rebate_rate) {
    return Err("rebate_rate must be a fraction in [0,1]".into());
}

Type guard

fn has_valid_rebate_rate(s: &FeeSchedule) -> bool {
    (Decimal::ZERO..=Decimal::ONE).contains(&s.rebate_rate)
}

Try / catch

match fee_model.get_commission(&order, qty, px, &instrument) {
    Ok(fee) => fee,
    Err(e) if e.to_string().contains("rebate rate must be in [0, 1]") => {
        log::error!("rebate_rate out of range — check percent vs fraction");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: get_commission on a market whose FeeSchedule.rebate_rate is negative or greater than 1 — typically from misparsed API data (percent vs fraction) or bad fixtures.

Common situations: Fee schedule fields given as percentages (e.g. 25 meaning 0.25) by upstream data; corrupted or hand-edited fixtures; parsing errors mapping JSON fields to Decimal.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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