nautechsystems/nautilus_trader · error

historical {field} {value} is not exactly representable as a

Error message

historical {field} {value} is not exactly representable as a price

What it means

This is the final exactness check: after building a domain Price via Price::from_decimal, the adapter asserts price.as_decimal() equals the original value. If the Price conversion lost precision or rounded, the historical evidence is not exactly representable and the anyhow error fires. It catches cases the earlier scale check cannot (e.g. internal tick-size rounding inside Price).

Source

Thrown at crates/adapters/polymarket/src/execution/reconciliation.rs:319

        price.as_decimal() == value,
        "{field} {value} is not exactly representable with price precision {precision}",
    );
    Ok(())
}

fn validate_historical_price_evidence(value: Decimal, field: &str) -> anyhow::Result<Price> {
    anyhow::ensure!(
        value > Decimal::ZERO && value < Decimal::ONE,
        "{field} {value} must be greater than zero and less than one",
    );
    let evidence = value.normalize();
    anyhow::ensure!(
        evidence.scale() <= u32::from(FIXED_PRECISION),
        "historical {field} {value} exceeds the maximum representable price precision of {FIXED_PRECISION} decimals",
    );
    let price = Price::from_decimal(evidence)
        .with_context(|| format!("failed to represent historical {field} {value}"))?;
    anyhow::ensure!(
        price.as_decimal() == value,
        "historical {field} {value} is not exactly representable as a price",
    );
    Ok(price)
}

#[derive(Clone, Copy, Debug)]
struct ValidatedOrderRow {
    venue_order_id: VenueOrderId,
    ts_accepted: UnixNanos,
    expire_time: Option<UnixNanos>,
}

fn parse_provider_order_expiration(
    order: &PolymarketOpenOrder,
) -> anyhow::Result<Option<UnixNanos>> {
    match order.expiration.as_deref() {
        None => Ok(None),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Snap the value to the instrument's tick size (quantize) before validation and persist the snapped value as the evidence.
  2. Recompute historical evidence using the venue tick grid so it is exactly representable.
  3. Check the instrument's tick_size/price_increment configuration matches the era the historical data came from.

Example fix

// before
let p = Decimal::new(123456, 6); // 0.123456, not on tick grid
let price = validate_historical_price_evidence(p, "price")?;
// after
let p = p.round_dp_with_strategy(instrument.tick_size().as_decimal().scale(), RoundingStrategy::ToNearest);
let price = validate_historical_price_evidence(p, "price")?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_tick_aligned(price: rust_decimal::Decimal, tick: rust_decimal::Decimal) -> bool {
    (price % tick).is_zero()
}
// snap before validating: price = (price / tick).round() * tick;

Try / catch

let price = match validate_historical_price_evidence(value, field) {
    Err(e) if e.to_string().contains("not exactly representable as a price") => {
        snap_to_tick(value, instrument.tick_size())? // retry with snapped value
    }
    other => other?,
};

Prevention

When it happens

Trigger: Price::from_decimal succeeds but internally rounds to the venue tick size, so price.as_decimal() != value, in validate_trade_values -> validate_historical_price_evidence — e.g. a price not on the market's tick grid despite fitting FIXED_PRECISION decimals.

Common situations: Prices computed from fee-adjusted math or weighted averages landing between valid ticks; market tick size finer than FIXED_PRECISION or vice versa; importing evidence generated under a different tick regime.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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