Sample: 205,435 matches · 38 leagues · 2005–2025 · football-data mirror Baseline strategy: unit bet on the favourite (shortest odds), ex-post return.
The skewness of the betting market is not a dynamic process — it is a structural invariant. Each league has its own level of skewness, fixed in time, determined by its competitiveness. The observed fluctuation is pure sampling noise, not market memory. The asymmetry is a property of the sporting generator, exogenous to the bookmakers' pricing.
It evolved from "stable-with-memory" (draft v1) → "structural constant + white noise" (confirmed by 3 independent tests). And from skewness invariance → SHAPE invariance (Phases B1/B2): not only the 3rd moment, but the entire implied distribution (var/skew/kurtosis) is a unique function of competitiveness — it collapses across leagues once conditioned on it.
| Configuration | ADF | KPSS | Ljung-Box | ACF(1) |
|---|---|---|---|---|
| baseline (overlap) | <0.001 | >0.10 | <0.001 | 0.74 |
| league-demeaned (overlap) | <0.001 | >0.10 | <0.001 | 0.74 |
| non-overlap | <0.001 | >0.10 | 0.71 | −0.08 |
| league-demeaned non-overlap | <0.001 | >0.10 | 0.70 | −0.08 |
- Stationarity: armoured. Survives demeaning, non-overlap and windows of 500 to 3000 matches. ADF<0.001 and KPSS p>0.10 (the test's tabulated ceiling; stationarity is not rejected) in all 10 configs.
- Persistence: it was an artefact. The ACF=0.74 came 100% from window overlap. Without overlap → white noise (LB p=0.71). An honest correction vs v1.
- League composition is not a confound: demeaning moves almost nothing.
- sd falls monotonically with window size → the variation is sampling error.
B1 — by strategy: skewness monotone in the improbability of the bet. favourite +0.23 · draw +1.27 · longshot +2.26 · "always away" +2.40. Mean return follows along (favourite −4.8%, longshot −10.2%) = classic FLB.
B2 — favourite-longshot bias (the key table): skewness falls monotonically as the favourite strengthens, crossing zero at p≈0.50:
| p_favourite | mean ret | skewness | win% |
|---|---|---|---|
| (0.40] | −7.5% | +0.59 | 36% |
| (0.45,0.50] | −4.7% | +0.19 | 45% |
| (0.55,0.60] | −3.5% | −0.22 | 56% |
| (0.70,1.0] | −1.6% | −1.19 | 77% |
B3 — the mechanism of stability: corr(favourite strength, variance)=−0.90 but corr(strength, skewness)=−0.21 and corr(variance, skewness)≈0. The variance is sensitive to composition; the skewness is not, because the contributions of the two tails (weak favourites→+, strong→−) cancel in a stable way.
- Variance Ratio(2,4,8) = 0.94, 0.94, 1.00 (=1 → i.i.d.)
- AR(1) φ = −0.06, non-significant (p=0.39), half-life ≈ 0
- Conclusion: white noise confirmed by 3 tests. No temporal dynamics.
| metric | average odd | best odd |
|---|---|---|
| skewness | 0.229 | 0.242 |
| mean return | −4.75% | −0.16% |
| overround | 1.067 | 1.009 |
- The bookmaker's margin is mostly spread across bookmakers. Arbitraging odds recovers 4.6 p.p. of return; the "irreducible" margin is ~0.2%.
- Skewness is invariant to the bookmaker: temporal corr of the two series = 0.984. Bookmakers compete on the margin (level); the skewness is exogenous to that competition.
- Separates pricing (margin, varies) from structure (skewness, fixed).
- Skewness is not universal: it ranges from 0.10 (Netherlands) to 0.33 (Italy B), sd=0.06.
- corr(league predictability, skewness) = −0.83. Almost deterministic.
- Leagues with strong favourites (a few dominate) → low skewness; balanced leagues (2nd divisions) → high skewness.
- Generalisation: each league has a skewness-invariant = f(competitiveness). The same mechanism as B3 (within-window scale) reappears across leagues (cross-sectional scale). A single principle explains both scales.
- BRA +0.18, ARG +0.31 — South American leagues at the competitive/asymmetric extreme.
- The only "structural break" in 20 years was a sampling artefact: the dataset jumped from 21→37 leagues in 2012 (ARG, MEX, BRA, JAP, POL, ROM... came in), many high-skewness, temporarily inflating the window mean.
- There was no market event. It reinforces the real stability and validates the composition control.
Three levels of result, cohesive under one principle:
- Cross-sectional: FLB confirmed; skewness monotone in improbability.
- Temporal: skewness is constant + white noise (no dynamics) — robust.
- Structural: the level is f(league competitiveness); temporal stability follows from competitiveness being a slow property (decadal scale).
Unifying principle: structural competitiveness generates asymmetry; since it changes slowly, skewness is a temporal invariant. The bookmakers' margin is orthogonal to this (it affects the level of return, not the asymmetry).
- Opening→closing drift: the skewness of the price movement (needs opening odds; the dataset has partial coverage).
- Intra-season seasonality controlled per league (start vs end).
- Testing the invariance in another sport (basketball/tennis) — external generalisation.
Dataset frozen in
data/PROVENANCE.json(sha2566905ca53…, 205,435 matches ≥2005, 38 leagues, 2005-01→2025-06). Baseline reproduced inoutputs/phase0_baseline.log. Each phase below adds a block to the paper.
Blocks 00–06 reproduce FINDINGS exactly against the full 38-league archive.
Bug fixed in 06_league_hetero (tab.skew collided with DataFrame.skew()).
Dataset frozen by hash.
Primary object redefined: implied (de-vigged) skewness of the bet on the favourite, via the Shin model (mean z 3.4% of informed money; overround 1.068). Closed-form, free of sampling noise.
- Ex-ante ≈ ex-post. Global ex-ante skew +0.236 vs realised +0.230 → the implied object reproduces the realised; odds well calibrated in the aggregate.
- Decomposition (law of total cumulants) — establishes the mechanical core:
M3 = +102.6% within-match (Bernoulli/FLB) − 2.6% covariance − 0.0%
between-match. The market skewness is, at ~100%, the within-match asymmetry of
the distribution of p — not a pooling artefact.
within_frac≈ 1.00 in all p_fav bands. It turns the "tautology" criticism into the thesis itself: the risk asymmetry IS the algebraic image of the FLB. - Cross-league (clean object): corr(ex-ante, ex-post)=+0.872; corr(p_fav, skew)=−0.900 — but still circular (p comes from the odds). Target of W2: reproduce with odds-free competitiveness.
Artefacts: skewlib/devig.py, skewlib/exante.py, analysis/07_devig_exante.py,
outputs/exante_by_league.csv.
Competitiveness measured by Elo built from results only (no odds; chronological multi-league step + rating-diff→(P_H,P_D,P_A) map via MNLogit calibrated on the results — P(H) 0.444 = real 0.444, P(D) 0.264 = real 0.264).
- Odds and Elo measure the SAME structure: corr(elo_pfav, p_fav_dv)=+0.909 [0.83,0.97]. The odds merely read off sporting competitiveness (structural efficiency) — they do not create it.
- The law skewness=f(competitiveness) SURVIVES without odds: skew ~ upset_rate +0.826 [0.71,0.91] · skew ~ elo_entropy +0.719 [0.50,0.89] · skew ~ elo_pfav −0.748 · skew ~ elo_disp −0.731. All with CI95 far from zero. Circular reference (odds p_fav): −0.900.
- Attenuation −0.90→−0.75/+0.83 = measurement noise in the Elo proxy (errors-in-variables), not evidence against the law: corr(elo,odds)=0.91 shows they measure the same latent. The risk asymmetry is inherited from the competitive structure of the league, not from the pricing.
Artefacts: skewlib/elo.py, skewlib/stats.py (bootstrap_corr, ols),
analysis/08_mechanism_elo.py, outputs/mechanism_elo.csv.
Treating (league,season) as a unit dissolves the composition confound of Block F by construction. 638 obs, 38 leagues, 2005–2025.
- No secular trend: league FE + linear year (cluster SE by league): β=+0.00015/year (p=0.73, CI95 [−0.0007,+0.0010]). Drift over 20 years ≈ +0.003 vs between-league sd 0.052 → null.
- Structural dominance: between-league sd 0.052 vs within-league 0.034; ICC=0.70. Netting out the sampling noise (bootstrap SE 0.019), the real temporal fluctuation is sd≈0.028 — small, with no trend and mean-reverting (consistent with the white noise of Block C). The league invariant dominates ~2:1.
- Per league: idiosyncratic and tiny deviations (mean |slope| 0.0024/year; breaks rare, likely PELT over-segmentation in series of ~20 points). No market-wide regime.
- COVID vignette (natural experiment): empty stadiums in 2020 lowered the home win (0.447→0.417). The law predicts: HFA↓ → more parity → skewness↑. Observed: mean z +0.42 SD, 21/33 leagues with z>0. The only exogenous competitiveness shock in 20 years moved the skewness in the predicted direction — corroborates the cause without violating the secular invariance.
Artefacts: skewlib/panel.py, analysis/09_panel_temporal.py,
outputs/panel_league_season.csv.
148,261 matches with O/U 2.5 odds (Shin de-vig; overround 1.067, z 0.067 — more informed money than in 1X2). Impeccable calibration: real over 0.490 = de-vigged p_over 0.492.
- Same identity, different market: bet on the favourite side → ex-ante skew −0.210 (within-match 99.6%), ex-post −0.217. The closed form (1-2p)/√(p(1-p)) matches exactly per-match (max|diff|=0). By p band, ex-ante and ex-post move in lockstep (p=0.52→−0.09; p=0.74→−1.05).
- Conclusion: the mechanical core of W1 is not an artefact of the 3-way structure of 1X2 — it holds in a binary goals market. within≈100% replicates.
Artefacts: skewlib/overunder.py, analysis/10_overunder.py, outputs/overunder.csv.
- Margin vs structure (average vs maximum odds, 202,760 matches): taking the best market price collapses the overround 1.067→1.009 (return −4.8%→~0) but the ex-ante skewness barely moves: +0.236→+0.254 (per-match corr p_fav 0.996). The bookmaker competes on the MARGIN (level), not on the asymmetry — the margin is largely orthogonal to the skewness.
- Robustness to the de-vig method: global skew 0.224 (power) / 0.236 (shin) / 0.263 (mult) — ±8% in level; but the cross-league law is invariant: corr(p_fav,skew) = −0.906 / −0.900 / −0.891. The structural finding is not a de-vig-choice artefact.
Artefacts: skewlib/exante.py (market_skew), analysis/11_margin_robustness.py.
The literature (Lee & Fort 2012; Basini 2023; see docs/LITERATURA.md) finds
real regime breaks in EPL competitiveness tied to institutional shocks
(Champions League 94/95, Bosman 95, revenue inequality ~2003) — all prior to
the ≥2005 cut-off. We reposition the thesis from a "timeless constant" to a
league-specific structural baseline, stable WITHIN the competitive regime.
- Intra-window break test (conservative PELT): only 1 break across 38 leagues (F1/France 2020 = COVID, jump −0.064); no common break year (max 1 league/year) → no market-wide regime in 2005–2025.
- EPL (E0): 0 breaks in the cut-off; mean 0.165, sd 0.027 — stable intra-regime, consistent with the EPL's regime shocks being pre-2005.
- Conclusion: 2005–2025 ≈ a single modern regime; "no trend" (β≈0) is intra-regime invariance, not absolute timelessness. It confronts Lee & Fort/Basini head-on and uses them as a frame — the counter-evidence becomes an ally.
Artefacts: skewlib/panel.py (league_breaks), analysis/13_regimes.py.
Hardening of W2 with the size-robust indices from the literature (Gini out, Utt & Fort 2002), computed from the final standings (results, no odds or Elo) — a stronger attack on circularity.
- Law reproduced, sign predicted (imbalance → skewness↓): skew ~ Noll-Scully −0.625 [−0.83,−0.36] · ~ HHI* (Owen 2007) −0.593 · ~ Theil/GE1 (Borooah-Mangan) −0.478. All CI95 exclude zero.
- A clean errors-in-variables ladder: odds (circular) −0.90 > Elo (match-level) +0.83 > standings (season-level) 0.48–0.63. The closer to per-match p, the stronger the corr — the latent is strong, measured by proxies of varying fidelity.
- Team by team it holds: N1/EPL (NS~1.84, dominance) low skew; MLS (NS 1.13, salary cap) and Argentina (NS 1.20) high skew. The mechanism is visible in the standings.
Artefacts: skewlib/balance.py, analysis/14_balance_indices.py,
outputs/balance_indices.csv.
The law becomes a consequence of a model, not a fit. Ordered-probit (Goddard-Asimakopoulos 2004; Koning 2000): force r~N(0,σ_L²), latent margin y*=d+h+ε, cutoffs ±c → (A,D,H); favourite p=max; under fair odds the pooled skewness S(σ_L)=E[m₃(p)]/E[σ²(p)]^{3/2}.
- Calibration (pooled rates H 0.444 / D 0.264 / p_fav 0.499): h=0.220, c=0.373, σ_ref=0.291.
- 1st→3rd order validation: the model predicts the skewness of each league from the mean p_fav alone: corr(predicted, observed)=+0.904, RMSE 0.024 (< half the between-league sd 0.051). The 38 leagues fall on the derived curve (F5).
- Reading: the law skewness~competitiveness is an analytic consequence of the sport's force model + the FLB identity — it closes "the gap" (no one had tied the 3rd moment of odds to a force model). The theoretical curve covers skew −0.03..+0.30 (p_fav 0.44..0.76), bracketing the empirical range.
Artefacts: skewlib/model.py, analysis/15_model.py, outputs/fig/f5_model.png.
Angelini & De Angelis (2019) find the FLB weakening in recent European data; a bias in motion could fake skewness invariance. Tested 2005–2025:
- FLB with no significant trend: ret_dog (barometer) corr(year)=+0.27 [−0.23,+0.67] (CI includes 0; a mild hint in Angelini's direction, non-significant); flb_spread corr −0.02; calib_err corr −0.13. Δ20yr of the spread ≈ −0.002.
- Year-by-year calibration intact: mean |skew_exante − skew_expost| = 0.015; the favourite's calibration error ∈ [−0.004,+0.012] every year.
- Conclusion: the skewness invariance is not an artefact of a drifting FLB — the bias is stable and the skewness is mechanical in the distribution of p, robust to calibration micro-drift.
Artefacts: skewlib/decompose.py (flb_by_year), analysis/16_flb_stability.py,
outputs/flb_by_year.csv.
Generalisation of the mixture's moment decomposition (law of total moments) to var/skew/kurtosis/5th–6th order, and a test of each against the curve derived from the ordered-probit (P3 did only the skew). Object: is the entire implied distribution invariant after controlling for competitiveness?
- The whole shape is MECHANICAL (within-match), not pooling: the
withinfraction (the part coming from within-match asymmetry/shape, the FLB, vs between-match dispersion) is ≈1 at all orders — m2 +1.000, m3 +1.026, m4 +1.006, m5 +1.026, m6 +1.016. The whole shape is the algebraic image of the distribution of p, not a mixture artefact. - The force model predicts the entire shape from p_fav: corr(predicted, observed) across the 38 leagues = var +0.987 · skew +0.904 · exkurt +0.890. Skew and exkurt (standardised, scale-free) match in level and ordering; the var follows the ordering (r=0.99) with an overround scale offset (real odds o<1/p). Global: skew +0.236 (boot SE 0.001), exkurt −1.683 (strongly short-tailed, expected from a mixture of Bernoullis), std5 +0.85, std6 +2.18.
- Conclusion: "skewness invariance" strengthens to SHAPE invariance — the entire implied distribution is a unique function of league competitiveness.
Artefacts: skewlib/exante.py (pooled_moments, per_match_central_moments),
skewlib/model.py (league_moments, curve_moments), analysis/17_moments.py,
outputs/moments_by_league.csv, outputs/fig/f6_moments.png.
A "data collapse" test (statistical physics): is the shape universal, or does it collapse under competitiveness? KS on the favourite's return (effect = KS statistic, since the p-value saturates with large n).
- Without controlling for competitiveness (per-league z-scored returns, 38 leagues): pairwise KS with median statistic 0.474, 100% of pairs reject — the standardised shape differs across leagues (the skew varies), so it is not universal.
- Controlling for competitiveness (one-vs-rest within 8 p_fav bands, 264 tests): median KS statistic 0.059 — an 87% drop. Within each band the leagues are nearly indistinguishable; the league identity adds nothing beyond competitiveness.
- Conclusion: the distribution collapses when conditioned on competitiveness — a stylised fact that the shape is a (unique) function of competitiveness, not of the league.
Artefacts: skewlib/collapse.py, analysis/18_collapse.py,
outputs/collapse_ks.csv, outputs/fig/f7_collapse.png.
Decomposition of the favourite's return (exact identity) into margin + mechanical FLB level + residual, and a test of the residual against the per-league implied skewness.
- Return = margin + FLB (calibration): global ret −4.82% = vig −4.97% + FLB +0.15%. The loss is almost all margin; the favourite's FLB is small and positive (favourites slightly underpriced). The mechanical FLB curve is monotone in p_fav (weak favourites contribute −, strong +).
- No per-league skewness premium BEYOND the mechanical: corr(residual, skew) = +0.11 [−0.20,+0.38] (CI includes 0); corr(total FLB, skew) −0.04; corr(vig, skew) −0.29. The mispricing residual does not track the league's skewness — the bookmaker leaves no extra premium attached to the asymmetry. Consistent with the orthogonal margin (W4).
- Conclusion: the "skewness premium" is entirely the mechanical FLB (across bet types, already in W1/Block B); at the league level no pure premium remains — the pricing is efficient up to the margin + the mechanical bias.
Artefacts: skewlib/premium.py, analysis/19_premium.py,
outputs/return_decomp.csv, outputs/fig/f8_premium.png.
Fit of the Tversky-Kahneman probability weighting w(p)=p^γ/(p^γ+(1−p)^γ)^{1/γ}
to the calibration curve (PROPORTIONAL implied q vs objective hit π; the Shin
de-vig would erase the bias to be measured, so the proportional is used).
- Inverse-S confirmed (γ<1 = FLB): global γ 0.958; the calibration reveals the bias (longshot q 0.101 vs π 0.086 = overweighted; favourite q 0.711 vs π 0.743 = underweighted).
- γ is a TEMPORAL invariant: by season mean γ 0.955, sd 0.020, trend β=+0.0003/year (r=+0.08, Δ20yr ≈ +0.006) — no drift over 20 years. The weighting preference is stable over time, mirroring the skewness invariance (and the stable FLB of P4).
- Nearly invariant across leagues: mean γ 0.945, sd 0.040, range [0.85,1.00] — tight. It shows a mild association with competitiveness (corr(γ,p_fav) −0.45 [−0.74,−0.10]), an honest nuance (it may reflect the range of p sampled per league), not a break in temporal stability.
- Conclusion: the preference parameter behind the FLB is a stable structural constant (not a process) — the invariance holds also on the preference side, not only on the risk signature.
Artefacts: skewlib/cpt.py, analysis/20_cpt.py, outputs/cpt_by_league.csv,
outputs/cpt_by_season.csv, outputs/fig/f9_cpt.png.
P3/block 15 traces the law skewness=f(competitiveness) by SIMULATION over the
force d. Here we show that the expectation is a 1-D Gaussian INTEGRAL in d and
evaluate it by QUADRATURE — the closed form of
S(σ_L)=E[m₃(p_fav(d))]/E[σ²(p_fav(d))]^{3/2}, d~N(0,2σ_L²), deterministic and
free of MC noise.
- The quadrature reproduces the MC, without noise: max|MC−exact| = 0.0015 (with n=4·10⁵; this is the magnitude of the MC noise itself), mean 0.0006, over the whole σ_L grid. The theoretical curve becomes exact and smooth — the "derivation by simulation" becomes a closed-form derivation.
- Balanced limit in closed form:
S(σ_L→0) = (1−2p₀)/√(p₀(1−p₀)) = +0.2449, withp₀=Φ(h−c)=0.4392(the equilibrium favourite = home team). It is the per-match identity evaluated at p₀ — the law's intercept comes out analytically. The leading curvatureS₂=+8.44>0(the skew RISES on leaving equilibrium), valid for σ_L≲0.1. - The curve is NOT monotone (exact characterisation): concave, with a peak at σ=0.123 (S_max=+0.304, p_fav=0.446)** and crossing zero at σ_L≈1.09 (a strong favourite ⇒ skew→0 and turns negative). It corrects the "monotone" of the old docstring.
- Mathematical honesty:
p_fav(d)=max(p_H,p_D,p_A)has KINKS where the favourite switches →S(σ_L)is C^∞ but globally non-analytic (the Taylor series diverges beyond the near-balance regime, confirmed numerically). The legitimate closed form is the integral (quadrature), not an elementary series; the expansion S₀+S₂σ² is the local analytic anchor. - Predicts the 38 leagues from the closed curve: corr(predicted,observed) = +0.903, RMSE 0.024 — identical to block 15 by MC (r=0.904), now without resampling.
- Conclusion: the law skewness=f(competitiveness) is a closed-form consequence of the force model + FLB, derived from the Gaussian integral, not a fit nor a simulation artefact.
Artefacts: skewlib/model.py (league_moments_exact, league_skew_exact,
mean_pfav_exact, smallsigma_coeffs/skew, fav_switch_points, curve_exact),
analysis/21_closed_form.py, outputs/closed_form_curve.csv,
outputs/fig/f10_closed_form.png.
The model assumes Gaussian force, r~N(0,σ_L²). Does the law survive if the force
is heavy-tailed (Student-t), skewed (skew-normal) or bounded-support (uniform)?
Theoretical prediction: the force difference d=rᵢ−rⱼ is symmetric for any iid
force — the asymmetry of the force cannot bias the law; only the TAIL (kurtosis
of d) can move anything.
- The theory holds: exc.kurt(d) = normal 0.0, t₅ +2.8, t₃ +42.6 (very heavy tail), skew-normal ±0.3, uniform −0.6. skew(d)≈0 in ALL (incl. the skew-normals) — the skewed force generates a symmetric difference.
- The skew×competitiveness curve barely moves: reparametrising by observable
competitiveness (mean p_fav) and comparing to the Gaussian, max|ΔS| =
t₅ 0.017, t₃ 0.032, skew-normal ±0.012, uniform 0.011 — all below the
between-league sd (0.051). The shift scales with the TAIL of
d(t₃ is the largest), not with its asymmetry (skew-normal sticks to the Gaussian, as predicted). - At football's operating point (p_fav=0.499): skew ∈ [+0.223,+0.250], amplitude across families = 0.027 (small relative to the competitiveness effect, which sweeps +0.30→−0.02).
- Conclusion: the law is mixture geometry, not the Gaussian hypothesis — robust to heavy tails and force asymmetry. Gaussianity is convenience, not a premise carrying the result.
Artefacts: skewlib/model.py (force_diff, curve_family), analysis/22_force_robustness.py,
outputs/force_robustness.csv, outputs/fig/f11_force_robustness.png.
Adversarial robustness: is the Shin de-vig reliable and the skewness not a method artefact? Reliability diagram + Brier decomposition (Murphy: BS=REL−RES+UNC) of the favourite by league/year, and skewness under 5 de-vigs/bookmakers.
- De-vig calibrated almost perfectly: favourite hit 0.501 vs mean prob 0.499; global REL (calibration error) = 0.0000. Brier 0.236 = REL 0.000 − RES 0.014 + UNC 0.250.
- REL small and homogeneous: across 32 leagues mean 0.0005 (sd 0.0003, max 0.0014); across 21 seasons mean 0.0002 (sd 0.0001). No league/year miscalibrated — the de-vig residual is stable (no hidden bias producing the asymmetry).
- Skewness invariant to method/bookmaker: shin·odd +0.236, shin·max +0.254, mult +0.263, power +0.224, multi-bookmaker consensus +0.252 — amplitude 0.039, all positive. Extends W4: the finding depends on neither the de-vig nor the bookmaker.
- Conclusion: the skewness is not manufactured by the de-vig; the implied asymmetry is well calibrated against the results and robust to the method choice.
Artefacts: skewlib/adversarial.py (fav_won, reliability, brier_decomp,
reliability_by, skew_by_devig), analysis/23_devig_reliability.py,
outputs/reliability_by_league.csv, outputs/fig/f12_reliability.png.
The GLOBAL skewness series rebuilt using ONLY the leagues present in all 21 seasons (15 leagues: B1,D1,D2,E0–E3,F1,F2,I1,I2,N1,SP1,SP2,T1) — killing 100% of the composition confound that P1 attacked per-league.
- No trend with a fixed basket: β = −0.00013/year (r=−0.06, Δ20yr −0.003) in the balanced series vs −0.00009 in the full one; KPSS p>0.10 (stationary). Mean level +0.243 (sd 0.014) — extremely tight.
- Conclusion: the temporal invariance does NOT come from the league basket changing year on year; with the core fixed the global series stays flat. The "no drift" is real.
Artefacts: skewlib/adversarial.py (balanced_leagues, global_series_balanced),
analysis/24_balanced_panel.py, outputs/balanced_global_series.csv,
outputs/fig/f13_balanced_panel.png.
Honest CIs resampling whole SEASONS (with replacement), respecting the intra-year dependence that match resampling would break.
- Global skewness +0.236, CI95 [+0.232, +0.239] (SE 0.0019) — excludes 0 comfortably.
- Structural law corr(skew_league, p_fav_league) = −0.900, CI95 [−0.922, −0.876] (SE 0.011) — the skewness↔competitiveness relation survives the year resampling.
- Favourite return −4.82%, CI95 [−5.37%, −4.43%].
- Conclusion: the headline numbers carry a CI from season resampling; the sign and magnitude do not depend on a specific window of years.
Artefacts: skewlib/adversarial.py (season_block_bootstrap, stat_global_skew,
stat_league_corr), analysis/25_block_bootstrap.py.
Does the skewness diverge between the market's AVERAGE odd (Odd*, soft) and the BEST odd (Max*, ~sharp/arb)? By league.
- Best price almost zeroes the margin: overround soft 1.069 → sharp 1.008.
- Skew barely moves, and uniformly: soft +0.218 → sharp +0.238 (mean Δ +0.020, sd 0.006). corr(skew_soft, skew_sharp) across leagues = +0.993 — the ordering of the leagues is identical; the structural law survives in the sharp (corr(skew_sharp, p_fav) = −0.876).
- Conclusion: removing the margin shifts the skew little and uniformly; the bookmaker competes on margin, not on asymmetry (deepens W4) — the law is invariant to the book.
Artefacts: skewlib/microstructure.py (skew_by_book_league),
analysis/26_sharp_soft.py, outputs/sharp_soft_by_league.csv,
outputs/fig/f14_sharp_soft.png.
z is a by-product of the Shin de-vig: the fraction of the book attributed to insiders. z by league/year, its stability and its relation to competitiveness/overround.
- z low and tight: global 0.034 (3.4% of informed money in 1X2); across 38 leagues mean 0.035, sd 0.004, range [0.023, 0.042].
- z is essentially the margin reparametrised: corr(z, overround) = +0.999 (almost tautological in the Shin model — z is monotone in the booksum). The useful part is the orthogonality to competitiveness: corr(z, p_fav) = −0.04 [−0.37, +0.30] — the informational content does not drive the skewness law.
- Over time: a slight compression (β=−0.0009/year, Δ20yr −0.019), mirroring the smooth decline in the margin; small magnitude.
- Conclusion: the priced-in informed money is a low structural constant, tied to the margin and orthogonal to competitiveness — consistent with the invariance.
Artefacts: skewlib/microstructure.py (shin_z_frame, z_by),
analysis/27_shin_z_series.py, outputs/shin_z_by_league.csv,
outputs/fig/f15_shin_z.png.
Beyond 1X2 (W1) and O/U 2.5 (W5), the AH is a 2-way market with a MOVING line that balances the match to ~50/50. The sharpest test of the identity in a different p_fav regime.
- The line balances to ~0.5: 150,003 matches with a valid AH, mean p_fav 0.533 (vs 0.44 in 1X2). AH overround 1.044.
- Same identity, opposite sign: pooled ex-ante skew = −0.104 (within-match 102.7% = mechanical), because p_fav>0.5 (the favourite covers frequently ⇒ negative skew) — a mirror of 1X2 (+0.236, p_fav<0.5). Ex-post (70,965 settled) −0.117 ≈ ex-ante −0.112.
- By league on the curve: skew_ah vs the identity (1−2p)/√(p(1−p)) at the AH's p_fav → r=+0.80.
- Conclusion: a THIRD independent market confirms the mechanical core — the skewness is a function of p (the sign is fixed by which side of 0.5 the favourite falls), not an artefact of the 3-way structure of 1X2.
Artefacts: skewlib/microstructure.py (prep_ah, ah_league),
analysis/28_asian_handicap.py, outputs/asian_handicap_by_league.csv,
outputs/fig/f16_asian_handicap.png.
Does the skewness move from the start to the end of the season (Aug→Jul, thirds by date)?
- MILD and predicted drift: global by phase +0.243 → +0.235 → +0.229 (amplitude 0.015); p_fav rises 0.494 → 0.503 (favourites a bit stronger at the end, as the standings crystallise). Δskew(end−start) by league: mean −0.008, CI95 [−0.013, −0.0015] (just excludes 0).
- Conclusion: there is a small crystallisation — but ~3–4× smaller than the between-league sd (0.05) and predicted by the law itself (more p_fav ⇒ less skew). The invariance holds also WITHIN the season, up to this minimal drift.
Artefacts: skewlib/intraleague.py (add_season_phase, skew_by_phase,
phase_shift_by_league), analysis/29_intraseason.py,
outputs/intraseason_shift_by_league.csv, outputs/fig/f17_intraseason.png.
Decomposition of the pooled 3rd moment by MATCH competitiveness band (p_fav): which matches contribute the asymmetry.
- Law at the match level: skew by band ranges from +0.465 (p_fav 0.39, weak favourite) to −1.055 (p_fav 0.73, strong favourite) — exactly the identity (1−2p)/√(p(1−p)). WEAK-favourite matches (p<0.5) sum +126% of M₃; STRONG-favourite (p>0.5) −26%.
- Conclusion: the league skewness is the net sum of contributions that tail cancellation nearly zeroes; competitiveness at the MATCH level fixes the sign and magnitude of each contribution — the macro law emerges from the micro.
Artefacts: skewlib/intraleague.py (m3_contribution_by_bin),
analysis/30_game_contribution.py, outputs/m3_contribution_by_bin.csv,
outputs/fig/f18_game_contribution.png.
By club: dominance (mean Elo) vs the mean skewness of the matches it plays.
- Dominant clubs pull towards negative skew: Barcelona (Elo 1983, favourite 97%) match skew −1.10, Bayern −1.09, Real Madrid −0.90; weak clubs (Lahti Elo 1182) +0.06. corr(Elo, match skew) = −0.44 [−0.53,−0.34].
- The law, seen from inside: corr(league Elo dispersion, league skew) = −0.60 [−0.77,−0.42] — leagues with more super-clubs have lower skew.
- Conclusion: the league's skew signature is a function of its force dispersion at the TEAM level — the micro version of skewness=f(competitiveness).
Artefacts: skewlib/intraleague.py (team_long, team_dominance),
analysis/31_team_decomposition.py, outputs/team_dominance.csv,
outputs/fig/f19_team_decomposition.png.
The MLS (USA) is the only CLOSED league in the sample (salary cap, draft, no relegation), designed to compress force dispersion; the European ones are open. Prediction: a closed structure ⇒ more competitiveness ⇒ balanced skew.
- MLS is the most balanced by structural measure: Noll-Scully 1.13, rank 1/38 (the most competitive in the sample) — exactly what a cap + no-relegation predict. Ex-ante skew +0.162, below the open mean (+0.219); p_fav 0.503.
- On the curve, with an honest nuance: residual vs the open-league law −0.06 (~1 sd) — the MLS sits at the competitive/balanced extreme, consistent with the open-vs-closed theory.
- Conclusion: the closed league does not break the law — its structure tightens competitiveness and the skewness moves towards the balanced. It is not a sharp test (only 1 closed league in the sample; a full test needs more closed leagues = external data).
Artefacts: analysis/32_open_vs_closed.py, outputs/open_vs_closed.csv,
outputs/fig/f20_open_vs_closed.png.
What does the asymmetry imply for optimal bankroll growth?
- Kelly says DON'T bet: under the real margin, 0.0% of bets have EV>0 (f*=0 in all) — after the vig there is no growth to extract (echoes the C1 efficiency).
- Skewness is the FLB channel: decomposing the log-growth (g ≈ μ − σ²/2 + m₃/3) at a fixed fraction, the longshot SKEWNESS term is +0.60 (×1e3) vs +0.01 for the favourite — the positive asymmetry offsets part of the negative EV in growth/utility. It is the channel through which the preference for skew (FLB) survives being EV-negative.
- Conclusion: the skewness structure does not open growth (efficient market), but it quantitatively explains why the longshot bettor pays EV in exchange for asymmetry — the skewness premium in growth/utility terms.
Artefacts: skewlib/staking.py (kelly_fraction, growth_rate, moment_growth_terms),
analysis/33_kelly_staking.py, outputs/fig/f21_kelly.png.
Calibration of (h, c, σ_L) PER league (vs the global of P3/block 15): endogenous home advantage, draw cutoff and force dispersion.
- Plausible endogenous parameters (32 leagues): h [0.085, 0.350], c [0.297, 0.449] (per-league draw cutoff), σ_L [0.137, 0.436]. corr(c, draw rate) = +0.906 — c captures the league's "draw-proneness"; corr(σ_L, p_fav) = +0.874 — σ_L recovers observable competitiveness.
- The law survives: skew predicted by each league's OWN model vs observed r = +0.905, RMSE 0.026 — equal to the global (r=+0.90). Calibrating (h,c,σ) per league does not change the story.
- Conclusion: the invariance survives the endogenous draw cutoff; σ_L (competitiveness) keeps governing the skewness, league by league.
Artefacts: skewlib/model.py (calibrate_by_league),
analysis/34_per_league_calibration.py, outputs/per_league_calibration.csv,
outputs/fig/f22_per_league_calib.png.
1st round on the frozen dataset EXHAUSTED (2026-06-23): W1–W5 · P1–P5 · B1–B2 · C1–C3 · E1–E3 · D2–D4 · F1–F3 · G1–G3 · H2. The 2nd round (I…) follows, exploring untouched veins of the SAME dataset. Lineage in
lineage.json/docs/LINEAGE.md.
The law skewness=f(competitiveness) was derived from an ordered-probit over the latent margin. Here a COMPLETELY different model — a Poisson of GOALS (attack/defence + home advantage by league-season, result via Skellam) — generates the probabilities and the skewness. 617 league-seasons fitted.
- The goals model recovers competitiveness: corr(p_fav Poisson, empirical p_fav) across 38 leagues = +0.972 [+0.95,+0.99].
- And reproduces the skewness: corr(skew Poisson, empirical skew) = +0.925 [+0.85,+0.97]; the Poisson falls on the ordered-probit curve with r=+0.85 (vs empirical +0.90). The level is slightly lower (mean skew Poisson +0.177 vs +0.215) because the Poisson slightly underdisperses p_fav — the LAW (ordering) is what matters.
- Conclusion: three independent models — latent margin (ordered-probit), goals (Poisson) and the market (empirical) — fall on the SAME curve. The law is independent of the generating model; it is not an artefact of a chosen functional form.
Artefacts: skewlib/goals.py (fit_match_probs, league_season_table, by_league),
analysis/35_poisson_crossmodel.py, outputs/poisson_crossmodel_by_league.csv,
outputs/fig/f23_poisson_crossmodel.png.
Without opening odds (D1 out), the HALF-TIME RESULT is the information shock. The favourite's pre-match win probability updates with the HT scoreline, and the skewness of the "rest of the match" is again the identity (1−2q)/√(q(1−q)) at the conditional probability q. 150,950 matches with HT.
- The asymmetry RESOLVES with information: the favourite's HT state → the skew of the rest of the match: behind (20.6%) q=0.139, skew +2.08 (turned lottery-like); level (42.4%) q=0.402, skew +0.40; +1 (26.1%) q=0.757, skew −1.20; +2 or more (10.9%) q=0.945, skew −3.91 (almost certain). The mechanical identity holds at EVERY info state, not only at kick-off.
- Dynamic calibration (martingale): E[q conditional on the HT | p0 band] ≈ p0, mean error |p0−q| = 0.0035 — the pre-match probability is well calibrated and the HT refines it without bias.
- Conclusion: the FLB/identity is a DYNAMIC fact — the implied skewness tracks the win probability at any instant; it does not "discover" an efficient value over time, it already IS the algebraic image of the current probability. A temporal extension of W1.
Artefacts: skewlib/inplay.py (fav_state, conditional_table, martingale_check),
analysis/36_inplay_resolution.py, outputs/inplay_conditional.csv,
outputs/fig/f24_inplay.png.
The standardised skewness of the MEAN return of N (nearly) independent bets scales as skew(X)/√N. A diversified bankroll tends to the Gaussian; the isolated bet is strongly asymmetric.
- Single bet: realised skew favourite +0.230, longshot +2.254 (lottery-like). The mean return of N bets decays as skew/√N (empirical ≈ predicted).
- Diversifying kills the asymmetry: the favourite becomes ~Gaussian (skew<0.1) in ~6 bets; the longshot needs ~509 (much more skewed). The diversified syndicate sees ~Gaussian returns — only the negative EV.
- Conclusion: the asymmetry the bettor "loves" (Golec-Tamarkin) is that of the ISOLATED bet; it vanishes under diversification. The FLB survives because the RECREATIONAL bettor concentrates a few lottery-like bets — the microeconomic channel that sustains the bias being EV-negative (complements C3).
Artefacts: skewlib/portfolio.py (skew_decay, n_to_gaussian),
analysis/37_diversification.py, outputs/diversification.csv,
outputs/fig/f25_diversification.png.
Home advantage (HFA) has fallen in recent decades; does the skewness follow?
- HFA falls, skew does not: home win rate 0.449 (2005) → 0.431 (2025), β=−0.00133/year (Δ20yr −0.027, a marked fall); skewness β=−0.00009/year (Δ20yr −0.002, flat). corr(HFA, skew) year by year = −0.24 [−0.72,+0.55] (CI includes 0).
- Conclusion: the asymmetry depends on the DISPERSION of p_fav (competitiveness), not on the level of home advantage — closes the confound on the home-advantage side.
Artefacts: skewlib/extras.py (hfa_and_skew_by_year),
analysis/38_home_advantage.py, outputs/hfa_by_year.csv,
outputs/fig/f26_home_advantage.png.
The quant side: realised moments, VaR/CVaR and max drawdown of the cumulative P&L (unit bet, chronological order).
- Favourite vs longshot: favourite ret −4.82%, skew +0.23, exkurt −1.7, maxDD −9.9k units; longshot ret −10.2%, skew +2.25, exkurt +8.2, maxDD −20.9k. Both bleed the margin (negative final P&L), but the longshot is the TAIL — drawdown ~2× deeper, long losing streaks punctuated by rare prizes.
- Conclusion: the skewness structure translates into concrete bankroll risk — the longshot "lottery" is a deep drawdown + thick tails, not only a moment.
Artefacts: skewlib/extras.py (tail_metrics, max_drawdown),
analysis/39_tail_risk.py, outputs/fig/f27_tail_risk.png.
(1) Shannon entropy of the 1X2 distribution as an odds-based competitiveness index; (2) do the 1X2 skew and the O/U skew share a common factor?
- Entropy is competitiveness: mean 1.004 nats (3-way max 1.099); corr(entropy, skew) = +0.827 [+0.70,+0.91] — a robust alternative index (more entropy/more balanced ⇒ more positive skew).
- An honest NULL in the co-moment: corr(skew 1X2, skew O/U 2.5) = +0.15 [−0.39,+0.65] (CI includes 0). The two asymmetries are NOT a single factor: 1X2 measures the dispersion of who-wins (competitiveness), O/U measures the GOALS environment — largely orthogonal dimensions. Each market prices a different structural feature.
- Conclusion: entropy confirms the law via one more index; but the asymmetry is not a single latent across markets — it is specific to the dimension each market measures.
Artefacts: skewlib/extras.py (shannon_entropy, entropy_by_league),
analysis/40_entropy_comoment.py, outputs/entropy_comoment.csv,
outputs/fig/f28_entropy_comoment.png.
Front I validated the law with ONE alternative generator (a Poisson of goals). Here we subject it to a BATTERY of genuinely distinct generators, each producing (pH,pD,pA) per match per league-season, and measure whether they all reproduce the law and fall on the ordered-probit curve S(σ_L). 617 league-seasons, 38 leagues.
- Five families + the market, one curve. corr(model skew, empirical skew) across 38 leagues: Poisson +0.925 [+0.85,+0.97] · Dixon-Coles +0.874 [+0.77,+0.94] · Bradley-Terry-Davidson +0.840 [+0.71,+0.93] · results Elo (odds-free) +0.786 [+0.59,+0.90]. All recover competitiveness (corr p_fav +0.91…+0.97) and fall on the derived curve (r on the curve +0.85…+0.96 vs empirical +0.90).
- Dixon-Coles approximates the market LEVEL: the dependence correction at low scores (mean ρ −0.05, pushes 0-0/1-1 draws) raises the skew to +0.199 (vs Poisson +0.177), closer to the empirical +0.215 — the canonical football model best matches the level, not only the ordering.
- A family WITHOUT goals also falls on the law: Bradley-Terry-Davidson (multiplicative forces + draw, logistic pairwise comparison — no goals) reproduces the ordering at +0.84. And the ODDS-FREE generator (results Elo → ordinal map, the W2 machine) falls exactly on the curve (r=+0.96).
- Models designed to deviate COLLAPSE to the Poisson: football goals are nearly pure independent Poisson — median home×away covariance −0.07 (bivariate Poisson λ₃≈0) and over-dispersion ≈0 (Negative-Binomial α≈0). Both collapse to the Poisson and add nothing — reported as robustness, not as a series.
- Reproduction note (hardening): under the new stack (pandas 3 / numpy 2 /
current statsmodels) the GLM of a pathological league-season (JAP 2017) suffers
quasi-complete separation (p_fav≈1 in every match vs ~0.48 empirical), exploding
the fair-odds skewness. Added
goals.degenerate_fit(a separation guard); Front I returns exactly to the ledger (corr_skew +0.925) and the battery is armoured. - Conclusion: the law skewness=f(competitiveness) is not an artefact of any functional form — margin-probit, goals-Poisson, goals-with-dependence, logistic-forces and results-ratings all converge to the SAME curve. It is the geometry of the mixture of two-point bets over the league's competitiveness distribution.
Artefacts: skewlib/crossmodel.py (dc_probs, dc_rho, btd_probs, elo_by_league,
battery_table), skewlib/goals.py (fit_rates, degenerate_fit),
analysis/41_model_battery.py, outputs/model_battery_by_league.csv,
outputs/fig/f29_model_battery.png.
2nd round on the dataset (I–N) completed (2026-06-23): cross-model Poisson, HT→FT dynamics, diversification, secular HFA, realised tail, entropy+co-moment. Phase O (2026-06-23): battery of generating models — 5 independent families + the market on the same curve (model independence).
The 1st/2nd rounds exhausted the frozen mirror. These fronts use the canonical football-data.co.uk (
data/canonical/, downloaded byanalysis/50_fetch_canonical.py), which brings what the mirror lacks: OPENING vs CLOSING odds and pre-2005 depth. VAR (H1) uses the frozen mirror. Provenance of the canonical data incanonical_hash()(stamped in each phase).
The mirror has only the close; the canonical brings OPENING odds (Avg*) and CLOSING (Avg*C) for the same match (2019/20–2023/24, 21 leagues, 34,659 matches). A test of the thesis on the TEMPORAL axis of price formation: is the skewness inherited from the structure (present already at the open) or produced by trading (built up to the close)?
- The asymmetry is already in the opening price: global skew opening +0.248 → closing +0.249 (within ~1.03 in both); corr(skew_open, skew_close) across 21 leagues = +0.998 [+0.99,+1.00]. Mean Δskew per league +0.0005 (sd 0.0045).
- The market refines the MARGIN, not the asymmetry: overround opening 1.0609 → closing 1.0597; favourite Brier 0.2344 → 0.2330 (the close is sharper). But the structural law is present already at the open: corr(skew, p_fav) = −0.866 (open) / −0.874 (close). Favourite drift +0.0001 (no systematic steam).
- Conclusion: the asymmetry is not built up by trading — it is in the first price. The close tightens the margin and the calibration and leaves the skewness intact. A TEMPORAL extension of the margin orthogonality (W4/D2, which was across bookmakers).
Artefacts: skewlib/fdcanon.py, analysis/42_open_close.py,
outputs/open_close_by_league.csv, outputs/fig/f30_open_close.png.
VAR is an institutional shock that does NOT alter the force dispersion of the teams. Staggered difference-in-differences (frozen mirror): leagues adopt in 2018/2019/2020; lower English/Scottish divisions (no league VAR) are the never-treated control. 321 league-years, 10 treated + 6 controls.
- NULL effect on the skewness: β=−0.0066 [−0.035,+0.022], p=0.65 (=−0.14 SD of the league, CI includes 0). The favourite win rate β=+0.0024 (p=0.80) and the market p_fav β=+0.0050 (p=0.45) — also null. Event-study with no jump at adoption.
- Contrast with COVID (W3): the only shock that moved the skewness was COVID (+0.42 SD, via the HFA drop) — a REAL competitiveness shock. VAR, which does not touch the force structure, leaves the skewness invariant.
- Conclusion: a competitiveness placebo — only COMPETITIVENESS shocks move the skewness, not institutional factors. Confirms skewness = f(force dispersion).
Artefacts: skewlib/var.py, analysis/44_var.py, outputs/var_panel.csv,
outputs/fig/f31_var.png.
The paper predicts that the league baseline shifts at the regime shocks (Bosman 95, Champions 94/95, revenue ~2003). We extend backwards with WILLIAM HILL — the only bookmaker continuous over 2000–2025 (a consistent bookmaker; skewness is bookmaker-invariant, G1/D2). 158,323 matches, 21 leagues, 2000–2023.
- NO break in 2005: the study's ≥2005 cut-off is NOT a regime boundary — PELT finds breaks scattered (2005/2007/2010×2/2014/2016), none common. The per-league STRUCTURE is the same since 2000: corr(pre-2005 baseline, modern) across 17 leagues = +0.76.
- LEVEL drift weak and marginal: modern +0.232 vs pre-2005 +0.214 (Δ +0.018); league FE β=−0.019 [−0.037,−0.002] p=0.03, but paired p=0.10 and endpoint-sensitive (with partial 2024 included, p=0.25). The magnitude is well below the between-league variation (sd 0.047) — consistent with INTRA-regime invariance + slow balance evolution, not timelessness.
- Honest limit: 1X2 odds only exist since ~2000 — the 1990s shocks (Bosman/Champions) lie BEFORE the odds and remain untestable. We extend the invariance window by 5 years; the paper's full prediction requires pre-odds data.
Artefacts: skewlib/fdcanon.py (WH), analysis/43_pre2005.py,
outputs/pre2005_by_league.csv, outputs/fig/f32_pre2005.png.
3rd round (external data) completed (2026-06-23): D1 (opening→closing), H1 (VAR), P6 (pre-2005). 38 phases in the ledger.
The project's original objective was to measure the similarity of asymmetries. All the evidence above becomes a single APPARATUS (
skewlib/skewmeter.py) that measures the asymmetry signature of an entity and the distance between the asymmetries of two.
An apparatus that measures, by league/era/market/window: the signature (skew/shape
- competitiveness), the RAW distance, the RESIDUAL distance (competitiveness netted out by the closed-form law), the sampling floor and an equivalence verdict. 38 leagues.
-
Similarity of asymmetries = similarity of competitiveness. The raw distance |Δskew| has a median across leagues of 0.051; netting out competitiveness by 1 parameter (mean p_fav), it falls to a residual 0.023 — 1 number explains R²=0.82 of the variance.
-
Sufficiency ladder (a new result, correcting the 1st reading): 1 parameter R²=0.82 → 2 moments (mean+variance of p_fav) R²=0.98 → the ENTIRE distribution R²=0.99 (residual = sampling floor). The minimal sufficient statistic is the distribution of p_fav; the mean alone leaves a stable residual (temporal split-half r=0.98), which is the curvature of the law (captured by the 2nd moment), not noise.
-
A FEW-parameter apparatus: without Shin (inverse-odds, ~0 cost) corr 0.997 · 1 parameter 0.90 · odds-free (W/D/L only) 0.83. Real-time convergence: SE 0.026 in 200 matches, 0.018 in 400 (< between-league sd 0.051 → already ranks).
-
Verdict by EQUIVALENCE (TOST), not significance (with huge n everything rejects): margin ½·sd=0.026. E0 vs E3 (Premier League vs the English 4th division): raw skew 0.167 vs 0.294 (very different) → residual 0.009 → EQUIVALENT (competitiveness explains 93%). E0 vs SP1 equivalent; N1 vs I2 and BRA vs ARG distinct (a real residual).
-
Conclusion: "how similar are the asymmetries of A and B?" reduces to "how similar are the competitivenesses of A and B?" — measurable with 1 number, in real time, even without odds. The distribution of p_fav is what makes the reduction EXACT.
-
Hardening (rigour): SE by season block-bootstrap 0.0070 vs i.i.d. 0.0042 (×1.7, intra-year dependence); Mahalanobis shape distance (skew+ exkurt) corr 0.74 with the scalar; the law out-of-sample (calibrate even years, predict odd) R²=0.80 ≈ in-sample 0.82 — the residual ruler is not overfit.
Artefacts: skewlib/skewmeter.py (measure, distance, residual, sufficiency_ladder,
tost, skew_se_block, shape_distance, law_oos_r2), analysis/45_skewmeter.py,
outputs/fig/f33_skewmeter.png. Product: interactive widget site/src/components/ SkewMeter.astro (gauge + law/residual + similarity) in section §07 of the site, with
data exported by export_site_data.py (blocks skewmeter/convergence). Paper §4.8.
Each match offers three two-point bets: favourite (argmax p), draw (outcome D) and longshot (argmin p). Measuring the pooled ex-ante skewness of the three per league (38 leagues, market odds):
- All positively skewed (lottery-like): global skew +0.236 / +1.294 / +2.349 (favourite / draw / longshot). The FLB is a single-bet phenomenon, everywhere.
- All three governed by competitiveness, in opposite directions: less balance (p_fav ↑) lowers the favourite's skew (corr −0.90) and raises that of the draw and the longshot (corr +0.95 / +0.91), which become bigger longshots. It is not a law of the favourite — it is the SAME structural law, mirrored across the whole book.
- Diversification (block 37, now exposed): the skew of the mean return of N bets ≈ skew/√N. A favourites portfolio turns Gaussian in ~6 bets; the longshot's survives up to ~509 — which is why the bias bites the recreational bettor, not the syndicate.
Artefacts: skewlib/exante.py (fav_dog_draw, bettype_by), analysis/46_bettype.py,
outputs/fig/f34_bettype.png. Product: "Every side of the book" +
"What survives diversification" panels in the SkewMeter.astro widget; Mahalanobis
shape column (shape_cov_inv). Service: API study/api/ (POST /measure, modes
with-odds/odds-free, monitor /integrity). Data in export_site_data.py (blocks
bettype/diversification).
5th round (product) (2026-06-23): bet-type — the law skew=f(competitiveness) holds across the whole book (favourite/draw/longshot), with diversification ~6 vs ~509. The apparatus became a product: API
/measure(with-odds + odds-free + integrity) and a V2 widget (bet-type radar + diversification curve + Mahalanobis). 40 phases in the ledger. Remaining frontiers: other sports (decision: football exclusively) and pre-2000 odds (non-existent). Lineage inlineage.json/LINEAGE.md.
The CANONICAL layer (skewlib/canonical.py + adapters/) made the core
sport-agnostic — it only needs (p, o, won) per bet. Plugging in tennis
(tennis-data.co.uk, ATP+WTA 2005–2025, frozen snapshot in
data/PROVENANCE-tennis.json), a sport with a 2-outcome market (no draw) and
an independent odds source, over 62,865 matches and with ZERO new science:
- Calibration: mean p_fav 0.688 ≈ real favourite win 0.692 — the de-vig is reliable beyond football.
- The law reappears: the favourite's skew is more negative where the tournament is more imbalanced — corr(skew_fav, p_fav) by tier = −1.00 (ATP) / −0.98 (WTA) (football −0.90). Grand Slam (more imbalanced) has the most negative favourite in both tours.
- The longshot is lottery-like: skew +2.314 ≈ football +2.349.
The structural invariance is not an artefact of 1X2 nor of football: it is a property of the SPORT as a competitive system. External validity for §7 (the "one sport" limitation).
Artefacts: skewlib/adapters/tennis.py, analysis/00b_fetch_tennis.py,
analysis/48_tennis.py, outputs/fig/f35_crosssport.png,
outputs/tennis_by_tier.csv. The core (canonical/skewmeter) unchanged.
6th round (external validity) (2026-06-23): tennis — the law skew=f(competitiveness) and the lottery-like longshot reappear in a 2nd sport (ATP+WTA, 2-outcome market, independent odds), via the canonical layer without changing the core. 41 phases in the ledger. Adding a sport = one adapter (
docs/DATA-SCHEMA.md). Lineage inlineage.json/LINEAGE.md.
A 3rd sport on the same canonical layer, without touching the core. Plugging in
the NBA (sportsbookreviewsonline.com, 16 seasons 2007–08…2022–23, frozen
snapshot in data/PROVENANCE-basketball.json), the moneyline market (2
outcomes, no draw) and an odds source independent of football and tennis, over
19,621 matches and with ZERO new science:
- Calibration: mean p_fav 0.694 ≈ real favourite win 0.685 — the de-vig is reliable also in the NBA moneyline (the ~0.9pp gap is the favourite-longshot bias itself, with the favourite slightly overpriced).
- The law reappears: the favourite's skew is more negative in the more imbalanced seasons — corr(skew_fav, p_fav) by season = −0.95 (16 seasons; football −0.90, tennis −1.00/−0.98). The most lopsided NBA (2007–08, p_fav 0.71) has the most negative favourite; the most balanced (2022–23, p_fav 0.66) the least.
- The longshot is lottery-like: skew +2.609 ≈ football +2.349, tennis +2.314.
On the cross-sport curve (f36), tennis and basketball overlap in the same lopsided region (~0.66–0.75), both on the descending favourite curve and the ascending longshot curve that football traces at lower competitiveness. Three sports, three markets (1X2, match-odds, moneyline), three odds sources, one structural law.
Artefacts: skewlib/adapters/basketball.py, analysis/00c_fetch_basketball.py,
analysis/49_basketball.py, outputs/fig/f36_crosssport.png,
outputs/basketball_by_season.csv. The core (canonical/skewmeter) unchanged; no
new dependencies (the fetch uses the stdlib html.parser).
7th round (external validity, 3rd sport) (2026-06-23): basketball — the law skew=f(competitiveness) and the lottery-like longshot reappear in a 3rd sport (NBA, moneyline market, independent odds), via the canonical layer without changing the core. 42 phases in the ledger. f36 puts football+tennis+basketball on a single curve. Lineage in
lineage.json/LINEAGE.md.
W3 showed β_year≈0 with p=0.73 and a CI crossing zero. But a high p is only non-rejection of a trend — not evidence of absence (an underpowered test also gives a high p). Here we close the logical gap in §4.3 with an equivalence test (TOST):
- Pre-registered margin Δ = ½ the between-league SD (0.026), the SAME as in §4.8 — read as the largest 20-year drift we would treat as negligible.
- Result: the 90% CI of β falls entirely within [−Δ,+Δ] → we reject any drift larger than Δ, p_tost = 0.006. Robust to per-league bootstrap (p=0.005) and to the balanced panel (fixed basket of 15 leagues; β=−0.0003/year, p=0.043).
- Sensitivity: equivalent at ½ and 1× the SD; inconclusive only at a severe Δ of ¼ SD (drift < 0.013 over 20 years — below that the data neither affirms nor denies).
Conclusion: the skewness is not merely not shown to drift — it is statistically equivalent to not drifting, within a fraction of a single between-league difference accumulated over the whole history. It converts the temporal invariance from absence-of-evidence into evidence-of-absence.
Honest check (γ, C2): the same TOST applied to the preference parameter γ (annual series of 21 points, Δ=½ the between-league SD of γ ≈ 0.020) comes out INCONCLUSIVE (β_γ=+0.0003/year, drift +0.006 over 20yr, p_tost=0.19): the point drift is small, but the annual series lacks the power to certify equivalence at that margin. We record this deliberately — the test is not rigged to pass; only the skewness (n=638, league×season panel) has the power for the verdict. That is why the paper claims equivalence only for the skewness; the other "no drift" results (P4/FLB, C2/γ, L/HFA) stand as non-rejection.
Artefacts: skewlib/stats.py:tost, skewlib/panel.py:trend_boot,
analysis/51_temporal_equivalence.py, outputs/fig/f37_temporal_equivalence.png.
8th round (inferential rigour) (2026-06-23): temporal equivalence — the "no secular drift" (W3) becomes a positive claim by TOST (p_tost=0.006, margin ½ between-league SD; robust to bootstrap and balanced panel). 43 phases in the ledger. f37 (forest) enters as Figure 20; §4.3 and the abstract updated.
The /odds triangulation showed two observers — results-only Elo and a real
bookmaker — landing on the same curve. Phase V adds a third with a disjoint
information set: team strength = log market value of the actual starting
lineup (Transfermarkt, CC0 mirror; docs/SPIKE-O0.md), mapped to (P_A,P_D,P_H)
by the same MNLogit link the Elo observer uses (const+z+z², z = log value
ratio), fitted on 2013–2018 and evaluated on 2018–2025 — the state never
reads a result or a price; results touch only the low-dimensional link.
Three engines price the identical favourite bet at fair odds 1/p on the same 17,356 evaluation matches (top European leagues; the TM source has no valued lineups for the non-European first tiers). Bucketed by each observer's own p_fav:
| observer | information set | Brier | calib |p−f| | shape RMSE | corr law |
|---|---|---|---|---|---|
| elo | results only | 0.234 | 0.016 | 0.005 | +0.997 |
| book | the market | 0.230 | 0.012 | 0.006 | +0.994 |
| players | player values only | 0.235 | 0.032 | 0.005 | +0.994 |
The decomposition is the finding. The three are not equally good forecasters — the market is sharpest, the player observer (which never saw a result) is the most miscalibrated, 2.7× the book's level error. But the shape residual — realised skew minus the law evaluated at the bucket's realised frequency — is ~0.005 for all three, indistinguishable from zero. Calibration differs freely; the shape does not. Knowing more moves an observer along the law, never off it. This is the strong form of observer-invariance: the law holds for ANY p-generator, including one built from a completely different way of knowing the game.
WC overlay: each ledger fixture also carries the players' reading (log squad-
value ratio through the club-fitted link, values frozen in
wc_squad_values.json). On the three resolved R32 games with book odds the
players observer was the most seduced by Germany (p=0.790, skew −1.42 — the
loudest catastrophic-tail warning of the three; Germany fell on penalties).
Artefacts: skewlib/players.py (+ committed skewlib/data/{tm_club_map, value_link}.json), analysis/55_third_observer.py,
outputs/third_observer_buckets.csv, site /odds §"The third observer",
wc_squad_values.json. Tag evidence/observer-3.