nautechsystems/nautilus_trader · error · anyhow::Error

Failed to convert filled qty to Decimal: {filled}

Error message

Failed to convert filled qty to Decimal: {filled}

What it means

The adapter converts the IB-reported filled quantity (an f64) into a Decimal using from_f64_retain, which returns None for NaN, infinite, or otherwise non-representable values. When the filled quantity from an IB execution report is not a finite finite number, this error is thrown so no bogus fill progress is recorded.

Source

Thrown at crates/adapters/interactive_brokers/src/execution/core_updates.rs:933

        order_fill_progress: &Arc<Mutex<AHashMap<ClientOrderId, (Decimal, Decimal)>>>,
    ) -> anyhow::Result<()> {
        let is_spread_order = is_spread_instrument_id(instrument_id);
        if filled <= 0.0 || !parse::should_use_avg_fill_price(avg_fill_price, instrument_id) {
            return Ok(());
        }

        let Some(instrument) = instrument_provider.find(instrument_id) else {
            return Ok(());
        };

        let price_magnifier = instrument_provider.get_price_magnifier(instrument_id) as f64;
        let converted_avg_price = avg_fill_price * price_magnifier;
        let avg_px = Price::new(converted_avg_price, instrument.price_precision());

        order_avg_prices.lock().insert(client_order_id, avg_px);

        let filled_decimal = Decimal::from_f64_retain(filled)
            .ok_or_else(|| anyhow::anyhow!("Failed to convert filled qty to Decimal: {filled}"))?;
        let avg_decimal = Decimal::from_f64_retain(converted_avg_price).ok_or_else(|| {
            anyhow::anyhow!("Failed to convert avg fill price to Decimal: {converted_avg_price}")
        })?;

        let mut progress = order_fill_progress.lock();
        let (previous_filled, previous_notional) = progress
            .get(&client_order_id)
            .copied()
            .unwrap_or((Decimal::ZERO, Decimal::ZERO));
        let total_notional = filled_decimal * avg_decimal;
        progress.insert(client_order_id, (filled_decimal, total_notional));
        drop(progress);

        let fill_delta = filled_decimal - previous_filled;
        if fill_delta <= Decimal::ZERO || !is_spread_order {
            return Ok(());
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Validate that the filled quantity is finite before calling update_order_avg_price, and skip/quote the update otherwise.
  2. Check the IB execution report data for NaN/inf fields; upgrade or fix the ibapi parsing if it emits sentinel values.
  3. Log the raw execution data when this occurs to identify the upstream message producing the bad quantity.

Example fix

// before
let filled_decimal = Decimal::from_f64_retain(filled)
    .ok_or_else(|| anyhow::anyhow!("Failed to convert filled qty to Decimal: {filled}"))?;
// after
if !filled.is_finite() || filled < 0.0 {
    tracing::warn!("Skipping avg price update with invalid filled qty: {filled}");
    return Ok(());
}
let filled_decimal = Decimal::from_f64_retain(filled)
    .ok_or_else(|| anyhow::anyhow!("Failed to convert filled qty to Decimal: {filled}"))?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate the IB-reported fill quantity before conversion
fn valid_filled_qty(filled: f64) -> bool {
    filled.is_finite() && filled >= 0.0
}

Type guard

fn is_finite_positive(x: f64) -> bool {
    x.is_finite() && x > 0.0
}

Try / catch

let filled_decimal = match Decimal::from_f64_retain(filled) {
    Some(d) => d,
    None => {
        tracing::error!("Invalid filled qty {filled}; skipping avg price update");
        return Ok(());
    }
};

Prevention

When it happens

Trigger: update_order_avg_price is called with filled = NaN or +/-inf (from ibapi::OrderData fills or division in the combo-fill path), so Decimal::from_f64_retain(filled) returns None.

Common situations: Malformed or placeholder values in IB execution/commission reports; downstream arithmetic producing inf (e.g. division by zero fill delta) passed back into this function.

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