nautechsystems/nautilus_trader · error · anyhow::Error

Invalid spread symbol format for component: {component}

Error message

Invalid spread symbol format for component: {component}

What it means

parse_spread_instrument_id_to_legs decodes a spread instrument ID symbol whose components are encoded leg strings like 'SYMBOL:RATIO'; a component that matches neither the expected leg pattern nor a recognized special case produces this error.

Source

Thrown at crates/adapters/interactive_brokers/src/common/parse.rs:1253

            }
        }

        // Check for positive ratio: (ratio)symbol
        if let Some(rest) = component.strip_prefix('(')
            && let Some(pos) = rest.find(')')
        {
            let ratio_str = &rest[..pos];
            let symbol_value = &rest[pos + 1..];

            if let Ok(ratio) = ratio_str.parse::<i32>() {
                let leg_instrument_id =
                    InstrumentId::new(NautilusSymbol::from(symbol_value), venue);
                result.push((leg_instrument_id, ratio));
                continue;
            }
        }

        anyhow::bail!("Invalid spread symbol format for component: {component}");
    }

    // Sort result alphabetically by symbol
    result.sort_by(|a, b| a.0.symbol.as_str().cmp(b.0.symbol.as_str()));

    Ok(result)
}

#[cfg(test)]
mod tests {
    use ibapi::contracts::{Contract, Currency, Exchange, OptionRight, SecurityType, Symbol};
    use nautilus_model::identifiers::InstrumentId;
    use rstest::rstest;

    use super::{
        exchange_to_mic_venue, ib_contract_to_instrument_id_simplified,
        instrument_id_to_ib_contract, possible_exchanges_for_venue,
    };

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Construct spread instrument IDs with create_spread_instrument_id instead of manual strings
  2. Check the failing component has the expected 'SYMBOL:RATIO' encoding with an integer ratio
  3. Log/print the full spread symbol and compare each component against the parser's expected format
  4. If a legacy format is involved, regenerate the spread ID in the current format

Example fix

// before
let id = InstrumentId::from("AAPL251212C00250000-MSFT.OPT"); // no :ratio
// after
let id = create_spread_instrument_id(&[
    (InstrumentId::from("AAPL  251212C00250000.OPRA"), -1),
    (InstrumentId::from("AAPL  251212P00250000.OPRA"), 1),
])?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn looks_like_leg(c: &str) -> bool {
    let mut parts = c.rsplitn(2, ':');
    match (parts.next(), parts.next()) {
        (Some(ratio), Some(_)) => ratio.parse::<i32>().is_ok(),
        _ => false,
    }
}

Try / catch

match parse_spread_instrument_id_to_legs(&spread_id) {
    Ok(legs) => legs,
    Err(e) if e.to_string().starts_with("Invalid spread symbol format") => {
        tracing::error!("malformed spread symbol {}: {e}", spread_id.symbol);
        Vec::new()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling parse_spread_instrument_id_to_legs (directly or via fetch_spread_instrument) with a manually constructed or malformed spread symbol, e.g. missing the ':ratio' part, non-numeric ratio, or extra separators.

Common situations: Hand-writing spread symbols instead of using create_spread_instrument_id; symbols mutated/truncated by other tooling; legacy spread symbol formats no longer recognized.

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