nautechsystems/nautilus_trader · error

invalid candle volume: {e}

Error message

invalid candle volume: {e}

What it means

parse_candle_bar converts a Lighter historical candle into a Nautilus Bar. The raw candle's volume must be sign-positive and quantizable to the bar type's size precision; if Quantity::from_decimal_dp fails (or volume is negative, caught by the preceding ensure), this error wraps the underlying precision/conversion failure. It prevents constructing a Bar with an unrepresentable volume.

Source

Thrown at crates/adapters/lighter/src/http/parse.rs:228

    let ts_event = parse_millis_to_nanos(timestamp_ms)?;
    let price_precision = instrument.price_precision();
    let size_precision = instrument.size_precision();

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

    Bar::new_checked(bar_type, open, high, low, close, volume, ts_event, ts_init)
        .context("failed to construct Bar from Lighter candle")
}

/// Parses a Lighter historical funding row into a Nautilus [`FundingRateUpdate`].
///
/// Lighter returns `rate` as a magnitude and `direction` as the side paying
/// the funding. Nautilus uses the conventional signed rate: positive when
/// longs pay shorts and negative when shorts pay longs.
///
/// # Errors
///
/// Returns an error if the timestamp cannot be converted.
pub fn parse_funding_rate_update(
    funding: &LighterFunding,
    instrument_id: InstrumentId,
    interval: Option<u16>,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set the bar type's size_precision to at least the decimal places the venue reports for volume (Lighter's supported_volume_decimals).
  2. Pre-normalize candle volume with Decimal rounding to size_precision before parsing.
  3. Check the candle payload for negative or malformed volume values before parsing.
  4. Inspect the inner message from Quantity::from_decimal_dp in the wrapped {e} to see the exact precision failure.

Example fix

// before
let bar_type = BarType::new(instrument_id, aggregator, PriceType::Last); // size_precision=2
// after
let bar_type = BarType::new(
    instrument_id,
    aggregator,
    PriceType::Last,
);
// ensure aggregator's size_precision >= venue volume decimals (e.g. 6 for Lighter)
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate candle before parse_candle_bar
if !candle.volume_base.is_sign_positive() {
    return Err(anyhow!("skip candle: negative volume {}", candle.volume_base));
}
let dp = candle.volume_base.fract().scale() as u32;
anyhow::ensure!(dp <= size_precision as u32, "volume dp {} > size_precision {}", dp, size_precision);

Try / catch

match parse_candle_bar(&bar_type, &candle, size_precision) {
    Ok(bar) => bar,
    Err(e) => { warn!("skipping candle: {e:#}"); continue; }
}

Prevention

When it happens

Trigger: Parsing a /candles response whose candle has negative volume_base, or whose volume requires more decimal places than the bar type's size_precision supports (e.g. volume 1.00000005 with size_precision=4), causing from_decimal_dp rounding/precision rejection.

Common situations: Bar type built with too-coarse size_precision for the venue's quote volumes; malformed or adversarial candle data from the HTTP API; copying volumes from a different market with higher precision.

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/214bada5c18c90bb. Report an issue: GitHub.