nautechsystems/nautilus_trader · error

Tick size {tick_size} must be positive

Error message

Tick size {tick_size} must be positive

What it means

tick_relative_price_bounds computes the tradeable price range [tick_size, 1 - tick_size] for a Polymarket outcome and requires a strictly positive tick size, since a non-positive tick would produce an invalid/degenerate price band.

Source

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

        .maybe_min_notional(bo.min_notional)
        .max_price(max_price)
        .min_price(min_price)
        .margin_init(bo.margin_init)
        .margin_maint(bo.margin_maint)
        .maker_fee(bo.maker_fee)
        .taker_fee(bo.taker_fee)
        .maybe_info(bo.info.clone())
        .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(),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the tick_size value in the market payload before rebuilding the instrument.
  2. Guard with an explicit tick_size > Decimal::ZERO check and skip the update if not positive.
  3. Re-fetch authoritative instrument definitions from the Gamma/CLOB REST API when the streamed tick size looks invalid.

Example fix

// before
rebuild_instrument_with_tick_size(&instrument, &msg.new_tick_size)?;
// after
if Decimal::from_str(&msg.new_tick_size)? <= Decimal::ZERO {
    tracing::warn!("ignoring non-positive tick size {}", msg.new_tick_size);
    return Ok(());
}
rebuild_instrument_with_tick_size(&instrument, &msg.new_tick_size)?;
Defensive patterns

Strategy: validation

Validate before calling

let tick = parse_decimal_exact(&new_tick_size)?;
if tick <= Decimal::ZERO { bail!("tick size must be positive, got {tick}"); }

Type guard

fn is_positive_tick(d: Decimal) -> bool { d > Decimal::ZERO }

Try / catch

match tick_relative_price_bounds(tick_size) {
    Ok((min, max)) => (min, max),
    Err(e) => { warn!("invalid tick size {tick_size}: {e}"); return Ok(()); }
}

Prevention

When it happens

Trigger: Calling tick_relative_price_bounds (via create_instrument_from_def or rebuild_instrument_with_tick_size) with tick_size <= 0, e.g. Decimal::ZERO or a negative value parsed from market data.

Common situations: Venue sending tick_size "0" or "0.0" in a market message before the real value arrives, or a data bug producing negative tick sizes.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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