Skip to content

Commit 7c5a281

Browse files
valvesssclaude
andcommitted
feat(worldcup): reconcile R32 + freeze R16, add title-odds forecast
Live-annex update as the 2026 cup reaches the round of 16 (data through 2026-07-03, sha bcf26ebc). - Reconcile the pre-registered ledger: all 15 R32 fixtures now resolved from the martj42 feed (+12 this pass), each carrying scoreline, advancement and shootout flag. Dump-reconcile block enriched to keep the 90'/120' score and merge web-sourced advancement (Australia 1-1 Egypt → Egypt on pens). - Freeze the 8 round-of-16 predictions (favourite, p_fav, ex-ante skew). - New: tournament title odds. The same odds-free Elo engine that prices each single-bet skew, propagated EXACTLY through the official bracket tree (no odds, no Monte-Carlo — disjoint feeders make the product form exact). Draws resolve by a coin-flip-shrunk shootout. skewlib/worldcup.py:{elo_state, advance_prob,simulate_bracket} + analysis/predict_worldcup_winner.py → wc_forecast.json. Leaders: Argentina 23.3%, France 19.5%, Spain 16.2%, Brazil 15.7%. - /worldcup: new "Who wins it" section (title-odds bars + reach-by-round table). Fix stale reproduce reference (41→52) + list the new scripts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 51ad259 commit 7c5a281

9 files changed

Lines changed: 1521 additions & 501 deletions

File tree

