nautechsystems/nautilus_trader · error

quantity for {currency} exceeds Money raw bounds

Error message

quantity for {currency} exceeds Money raw bounds

What it means

wallet_money_from_quantity converts a Quantity's raw integer value into a Money amount in the account currency. After adjusting the raw value for the currency's precision it performs a fixed-width i128 range check (check_fixed_raw_i128) and then attempts to fit the value into Money's internal raw integer representation (MoneyRaw). If the value is too large to fit that narrower integer type, the TryInto conversion fails and this error is thrown.

Source

Thrown at crates/portfolio/src/manager.rs:1443

            let scale = 10_i128.pow(u32::from(target_precision - source_precision));
            raw.checked_mul(scale).ok_or_else(|| {
                anyhow::anyhow!("quantity for {currency} overflowed while increasing raw scale")
            })?
        }
        Ordering::Greater => {
            let scale = 10_i128.pow(u32::from(source_precision - target_precision));
            anyhow::ensure!(
                raw % scale == 0,
                "quantity for {currency} loses precision when decreasing raw scale"
            );
            raw / scale
        }
        Ordering::Equal => raw,
    };
    check_fixed_raw_i128(raw, currency.precision)?;
    let raw: MoneyRaw = raw
        .try_into()
        .map_err(|_| anyhow::anyhow!("quantity for {currency} exceeds Money raw bounds"))?;

    Money::from_raw_checked(raw, currency).map_err(Into::into)
}

fn reservation_precisions_match(
    account: &dyn Account,
    reservations: &AHashMap<Currency, Money>,
) -> bool {
    for reservation in reservations.values() {
        let Some(balance) = account.balance(Some(reservation.currency)) else {
            continue;
        };

        if balance.currency.precision != reservation.currency.precision {
            log::error!(
                "Cannot update {} reservation: precision {} differed from balance precision {}",
                reservation.currency,
                reservation.currency.precision,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the quantity/balance value from the venue is scaled correctly for the currency precision before it reaches the wallet
  2. Check currency.precision: low-precision currencies (fewer decimals) allow less headroom before overflow — use a higher-precision currency or cap the value
  3. Confirm the build's MoneyRaw width (fixed i128 bounds via check_fixed_raw_i128) matches the magnitudes your strategy trades
  4. Log the failing quantity, currency, and precision at the call site to confirm which side (quantity vs precision scaling) produces the out-of-range value

Example fix

// before: passing a raw quantity already scaled for the venue, re-scaled again
let money = wallet_money_from_quantity(&qty, &usd_currency)?;
// after: ensure the quantity is in units, not scaled raw, before conversion
let qty = Quantity::new(balance_total.as_f64(), usd_currency.precision);
let money = wallet_money_from_quantity(&qty, &usd_currency)?;
Defensive patterns

Strategy: validation

Validate before calling

fn quantity_fits_money_raw(qty: &Quantity, currency: &Currency) -> bool {
    let raw = qty.raw.as_i128();
    // 10^precision scaling must stay within i128 bounds used by MoneyRaw
    let scaled = raw.checked_mul(10i128.checked_pow(currency.precision as u32).unwrap_or(i128::MAX));
    match scaled {
        Some(v) => v >= i128::MIN / 1_000_000 && v <= i128::MAX / 1_000_000, // conservative bound
        None => false,
    }
}

Prevention

When it happens

Trigger: Calling update_balance_locked_wallet (directly or via balance updates) with a quantity whose raw value, after precision adjustment to the currency's precision, exceeds the maximum (or minimum) value representable by MoneyRaw — e.g. very large account balances in low-precision currencies like USD (2 dp).

Common situations: Trading venues or test environments reporting enormous balances; manually seeded account state with unadjusted raw quantities; currencies with few decimals where a large quantity multiplied by precision overflows; bugs in upstream balance parsing that pass unscaled values.

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