Skip to content

Commit 79a08d2

Browse files
valvesssclaude
andcommitted
feat(worldcup): live spin-off — structural skewness law on the World Cup
Takes the law (1−2p)/√(p(1−p)) out of the paper's population: clubs → national teams, market odds → NO odds. A results-only Elo over ~49k internationals predicts the favourite 1X2 bet skewness for every World Cup, 1930→2026. - skewlib/worldcup.py: martj42 loader, Elo+favourite, cuts (p_fav bucket, edition, phase), out-of-time forecast, scheduled fixtures, pre-registered ledger. - skewlib/elo.py: neutral-aware HFA (backward-compatible; study --check stays green). - analysis/41_worldcup.py: main validation → corr(predicted,realised)=+0.998 by p_fav bucket, crossing zero at p=½. 2022 hold-out proves the out-of-time forecast. - analysis/predict_worldcup.py: append-only ledger (wc_predictions.json) — freezes each upcoming game's prediction, reconciles when the score lands. - site /worldcup: page + interactive "next match" widget + live scorecard. Live annex: stays out of the frozen drift-ledger (carries its own sha256+date). Reuses the football elo/exante path (still football 1X2, national teams, no odds). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 79c36ca commit 79a08d2

16 files changed

Lines changed: 2137 additions & 4 deletions

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,24 @@ Every reported number regenerates from the frozen dataset (sha256 in
6565
for the full per-phase log and [`study/docs/METHODOLOGY.md`](study/docs/METHODOLOGY.md)
6666
for methods.
6767

68+
## Annex — the World Cup, live
69+
70+
The same law, taken **out of its own population**: from clubs to national teams,
71+
from market odds to **no odds at all**. A results-only Elo over ~49k internationals
72+
predicts the favourite-bet skewness of every World Cup from 1930 to 2026 — and
73+
**predicted vs realised correlate at +0.998** across favourite-probability buckets,
74+
crossing zero at `p = ½` exactly as `(1−2p)/√(p(1−p))` requires. A pre-registered
75+
ledger freezes the prediction for each upcoming match and reconciles it when the
76+
result lands. It updates during the 2026 tournament. Annex:
77+
[`study/docs/WORLDCUP.md`](study/docs/WORLDCUP.md) · page:
78+
[skew-dynamic.coldcodelabs.com/worldcup](https://skew-dynamic.coldcodelabs.com/worldcup).
79+
80+
```bash
81+
python study/analysis/41_worldcup.py # the analysis
82+
python study/analysis/export_worldcup_data.py # regenerate the page data
83+
python study/analysis/predict_worldcup.py # predict next games + reconcile ledger
84+
```
85+
6886
## The paper
6987

7088
Working draft (RSOS target): [`study/docs/paper/draft.md`](study/docs/paper/draft.md).

site/src/components/Nav.astro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const links = [
88
['Theory', '#theory'],
99
['Timeline', '#timeline'],
1010
['Reproduce', '#reproduce'],
11+
['World Cup', '/worldcup'],
1112
];
1213
---
1314
<nav class="nav">

site/src/data/wc_live.json

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
{
2+
"meta": {
3+
"data_date": "2026-06-28",
4+
"n_played_wc": 1037,
5+
"n_upcoming": 15,
6+
"source": "martj42/international_results",
7+
"sha256": "df6a30676640"
8+
},
9+
"next": {
10+
"match_id": "2026-06-29|Brazil|Japan",
11+
"date": "2026-06-29",
12+
"home": "Brazil",
13+
"away": "Japan",
14+
"neutral": true,
15+
"fav_team": "Brazil",
16+
"fav_pick": "H",
17+
"p_fav": 0.556,
18+
"skew_pred": -0.2255,
19+
"win_payoff": 0.8,
20+
"lose_payoff": -1.0,
21+
"verdict": "neg"
22+
},
23+
"upcoming": [
24+
{
25+
"match_id": "2026-06-29|Brazil|Japan",
26+
"date": "2026-06-29",
27+
"home": "Brazil",
28+
"away": "Japan",
29+
"neutral": true,
30+
"fav_team": "Brazil",
31+
"fav_pick": "H",
32+
"p_fav": 0.556,
33+
"skew_pred": -0.2255,
34+
"win_payoff": 0.8,
35+
"lose_payoff": -1.0,
36+
"verdict": "neg"
37+
},
38+
{
39+
"match_id": "2026-06-29|Germany|Paraguay",
40+
"date": "2026-06-29",
41+
"home": "Germany",
42+
"away": "Paraguay",
43+
"neutral": true,
44+
"fav_team": "Germany",
45+
"fav_pick": "H",
46+
"p_fav": 0.6233,
47+
"skew_pred": -0.5087,
48+
"win_payoff": 0.6,
49+
"lose_payoff": -1.0,
50+
"verdict": "neg_strong"
51+
},
52+
{
53+
"match_id": "2026-06-29|Netherlands|Morocco",
54+
"date": "2026-06-29",
55+
"home": "Netherlands",
56+
"away": "Morocco",
57+
"neutral": true,
58+
"fav_team": "Netherlands",
59+
"fav_pick": "H",
60+
"p_fav": 0.4809,
61+
"skew_pred": 0.0764,
62+
"win_payoff": 1.08,
63+
"lose_payoff": -1.0,
64+
"verdict": "flat"
65+
},
66+
{
67+
"match_id": "2026-06-30|France|Sweden",
68+
"date": "2026-06-30",
69+
"home": "France",
70+
"away": "Sweden",
71+
"neutral": true,
72+
"fav_team": "France",
73+
"fav_pick": "H",
74+
"p_fav": 0.7776,
75+
"skew_pred": -1.3352,
76+
"win_payoff": 0.29,
77+
"lose_payoff": -1.0,
78+
"verdict": "neg_strong"
79+
},
80+
{
81+
"match_id": "2026-06-30|Ivory Coast|Norway",
82+
"date": "2026-06-30",
83+
"home": "Ivory Coast",
84+
"away": "Norway",
85+
"neutral": true,
86+
"fav_team": "Norway",
87+
"fav_pick": "A",
88+
"p_fav": 0.4014,
89+
"skew_pred": 0.4021,
90+
"win_payoff": 1.49,
91+
"lose_payoff": -1.0,
92+
"verdict": "pos_strong"
93+
},
94+
{
95+
"match_id": "2026-06-30|Mexico|Ecuador",
96+
"date": "2026-06-30",
97+
"home": "Mexico",
98+
"away": "Ecuador",
99+
"neutral": false,
100+
"fav_team": "Mexico",
101+
"fav_pick": "H",
102+
"p_fav": 0.5004,
103+
"skew_pred": -0.0017,
104+
"win_payoff": 1.0,
105+
"lose_payoff": -1.0,
106+
"verdict": "flat"
107+
},
108+
{
109+
"match_id": "2026-07-01|Belgium|Senegal",
110+
"date": "2026-07-01",
111+
"home": "Belgium",
112+
"away": "Senegal",
113+
"neutral": true,
114+
"fav_team": "Belgium",
115+
"fav_pick": "H",
116+
"p_fav": 0.551,
117+
"skew_pred": -0.205,
118+
"win_payoff": 0.81,
119+
"lose_payoff": -1.0,
120+
"verdict": "neg"
121+
},
122+
{
123+
"match_id": "2026-07-01|England|DR Congo",
124+
"date": "2026-07-01",
125+
"home": "England",
126+
"away": "DR Congo",
127+
"neutral": true,
128+
"fav_team": "England",
129+
"fav_pick": "H",
130+
"p_fav": 0.7811,
131+
"skew_pred": -1.3593,
132+
"win_payoff": 0.28,
133+
"lose_payoff": -1.0,
134+
"verdict": "neg_strong"
135+
},
136+
{
137+
"match_id": "2026-07-01|United States|Bosnia and Herzegovina",
138+
"date": "2026-07-01",
139+
"home": "United States",
140+
"away": "Bosnia and Herzegovina",
141+
"neutral": false,
142+
"fav_team": "United States",
143+
"fav_pick": "H",
144+
"p_fav": 0.7416,
145+
"skew_pred": -1.1038,
146+
"win_payoff": 0.35,
147+
"lose_payoff": -1.0,
148+
"verdict": "neg_strong"
149+
},
150+
{
151+
"match_id": "2026-07-02|Portugal|Croatia",
152+
"date": "2026-07-02",
153+
"home": "Portugal",
154+
"away": "Croatia",
155+
"neutral": true,
156+
"fav_team": "Portugal",
157+
"fav_pick": "H",
158+
"p_fav": 0.5381,
159+
"skew_pred": -0.1529,
160+
"win_payoff": 0.86,
161+
"lose_payoff": -1.0,
162+
"verdict": "neg"
163+
},
164+
{
165+
"match_id": "2026-07-02|Spain|Austria",
166+
"date": "2026-07-02",
167+
"home": "Spain",
168+
"away": "Austria",
169+
"neutral": true,
170+
"fav_team": "Spain",
171+
"fav_pick": "H",
172+
"p_fav": 0.7168,
173+
"skew_pred": -0.9623,
174+
"win_payoff": 0.4,
175+
"lose_payoff": -1.0,
176+
"verdict": "neg_strong"
177+
},
178+
{
179+
"match_id": "2026-07-02|Switzerland|Algeria",
180+
"date": "2026-07-02",
181+
"home": "Switzerland",
182+
"away": "Algeria",
183+
"neutral": true,
184+
"fav_team": "Switzerland",
185+
"fav_pick": "H",
186+
"p_fav": 0.4806,
187+
"skew_pred": 0.0777,
188+
"win_payoff": 1.08,
189+
"lose_payoff": -1.0,
190+
"verdict": "flat"
191+
},
192+
{
193+
"match_id": "2026-07-03|Argentina|Cape Verde",
194+
"date": "2026-07-03",
195+
"home": "Argentina",
196+
"away": "Cape Verde",
197+
"neutral": true,
198+
"fav_team": "Argentina",
199+
"fav_pick": "H",
200+
"p_fav": 0.9104,
201+
"skew_pred": -2.8731,
202+
"win_payoff": 0.1,
203+
"lose_payoff": -1.0,
204+
"verdict": "neg_strong"
205+
},
206+
{
207+
"match_id": "2026-07-03|Australia|Egypt",
208+
"date": "2026-07-03",
209+
"home": "Australia",
210+
"away": "Egypt",
211+
"neutral": true,
212+
"fav_team": "Australia",
213+
"fav_pick": "H",
214+
"p_fav": 0.4958,
215+
"skew_pred": 0.0168,
216+
"win_payoff": 1.02,
217+
"lose_payoff": -1.0,
218+
"verdict": "flat"
219+
},
220+
{
221+
"match_id": "2026-07-03|Colombia|Ghana",
222+
"date": "2026-07-03",
223+
"home": "Colombia",
224+
"away": "Ghana",
225+
"neutral": true,
226+
"fav_team": "Colombia",
227+
"fav_pick": "H",
228+
"p_fav": 0.8442,
229+
"skew_pred": -1.8977,
230+
"win_payoff": 0.18,
231+
"lose_payoff": -1.0,
232+
"verdict": "neg_strong"
233+
}
234+
],
235+
"backtest2026": {
236+
"n": 73,
237+
"skew_pred_pool": -0.0108,
238+
"skew_real_pool": -0.1236,
239+
"p_fav_mean": 0.6135,
240+
"fav_hit_rate": 0.6164,
241+
"brier": 0.232
242+
},
243+
"ledger": {
244+
"n_pending": 15,
245+
"n_resolved": 0
246+
},
247+
"recent_resolved": []
248+
}

0 commit comments

Comments
 (0)