nautechsystems/nautilus_trader · error · anyhow::Error

Failed to create avg_px price: {e}

Error message

Failed to create avg_px price: {e}

What it means

For combo (legged) fills, the adapter computes a partial average price Decimal and converts it to a Price via Price::from_decimal_dp. This error wraps any failure of that conversion — typically because the partial average decimal exceeds the instrument's price precision or cannot be represented at that precision.

Source

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

        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(());
        }

        let notional_delta = total_notional - previous_notional;
        let partial_avg_decimal = notional_delta / fill_delta;
        let partial_avg_px =
            Price::from_decimal_dp(partial_avg_decimal, instrument.price_precision())
                .map_err(|e| anyhow::anyhow!("Failed to create avg_px price: {e}"))?;

        pending_combo_fill_avgs
            .lock()
            .entry(client_order_id)
            .or_default()
            .push_back((fill_delta, partial_avg_px));

        Ok(())
    }

    pub(super) fn flush_pending_combo_fills(
        client_order_id: ClientOrderId,
        pending_combo_fills: &Arc<Mutex<AHashMap<ClientOrderId, VecDeque<PendingComboFill>>>>,
        pending_combo_fill_avgs: &Arc<Mutex<AHashMap<ClientOrderId, VecDeque<(Decimal, Price)>>>>,
        order_fill_progress: &Arc<Mutex<AHashMap<ClientOrderId, (Decimal, Decimal)>>>,
        exec_sender: &tokio::sync::mpsc::UnboundedSender<ExecutionEvent>,
    ) -> anyhow::Result<()> {
        let mut combo_fills = pending_combo_fills.lock();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm the instrument definition's price_precision matches the actual IB contract (check the instrument provider data).
  2. Round/quantize partial_avg_decimal to the instrument precision before building the Price.
  3. Guard against fill_delta == 0 or degenerate notional deltas before the division.

Example fix

// before
let partial_avg_px =
    Price::from_decimal_dp(partial_avg_decimal, instrument.price_precision())
        .map_err(|e| anyhow::anyhow!("Failed to create avg_px price: {e}"))?;
// after
let rounded = partial_avg_decimal
    .round_dp(u32::from(instrument.price_precision()));
let partial_avg_px = Price::from_decimal_dp(rounded, instrument.price_precision())
    .map_err(|e| anyhow::anyhow!("Failed to create avg_px price: {e}"))?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: check precision and divisor before building the Price
if fill_delta <= Decimal::ZERO {
    tracing::warn!("Zero fill delta; skipping partial avg price");
    return Ok(());
}
let dp = u32::from(instrument.price_precision());
let rounded = partial_avg_decimal.round_dp(dp);

Type guard

fn fits_precision(d: Decimal, precision: u8) -> bool {
    (d.round_dp(u32::from(precision)) - d).abs() < Decimal::new(1, i64::from(precision))
}

Try / catch

let partial_avg_px = match Price::from_decimal_dp(partial_avg_decimal, instrument.price_precision()) {
    Ok(px) => px,
    Err(e) => {
        tracing::error!("Partial avg price out of range: {e}");
        return Ok(());
    }
};

Prevention

When it happens

Trigger: update_order_avg_price computes partial_avg_decimal = notional_delta / fill_delta and Price::from_decimal_dp(partial_avg_decimal, instrument.price_precision()) fails — e.g. price_precision is 0/invalid or the decimal cannot be rounded to the allowed decimal places.

Common situations: Instrument loaded with wrong price_precision from the venue/instrument provider; very small fill deltas producing extreme averages that can't fit the precision.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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