{"record":{"id":"bfe29e25511aadf9","repo":"nautechsystems/nautilus_trader","slug":"invalid-generic-spread-leg-component-component","errorCode":null,"errorMessage":"Invalid generic spread leg component: {component}","messagePattern":"Invalid generic spread leg component: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/execution/src/models/fee.rs","lineNumber":355,"sourceCode":"        let contracts = spread_contract_count(instrument)?;\n        let total = mul_checked(self.commission.as_decimal(), fill_quantity.as_decimal())\n            .and_then(|v| mul_checked(v, contracts))?;\n        Money::from_decimal(total, self.commission.currency).map_err(Into::into)\n    }\n}\n\nfn spread_contract_count(instrument: &InstrumentAny) -> anyhow::Result<Decimal> {\n    let instrument_id = instrument.id();\n    let symbol = instrument_id.symbol.as_str();\n    if !instrument.is_spread() || !symbol.contains(GENERIC_SPREAD_ID_SEPARATOR) {\n        return Ok(Decimal::ONE);\n    }\n\n    let mut total = 0_i64;\n\n    for component in symbol.split(GENERIC_SPREAD_ID_SEPARATOR) {\n        let ratio = spread_leg_ratio(component)\n            .ok_or_else(|| anyhow::anyhow!(\"Invalid generic spread leg component: {component}\"))?;\n        total = total.checked_add(ratio).ok_or_else(|| {\n            anyhow::anyhow!(\"Generic spread contract count overflowed for {symbol}\")\n        })?;\n    }\n\n    Ok(total.into())\n}\n\nfn spread_leg_ratio(component: &str) -> Option<i64> {\n    if let Some(rest) = component.strip_prefix(\"((\") {\n        let (ratio, symbol) = rest.split_once(\"))\")?;\n        return spread_leg_ratio_parts(ratio, symbol);\n    }\n\n    let rest = component.strip_prefix('(')?;\n    let (ratio, symbol) = rest.split_once(')')?;\n    spread_leg_ratio_parts(ratio, symbol)\n}","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/execution/src/models/fee.rs#L337-L373","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the symbol format: each separator-delimited component must encode a valid leg ratio.","Build generic spread symbols only via the library's instrument/symbol factories rather than string concatenation.","Print the failing component (included in the message) and correct that segment."],"exampleFix":"// before\nlet symbol = \"ES-ES.N.202512-ES.H.202603\"; // wrong components\n// after: use the canonical generic spread ID format with valid leg components\nlet symbol = instrument_id.symbol.as_str(); // from a GenericSpreadInstrument","handlingStrategy":"validation","validationCode":"components = symbol.split(GENERIC_SPREAD_ID_SEPARATOR)\nassert all(spread_leg_ratio(c) is not None for c in components), f\"bad component in {symbol}\"","typeGuard":"fn is_valid_spread_symbol(symbol: &str) -> bool {\n    symbol.split(GENERIC_SPREAD_ID_SEPARATOR).all(|c| spread_leg_ratio(c).is_some())\n}","tryCatchPattern":"let total = match spread_contract_count(symbol) {\n    Ok(n) => n,\n    Err(e) => { log::warn!(\"bad spread symbol {symbol}: {e}\"); return; }\n};","preventionTips":["Construct generic spread symbols via library factories, not manual strings.","Validate symbol format at instrument registration time.","Note the failing component in the message when debugging format issues."],"tags":["symbol","spread","fee","parsing","rust"],"backgroundTag":"invalid-argument-format","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"}