nautechsystems/nautilus_trader · error

invalid Futures kline {}: {e}

Error message

invalid Futures kline {}: {e}

What it means

Thrown by parse_futures_kline_binance_bar when a kline's open/high price field cannot be parsed into a Price at the instrument's price_precision. Each of open/high/low/close (and volume) is wrapped with the same message keyed by kline.open_time, so this specific instance fires when the OHLC parse fails on one of the first fields; the underlying error is either a malformed decimal string or a value whose precision exceeds the cached instrument's price_precision.

Source

Thrown at crates/adapters/binance/src/futures/http/client.rs:3212

    };
    parse_futures_trade_tick(
        &trade,
        instrument_id,
        price_precision,
        size_precision,
        ts_init,
    )
}

fn parse_futures_kline_binance_bar(
    kline: &BinanceFuturesKline,
    bar_type: BarType,
    price_precision: u8,
    size_precision: u8,
    ts_init: UnixNanos,
) -> anyhow::Result<BinanceBar> {
    let open = parse_required_price_at_precision(&kline.open, price_precision, "kline.open")
        .map_err(|e| anyhow::anyhow!("invalid Futures kline {}: {e}", kline.open_time))?;
    let high = parse_required_price_at_precision(&kline.high, price_precision, "kline.high")
        .map_err(|e| anyhow::anyhow!("invalid Futures kline {}: {e}", kline.open_time))?;
    let low = parse_required_price_at_precision(&kline.low, price_precision, "kline.low")
        .map_err(|e| anyhow::anyhow!("invalid Futures kline {}: {e}", kline.open_time))?;
    let close = parse_required_price_at_precision(&kline.close, price_precision, "kline.close")
        .map_err(|e| anyhow::anyhow!("invalid Futures kline {}: {e}", kline.open_time))?;
    let volume =
        parse_required_quantity_at_precision(&kline.volume, size_precision, "kline.volume")
            .map_err(|e| anyhow::anyhow!("invalid Futures kline {}: {e}", kline.open_time))?;
    let ts_event = parse_millis(kline.close_time, "Futures kline close time")?;

    let quote_volume = kline.quote_volume.parse::<Decimal>().map_err(|e| {
        anyhow::anyhow!(
            "invalid Futures kline {} quote volume: {e}",
            kline.open_time
        )
    })?;
    let taker_buy_base_volume = kline

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Reload instrument definitions to pick up current price_precision, then retry the kline request
  2. Log the failing kline's open_time and compare the raw JSON decimals against the cached precision
  3. Use raw venue exchangeInfo to confirm whether PRICE_FILTER tickSize changed for the symbol
  4. In tests, generate fixtures with prices consistent with the instrument precision used to seed the cache
Defensive patterns

Strategy: validation

Validate before calling

// ensure cache precision matches live metadata before backfill
client.request_instruments(None).await?; // refresh before long backfills
let (symbol, pp, sp) = client.cached_precisions_by_id(instrument_id)?;

Try / catch

Catch with the kline open_time from the message; log the raw kline, refresh the instrument cache once, and retry the request window; abort the backfill if mismatches persist.

Prevention

When it happens

Trigger: Kline payloads (request_binance_bars / historical klines) with more price decimals than the cached instrument declares — classic stale-instrument-cache drift after Binance tick-size changes; malformed strings from fixtures or proxies; negative/zero prices that Price rejects.

Common situations: Historical backfill jobs running long after session start against a stale instrument cache; testnet/mainnet metadata divergence; hand-built kline fixtures in tests with arbitrary precision; schema drift after Binance API updates.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/939c5a990d457f10. Report an issue: GitHub.