{"record":{"id":"f2aaa73ffffa6eec","repo":"nautechsystems/nautilus_trader","slug":"limit-order-missing-price","errorCode":null,"errorMessage":"Limit order missing price","messagePattern":"Limit order missing price","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/betfair/src/execution.rs","lineNumber":1778,"sourceCode":"\n        let instrument_id = order.instrument_id();\n        let market_id = extract_market_id(&instrument_id)?;\n        let (selection_id, handicap) = extract_selection_id(&instrument_id)?;\n\n        let side = BetfairSide::from(order.order_side());\n        let size = order.quantity().as_decimal();\n        let handicap_opt = if handicap == Decimal::ZERO {\n            None\n        } else {\n            Some(handicap)\n        };\n        let customer_order_ref = Some(make_customer_order_ref(order.client_order_id().as_str()));\n\n        let instruction = match order.order_type() {\n            OrderType::Limit => {\n                let price = order\n                    .price()\n                    .ok_or_else(|| anyhow::anyhow!(\"Limit order missing price\"))?\n                    .as_decimal();\n\n                // BSP LimitOnClose: participates in starting price calculation\n                // with a price limit, using liability instead of size\n                if matches!(\n                    order.time_in_force(),\n                    TimeInForce::AtTheClose | TimeInForce::AtTheOpen\n                ) {\n                    PlaceInstruction {\n                        order_type: BetfairOrderType::LimitOnClose,\n                        selection_id,\n                        handicap: handicap_opt,\n                        side,\n                        limit_order: None,\n                        limit_on_close_order: Some(LimitOnCloseOrder {\n                            liability: size,\n                            price,\n                        }),","sourceCodeStart":1760,"sourceCodeEnd":1796,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/betfair/src/execution.rs#L1760-L1796","documentation":"Thrown by the Betfair execution client's submit_order when it translates a NautilusTrader order into a Betfair PlaceInstruction. Betfair limit orders are priced bets, so an OrderType::Limit order must carry a Price; order.price() returned None and instruction building aborts before any HTTP request is sent. This is a local validation failure, not a venue rejection.","triggerScenarios":"Calling submit_order with a Limit order whose builder chain never set a price, e.g. OrderFactory/OrderBuilder usage that omits .price(...) or passes None, routed to the Betfair place_orders instruction builder at execution.rs:1778.","commonSituations":"Strategies ported from venues where limit orders can be marketable without an explicit price; test/kit orders built with stub factories that skip price; conditional code that submits before the price is computed.","solutions":["Set the price when building the limit order: .price(Price::new(value, instrument.price_increment)) using the instrument's tick size.","If you meant a BSP (Betfair Starting Price) bet, keep a price but set TimeInForce::AtTheClose or AtTheOpen (LimitOnClose) — the order quantity then acts as liability.","Pre-validate orders in the strategy before submit_order and log client_order_id so bad orders are caught before reaching the adapter."],"exampleFix":"// before\nlet order = factory.limit(\n    instrument_id,\n    side,\n    order_qty,\n) // no price set\n    .build()?;\n\n// after\nlet price = Price::new(\"2.04\", instrument.price_increment);\nlet order = factory.limit(\n    instrument_id,\n    side,\n    order_qty,\n    price,\n).build()?;","handlingStrategy":"validation","validationCode":"fn is_betfair_submit_ready(order: &OrderAny) -> bool {\n    match order.order_type() {\n        OrderType::Limit => order.price().is_some(),\n        OrderType::Market => order.time_in_force() == TimeInForce::AtTheClose,\n        _ => false,\n    }\n}\n\n// before engine.submit_order(&order):\nassert!(is_betfair_submit_ready(&order), \"order {} not valid for BETFAIR\", order.client_order_id());","typeGuard":"fn limit_order_has_price(order: &OrderAny) -> bool {\n    order.order_type() == OrderType::Limit && order.price().is_some()\n}","tryCatchPattern":null,"preventionTips":["Always construct limit orders through the factory with an explicit Price derived from instrument.price_increment.","Log the full order (type, TIF, price, quantity) before submit so invalid orders are traceable.","Run strategy unit tests that assert price presence for every Betfair-bound order path."],"tags":["betfair","order-entry","limit-order","price","validation"],"backgroundTag":"missing-required-field","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}