nautechsystems/nautilus_trader · error · anyhow::Error

ratio cannot be zero

Error message

ratio cannot be zero

What it means

A spread leg ratio of zero is invalid: ratios express the hedge/multiplicity of each leg in the combo, and a zero ratio would make the leg contribute nothing while still requiring margin. The adapter bails out rather than producing a malformed spread symbol.

Source

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

///
/// # Errors
///
/// Returns an error if:
/// - Less than 2 legs provided
/// - Any ratio is zero
/// - Venues don't match across legs
pub fn create_spread_instrument_id(
    leg_tuples: &[(InstrumentId, i32)],
) -> anyhow::Result<InstrumentId> {
    if leg_tuples.len() < 2 {
        anyhow::bail!("instrument_ratios list needs to have at least 2 legs");
    }

    let first_venue = leg_tuples[0].0.venue;

    for (instrument_id, ratio) in leg_tuples {
        if *ratio == 0 {
            anyhow::bail!("ratio cannot be zero");
        }

        if instrument_id.venue != first_venue {
            anyhow::bail!(
                "All venues must match. Expected {}, was {}",
                first_venue,
                instrument_id.venue
            );
        }
    }

    let mut sorted_ratios = leg_tuples.to_vec();
    sorted_ratios.sort_by(|a, b| a.0.symbol.as_str().cmp(b.0.symbol.as_str()));

    let symbol_parts = sorted_ratios
        .iter()
        .map(|(instrument_id, ratio)| {
            if *ratio > 0 {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Validate all leg ratios are non-zero before calling create_spread_instrument_id
  2. Check where leg ratios are extracted from the IB contract and fix any default-to-zero path
  3. Correct the ratio values at the source (IB comboLegs should carry ±1 for verticals etc.)

Example fix

// before
let legs = vec![(id1, 0), (id2, 1)];
let spread = create_spread_instrument_id(&legs)?;
// after
let legs = vec![(id1, -1), (id2, 1)]; // short front / long back
let spread = create_spread_instrument_id(&legs)?;
Defensive patterns

Strategy: validation

Validate before calling

if legs.iter().any(|(_, ratio)| *ratio == 0) { return Err(anyhow::anyhow!("zero ratio in spread legs")); }

Try / catch

match create_spread_instrument_id(&legs) {
    Ok(id) => id,
    Err(e) if e.to_string().contains("ratio cannot be zero") => {
        tracing::error!("zero leg ratio, contract data corrupt");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling create_spread_instrument_id with any (instrument_id, 0) tuple — typically from combo legs where ratio was uninitialized, defaulted to 0, or corrupted during deserialization of the IB contract.

Common situations: IB combo legs fetched with missing/zero ratio fields; hand-constructed leg tuples; data conversion bugs that zero out ratios.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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