Hire Me
← All Writing Betfair

Weight of Money — Computing WoM from the Full Price Ladder in Java

How to compute Weight of Money from the full Betfair Exchange price ladder, why naive WoM is misleading, and how to build a robust signal that accounts for large orders and market depth.

Weight of Money (WoM) measures the relative balance of available back and lay money in the order book. A back-heavy book suggests the market expects the price to shorten — more people want to back than lay at current prices. A lay-heavy book suggests drift. As a standalone signal, naive WoM is noisy and easily distorted by single large orders. As part of a composite signal, computed carefully from the full ladder, it is one of the most reliable short-term indicators available in the Betfair pre-race market.

What WoM is

The simplest form:

WoM = total back available / (total back available + total lay available)

A value above 0.5 means more money wants to back than lay. Below 0.5, the reverse. At exactly 0.5, the book is perfectly balanced — unusual in practice.

Data source: bdatb and bdatl

batb/batl give three price levels. For a meaningful WoM signal, use bdatb/bdatl — the full display ladder with up to ten levels per side. Subscribe with:

MarketDataFilter dataFilter = new MarketDataFilter(
    List.of("EX_BEST_OFFERS"),
    10   // ladder levels — request 10
);

Computing naive WoM

public double naiveWoM(LadderBook book) {
    double backTotal = book.backLevels().stream()
        .mapToDouble(PriceLevel::size)
        .sum();

    double layTotal = book.layLevels().stream()
        .mapToDouble(PriceLevel::size)
        .sum();

    double total = backTotal + layTotal;
    return total == 0.0 ? 0.5 : backTotal / total;
}

This is correct arithmetically but problematic practically. A single £5,000 back order at a level skews WoM to 0.9+ regardless of what the rest of the book looks like.

Robust WoM: capping outliers

Cap each level at a multiple of the ladder average to prevent single large orders from dominating:

public double robustWoM(LadderBook book) {
    double backTotal = cappedTotal(book.backLevels());
    double layTotal  = cappedTotal(book.layLevels());
    double total     = backTotal + layTotal;
    return total == 0.0 ? 0.5 : backTotal / total;
}

private double cappedTotal(List<PriceLevel> levels) {
    if (levels.isEmpty()) return 0.0;
    double avg = levels.stream()
        .mapToDouble(PriceLevel::size)
        .average()
        .orElse(0.0);
    double cap = avg * 5.0;
    return levels.stream()
        .mapToDouble(l -> Math.min(l.size(), cap))
        .sum();
}

The cap multiplier (5× here) is a parameter to tune per market type. Lower values produce a smoother, less reactive WoM; higher values are more sensitive to genuine large participation.

Price-weighted WoM

Prices close to the current best are more relevant than prices far from the spread. Weight each level by its proximity to the best price:

public double priceWeightedWoM(LadderBook book) {
    double bestBack = book.backLevels().stream()
        .mapToDouble(PriceLevel::price).max().orElse(Double.NaN);
    double bestLay  = book.layLevels().stream()
        .mapToDouble(PriceLevel::price).min().orElse(Double.NaN);

    if (Double.isNaN(bestBack) || Double.isNaN(bestLay)) return 0.5;

    double backTotal = weightedTotal(book.backLevels(), bestBack, false);
    double layTotal  = weightedTotal(book.layLevels(),  bestLay,  true);

    double total = backTotal + layTotal;
    return total == 0.0 ? 0.5 : backTotal / total;
}

private double weightedTotal(List<PriceLevel> levels, double bestPrice, boolean ascending) {
    return levels.stream().mapToDouble(l -> {
        double distance = Math.abs(l.price() - bestPrice);
        double weight   = 1.0 / (1.0 + distance);   // closer = higher weight
        return l.size() * weight;
    }).sum();
}

This gives greatest weight to money sitting at or near the spread, and discounts money parked at extreme prices that is unlikely to trade.

WoM smoothing over time

A single WoM snapshot is noisy. Smooth it with an exponential moving average across ticks:

public class WomEma {

    private final double alpha;   // 0.0 < alpha <= 1.0; higher = more reactive
    private double ema = 0.5;
    private boolean initialised = false;

    public WomEma(double alpha) {
        this.alpha = alpha;
    }

    public double update(double rawWom) {
        if (!initialised) {
            ema = rawWom;
            initialised = true;
        } else {
            ema = alpha * rawWom + (1.0 - alpha) * ema;
        }
        return ema;
    }

    public double current() { return ema; }
}

alpha = 0.1 produces a slow-moving average suitable for detecting sustained directional pressure. alpha = 0.4 is more responsive, suitable for detecting rapid shifts.

WoM divergence as a signal

The strongest signal is not WoM itself but the divergence between WoM and price direction. If WoM is strongly back-heavy (>0.7) but the price is drifting (lengthening), someone is laying heavily against the public sentiment — often informed money. This divergence is a candidate entry signal for a lay trade.

public record WomSignal(
    double currentWom,
    double smoothedWom,
    double priceChange5ticks,   // positive = drifting, negative = shortening
    boolean isBackHeavy,
    boolean isDiverging
) {
    public static WomSignal compute(double wom, WomEma ema, double priceChange) {
        double smoothed = ema.update(wom);
        boolean backHeavy  = smoothed > 0.65;
        boolean diverging  = backHeavy && priceChange > 0.02;   // back-heavy but drifting
        return new WomSignal(wom, smoothed, priceChange, backHeavy, diverging);
    }
}

Practical considerations

WoM is meaningful only in liquid markets. In thin markets with five runners and £10,000 matched, a single £500 order moves WoM dramatically. Set a minimum matched volume threshold below which WoM is treated as unreliable.

WoM changes near the off. In the final minutes before a race, large bets appear rapidly and WoM becomes more volatile. A strategy that acts on WoM should reduce position size or pause activity close to the off unless it is specifically calibrated for that window.

WoM vs matched volume. WoM measures available (unmatched) money. Matched volume measures executed trades. The two can diverge significantly — a high WoM with low matched volume may reflect spoofed orders that will be pulled. Always look at both.

With a robust WoM computation wired to the full ladder stream, you have a reliable measure of order book sentiment that updates on every tick without requiring any external data feed.

If you’re building pre-race trading signals in Java and want help with signal design, 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.