What 'deterministic' actually means at the settlement layer

Deterministic settlement is a contract: the same input must always produce the same output, forever. Given a bet record and an official result record, the settlement function returns one of three values — settled, void, pending_review — and the second call to the function with the same arguments must return the same value an hour, a week or a year later. No timestamps in the logic, no calls to Math.random(), no reads from shared mutable state.

That sounds obvious until you see how often it gets violated in production. A settlement worker that reads the current odds cache to compute a void refund is non-deterministic. A worker that branches on whether the source feed has 'arrived yet' is non-deterministic. A worker that takes a fast path on a hot cache and a slow path on a cold cache and produces a slightly different rounding result is non-deterministic. Every one of those is a dispute waiting to happen.

The Sporbet Soft engine enforces determinism by treating settlement as a pure function of two arguments — the bet and the result — versioned and replayable. The architectural rationale lives in our integration anatomy piece; this article zooms into the function itself.

Step one: source-feed validation before the function runs

Before the settlement function executes, the engine validates the official result. The result lands from one of the primary feeds (Sportradar/Betradar, Genius Sports, BetGenius) plus an optional secondary feed cross-check, and the validation layer rejects results that fail any of five gates. Outlier scores — a 47-12 final in a football match — are flagged. Conflicting scores between two feeds for the same match ID are queued for trading review. Results that arrive before the match's official end-time threshold are held until the minimum-minute threshold (typically 85 minutes for football) passes. Results for markets that were already suspended at the moment of the triggering event are downgraded to manual review. Results from feeds whose health-check has degraded in the last five minutes are quarantined.

Only after a result passes all five gates does it become a settlement input. This sounds like overhead until you remember that one bad feed event auto-settling a market the wrong way can cost the operator more than a quarter's vendor fee in payouts. The five-gate validation is the cheapest insurance the platform sells.

Operators with their own trading floor often want to layer additional rules on top of the default validation — per-league freshness thresholds, per-market manual-confirm requirements, dispute hold windows. The partner panel exposes those as configuration-as-data, not as code changes, so a trader can adjust the rules during a live match without an engineering deploy.

Step two: market template lookup and function selection

Every market the sportsbook offers is backed by a versioned market template: a JSON document that captures the market's name, its selections, its parameters (handicap line, total line, period scope) and — critically — a reference to the settlement function that knows how to settle it. A 1X2 full-time market points at the settle_1x2_ft function. An Asian handicap -1.25 points at settle_asian_handicap with the line parameter baked into the bet record. A player-props goalscorer market points at settle_player_props with the player ID and stat type.

The template is versioned because market definitions change. When a league switches from sudden-death overtime to a two-period overtime format, the corresponding template ships a new version and the engine routes new bets to the new function while in-flight bets remain bound to the version they were placed under. The bet record stores the template version it was placed against, so a five-year-old bet still settles under the rules that applied the day it was placed.

This pattern is borrowed from event-sourcing systems and it pays for itself the first time an operator runs a retroactive audit. Every settled bet can be replayed against its original template and produce the original outcome, regardless of how many template versions have shipped since.

Step three: settlement function execution and replay logging

With the result validated and the template selected, the settlement function runs. The function is a pure-ish piece of code — it takes the bet and the result, optionally the previous settlement attempt (for replay scenarios), and returns the settlement decision plus the payout amount in the bet's currency. No I/O, no clock reads, no random numbers.

Every execution writes a settlement event to the append-only audit log. The event captures the bet ID, the result ID, the template ID and version, the function ID and version, the decision, the payout, the wall-clock timestamp (for human debugging only — never used in logic), the worker ID and the input hashes. The log is the source of truth: if the wallet bridge crashes between settlement and credit, the engine re-reads the log on restart and resumes from the last confirmed wallet write. If a customer disputes a payout six months later, a support agent pulls the bet's audit-trail link and sees the exact result, template version, function version and decision used.

Sporbet Soft pairs the audit log with a synthetic replay harness that re-runs every settlement nightly against the latest function versions and flags any drift. In four years of operation we have caught two genuine settlement bugs that way before any customer noticed — both involved corner-case period-end rules in low-volume markets. The harness is also how new function versions get green-lit for production: a candidate function must replay 10M historical bets with identical outcomes to its predecessor before it ships.

Void semantics and the immutability of already-settled selections

Void is where naive settlement engines fall apart. The intuitive interpretation — 'a match got voided, so let's just reverse everything' — is dangerous. If a player placed a four-leg accumulator and the third leg has already been settled and credited, voiding the entire match retroactively would force the engine to claw back credit from a wallet that has since been spent or withdrawn. Operators that do this end up in regulator-mediated chargeback disputes within a quarter.

The Sporbet Soft rule is explicit: already-settled selections are immutable on void. When a match is voided, the engine voids only the selections that are still pending at the moment the void is processed; selections that are already settled remain settled, and the multi-bet payout is recomputed treating the voided leg as a 'void leg' — i.e. its effective odds become 1.00 and the remaining legs are repriced accordingly. The bet record retains both the original payout and the post-void payout, with a void event in the audit log linking them.

Per the operator-controlled match finalisation rules — see operator-grade risk controls — voiding requires a reason code from a fixed taxonomy (abandonment, walkover, retrospective annulment, etc.) and an operator user ID. The void is a first-class event in the audit log, not a destructive overwrite of the original settlement. Replay still works.

Multi-bet recompute and partial cashout reconciliation

Multi-bet recompute is one of the higher-leverage features in the engine. When a single leg of an accumulator settles, the engine doesn't wait for the whole bet to be ready — it incrementally recomputes the bet's exposure and the operator's open liability. If the leg loses, the bet is settled lost immediately and the operator's liability for that bet drops to zero. If the leg wins, the bet remains pending but the per-leg implied probability and the cashout offer are updated in real time.

Partial cashout adds a second axis. A player who cashes out 40% of a four-leg accumulator after two legs have won leaves 60% of the bet active; the engine must reconcile the 40% cashout against the wallet, mark 60% of the original stake as still-at-risk, and continue recomputing the multi as the remaining legs settle. The audit log captures every partial cashout as its own event with its own decision row, linked to the parent bet, so a player who partially-cashes-out three times sees three reconciliation entries and a final settlement entry — all replayable.

The engineering challenge isn't the math; it's the concurrency. Two settlement workers must never both attempt to credit the same partial cashout, and a wallet bridge that times out mid-credit must be safe to retry. Sporbet Soft uses idempotency keys derived from the bet ID, leg ID and settlement attempt counter — see our odds latency piece for how the same pattern shows up in the live odds fan-out.