nautechsystems/nautilus_trader · error

Generic spread contract count overflowed for {symbol}

Error message

Generic spread contract count overflowed for {symbol}

What it means

While summing generic spread leg ratios into an i64 contract count, `spread_contract_count` uses `checked_add` and raises this error if the running total overflows. It protects against absurdly large or corrupt spread symbols producing an invalid commission.

Source

Thrown at crates/execution/src/models/fee.rs:357

            .and_then(|v| mul_checked(v, contracts))?;
        Money::from_decimal(total, self.commission.currency).map_err(Into::into)
    }
}

fn spread_contract_count(instrument: &InstrumentAny) -> anyhow::Result<Decimal> {
    let instrument_id = instrument.id();
    let symbol = instrument_id.symbol.as_str();
    if !instrument.is_spread() || !symbol.contains(GENERIC_SPREAD_ID_SEPARATOR) {
        return Ok(Decimal::ONE);
    }

    let mut total = 0_i64;

    for component in symbol.split(GENERIC_SPREAD_ID_SEPARATOR) {
        let ratio = spread_leg_ratio(component)
            .ok_or_else(|| anyhow::anyhow!("Invalid generic spread leg component: {component}"))?;
        total = total.checked_add(ratio).ok_or_else(|| {
            anyhow::anyhow!("Generic spread contract count overflowed for {symbol}")
        })?;
    }

    Ok(total.into())
}

fn spread_leg_ratio(component: &str) -> Option<i64> {
    if let Some(rest) = component.strip_prefix("((") {
        let (ratio, symbol) = rest.split_once("))")?;
        return spread_leg_ratio_parts(ratio, symbol);
    }

    let rest = component.strip_prefix('(')?;
    let (ratio, symbol) = rest.split_once(')')?;
    spread_leg_ratio_parts(ratio, symbol)
}

fn spread_leg_ratio_parts(ratio: &str, symbol: &str) -> Option<i64> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Validate the instrument symbol and its leg ratios come from a trusted source before fee computation.
  2. Cap or sanity-check leg ratios when constructing generic spread instruments.
  3. Investigate the symbol string reported in the message for corruption.
Defensive patterns

Strategy: validation

Validate before calling

ratios = [spread_leg_ratio(c) for c in symbol.split(SEP)]
assert all(r is not None and 0 < r < 10**6 for r in ratios), "implausible leg ratios"

Try / catch

let count = spread_contract_count(symbol).unwrap_or_else(|e| {
    log::error!("overflow for {symbol}: {e}"); Decimal::ONE
});

Prevention

When it happens

Trigger: Calling `get_commission` on a generic spread symbol whose leg ratios sum beyond i64::MAX — practically only possible with pathological/corrupt symbol components carrying huge ratios.

Common situations: Corrupted or adversarially crafted instrument IDs; parsing symbols with extremely large numeric leg components.

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