{"record":{"id":"583ba2f00dd4f623","repo":"nautechsystems/nautilus_trader","slug":"ratio-cannot-be-zero","errorCode":null,"errorMessage":"ratio cannot be zero","messagePattern":"ratio cannot be zero","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/interactive_brokers/src/common/parse.rs","lineNumber":1166,"sourceCode":"///\n/// # Errors\n///\n/// Returns an error if:\n/// - Less than 2 legs provided\n/// - Any ratio is zero\n/// - Venues don't match across legs\npub fn create_spread_instrument_id(\n    leg_tuples: &[(InstrumentId, i32)],\n) -> anyhow::Result<InstrumentId> {\n    if leg_tuples.len() < 2 {\n        anyhow::bail!(\"instrument_ratios list needs to have at least 2 legs\");\n    }\n\n    let first_venue = leg_tuples[0].0.venue;\n\n    for (instrument_id, ratio) in leg_tuples {\n        if *ratio == 0 {\n            anyhow::bail!(\"ratio cannot be zero\");\n        }\n\n        if instrument_id.venue != first_venue {\n            anyhow::bail!(\n                \"All venues must match. Expected {}, was {}\",\n                first_venue,\n                instrument_id.venue\n            );\n        }\n    }\n\n    let mut sorted_ratios = leg_tuples.to_vec();\n    sorted_ratios.sort_by(|a, b| a.0.symbol.as_str().cmp(b.0.symbol.as_str()));\n\n    let symbol_parts = sorted_ratios\n        .iter()\n        .map(|(instrument_id, ratio)| {\n            if *ratio > 0 {","sourceCodeStart":1148,"sourceCodeEnd":1184,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/interactive_brokers/src/common/parse.rs#L1148-L1184","documentation":"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.","triggerScenarios":"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.","commonSituations":"IB combo legs fetched with missing/zero ratio fields; hand-constructed leg tuples; data conversion bugs that zero out ratios.","solutions":["Validate all leg ratios are non-zero before calling create_spread_instrument_id","Check where leg ratios are extracted from the IB contract and fix any default-to-zero path","Correct the ratio values at the source (IB comboLegs should carry ±1 for verticals etc.)"],"exampleFix":"// before\nlet legs = vec![(id1, 0), (id2, 1)];\nlet spread = create_spread_instrument_id(&legs)?;\n// after\nlet legs = vec![(id1, -1), (id2, 1)]; // short front / long back\nlet spread = create_spread_instrument_id(&legs)?;","handlingStrategy":"validation","validationCode":"if legs.iter().any(|(_, ratio)| *ratio == 0) { return Err(anyhow::anyhow!(\"zero ratio in spread legs\")); }","typeGuard":null,"tryCatchPattern":"match create_spread_instrument_id(&legs) {\n    Ok(id) => id,\n    Err(e) if e.to_string().contains(\"ratio cannot be zero\") => {\n        tracing::error!(\"zero leg ratio, contract data corrupt\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate ratios are non-zero (typically ±1 for verticals, ±1/N for ratios) before building spreads","Check IB comboLeg deserialization for zero-defaulting ratio fields","Log leg tuples on failure to spot data corruption early"],"tags":["rust","options-spreads","validation","interactive-brokers"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}