nautechsystems/nautilus_trader · error

total PnL overflow

Error message

total PnL overflow

What it means

try_total_pnl returns 'total PnL overflow' when Money::checked_add of realized and unrealized PnL fails because the sum cannot be represented in the Money type (fixed-precision raw value overflow for that currency). The currency check passed, but the arithmetic result exceeds the representable range.

Solutions

  1. Verify the instrument's multiplier, size precision, and price precision are configured correctly — inflated values overflow Money.
  2. Check for duplicated fills inflating realized PnL and rebuild the position from authoritative history.
  3. Work with raw f64 PnL components (calculate_pnl_raw paths) and aggregate in a wider numeric type if your magnitudes are genuinely large.

Example fix

// before
let total = position.try_total_pnl(last)?; // Err: total PnL overflow
// after: fall back to f64 aggregation for huge magnitudes
let total = match position.try_total_pnl(last) {
    Ok(m) => m,
    Err(_) => {
        let r = position.realized_pnl.map(|m| m.as_f64()).unwrap_or(0.0);
        Money::new(r + position.try_unrealized_pnl(last)?.as_f64(), currency, precision)
    }
};
Defensive patterns

Strategy: try-catch

Validate before calling

// estimate magnitude before calling
let approx = position.realized_pnl.as_ref().map(|m| m.as_f64()).unwrap_or(0.0)
    + position.try_unrealized_pnl(last)?.as_f64();
if approx.abs() > 9.0e15 { /* use f64 aggregation path */ }

Try / catch

let total = match position.try_total_pnl(last) {
    Ok(m) => m,
    Err(e) if e.to_string().contains("overflow") => {
        let r = position.realized_pnl.map(|m| m.as_f64()).unwrap_or(0.0);
        let u = position.try_unrealized_pnl(last)?.as_f64();
        Money::new(r + u, position.quote_currency, position.size_precision)
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Summing extremely large realized and unrealized PnL values whose combined raw integer amount exceeds the Money precision limit — e.g. very high notional instruments, wrong multiplier (contract size) configured orders of magnitude too large, or accumulated PnL over a very long-lived position.

Common situations: Misconfigured contract multiplier or size precision inflating PnL amounts; simulators generating unrealistic prices; running the same fills through the position repeatedly so realized PnL accumulates beyond bounds.

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

Appendix: source

Thrown at crates/model/src/position.rs:1231

    /// Returns total P&L (realized + unrealized) based on the last price.
    ///
    /// # Errors
    ///
    /// Returns an error if unrealized P&L cannot be calculated, the realized and unrealized
    /// currencies differ, or the total cannot be represented as [`Money`].
    pub fn try_total_pnl(&self, last: Price) -> anyhow::Result<Money> {
        let unrealized = self.try_unrealized_pnl(last)?;

        match self.realized_pnl {
            Some(realized) => {
                anyhow::ensure!(
                    realized.currency == unrealized.currency,
                    "realized and unrealized PnL currencies differ"
                );
                realized
                    .checked_add(unrealized)
                    .ok_or_else(|| anyhow::anyhow!("total PnL overflow"))
            }
            None => Ok(unrealized),
        }
    }

    /// Returns total P&L (realized + unrealized) based on the last price.
    #[must_use]
    pub fn total_pnl(&self, last: Price) -> Money {
        self.try_total_pnl(last).unwrap_or_else(|e| {
            log::error!("Error calculating total PnL: {e}");
            Money::zero(self.settlement_currency)
        })
    }

    /// Returns unrealized P&L based on the last price.
    ///
    /// # Errors
    ///

View on GitHub (pinned to 18893faf8b)