{"record":{"id":"1bb2fc85597e21ec","repo":"nautechsystems/nautilus_trader","slug":"pending-position-quantity-overflow","errorCode":null,"errorMessage":"Pending position quantity overflow","messagePattern":"Pending position quantity overflow","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/execution/src/matching_engine/mod.rs","lineNumber":5089,"sourceCode":"    }\n\n    fn position_quantity_remaining(\n        &mut self,\n        order: &OrderAny,\n        position: &Position,\n    ) -> anyhow::Result<Quantity> {\n        self.purge_applied_fills();\n        let mut quantity = match position.side {\n            PositionSide::Long => position.quantity.as_decimal(),\n            PositionSide::Short => -position.quantity.as_decimal(),\n            PositionSide::Flat => Decimal::ZERO,\n        };\n\n        for fill in self.pending_fills.values() {\n            if fill.position_id == Some(position.id) {\n                quantity = quantity\n                    .checked_add(fill.quantity_change)\n                    .ok_or_else(|| anyhow::anyhow!(\"Pending position quantity overflow\"))?;\n            }\n        }\n\n        if (order.is_buy() && quantity >= Decimal::ZERO)\n            || (order.is_sell() && quantity <= Decimal::ZERO)\n        {\n            return Ok(Quantity::zero(position.quantity.precision));\n        }\n        Ok(Quantity::from_decimal_dp(\n            quantity.abs(),\n            position.quantity.precision,\n        )?)\n    }\n\n    fn purge_applied_fills(&mut self) {\n        let cache = self.cache.borrow();\n        self.pending_fills.retain(|trade_id, fill| {\n            fill.position_id = fill","sourceCodeStart":5071,"sourceCodeEnd":5107,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/execution/src/matching_engine/mod.rs#L5071-L5107","documentation":"When computing a position's quantity for an order (used to decide whether the order closes or opens the position), the matching engine starts from the position's current quantity and checked-adds the quantity_change of every pending fill belonging to that position. If the accumulated Decimal overflows its maximum precision/width, the engine raises this error instead of producing a wrong signed quantity.","triggerScenarios":"Summing position.quantity with many pending fills' quantity_change values whose total exceeds the Decimal max (e.g. extremely large order quantities combined with high precision, or a runaway loop creating unbounded pending fills against one position).","commonSituations":"Backtests with unrealistic quantity magnitudes or very high price precision instruments; a bug causing pending_fills to accumulate without being drained; OCO/contingent logic repeatedly re-adding fills for the same position.","solutions":["Inspect pending_fills for the position and confirm they are being consumed/drained after each match cycle.","Reduce order quantity or precision so accumulated quantities fit within the Decimal max supported (max ~18-20 digits with configured precision).","Check for logic that re-queues the same fill repeatedly (e.g. reduce-only or OCO handling) and fix the duplication.","Sanitize instrument quantity/precision configuration so position quantity plus pending fills stays in representable range."],"exampleFix":"// before: accumulating pending fills without bound\nlet mut quantity = position.quantity();\nfor fill in pending_fills.values() { quantity = quantity.checked_add(fill.quantity_change).expect(\"overflow\"); }\n// after: cap/validate incoming order quantity against representable range\nassert!(order.quantity() <= max_representable_qty(instrument), \"quantity too large\");\nlet mut quantity = position.quantity();","handlingStrategy":"validation","validationCode":"// keep order quantities within a range that survives accumulation with pending fills\nlet max_qty = Decimal::from_str(\"999999999999999999\")?; // leave headroom below Decimal max\nassert!(order.quantity() <= max_qty, \"order quantity risks position quantity overflow\");","typeGuard":null,"tryCatchPattern":"match engine_result {\n    Err(e) if e.to_string().contains(\"Pending position quantity overflow\") => {\n        log::error!(\"quantity overflow computing position for {}: {e}\", order.client_order_id());\n        // cancel the order and investigate pending_fills accumulation\n    }\n    other => other,\n}","preventionTips":["Use realistic quantity magnitudes and instrument precision in backtests.","Verify pending_fills are drained each match cycle when writing custom order handling.","Sanity-check instrument size precision in config before large runs."],"tags":["matching-engine","decimal-overflow","position-quantity","checked-arithmetic"],"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"}