{"record":{"id":"960bc501a4f4622a","repo":"nautechsystems/nautilus_trader","slug":"invalid-parity-transformed-price-for-ownorderbook","errorCode":null,"errorMessage":"Invalid parity transformed price for OwnOrderBook::combined_with_opposite","messagePattern":"Invalid parity transformed price for OwnOrderBook::combined_with_opposite","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/orderbook/own.rs","lineNumber":556,"sourceCode":"\n        for client_order_id in asks_to_remove {\n            log_audit_error(&client_order_id);\n            if let Err(e) = self.asks.remove(&client_order_id) {\n                log::error!(\"{e}\");\n            }\n        }\n    }\n}\n\nfn log_audit_error(client_order_id: &ClientOrderId) {\n    log::error!(\n        \"Audit error - {client_order_id} absent from valid order IDs, deleting from own book\"\n    );\n}\n\nfn transform_opposite_order(order: OwnBookOrder, side: OrderSide) -> OwnBookOrder {\n    let parity_price = Price::from_decimal(Decimal::ONE - order.price.as_decimal())\n        .expect(\"Invalid parity transformed price for OwnOrderBook::combined_with_opposite\");\n\n    OwnBookOrder::new(\n        order.trader_id,\n        order.client_order_id,\n        order.venue_order_id,\n        side,\n        parity_price,\n        order.size,\n        order.order_type,\n        order.time_in_force,\n        order.status,\n        order.ts_last,\n        order.ts_accepted,\n        order.ts_submitted,\n        order.ts_init,\n    )\n}\n","sourceCodeStart":538,"sourceCodeEnd":574,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/orderbook/own.rs#L538-L574","documentation":"OwnOrderBook::combined_with_opposite transforms each order from the opposite book by mirroring it across the 0.5 price point: parity_price = 1 - price, computed via Price::from_decimal(Decimal::ONE - order.price.as_decimal()).expect(...). This panics when the parity result is invalid for a Price — i.e. when the original price is < 0 or > 1 (making 1-p negative or invalid), NaN, infinite, or violates Price precision. Own books for prediction/binary markets use prices in [0,1], and an out-of-range price violates that domain.","triggerScenarios":"Calling combined_with_opposite when any OwnBookOrder in the opposite book has a price outside the [0,1] domain (or non-finite), so `1 - price` cannot be represented as a valid Price.","commonSituations":"Loading own-book state persisted with raw/out-of-range prices; a bug upstream that stored probabilities not normalized to [0,1]; NaN prices from external math; precision mismatch after Price type changes between versions.","solutions":["Verify every OwnBookOrder price is within 0 <= p <= 1 before calling combined_with_opposite.","Normalize probabilities at ingestion (clamp or reject values outside [0,1] with a clear error) when building the book.","Check the persistence/serialization path for price corruption or unit changes (e.g. stored as percent or fixed-point raw).","Skip and log offending orders instead of combining the whole book, while investigating the data source.","If prices come from a model/strategy, add an assertion where they are produced to catch out-of-domain values early."],"exampleFix":"// before\nlet combined = own_book.combined_with_opposite(other_book); // other_book has price 1.5\n// after\nfor order in other_book.orders() {\n    let p = order.price.as_decimal();\n    assert!(p.is_finite() && p >= Decimal::ZERO && p <= Decimal::ONE,\n        \"own book price {p} outside [0,1]\");\n}\nlet combined = own_book.combined_with_opposite(other_book);","handlingStrategy":"validation","validationCode":"fn in_parity_domain(p: &Price) -> bool {\n    let d = p.as_decimal();\n    d.is_finite() && d >= Decimal::ZERO && d <= Decimal::ONE\n}\nassert!(book.orders().iter().all(|o| in_parity_domain(&o.price)));","typeGuard":"fn is_probability_price(p: &Price) -> bool {\n    let d = p.as_decimal();\n    d.is_finite() && d >= Decimal::ZERO && d <= Decimal::ONE\n}","tryCatchPattern":"// Validate [0,1] domain before combining; the expect panics otherwise. Python:\ntry:\n    combined = own_book.combined_with_opposite(other)\nexcept BaseException as e:\n    log.warning(\"combined_with_opposite failed: %s\", e)","preventionTips":["Normalize/clamp prices to [0,1] at book ingestion.","Verify persisted own-book state for price unit corruption (percent vs probability).","Assert probability domain where strategy/model prices are produced."],"tags":["rust","orderbook","panic","value-out-of-range","decimal"],"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-14T00:17:10.932Z"}