Hire Me
← All Writing Betfair

Market Status in the Streaming API — Handling ACTIVE, SUSPENDED, and CLOSED States

How market status transitions are signalled in the Betfair Streaming API, what each status means for your order book and positions, and how to implement correct state machine handling in Java.

A Betfair market moves through a series of statuses during its lifetime — from inactive before trading opens, through active during pre-race betting, to suspended at the off and after in-play events, and finally closed after settlement. Each transition has implications for your order book state, open positions, and what actions are valid. Handling them correctly prevents trading on a suspended market, misinterpreting stale prices, and missing the transition to in-play.

The market status sequence

INACTIVE → OPEN → SUSPENDED → (OPEN ↔ SUSPENDED)* → CLOSED

INACTIVE: The market exists but betting is not yet available. The order book is empty.

OPEN (value "o" in the stream): Betting is live. Back and lay orders accumulate. The order book reflects current matched and unmatched money.

SUSPENDED (value "s"): Temporarily halted — at the off, during in-play events (goals, non-runners), or by Betfair for administrative reasons. In-play markets oscillate between SUSPENDED and OPEN as events occur.

CLOSED (value "c"): Market is settled or voided. No further bets are accepted. Price data may no longer be accurate.

How the stream signals status

Market status arrives in the MarketChange object as the status field:

private void handleMarketChange(MarketChange mc) {
    if (mc.status() != null) {
        MarketStatus newStatus = MarketStatus.from(mc.status());
        MarketStatus prev = currentStatus.get(mc.id());
        currentStatus.put(mc.id(), newStatus);

        if (prev != null && prev != newStatus) {
            onStatusTransition(mc.id(), prev, newStatus);
        }
    }

    // Only process price updates when OPEN
    if (MarketStatus.OPEN.equals(currentStatus.get(mc.id()))) {
        applyPriceUpdates(mc);
    }
}

The status field is only present in the message when it changes — absence means no change, not OPEN.

Market state machine

Model the status transitions explicitly rather than a simple field:

public enum MarketStatus {
    INACTIVE("i"),
    OPEN("o"),
    SUSPENDED("s"),
    CLOSED("c");

    private final String streamCode;

    MarketStatus(String streamCode) {
        this.streamCode = streamCode;
    }

    public static MarketStatus from(String code) {
        for (MarketStatus s : values()) {
            if (s.streamCode.equals(code)) return s;
        }
        throw new IllegalArgumentException("Unknown market status: " + code);
    }

    public boolean isTrading() {
        return this == OPEN;
    }

    public boolean isFinal() {
        return this == CLOSED;
    }
}

Reacting to transitions

Each transition requires different action:

private void onStatusTransition(String marketId, MarketStatus from, MarketStatus to) {
    log.info("Market {} status: {} → {}", marketId, from, to);

    switch (to) {
        case SUSPENDED -> {
            orderManager.pauseNewOrders(marketId);
            signalEngine.invalidateSignals(marketId);
        }
        case OPEN -> {
            if (from == MarketStatus.SUSPENDED) {
                // Resuming from suspension — prices may have moved significantly
                signalEngine.recalibrate(marketId);
                orderManager.resumeOrders(marketId);
            }
        }
        case CLOSED -> {
            positionManager.settle(marketId);
            marketStateMap.remove(marketId);
        }
    }
}

The SUSPENDED → OPEN transition (resuming after an in-play event) is distinct from the initial INACTIVE → OPEN transition. Signals built on pre-suspension price history may no longer be valid after a non-runner or goal.

The inplay flag

Separate from status, the inplay boolean signals that the market has gone in-play. A suspended market at the off goes in-play when the race starts — the sequence is:

  1. OPEN pre-race
  2. SUSPENDED at off
  3. OPEN + inplay=true once betting resumes in-play
private void handleMarketChange(MarketChange mc) {
    if (mc.inplay() != null && mc.inplay()) {
        onMarketGoesInPlay(mc.id());
    }
    // ...
}

private void onMarketGoesInPlay(String marketId) {
    log.info("Market {} is now in-play", marketId);
    strategyEngine.deactivatePreRaceStrategies(marketId);
    // Pre-race positions should have been closed by now
    positionManager.verifyAllPreRaceClosed(marketId);
}

Distinguishing suspension types

The streaming API does not provide a reason code for suspension — you must infer it from context:

For pre-race strategies, only the first matters — detect it and confirm your positions are closed before going in-play.

Stale prices after suspension

When a market resumes from suspension, the first price update may reflect significant movement. Do not trade on the pre-suspension order book state as if prices are current. Mark the order book as stale on SUSPENDED and only mark it fresh after receiving at least one price update following the resume:

public class MarketOrderBook {

    private boolean stale = false;

    public void onSuspended() {
        stale = true;
    }

    public void onPriceUpdate(RunnerChange rc) {
        stale = false;   // first update after suspend clears stale flag
        applyDelta(rc);
    }

    public boolean isReliable() {
        return !stale;
    }
}

Any signal computation should check isReliable() before using prices.

CLOSED without settlement data

The streaming API signals CLOSED before settlement data is available via the REST API. If you need BSP or settlement results immediately after the race, poll listMarketBook with PriceData.SP_TRADED after receiving CLOSED — the BSP is usually available within 1–2 minutes.

With status transitions handled as first-class events — not afterthoughts — your trading system responds correctly to every phase of a market’s lifetime.

If you’re building Betfair trading infrastructure in Java and want help with market lifecycle handling, 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.