{"record":{"id":"b269551e4d5c58af","repo":"nautechsystems/nautilus_trader","slug":"failed-to-convert-decimal-to-f64","errorCode":null,"errorMessage":"Failed to convert Decimal to f64","messagePattern":"Failed to convert Decimal to f64","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/position.rs","lineNumber":672,"sourceCode":"    ///\n    /// # Panics\n    ///\n    /// Panics if the adjustment's `quantity_change` cannot be converted to f64.\n    pub fn apply_adjustment(&mut self, adjustment: PositionAdjusted) {\n        self.apply_adjustment_state(adjustment, true);\n    }\n\n    fn apply_adjustment_state(&mut self, adjustment: PositionAdjusted, record_replay: bool) {\n        if record_replay {\n            self.replay_events\n                .push(PositionReplayEvent::Adjusted(adjustment));\n        }\n\n        // Apply quantity change if present\n        if let Some(quantity_change) = adjustment.quantity_change {\n            self.signed_qty += quantity_change\n                .to_f64()\n                .expect(\"Failed to convert Decimal to f64\");\n\n            self.quantity = Quantity::new(self.signed_qty.abs(), self.size_precision);\n\n            if self.quantity > self.peak_qty {\n                self.peak_qty = self.quantity;\n            }\n        }\n\n        // Apply PnL change if present\n        if let Some(pnl_change) = adjustment.pnl_change {\n            self.realized_pnl = Some(match self.realized_pnl {\n                Some(current) => current + pnl_change,\n                None => pnl_change,\n            });\n        }\n\n        // Update position state based on quantity (source of truth for zero check)\n        // This handles floating-point precision edge cases","sourceCodeStart":654,"sourceCodeEnd":690,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/position.rs#L654-L690","documentation":"Position adjustments carry an optional quantity_change as a Decimal; apply_adjustment_state converts it to f64 to update signed_qty and panics if the Decimal cannot be represented (e.g. beyond f64 range). Conversion to f64 fails only for extreme magnitudes since Decimal supports far wider ranges.","triggerScenarios":"Applying a PositionAdjustment (via apply_adjustment, purge_events_for_order, rebuild_from_replay, or apply_base_commission_adjustment) whose quantity_change is too large for f64 — astronomically large position sizes/quantities.","commonSituations":"Unit bugs creating adjustments with raw/unsealed Decimal values (e.g. passing raw scaled integers as quantity_change); corrupted adjustment data from external reconciliation; wrong scale/precision when constructing the Decimal.","solutions":["Sanity-check adjustment.quantity_change magnitude before applying (must be within f64 range, realistically within the instrument's size limits)","Construct quantity_change from a proper Quantity/domain type rather than an arbitrary Decimal","Use try_from-style conversion with explicit error handling in custom adjustment code instead of expect"],"exampleFix":"// before\nself.signed_qty += quantity_change.to_f64().expect(\"Failed to convert Decimal to f64\");\n// after\nlet delta = quantity_change.to_f64().filter(|v| v.is_finite()).ok_or_else(|| anyhow::anyhow!(\"quantity_change out of range\"))?;\nself.signed_qty += delta;","handlingStrategy":"validation","validationCode":"// Rust: before apply_adjustment\nlet q = adjustment.quantity_change.map(|d| d.to_f64());\nif let Some(Some(v)) = q { assert!(v.is_finite() && v.abs() < 1e15, \"adjustment out of range\"); }","typeGuard":"fn valid_qty_change(d: &Decimal) -> Option<f64> { d.to_f64().filter(|v| v.is_finite()) }","tryCatchPattern":null,"preventionTips":["Build quantity_change from domain Quantity types, not arbitrary Decimals","Bound-check adjustments against instrument size limits before applying","Validate adjustment data coming from external reconciliation feeds"],"tags":["rust","panic","decimal","positions"],"backgroundTag":"value-out-of-range","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"}