Hire Me
← All Writing Betfair

Each-Way Betting — Place Markets, Price Structure, and API Differences in Java

How each-way betting works on Betfair Exchange, how place markets differ from win markets in the API, how to identify them programmatically, and what this means for price analysis.

Each-way betting splits a bet between two markets: a win bet at full odds and a place bet at a fraction of the win odds. On Betfair Exchange, these are separate markets — the win market and the place market — rather than a combined bet. Understanding how to identify them, how their prices relate, and how the exchange calculates place terms is essential for any strategy that spans both markets or analyses implied probabilities.

Win and place markets on the Exchange

On Betfair, a traditional each-way bet from a bookmaker is represented as two independent Exchange markets:

They share the same event but have separate market IDs, separate order books, and settle independently.

Identifying markets via the API

When cataloguing markets, filter by marketType to find both:

MarketFilter filter = new MarketFilter()
    .withEventTypeIds(Set.of("7"))           // horse racing
    .withMarketTypes(Set.of("MATCH_ODDS", "PLACE"))
    .withMarketCountries(Set.of("GB"))
    .withBspOnly(false)
    .withMarketStartTime(new TimeRange()
        .withFrom(Date.from(Instant.now()))
        .withTo(Date.from(Instant.now().plus(Duration.ofHours(2)))));

List<MarketCatalogue> catalogues = exchange.listMarketCatalogue(
    filter,
    Set.of(MarketProjection.MARKET_DESCRIPTION, MarketProjection.RUNNER_METADATA,
           MarketProjection.EVENT),
    null,
    100
);

Group by event ID to pair win and place markets for the same race:

Map<String, List<MarketCatalogue>> byEvent = catalogues.stream()
    .collect(Collectors.groupingBy(mc -> mc.getEvent().getId()));

byEvent.forEach((eventId, markets) -> {
    Optional<MarketCatalogue> win   = markets.stream()
        .filter(m -> "MATCH_ODDS".equals(m.getDescription().getMarketType()))
        .findFirst();
    Optional<MarketCatalogue> place = markets.stream()
        .filter(m -> "PLACE".equals(m.getDescription().getMarketType()))
        .findFirst();

    win.ifPresent(w -> place.ifPresent(p -> processPair(w, p)));
});

Place terms

The number of places paid and the fraction paid on each place are in the market description:

MarketDescription desc = catalogue.getDescription();
int numberOfWinners     = desc.getNumberOfWinners();   // places paid
double eachWayDivisor   = desc.getEachWayDivisor();    // e.g. 4.0 = 1/4 odds

For a 5-runner race, typically 2 places at 1/4 odds. For a 16-runner handicap, 4 places at 1/4. For a 7-runner race, 3 places at 1/5. These terms vary and are set by Betfair — always read them from the API rather than hard-coding.

Implied probability and price relationships

In an efficient market, the place market price for a runner should be derivable from the win price and the field composition. For a 2-place market with n runners:

public double expectedPlacePrice(double winPrice, int runners, int places) {
    double winProbability = 1.0 / winPrice;
    // Harville formula for place probability (simplified)
    double placeProbability = winProbability * places;   // rough estimate
    placeProbability = Math.min(placeProbability, 0.98); // cap at near-certainty
    return Math.max(1.01, 1.0 / placeProbability);
}

In practice, place markets frequently misprice relative to win markets — especially on outsiders in large fields where the public over-backs win prices without considering place value. This divergence is a potential signal.

Win-to-place ratio analysis

Compare win and place market prices to detect mispricing:

public record WinPlaceRatio(
    long selectionId,
    String runnerName,
    double winPrice,
    double placePrice,
    double eachWayDivisor,
    double impliedPlacePrice,
    double deviation
) {
    public static WinPlaceRatio compute(RunnerBook win, RunnerBook place,
                                        double divisor, int numPlaces, int numRunners) {
        double wp = bestBack(win);
        double pp = bestBack(place);
        double implied = computeImpliedPlacePrice(wp, numPlaces, numRunners);
        return new WinPlaceRatio(
            win.getSelectionId(), null, wp, pp, divisor, implied, pp - implied);
    }

    public boolean isValueInPlace() {
        return deviation > 0.5;   // place market offers more than implied
    }
}

Streaming both markets

Subscribe to both the win and place market IDs in a single streaming subscription:

MarketSubscriptionMessage sub = new MarketSubscriptionMessage(
    1, "SUBSCRIBE",
    new MarketFilter(List.of(winMarketId, placeMarketId), null, null),
    new MarketDataFilter(List.of("EX_BEST_OFFERS"), 3),
    null, null
);

Maintain separate CondensedOrderBook instances for each market and cross-reference by selectionId. Both markets share the same selection IDs for their runners, making correlation straightforward.

Settlement differences

Win market: one selection wins, all others lose. Place market: multiple selections can finish in the money. Settlement is automatic — Betfair settles based on the official result.

A lay on the place market is more risky than a win market lay for the same runner — multiple selections can beat you, not just one winner. Price your place lays accordingly.

With win and place markets modelled separately but correlated by event and selection, you have the foundation for each-way analysis, cross-market arbitrage detection, and informed position sizing.

If you’re building Betfair trading analysis tools in Java and want help with market data modelling, 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.