nautechsystems/nautilus_trader · error
PolymarketFeeModel requires a taker-only fee schedule
Error message
PolymarketFeeModel requires a taker-only fee schedule
What it means
PolymarketFeeModel only supports taker-only fee schedules. During commission calculation, validate_schedule rejects any schedule where taker_only is false, because Polymarket fee logic cannot model maker rebates correctly. This is a fail-fast invariant to prevent silently wrong commission math.
Source
Thrown at crates/adapters/polymarket/src/models.rs:126
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},
};
use rstest::rstest;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
View on GitHub (pinned to 18893faf8b)
Solutions
- Set taker_only=true on the FeeModel schedule before passing it to PolymarketFeeModel
- Remove any maker/rebate fields and construct the schedule via the Polymarket-specific constructor that defaults to taker_only
- If maker fees are genuinely needed, use a different fee model — PolymarketFeeModel intentionally does not support them
Example fix
// before let schedule = FeeModel::new(rebate_rate, false /* taker_only */); PolymarketFeeModel::new(schedule)?; // after let schedule = FeeModel::new(Decimal::ZERO, true /* taker_only */); PolymarketFeeModel::new(schedule)?;
Defensive patterns
Strategy: validation
Validate before calling
if !schedule.taker_only {
return Err(anyhow!("schedule must be taker_only for Polymarket"));
}
let fee_model = PolymarketFeeModel::new(schedule)?; Type guard
fn is_taker_only(s: &FeeModel) -> bool { s.taker_only } Prevention
- Always construct Polymarket fee schedules via the Polymarket-specific constructor
- Never reuse fee schedules across venue adapters
- Assert taker_only in config loading tests
When it happens
Trigger: Constructing a PolymarketFeeModel (or calling get_commission) with a FeeModel whose taker_only flag is false, e.g. reusing a schedule copied from another venue that includes maker/rebate tiers.
Common situations: Reusing a generic fee schedule across adapters; building a schedule from config where a maker rebate rate was set; copying a CEX fee schedule that supports maker rebates.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- heartbeat_secs must be positive when set
- signature_expiry_secs {signature_expiry_secs}s must be great
- Timeout must be greater than 0
- Timeout must be less than 3600 seconds
- VNC port must be between 5900 and 5999
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/28102a2735cf85e7.
Report an issue: GitHub.