nautechsystems/nautilus_trader · error · anyhow::Error

invalid low price: {e}

Error message

invalid low price: {e}

What it means

During Hyperliquid candle-to-Bar conversion, the low price fails `Price::from_decimal_dp(candle.low, price_precision)` and is wrapped as "invalid low price". The low value from the candle payload cannot be parsed or represented at the instrument's price precision.

Source

Thrown at crates/adapters/hyperliquid/src/data.rs:2078

pub(crate) fn candle_to_bar(
    candle: &HyperliquidCandle,
    bar_type: BarType,
    price_precision: u8,
    size_precision: u8,
) -> anyhow::Result<Bar> {
    let ts_event = millis_to_nanos(candle.timestamp)?;
    let close_boundary = candle
        .end_timestamp
        .checked_add(1)
        .context("candle close boundary overflow")?;
    let ts_init = millis_to_nanos(close_boundary)?;

    let open = Price::from_decimal_dp(candle.open, price_precision)
        .map_err(|e| anyhow::anyhow!("invalid open price: {e}"))?;
    let high = Price::from_decimal_dp(candle.high, price_precision)
        .map_err(|e| anyhow::anyhow!("invalid high price: {e}"))?;
    let low = Price::from_decimal_dp(candle.low, price_precision)
        .map_err(|e| anyhow::anyhow!("invalid low price: {e}"))?;
    let close = Price::from_decimal_dp(candle.close, price_precision)
        .map_err(|e| anyhow::anyhow!("invalid close price: {e}"))?;
    let volume = Quantity::from_decimal_dp(candle.volume, size_precision)
        .map_err(|e| anyhow::anyhow!("invalid volume: {e}"))?;

    Ok(Bar::new(
        bar_type, open, high, low, close, volume, ts_event, ts_init,
    ))
}

/// Request bars from HTTP API.
async fn request_bars_from_http(
    http_client: HyperliquidHttpClient,
    bar_type: BarType,
    start: Option<Timestamp>,
    end: Option<Timestamp>,
    limit: Option<u32>,
    instruments: Arc<AtomicMap<InstrumentId, InstrumentAny>>,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm instrument price_precision matches the Hyperliquid market definition.
  2. Inspect and fix the offending candle `low` value at the source.
  3. Re-request the affected candles.
  4. Pre-validate decimal strings (parseable, within precision) before constructing Candles.

Example fix

// before
Candle { low: "".to_string(), .. }
// after
Candle { low: "4248.25".to_string(), .. }
Defensive patterns

Strategy: validation

Validate before calling

fn candle_fits_precision(low: &str, price_precision: u8) -> bool {
    rust_decimal::Decimal::from_str(low)
        .map(|d| d.scale() <= price_precision as u32)
        .unwrap_or(false)
}

Try / catch

match result {
    Err(e) if e.to_string().contains("invalid low price") => {
        tracing::warn!("dropping candle with bad low: {e:#}");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Candle response containing an `low` field that is empty, malformed, or has more decimals than the configured instrument price_precision while building a Bar.

Common situations: Precision mismatch between local instrument definition and exchange; bad payloads from a data proxy; hand-written fixtures with wrongly formatted decimals.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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