nautechsystems/nautilus_trader · error

Invalid generic spread leg component: {component}

Error message

Invalid generic spread leg component: {component}

What it means

For generic spread symbols, `spread_contract_count` splits the symbol on the generic spread ID separator and parses each component into a leg ratio. This error fires when a component (e.g. a price or non-numeric segment) cannot be interpreted as a valid spread leg ratio.

Source

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

        let contracts = spread_contract_count(instrument)?;
        let total = mul_checked(self.commission.as_decimal(), fill_quantity.as_decimal())
            .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)
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the symbol format: each separator-delimited component must encode a valid leg ratio.
  2. Build generic spread symbols only via the library's instrument/symbol factories rather than string concatenation.
  3. Print the failing component (included in the message) and correct that segment.

Example fix

// before
let symbol = "ES-ES.N.202512-ES.H.202603"; // wrong components
// after: use the canonical generic spread ID format with valid leg components
let symbol = instrument_id.symbol.as_str(); // from a GenericSpreadInstrument
Defensive patterns

Strategy: validation

Validate before calling

components = symbol.split(GENERIC_SPREAD_ID_SEPARATOR)
assert all(spread_leg_ratio(c) is not None for c in components), f"bad component in {symbol}"

Type guard

fn is_valid_spread_symbol(symbol: &str) -> bool {
    symbol.split(GENERIC_SPREAD_ID_SEPARATOR).all(|c| spread_leg_ratio(c).is_some())
}

Try / catch

let total = match spread_contract_count(symbol) {
    Ok(n) => n,
    Err(e) => { log::warn!("bad spread symbol {symbol}: {e}"); return; }
};

Prevention

When it happens

Trigger: Calling `get_commission` on a generic spread instrument whose symbol contains a component that `spread_leg_ratio` cannot parse — malformed or non-standard generic spread symbol format.

Common situations: Constructing generic spread instrument IDs by hand with the wrong separator/format; symbols copied from another venue with different conventions; legacy symbol formats after a version change.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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