Hire Me
← All Writing Betfair

Exchange Order Types — Limit, LimitOnClose, and MarketOnClose Explained

The three Betfair Exchange order types — Limit, LimitOnClose, and MarketOnClose — what each one does, when to use each, and how to construct them correctly in a Java API client.

The Betfair Exchange supports three order types. Most developers only use Limit orders — the standard back and lay with a specific price. The other two, LimitOnClose and MarketOnClose, participate in the BSP (Betfair Starting Price) pool and have different semantics. Understanding all three, and the time-of-use restrictions on each, prevents order placement errors and enables strategies that use BSP for execution.

Limit orders

A Limit order specifies a price, a size, and a side. It is matched when a counterparty places an opposing order at the same or better price.

LimitOrder limitOrder = new LimitOrder()
    .withSize(10.0)              // stake in GBP
    .withPrice(2.5)              // odds
    .withPersistenceType(PersistenceType.LAPSE);  // cancel at off if unmatched

PersistenceType determines what happens to unmatched remainder at the start of the race:

Value Behaviour
LAPSE Unmatched portion is cancelled at the off
PERSIST Unmatched portion remains as a limit order in-play
MARKET_ON_CLOSE Unmatched portion moves to the BSP pool

LAPSE is the right default for pre-race strategies — you do not want positions accidentally entering in-play.

PlaceInstruction instruction = new PlaceInstruction()
    .withOrderType(OrderType.LIMIT)
    .withSide(Side.BACK)
    .withLimitOrder(limitOrder);

PlaceExecutionReport report = exchange.placeOrders(
    marketId, List.of(instruction), null, null, null);

LimitOnClose orders

A LimitOnClose order specifies a price limit and participates in the BSP pool — but only if the BSP is at or better than your limit. It is the way to say “back this runner at BSP, but only if BSP is 3.0 or better.”

LimitOnCloseOrder loc = new LimitOnCloseOrder()
    .withLiability(10.0)    // maximum stake (back) or liability (lay)
    .withPrice(3.0);        // minimum acceptable BSP for a back; maximum for a lay

PlaceInstruction instruction = new PlaceInstruction()
    .withOrderType(OrderType.LIMIT_ON_CLOSE)
    .withSide(Side.BACK)
    .withLimitOnCloseOrder(loc);

For a back LimitOnClose, price is the minimum BSP — if BSP is 2.8, the bet is not placed. For a lay LimitOnClose, price is the maximum BSP you are willing to lay at.

Key restriction: LimitOnClose orders can only be placed before the market goes in-play. Once in-play has started, attempting to place one returns BET_LAPSED_OFFSIDE or MARKET_NOT_OPEN_FOR_BSP_BETTING.

MarketOnClose orders

A MarketOnClose order participates in the BSP pool with no price limit — you accept whatever BSP the algorithm calculates.

MarketOnCloseOrder moc = new MarketOnCloseOrder()
    .withLiability(10.0);   // stake (back) or liability (lay)

PlaceInstruction instruction = new PlaceInstruction()
    .withOrderType(OrderType.MARKET_ON_CLOSE)
    .withSide(Side.BACK)
    .withMarketOnCloseOrder(moc);

MarketOnClose is the simplest BSP bet. It guarantees execution (assuming there is a valid BSP), but at whatever price the algorithm determines.

Cancelling orders

Only Limit orders can be cancelled before the off. LimitOnClose and MarketOnClose orders cannot be cancelled after submission. This is a hard exchange rule — verify order type before submitting BSP bets.

// Check before cancelling
if (order.getOrderType() == OrderType.LIMIT) {
    exchange.cancelOrders(marketId,
        List.of(new CancelInstruction().withBetId(order.getBetId())),
        null);
} else {
    log.warn("Cannot cancel BSP order {} after submission", order.getBetId());
}

Execution report interpretation

The PlaceExecutionReport status is the first check:

PlaceExecutionReport report = exchange.placeOrders(marketId, instructions, null, null, null);

if (report.getStatus() != ExecutionReportStatus.SUCCESS) {
    throw new OrderPlacementException("Batch failed: " + report.getErrorCode());
}

for (PlaceInstructionReport instr : report.getInstructionReports()) {
    if (instr.getStatus() != InstructionReportStatus.SUCCESS) {
        log.error("Order failed: {} — {}",
            instr.getErrorCode(), instr.getInstruction().getSide());
    } else {
        log.info("Order placed: {} size matched: {}",
            instr.getBetId(), instr.getSizeMatched());
    }
}

Common InstructionReportErrorCode values:

The price ladder

Betfair prices are constrained to the official Betfair price ladder — not every decimal value is valid. The ladder has irregular spacing:

1.01–2.00: increments of 0.01
2.0–3.0:   increments of 0.02
3.0–4.0:   increments of 0.05
4.0–6.0:   increments of 0.1
6.0–10.0:  increments of 0.2
10.0–20.0: increments of 0.5
20.0–30.0: increments of 1.0
30.0–50.0: increments of 2.0
50.0–100:  increments of 5.0
100–1000:  increments of 10.0

Round any price to the nearest valid tick before placing an order:

public static double roundToTick(double price) {
    if (price < 2.0)   return Math.round(price * 100.0) / 100.0;
    if (price < 3.0)   return Math.round(price * 50.0)  / 50.0;
    if (price < 4.0)   return Math.round(price * 20.0)  / 20.0;
    if (price < 6.0)   return Math.round(price * 10.0)  / 10.0;
    if (price < 10.0)  return Math.round(price * 5.0)   / 5.0;
    if (price < 20.0)  return Math.round(price * 2.0)   / 2.0;
    if (price < 30.0)  return Math.round(price);
    if (price < 50.0)  return Math.round(price / 2.0)   * 2.0;
    if (price < 100.0) return Math.round(price / 5.0)   * 5.0;
    return Math.round(price / 10.0) * 10.0;
}

An invalid price returns INVALID_ODDS — a common early mistake.

If you’re building a Betfair order management system in Java and want a review, get in touch.

Samuel Jackson

Samuel Jackson

Senior Java Back End Developer & Contractor

Senior Java Back End Developer — Betfair Exchange API specialist, Spring Boot, AWS, and event-driven architecture. 20+ years delivering high-performance systems across betting, finance, energy, retail, and government. Available for Java contracting.