nautechsystems/nautilus_trader · error
PolymarketFeeModel requires a fill price in [0, 1]
Error message
PolymarketFeeModel requires a fill price in [0, 1]
What it means
Polymarket fees are charged as fee_rate * shares * price, which is only defined for prices in [0, 1] (probability space). get_commission validates the fill price and bails if it falls outside that closed interval. An out-of-range fill price indicates corrupted pricing data or a wrong instrument/price precision.
Source
Thrown at crates/adapters/polymarket/src/models.rs:87
}
};
let Some(schedule) = binary
.info
.as_ref()
.and_then(|info| info.get("fee_schedule"))
.map(|value| serde_json::from_value::<FeeSchedule>(value.clone()))
.transpose()
.context("invalid Polymarket fee schedule")?
else {
return Ok(Money::zero(instrument.quote_currency()));
};
validate_schedule(&schedule)?;
let fill_price = fill_px.as_decimal();
if !(Decimal::ZERO..=Decimal::ONE).contains(&fill_price) {
anyhow::bail!("PolymarketFeeModel requires a fill price in [0, 1]");
}
let fee_equivalent = fill_quantity
.as_decimal()
.checked_mul(schedule.rate)
.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)View on GitHub (pinned to 18893faf8b)
Solutions
- Convert fill prices to Polymarket's 0–1 decimal scale before fee calculation (divide by 100 if in percent).
- Verify the Price was constructed with POLYMARKET_PRICE_PRECISION and the correct scale.
- Validate fill prices at ingestion (reject/clamp values outside [0,1]) before the engine computes commission.
- Check the data feed producing the fill price for precision bugs.
Example fix
// before let fill_px = Price::from_raw(55000000000); // wrong scale // after let fill_px = Price::new(0.55.into(), POLYMARKET_PRICE_PRECISION)?;
Defensive patterns
Strategy: validation
Validate before calling
let d = fill_px.as_decimal();
if !(Decimal::ZERO..=Decimal::ONE).contains(&d) {
return Err(format!("fill price {d} outside [0,1]"));
} Type guard
fn is_valid_probability_price(px: &Price) -> bool {
let d = px.as_decimal();
d >= Decimal::ZERO && d <= Decimal::ONE
} Try / catch
match fee_model.get_commission(&order, qty, px, &instrument) {
Ok(fee) => fee,
Err(e) if e.to_string().contains("fill price in [0, 1]") => {
log::error!("invalid fill price {} — check data feed precision", px);
return Err(e);
}
Err(e) => return Err(e),
} Prevention
- Build all Polymarket prices with POLYMARKET_PRICE_PRECISION on the 0–1 scale.
- Never reuse prices from percent-scaled feeds without conversion.
- Validate prices at ingestion boundaries.
When it happens
Trigger: Calling get_commission with a fill_px outside [0, 1] — e.g. a price in cents (55 instead of 0.55), a price built with the wrong precision, or a negative/garbage price from a bad data feed.
Common situations: Mixing prices from another venue/precision; synthetic test fills using 0–100 probability scale; corrupted fill reports from a broken data source.
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
- PolymarketFeeModel requires fee schedule exponent 1, was {}
- Polymarket fee rate must be greater than or equal to zero
- Polymarket rebate rate must be in [0, 1]
- builder fee rate must be non-negative
- fee quantity must be non-negative
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f87d6a6caf1966db.
Report an issue: GitHub.