{"record":{"id":"a25f756dd520eabe","repo":"nautechsystems/nautilus_trader","slug":"failed-to-convert-avg-fill-price-to-decimal-conv","errorCode":null,"errorMessage":"Failed to convert avg fill price to Decimal: {converted_avg_price}","messagePattern":"Failed to convert avg fill price to Decimal: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/interactive_brokers/src/execution/core_updates.rs","lineNumber":935,"sourceCode":"        let is_spread_order = is_spread_instrument_id(instrument_id);\n        if filled <= 0.0 || !parse::should_use_avg_fill_price(avg_fill_price, instrument_id) {\n            return Ok(());\n        }\n\n        let Some(instrument) = instrument_provider.find(instrument_id) else {\n            return Ok(());\n        };\n\n        let price_magnifier = instrument_provider.get_price_magnifier(instrument_id) as f64;\n        let converted_avg_price = avg_fill_price * price_magnifier;\n        let avg_px = Price::new(converted_avg_price, instrument.price_precision());\n\n        order_avg_prices.lock().insert(client_order_id, avg_px);\n\n        let filled_decimal = Decimal::from_f64_retain(filled)\n            .ok_or_else(|| anyhow::anyhow!(\"Failed to convert filled qty to Decimal: {filled}\"))?;\n        let avg_decimal = Decimal::from_f64_retain(converted_avg_price).ok_or_else(|| {\n            anyhow::anyhow!(\"Failed to convert avg fill price to Decimal: {converted_avg_price}\")\n        })?;\n\n        let mut progress = order_fill_progress.lock();\n        let (previous_filled, previous_notional) = progress\n            .get(&client_order_id)\n            .copied()\n            .unwrap_or((Decimal::ZERO, Decimal::ZERO));\n        let total_notional = filled_decimal * avg_decimal;\n        progress.insert(client_order_id, (filled_decimal, total_notional));\n        drop(progress);\n\n        let fill_delta = filled_decimal - previous_filled;\n        if fill_delta <= Decimal::ZERO || !is_spread_order {\n            return Ok(());\n        }\n\n        let notional_delta = total_notional - previous_notional;\n        let partial_avg_decimal = notional_delta / fill_delta;","sourceCodeStart":917,"sourceCodeEnd":953,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/interactive_brokers/src/execution/core_updates.rs#L917-L953","documentation":"The adapter converts the IB average fill price (already scaled by the instrument's price magnifier) into a Decimal with from_f64_retain. If the computed avg fill price is NaN or infinite, the conversion returns None and this error is thrown, preventing corrupt notional tracking in order_fill_progress.","triggerScenarios":"update_order_avg_price computes converted_avg_price = avg_fill_price * price_magnifier; if avg_fill_price is NaN/inf (or the magnifier is bogus producing inf), Decimal::from_f64_retain fails and the error is raised.","commonSituations":"IB reports an avg fill price of NaN (e.g. certain combo or what-if executions); wrong price_magnifier configured for the instrument causing overflow to infinity.","solutions":["Validate avg_fill_price is finite and positive before multiplying by the magnifier.","Verify the instrument's price_magnifier matches the IB contract definition (e.g. 100 for US futures).","Log the raw IB execution report when triggered to find the malformed price source."],"exampleFix":"// before\nlet converted_avg_price = avg_fill_price * price_magnifier;\n// after\nif !avg_fill_price.is_finite() || avg_fill_price <= 0.0 {\n    tracing::warn!(\"Skipping avg price update, invalid avg fill price: {avg_fill_price}\");\n    return Ok(());\n}\nlet converted_avg_price = avg_fill_price * price_magnifier;","handlingStrategy":"validation","validationCode":"// Rust: validate avg fill price and magnifier before scaling\nfn valid_avg_price(avg_fill_price: f64, magnifier: f64) -> bool {\n    avg_fill_price.is_finite()\n        && avg_fill_price > 0.0\n        && magnifier.is_finite()\n        && magnifier > 0.0\n        && (avg_fill_price * magnifier).is_finite()\n}","typeGuard":"fn is_representable_decimal(x: f64) -> bool {\n    x.is_finite() && Decimal::from_f64_retain(x).is_some()\n}","tryCatchPattern":"let avg_decimal = match Decimal::from_f64_retain(converted_avg_price) {\n    Some(d) => d,\n    None => {\n        tracing::error!(\"Invalid avg fill price {converted_avg_price}; skipping update\");\n        return Ok(());\n    }\n};","preventionTips":["Verify each instrument's price_magnifier against the IB contract specification","Validate f64 prices from IB reports before arithmetic","Add unit tests for NaN/inf fill prices in adapter tests"],"tags":["decimal","conversion","nan","price","rust"],"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-14T00:17:10.932Z"}