site/src/data/wc_forecast.json

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
{
2+
"schema": "wc-forecast@1",
3+
"generated_asof": "2026-07-03",
4+
"data_sha256": "bcf26ebc26bd911f",
5+
"model": "elo-results-only",
6+
"method": "exact bracket propagation (no odds, no Monte-Carlo)",
7+
"n_intl": 49493,
8+
"n_wc": 1052,
9+
"bracket": {
10+
"r16": [
11+
{
12+
"home": "Canada",
13+
"away": "Morocco",
14+
"home_flag": "ca",
15+
"away_flag": "ma",
16+
"neutral": true,
17+
"fav": "Morocco",
18+
"p_fav": 0.5747
19+
},
20+
{
21+
"home": "Paraguay",
22+
"away": "France",
23+
"home_flag": "py",
24+
"away_flag": "fr",
25+
"neutral": true,
26+
"fav": "France",
27+
"p_fav": 0.7932
28+
},
29+
{
30+
"home": "Brazil",
31+
"away": "Norway",
32+
"home_flag": "br",
33+
"away_flag": "no",
34+
"neutral": true,
35+
"fav": "Brazil",
36+
"p_fav": 0.7847
37+
},
38+
{
39+
"home": "Mexico",
40+
"away": "England",
41+
"home_flag": "mx",
42+
"away_flag": "gb-eng",
43+
"neutral": false,
44+
"fav": "Mexico",
45+
"p_fav": 0.5347
46+
},
47+
{
48+
"home": "Portugal",
49+
"away": "Spain",
50+
"home_flag": "pt",
51+
"away_flag": "es",
52+
"neutral": true,
53+
"fav": "Spain",
54+
"p_fav": 0.5888
55+
},
56+
{
57+
"home": "United States",
58+
"away": "Belgium",
59+
"home_flag": "us",
60+
"away_flag": "be",
61+
"neutral": false,
62+
"fav": "Belgium",
63+
"p_fav": 0.5327
64+
},
65+
{
66+
"home": "Argentina",
67+
"away": "Egypt",
68+
"home_flag": "ar",
69+
"away_flag": "eg",
70+
"neutral": true,
71+
"fav": "Argentina",
72+
"p_fav": 0.9064
73+
},
74+
{
75+
"home": "Switzerland",
76+
"away": "Colombia",
77+
"home_flag": "ch",
78+
"away_flag": "co",
79+
"neutral": true,
80+
"fav": "Colombia",
81+
"p_fav": 0.5769
82+
}
83+
],
84+
"qf_feeders": [
85+
[
86+
0,
87+
1
88+
],
89+
[
90+
2,
91+
3
92+
],
93+
[
94+
4,
95+
5
96+
],
97+
[
98+
6,
99+
7
100+
]
101+
],
102+
"sf_feeders": [
103+
[
104+
0,
105+
1
106+
],
107+
[
108+
2,
109+
3
110+
]
111+
],
112+
"note": "R16 ties pair sequentially into the QFs; QFs into the SFs; SFs into the final."
113+
},
114+
"teams": [
115+
{
116+
"team": "Argentina",
117+
"flag": "ar",
118+
"elo": 2158.8,
119+
"r16_opp": "Egypt",
120+
"r16_opp_flag": "eg",
121+
"r16_neutral": true,
122+
"p_r16": 0.9064,
123+
"p_qf": 0.9064,
124+
"p_sf": 0.6864,
125+
"p_final": 0.3915,
126+
"p_win": 0.2327
127+
},
128+
{
129+
"team": "France",
130+
"flag": "fr",
131+
"elo": 2098.6,
132+
"r16_opp": "Paraguay",
133+
"r16_opp_flag": "py",
134+
"r16_neutral": true,
135+
"p_r16": 0.7932,
136+
"p_qf": 0.7932,
137+
"p_sf": 0.5463,
138+
"p_final": 0.3512,
139+
"p_win": 0.1946
140+
},
141+
{
142+
"team": "Spain",
143+
"flag": "es",
144+
"elo": 2138.3,
145+
"r16_opp": "Portugal",
146+
"r16_opp_flag": "pt",
147+
"r16_neutral": true,
148+
"p_r16": 0.5888,
149+
"p_qf": 0.5888,
150+
"p_sf": 0.4754,
151+
"p_final": 0.2855,
152+
"p_win": 0.1617
153+
},
154+
{
155+
"team": "Brazil",
156+
"flag": "br",
157+
"elo": 2087.3,
158+
"r16_opp": "Norway",
159+
"r16_opp_flag": "no",
160+
"r16_neutral": true,
161+
"p_r16": 0.7847,
162+
"p_qf": 0.7847,
163+
"p_sf": 0.5404,
164+
"p_final": 0.2909,
165+
"p_win": 0.1566
166+
},
167+
{
168+
"team": "Portugal",
169+
"flag": "pt",
170+
"elo": 2031.4,
171+
"r16_opp": "Spain",
172+
"r16_opp_flag": "es",
173+
"r16_neutral": true,
174+
"p_r16": 0.4112,
175+
"p_qf": 0.4112,
176+
"p_sf": 0.2878,
177+
"p_final": 0.1309,
178+
"p_win": 0.0549
179+
},
180+
{
181+
"team": "England",
182+
"flag": "gb-eng",
183+
"elo": 2038.8,
184+
"r16_opp": "Mexico",
185+
"r16_opp_flag": "mx",
186+
"r16_neutral": false,
187+
"p_r16": 0.4653,
188+
"p_qf": 0.4653,
189+
"p_sf": 0.1968,
190+
"p_final": 0.0932,
191+
"p_win": 0.0439
192+
},
193+
{
194+
"team": "Morocco",
195+
"flag": "ma",
196+
"elo": 1956.4,
197+
"r16_opp": "Canada",
198+
"r16_opp_flag": "ca",
199+
"r16_neutral": true,
200+
"p_r16": 0.5747,
201+
"p_qf": 0.5747,
202+
"p_sf": 0.2522,
203+
"p_final": 0.113,
204+
"p_win": 0.0407
205+
},
206+
{
207+
"team": "Colombia",
208+
"flag": "co",
209+
"elo": 2043.2,
210+
"r16_opp": "Switzerland",
211+
"r16_opp_flag": "ch",
212+
"r16_neutral": true,
213+
"p_r16": 0.5769,
214+
"p_qf": 0.5769,
215+
"p_sf": 0.1897,
216+
"p_final": 0.0784,
217+
"p_win": 0.0341
218+
},
219+
{
220+
"team": "Belgium",
221+
"flag": "be",
222+
"elo": 1986.0,
223+
"r16_opp": "United States",
224+
"r16_opp_flag": "us",
225+
"r16_neutral": false,
226+
"p_r16": 0.5327,
227+
"p_qf": 0.5327,
228+
"p_sf": 0.158,
229+
"p_final": 0.0622,
230+
"p_win": 0.0224
231+
},
232+
{
233+
"team": "Mexico",
234+
"flag": "mx",
235+
"elo": 1952.2,
236+
"r16_opp": "England",
237+
"r16_opp_flag": "gb-eng",
238+
"r16_neutral": false,
239+
"p_r16": 0.5347,
240+
"p_qf": 0.5347,
241+
"p_sf": 0.1678,
242+
"p_final": 0.0608,
243+
"p_win": 0.0216
244+
},
245+
{
246+
"team": "Canada",
247+
"flag": "ca",
248+
"elo": 1859.5,
249+
"r16_opp": "Morocco",
250+
"r16_opp_flag": "ma",
251+
"r16_neutral": true,
252+
"p_r16": 0.4253,
253+
"p_qf": 0.4253,
254+
"p_sf": 0.1358,
255+
"p_final": 0.0434,
256+
"p_win": 0.0107
257+
},
258+
{
259+
"team": "Switzerland",
260+
"flag": "ch",
261+
"elo": 1944.7,
262+
"r16_opp": "Colombia",
263+
"r16_opp_flag": "co",
264+
"r16_neutral": true,
265+
"p_r16": 0.4231,
266+
"p_qf": 0.4231,
267+
"p_sf": 0.0968,
268+
"p_final": 0.0284,
269+
"p_win": 0.0088
270+
},
271+
{
272+
"team": "Norway",
273+
"flag": "no",
274+
"elo": 1905.9,
275+
"r16_opp": "Brazil",
276+
"r16_opp_flag": "br",
277+
"r16_neutral": true,
278+
"p_r16": 0.2153,
279+
"p_qf": 0.2153,
280+
"p_sf": 0.095,
281+
"p_final": 0.0293,
282+
"p_win": 0.0087
283+
},
284+
{
285+
"team": "United States",
286+
"flag": "us",
287+
"elo": 1853.1,
288+
"r16_opp": "Belgium",
289+
"r16_opp_flag": "be",
290+
"r16_neutral": false,
291+
"p_r16": 0.4673,
292+
"p_qf": 0.4673,
293+
"p_sf": 0.0788,
294+
"p_final": 0.0187,
295+
"p_win": 0.004
296+
},
297+
{
298+
"team": "Paraguay",
299+
"flag": "py",
300+
"elo": 1824.9,
301+
"r16_opp": "France",
302+
"r16_opp_flag": "fr",
303+
"r16_neutral": true,
304+
"p_r16": 0.2068,
305+
"p_qf": 0.2068,
306+
"p_sf": 0.0656,
307+
"p_final": 0.0182,
308+
"p_win": 0.0038
309+
},
310+
{
311+
"team": "Egypt",
312+
"flag": "eg",
313+
"elo": 1800.2,
314+
"r16_opp": "Argentina",
315+
"r16_opp_flag": "ar",
316+
"r16_neutral": true,
317+
"p_r16": 0.0936,
318+
"p_qf": 0.0936,
319+
"p_sf": 0.0271,
320+
"p_final": 0.0044,
321+
"p_win": 0.0007
322+
}
323+
]
324+
}

0 commit comments

Comments
 (0)