nautechsystems/nautilus_trader · error · anyhow::Error
Cannot calculate average price: both quantities are zero
Error message
Cannot calculate average price: both quantities are zero
What it means
Position::calculate_avg_px computes a weighted-average price from an existing quantity/avg price and a new fill; if both the existing quantity and the fill quantity are zero there is no denominator and no meaningful average, so it bails.
Source
Thrown at crates/model/src/position.rs:1074
/// - `last_qty` is zero (prevents division by zero).
/// - `total_qty` is zero or negative (arithmetic error).
fn calculate_avg_px(
&self,
qty: f64,
avg_pg: f64,
last_px: f64,
last_qty: f64,
) -> anyhow::Result<f64> {
// Prices can be negative for options and spreads, so only quantities
// are checked for non-negativity here.
debug_assert!(
qty >= 0.0 && last_qty >= 0.0,
"Invariant: average price calc requires non-negative quantities \
(qty={qty}, last_qty={last_qty})"
);
if qty == 0.0 && last_qty == 0.0 {
anyhow::bail!("Cannot calculate average price: both quantities are zero");
}
if last_qty == 0.0 {
anyhow::bail!("Cannot calculate average price: fill quantity is zero");
}
if qty == 0.0 {
return Ok(last_px);
}
let start_cost = avg_pg * qty;
let event_cost = last_px * last_qty;
let total_qty = qty + last_qty;
// Runtime check to prevent division by zero even in release builds
if total_qty <= 0.0 {
anyhow::bail!(
"Total quantity unexpectedly zero or negative in average price calculation: qty={qty}, last_qty={last_qty}, total_qty={total_qty}"View on GitHub (pinned to 18893faf8b)
Solutions
- Check that the position has at least one nonzero quantity (position.quantity != 0 or the event quantity > 0) before requesting average prices.
- Return Option/None for avg px when the position is flat instead of forcing the calculation.
- Inspect why a zero-quantity fill event reached the position — zero-qty fills usually should be filtered upstream.
Example fix
// before
let avg = position.calculate_avg_px_open_px();
// after
let avg = if position.quantity().as_f64() != 0.0 {
Some(position.calculate_avg_px_open_px())
} else { None }; Defensive patterns
Strategy: validation
Validate before calling
if qty == 0.0 && last_qty == 0.0 { return Ok(None); /* undefined average price */ } Try / catch
match std::panic::catch_unwind(|| position.calculate_avg_px_open_px()) { ... }
// or preferably: check quantities before calling and return None for flat positions Prevention
- Return Option<Price> for averages on flat positions instead of forcing computation.
- Drop zero-quantity fill events before position updates.
When it happens
Trigger: Calling calculate_avg_px_open_px or calculate_avg_px_close_px on a position/flow where qty == 0.0 and last_qty == 0.0 — e.g. computing open/close average price for an empty position with an empty (zero-quantity) event.
Common situations: Querying average open price on a flat position that has never had a fill, or processing generated/placeholder order-filled events with zero quantity.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot calculate average price: fill quantity is zero
- Total quantity unexpectedly zero or negative in average pric
- Cannot update position {}: not found in cache
- Cannot update position {position_id}: not found in cache
- Duplicate position fill
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1e572bf0a7304213.
Report an issue: GitHub.