{"record":{"id":"fc8516246758baca","repo":"nautechsystems/nautilus_trader","slug":"invalid-bid-quantity-for-orderbook-filtered-view","errorCode":null,"errorMessage":"Invalid bid quantity for OrderBook::filtered_view","messagePattern":"Invalid bid quantity for OrderBook::filtered_view","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/orderbook/book.rs","lineNumber":825,"sourceCode":"        let mut filtered_book = Self::new(self.instrument_id, self.book_type);\n        filtered_book.sequence = self.sequence;\n        filtered_book.ts_last = self.ts_last;\n\n        let sequence = self.sequence;\n        let ts_event = self.ts_last;\n\n        let mut order_id = 1_u64;\n\n        for (price, quantity) in bids_map {\n            if quantity <= Decimal::ZERO {\n                continue;\n            }\n\n            let order = BookOrder::new(\n                OrderSide::Buy,\n                Price::from_decimal(price).expect(\"Invalid bid price for OrderBook::filtered_view\"),\n                Quantity::from_decimal(quantity)\n                    .expect(\"Invalid bid quantity for OrderBook::filtered_view\"),\n                order_id,\n            );\n            order_id += 1;\n            filtered_book.add(order, 0, sequence, ts_event);\n        }\n\n        for (price, quantity) in asks_map {\n            if quantity <= Decimal::ZERO {\n                continue;\n            }\n\n            let order = BookOrder::new(\n                OrderSide::Sell,\n                Price::from_decimal(price).expect(\"Invalid ask price for OrderBook::filtered_view\"),\n                Quantity::from_decimal(quantity)\n                    .expect(\"Invalid ask quantity for OrderBook::filtered_view\"),\n                order_id,\n            );","sourceCodeStart":807,"sourceCodeEnd":843,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/orderbook/book.rs#L807-L843","documentation":"In OrderBook::filtered_view, each synthetic bid quantity is built with Quantity::from_decimal(...).expect(...). Quantity::from_decimal returns None when the decimal is negative, non-finite, or exceeds the representable precision/range, and the expect converts that into this panic. Zero/negative quantities are already skipped by the `quantity <= 0` check, so this fires for non-finite or precision/range violations.","triggerScenarios":"Calling filtered_view / py_filtered_view with a bids map whose quantity is NaN, infinite, or has more decimal places than the Quantity precision supports (e.g. unrounded floating-point results like 0.30000000000000004 at high precision).","commonSituations":"Feeding raw float math results into the quantity map; constructing quantities from strings with excessive precision; instrument precision changed but quantities not re-normalized.","solutions":["Validate quantities are finite and round them to the instrument size precision before calling filtered_view.","Reuse the same Quantity normalization used elsewhere in the pipeline (from_raw / fixed-point conversion) rather than ad-hoc from_decimal.","Filter out non-finite quantity entries when building the bids map.","Sanitize external input (JSON/config) at parse time, rejecting non-finite values.","Keep quantity precision aligned with the instrument definition whenever it changes."],"exampleFix":"// before\nlet qty = Quantity::from_decimal(Decimal::from_f64_retain(raw_qty).unwrap()); // may exceed precision\n// after\nlet qty_dec = Decimal::from_f64_retain(raw_qty).unwrap().round_dp(instrument.size_precision as u32);\nassert!(qty_dec.is_finite() && qty_dec.is_sign_positive());\nlet qty = Quantity::from_decimal(qty_dec);","handlingStrategy":"validation","validationCode":"fn valid_qty(q: &Quantity) -> bool {\n    let d = q.as_decimal();\n    d.is_finite() && d.is_sign_positive()\n}\nassert!(map.iter().all(|(_, q)| valid_qty(q)));","typeGuard":"fn is_valid_quantity(q: &Quantity) -> bool {\n    q.as_decimal().is_finite() && q.as_decimal().is_sign_positive()\n}","tryCatchPattern":"// Validate quantities before filtered_view; panics aren't catchable in Rust. Python:\ntry:\n    view = book.filtered_view(bids, asks, None)\nexcept BaseException as e:\n    log.warning(\"filtered_view failed: %s\", e)","preventionTips":["Round quantities to instrument size precision.","Avoid raw f64 -> Decimal conversions without finiteness checks.","Keep size precision synced with instrument definitions."],"tags":["rust","orderbook","panic","invalid-argument-value","decimal"],"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"}