nautechsystems/nautilus_trader · error
Limit order missing price
Error message
Limit order missing price
What it means
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.
Source
Thrown at crates/adapters/betfair/src/execution.rs:1778
let instrument_id = order.instrument_id();
let market_id = extract_market_id(&instrument_id)?;
let (selection_id, handicap) = extract_selection_id(&instrument_id)?;
let side = BetfairSide::from(order.order_side());
let size = order.quantity().as_decimal();
let handicap_opt = if handicap == Decimal::ZERO {
None
} else {
Some(handicap)
};
let customer_order_ref = Some(make_customer_order_ref(order.client_order_id().as_str()));
let instruction = match order.order_type() {
OrderType::Limit => {
let price = order
.price()
.ok_or_else(|| anyhow::anyhow!("Limit order missing price"))?
.as_decimal();
// BSP LimitOnClose: participates in starting price calculation
// with a price limit, using liability instead of size
if matches!(
order.time_in_force(),
TimeInForce::AtTheClose | TimeInForce::AtTheOpen
) {
PlaceInstruction {
order_type: BetfairOrderType::LimitOnClose,
selection_id,
handicap: handicap_opt,
side,
limit_order: None,
limit_on_close_order: Some(LimitOnCloseOrder {
liability: size,
price,
}),View on GitHub (pinned to a4b06ed870)
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.
Example fix
// before
let order = factory.limit(
instrument_id,
side,
order_qty,
) // no price set
.build()?;
// after
let price = Price::new("2.04", instrument.price_increment);
let order = factory.limit(
instrument_id,
side,
order_qty,
price,
).build()?; Defensive patterns
Strategy: validation
Validate before calling
fn is_betfair_submit_ready(order: &OrderAny) -> bool {
match order.order_type() {
OrderType::Limit => order.price().is_some(),
OrderType::Market => order.time_in_force() == TimeInForce::AtTheClose,
_ => false,
}
}
// before engine.submit_order(&order):
assert!(is_betfair_submit_ready(&order), "order {} not valid for BETFAIR", order.client_order_id()); Type guard
fn limit_order_has_price(order: &OrderAny) -> bool {
order.order_type() == OrderType::Limit && order.price().is_some()
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- height must be positive, was {self.height}
- pandas is required for report generation; install it with `p
- {name} must not be None
- Chart name cannot be empty
- Chart function must be callable, was {type(func)}
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/f2aaa73ffffa6eec.
Report an issue: GitHub.