Odds Generation — game · set + set · set (tennis)
Enter the match's Game Handicap and Game Total in Malay and its Moneyline in decimal — margins still embedded. Tennis scores are not counts: they are a nested race — point → game (win by 2 from deuce) → set (first to 6 by 2, tiebreak at 6-6) → match — so totals come out bimodal (straight sets vs a decider) with a parity comb (a set never totals 11 games; a 7-6 counts 13). The engine (ADR-030):
- strips each bookmaker margin (Power method — every book here is two-way; tennis has no draw);
- pins the serve sum pA + pB to the tour/surface prior (ATP ≈ 1.29, WTA ≈ 1.14) and bisects the serve difference against the Game Handicap — the match quotes leave the sum on a near-flat ridge, so it is a prior, not a fit;
- consumes the Game Total with whichever knob can reach it: τ, a match-level form mixture (matches shorten), or extra serve sum (matches lengthen) — the difference re-solves at every trial;
- never fits the Moneyline: on the ridge it is the cross-anchor consistency readout (Δ ML) — a large value means the book's ML disagrees with its own game lines at this prior, a risk signal;
- prices both scopes off the one solved distribution — set · set (Moneyline, Set Handicap, Set Total) and game · set (match game lines plus every per-set sheet, conditional on the set being played).

Each stage re-solves the same fair targets, so the stage switcher is a true before/after — flip iid @ prior / Length and watch the totals hump move while the handicap holds. Best-of-3 and best-of-5 are format configs (all four slams play a 10-point decider tiebreak since 2022); switching loads that format's seed quotes.
↑/↓ steps a field, Shift steps bigger; Malay pairs step their partner the opposite way. The Moneyline is never fitted — it is the cross-anchor consistency readout (Δ ML in the diagnostics); clear it to skip the readout.
Valid inputs: Malay in [−1, +1] non-zero with margin, decimals above 1, serve-sum prior in (1.04, 1.54). Quotes are whole-match; per-set sheets derive from the same solve, conditional on the set being played.
| A=0 | A=1 | A=2 | |
|---|---|---|---|
| H=0 | 17.6 | ||
| H=1 | 16.8 | ||
| H=2 | 40.9 | 24.7 |
| -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 | 0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0.1 | 0.3 | 0.7 | 1.9 | 3.1 | 5.2 | 6.7 | 6.2 | 4.8 | 3.5 | 3.8 | 4.1 | 6.6 | 10.0 | 12.8 | 11.4 | 8.4 | 5.9 | 2.7 | 1.4 | 0.4 | 0.1 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0.1 | 0.5 | 1.6 | 3.0 | 5.8 | 6.9 | 8.3 | 7.9 | 5.3 | 8.3 | 6.2 | 1.1 | 3.4 | 4.6 | 3.1 | 3.9 | 4.2 | 4.3 | 5.0 | 4.9 | 3.1 | 2.3 | 3.0 | 1.8 | 0.3 | 0.6 | 0.5 |
| Outcome | Fair | Dec | Priced | Malay |
|---|---|---|---|---|
| Home | 65.6% | 1.524 | 1.491 | +0.49 |
| Away | 34.4% | 2.909 | 2.752 | -0.57 |
How these numbers are computed
| Home | Away | |
|---|---|---|
| Malay quote | +0.82 | -0.95 |
| Decimal | 1.8200 | 2.0526 |
| Implied | 0.5495 | 0.4872 |
| Fair | 0.5317 | 0.4683 |
| Fair decimal | 1.8808 | 2.1353 |
| Over | Under | |
|---|---|---|
| Malay quote | +0.85 | -0.98 |
| Decimal | 1.8500 | 2.0204 |
| Implied | 0.5405 | 0.4949 |
| Fair | 0.5232 | 0.4768 |
| Fair decimal | 1.9114 | 2.0972 |
| Home | Away | |
|---|---|---|
| Decimal quote | 1.50 | 2.79 |
| Decimal | 1.5000 | 2.7900 |
| Implied | 0.6667 | 0.3584 |
| Fair | 0.6560 | 0.3440 |
| Fair decimal | 1.5245 | 2.9067 |
Standalone strips: Margin — two-way (Power).
Tennis games are not counts: every level is a race with absorbing barriers. A server holding with point probability wins the game with the closed form
(deuce is a geometric race; this run: hold(A) = 0.853, hold(B) = 0.785). A tiny DP walks the tiebreak over the real serve rotation (F, S, S, F, F, …; the 6-6 deuce closes as ), the set lattice alternates servers by game — a set totals {6..10, 12, 13} games, never 11, and a 7-6 counts 13 — and the match folds sets carrying the games margin and total pmfs, the sets joint and every per-set grid. The pre-match serve coin toss is averaged. Everything is exact DP; the engine was validated against a 200k-run Monte Carlo.
The handicap pins the serve difference, but (HDP, OU, ML) leave the serve sum and the form shock τ on a near-flat ridge: a weak-serve iid fit can match all three quotes yet misprice every tiebreak-sensitive tail. So the sum comes from the tour/surface prior (1.29 here; ATP ≈ 1.29, WTA ≈ 1.14 — per-set quotes identify it per match, ADR-030 O2), and the totals anchor is consumed by — a 3-node mixture that preserves the sum but compounds over sets, fixing the iid model's known favorite overconfidence — or by extra sum when the market runs longer than the iid baseline.
| Stage | Σ = p_A + p_B | p_A | p_B | τ | knob | Δ cover | Δ total | Δ ML (readout) |
|---|---|---|---|---|---|---|---|---|
| iid @ prior | 1.2900 | 0.6642 | 0.6258 | 0.0000 | — | 1.4e-7 | 6.4e-2 | 2.75% |
| Length (τ) | 1.2900 | 0.6650 | 0.6250 | 0.0300 | tau | 2.0e-7 | 2.6e-6 | 0.03% |
The ML is deliberately not a fitting target: with the prior right it lands on its own (ΔML ≈ 0 on the seed quotes); a large ΔML means the book's ML disagrees with its own game lines at this prior — a risk signal, not a solver failure.
Unlike basketball quarters, set 3 exists only with probability 41.5% here (set 3 with 41.5%). Per-set markets are void if the set is not played, so their fair prices condition on it — and under the τ mixture the conditioning re-weights the form nodes (deciders come disproportionately from the close-form nodes). Set 1 is unconditional. The same fold also yields the set·set scope for free: the sets joint prices the ML, set handicap and set totals with zero extra parameters.
Full match: P(home wins the set race) off the sets joint — no tie leg. Per set: the set winner, conditional on the set being played (void otherwise). Fair groups are re-margined exactly as in goal-regular (Power ladder two-way, Shin multi-way).
targets tH = powerStrip(HDP pair) tO = powerStrip(OU pair) [ML stripped → readout only]
stage iid : sum = PRIOR(tour/surface) bisect diff until P(A covers | dist) = tH
stage length : model over > tO ? bisect τ ∈ [0, .06] (form mixture — matches shorten)
: bisect sum upward (more serve — matches lengthen)
— the difference re-solves inside every trial (same-targets discipline)
dist: hold(p) closed form → tiebreak DP (serve rotation) → set lattice (alternating serve;
totals ∈ {6..10,12,13}, 7-6 = 13 games) → match fold over (sets, first server)
scopes: ONE dist prices set·set (ML, set HDP/OU) and game·set (match + per-set, conditional)Engine + invariants: docs/src/lib/odds/tennis.ts, __tests__/tennis.test.ts; decision record ADR-030 (extends ADR-029).