nautechsystems/nautilus_trader · error

Tick size {tick_size} is not exactly representable at Polyma

Error message

Tick size {tick_size} is not exactly representable at Polymarket price precision {POLYMARKET_PRICE_PRECISION}

What it means

After computing min_price = tick_size at POLYMARKET_PRICE_PRECISION, tick_relative_price_bounds verifies the Decimal round-trips exactly. This error is thrown when the tick size has more decimal places than Polymarket's price precision supports, so it cannot be represented exactly.

Source

Thrown at crates/adapters/polymarket/src/http/parse.rs:325

        .ts_event(ts_event)
        .ts_init(ts_init)
        .build()?;

    Ok(InstrumentAny::BinaryOption(rebuilt))
}

// Returns the tradeable price bounds `[tick_size, 1 - tick_size]` for a Polymarket outcome,
// mirroring the venue range enforced in `PolymarketOrderBuilder::validate_limit_price`.
pub(crate) fn tick_relative_price_bounds(tick_size: Decimal) -> anyhow::Result<(Price, Price)> {
    anyhow::ensure!(
        tick_size > Decimal::ZERO,
        "Tick size {tick_size} must be positive"
    );

    let min_price = Price::from_decimal_dp(tick_size, POLYMARKET_PRICE_PRECISION)?;
    let max_price = Price::from_decimal_dp(Decimal::ONE - tick_size, POLYMARKET_PRICE_PRECISION)?;

    anyhow::ensure!(
        min_price.as_decimal() == tick_size,
        "Tick size {tick_size} is not exactly representable at Polymarket price precision {POLYMARKET_PRICE_PRECISION}"
    );
    Ok((min_price, max_price))
}

fn build_info_json(def: &PolymarketInstrumentDef) -> serde_json::Value {
    let mut map = serde_json::Map::new();
    map.insert(
        "token_id".to_string(),
        serde_json::Value::String(def.token_id.to_string()),
    );
    map.insert(
        "condition_id".to_string(),
        serde_json::Value::String(def.condition_id.to_string()),
    );
    map.insert(
        "market_id".to_string(),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use only tick sizes that are exact at POLYMARKET_PRICE_PRECISION (e.g. 0.1, 0.01, 0.001 depending on the constant).
  2. If the venue added a new tick precision, bump POLYMARKET_PRICE_PRECISION or extend the supported-tick handling and update tests.
  3. Drop/ignore the instrument update with a warning rather than rebuilding with a rounded price.

Example fix

// before
let tick_size = parse_decimal_exact(new_tick_size)?;
let (min_price, max_price) = tick_relative_price_bounds(tick_size)?;
// after
let tick_size = parse_decimal_exact(new_tick_size)?;
if tick_size.normalize().scale() > POLYMARKET_PRICE_PRECISION as u32 {
    anyhow::bail!("unsupported tick precision for {new_tick_size}");
}
let (min_price, max_price) = tick_relative_price_bounds(tick_size)?;
Defensive patterns

Strategy: validation

Validate before calling

fn tick_representable(tick: Decimal, precision: u8) -> bool {
    tick.normalize().scale() <= precision as u32
}

Type guard

fn is_supported_tick(tick: Decimal) -> bool {
    [dec!(0.1), dec!(0.01), dec!(0.001)].contains(&tick.normalize())
}

Try / catch

match tick_relative_price_bounds(tick_size) {
    Ok(bounds) => bounds,
    Err(e) => { warn!("tick not representable: {e}"); bail!("unsupported tick precision") }
}

Prevention

When it happens

Trigger: create_instrument_from_def or rebuild_instrument_with_tick_size receiving a tick size like 0.001 when POLYMARKET_PRICE_PRECISION only allows fewer decimal places (e.g. 0.01/0.001 vs a 2-dp precision), making min_price.as_decimal() != tick_size.

Common situations: Polymarket introducing non-power-of-ten or finer tick sizes than the adapter's fixed precision, or a market message carrying a tick size with excessive precision.

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/8d858b80d0e0b65d. Report an issue: GitHub.