{"record":{"id":"7512740e309c129b","repo":"nautechsystems/nautilus_trader","slug":"error-unable-to-calculate-midpoint-no-bid-or-a","errorCode":null,"errorMessage":"Error: Unable to calculate `midpoint` (no bid or ask)","messagePattern":"Error: Unable to calculate `midpoint` \\(no bid or ask\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/model/src/ffi/orderbook/book.rs","lineNumber":319,"sourceCode":"/// # Panics\n///\n/// Panics if unable to calculate spread (requires at least one bid and one ask).\n#[unsafe(no_mangle)]\npub extern \"C\" fn orderbook_spread(book: &mut OrderBook) -> f64 {\n    abort_on_panic(|| {\n        book.spread()\n            .expect(\"Error: Unable to calculate `spread` (no bid or ask)\")\n    })\n}\n\n/// # Panics\n///\n/// Panics if unable to calculate midpoint (requires at least one bid and one ask).\n#[unsafe(no_mangle)]\npub extern \"C\" fn orderbook_midpoint(book: &mut OrderBook) -> f64 {\n    abort_on_panic(|| {\n        book.midpoint()\n            .expect(\"Error: Unable to calculate `midpoint` (no bid or ask)\")\n    })\n}\n\n/// # Panics\n///\n/// Panics if `order_side` is `NoOrderSide`.\n#[unsafe(no_mangle)]\n#[cfg_attr(feature = \"high-precision\", allow(improper_ctypes_definitions))]\npub extern \"C\" fn orderbook_get_avg_px_for_quantity(\n    book: &mut OrderBook,\n    qty: Quantity,\n    order_side: OrderSideOptional,\n) -> f64 {\n    book.get_avg_px_for_quantity(\n        qty,\n        order_side\n            .as_option()\n            .expect(\"Order side must be Buy or Sell\"),","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/ffi/orderbook/book.rs#L301-L337","documentation":"This panic fires in the FFI wrapper `orderbook_midpoint` when `OrderBook::midpoint()` returns None because the book does not contain at least one bid and one ask, so the midpoint is undefined. The `abort_on_panic` wrapper turns the panic into a process abort rather than a recoverable FFI error.","triggerScenarios":"Calling `orderbook_midpoint(book)` when the bid or ask side is empty — partial quotes, pre-snapshot books, or books cleared by a reset.","commonSituations":"Warm-up periods before market data lands; one-sided liquidity; data feeds that publish only trades; querying after a `clear` in response to a gap fill.","solutions":["Guard with a check that both bid and ask sides are non-empty before calling the midpoint FFI function.","Defer midpoint calculations until a full two-sided snapshot has been applied.","Compute the midpoint from optional best bid/ask in caller code, returning None when either side is absent.","Ensure subscriptions deliver both bid and ask quotes/depth for the instrument."],"exampleFix":"// before (Python FFI caller)\nmid = orderbook_midpoint(book)\n// after\nif has_bid_orders(book) and has_ask_orders(book):\n    mid = orderbook_midpoint(book)\nelse:\n    mid = None","handlingStrategy":"validation","validationCode":"def can_compute_midpoint(book) -> bool:\n    return book.bids_len() > 0 and book.asks_len() > 0","typeGuard":"def is_two_sided(book) -> bool:\n    return len(book.bids()) > 0 and len(book.asks()) > 0","tryCatchPattern":null,"preventionTips":["Defer midpoint math until both sides are populated","Treat empty books as an expected state and query optional accessors instead of FFI panicking ones","Remember FFI panics abort the process — they cannot be caught as exceptions"],"tags":["rust","ffi","orderbook","panic","midpoint","process-abort"],"backgroundTag":"empty-result-set","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"}