nautechsystems/nautilus_trader · error
Market orders on Betfair are only supported with AtTheClose
Error message
Market orders on Betfair are only supported with AtTheClose time in force (BSP MarketOnClose)
What it means
Betfair has no immediate market order that sweeps the book; the only market-style bet is the BSP (Betfair Starting Price) MarketOnClose bet accepted before the market closes. The adapter therefore accepts OrderType::Market only when time_in_force is AtTheClose, mapping it to a MarketOnCloseOrder whose liability equals the order quantity. Any other TIF on a market order is rejected locally before the request reaches Betfair.
Source
Thrown at crates/adapters/betfair/src/execution.rs:1835
side,
limit_order: Some(LimitOrder {
size,
price,
persistence_type,
time_in_force,
min_fill_size,
bet_target_type: None,
bet_target_size: None,
}),
limit_on_close_order: None,
market_on_close_order: None,
customer_order_ref,
}
}
}
OrderType::Market => {
if order.time_in_force() != TimeInForce::AtTheClose {
anyhow::bail!(
"Market orders on Betfair are only supported with AtTheClose \
time in force (BSP MarketOnClose)"
);
}
PlaceInstruction {
order_type: BetfairOrderType::MarketOnClose,
selection_id,
handicap: handicap_opt,
side,
limit_order: None,
limit_on_close_order: None,
market_on_close_order: Some(MarketOnCloseOrder { liability: size }),
customer_order_ref,
}
}
other => {
anyhow::bail!("Unsupported order type for Betfair: {other:?}");
}View on GitHub (pinned to a4b06ed870)
Solutions
- Set .time_in_force(TimeInForce::AtTheClose) on the market order and treat its quantity as BSP liability.
- For immediate execution, place a Limit order priced through the current best available level instead of a market order.
- Gate market-order logic in shared strategy code when instrument_id.venue is BETFAIR.
Example fix
// before
let order = factory.market(
instrument_id,
side,
qty,
).build()?; // default TIF = GTC -> rejected
// after
let order = factory.market(
instrument_id,
side,
qty,
).time_in_force(TimeInForce::AtTheClose)
.build()?; Defensive patterns
Strategy: validation
Validate before calling
fn market_order_tif_ok(order: &OrderAny) -> bool {
order.order_type() != OrderType::Market
|| order.time_in_force() == TimeInForce::AtTheClose
}
if !market_order_tif_ok(&order) {
log::error!("Market order {} needs AtTheClose TIF for Betfair BSP", order.client_order_id());
return;
} Type guard
fn is_bsp_market_order(order: &OrderAny) -> bool {
order.order_type() == OrderType::Market
&& order.time_in_force() == TimeInForce::AtTheClose
} Prevention
- Wrap Betfair market-order creation in one helper that always sets AtTheClose.
- Remember quantity on BSP orders is liability, not stake — size accordingly.
- Keep venue-specific order builders instead of sharing one generic builder across venues.
When it happens
Trigger: Submitting a Market order with the default GTC (or Day/IOC/FOK) time in force via submit_order; the check at execution.rs:1835 fails and the instruction is never built.
Common situations: Strategies reused from crypto/equity adapters where plain market orders work and backtests pass; forgetting Betfair's only 'market' semantics is BSP; submitting after the market has gone in-play where BSP is unavailable.
Related errors
- missing persistence type for order update {}
- height must be positive, was {self.height}
- pandas is required for report generation; install it with `p
- pandas is required for visualization; install it with `pip i
- {name} must not be None
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/ac05f88e551870fc.
Report an issue: GitHub.