nautechsystems/nautilus_trader · error
PolymarketFeeModel requires fee schedule exponent 1, was {}
Error message
PolymarketFeeModel requires fee schedule exponent 1, was {} What it means
validate_schedule enforces that the market's FeeSchedule has exponent 1, the only exponent formula PolymarketFeeModel supports for its linear fee computation. A schedule with any other exponent (e.g. Polymarket's historical nonlinear exponent schedules) is rejected.
Source
Thrown at crates/adapters/polymarket/src/models.rs:111
.and_then(|value| value.checked_mul(fill_price))
.and_then(|value| value.checked_mul(Decimal::ONE - fill_price))
.context("commission calculation overflow")?;
let commission = match liquidity_side {
LiquiditySide::Maker => -fee_equivalent
.checked_mul(schedule.rebate_rate)
.context("commission calculation overflow")?
.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(())
}View on GitHub (pinned to 18893faf8b)
Solutions
- Only apply PolymarketFeeModel to markets whose fee schedule has exponent 1.
- Inspect the market's fee_schedule in instrument info and confirm what the CLOB API returns.
- Extend validate_schedule/commission math to support the observed exponent if that market type must be supported.
- Update stale fee schedule fixtures to exponent 1 if the data is simply outdated.
Example fix
// before
let schedule = FeeSchedule { exponent: Decimal::new(2, 0), .. };
// after
let schedule = FeeSchedule { exponent: Decimal::ONE, .. }; Defensive patterns
Strategy: validation
Validate before calling
if schedule.exponent != Decimal::ONE {
return Err(format!("unsupported fee exponent {}", schedule.exponent));
} Type guard
fn is_linear_schedule(s: &FeeSchedule) -> bool {
s.exponent == Decimal::ONE && s.rate >= Decimal::ZERO && (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("exponent 1") => {
log::warn!("market uses nonlinear fee schedule; skipping fee model");
Money::zero(instrument.quote_currency())
}
Err(e) => return Err(e),
} Prevention
- Check the market's fee schedule exponent when selecting markets to trade.
- Validate schedules at instrument build time.
- Keep fixtures in sync with the live CLOB API.
When it happens
Trigger: A market's taker fee schedule (from instrument info) carries exponent != 1 when get_commission runs validate_schedule — e.g. markets whose fee schedule uses a nonlinear exponent.
Common situations: Trading markets with legacy or special fee schedules fetched from the CLOB API; fee schedule data drift where the API starts returning exponent 2 or 0.5; hard-coded test fixtures with wrong exponent values.
Related errors
- builder fee rate must be non-negative
- event_slug_builder.interval_mins must be positive
- event_slug_builder.periods must be positive
- event_slug_builder.assets must include at least one non-empt
- event_slug_builder offset resolves before the Unix epoch
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/68a4ccf02f8357a9.
Report an issue: GitHub.