Elo trajectories: a century of strength on every team page

buildRatings samples each WC team's rating along the full walk (yearly
historically, monthly since 2022) into elohistory.json (~72KB, 48
teams). Team pages plot it as a clean SVG line with year and rating
gridlines — Germany's century, 1908 → today, in one glance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 01:12:36 +02:00
parent f58d6566fb
commit 047e9f8767
6 changed files with 108 additions and 0 deletions
+23
View File
@@ -74,6 +74,19 @@ function main(): void {
let sxy = 0, sxx = 0; // regression of goal-diff on effective Elo diff
const calib: { d: number; h: number; a: number }[] = [];
// Per-WC-team Elo trajectories: one sample per year historically, one per
// month for the recent window — small enough to serve, rich enough to plot.
const wcSet = new Set(ALL_TEAMS);
const RECENT_FROM = '2022-01-01';
const history = new Map<string, Map<string, number>>(); // team → periodKey → rating
const sample = (team: string, date: string, rating: number): void => {
if (!wcSet.has(team)) return;
const key = date >= RECENT_FROM ? date.slice(0, 7) : date.slice(0, 4);
let m = history.get(team);
if (!m) { m = new Map(); history.set(team, m); }
m.set(key, Math.round(rating));
};
let lastDate = '';
for (const r of rows) {
const home = canonicalTeam(r.home);
@@ -98,6 +111,8 @@ function main(): void {
const fastDelta = eloDelta(r.hs, r.as, feh, fea, k * FAST_M, homeAdv);
eloFast.set(home, feh + fastDelta);
eloFast.set(away, fea - fastDelta);
sample(home, r.date, eh + delta);
sample(away, r.date, ea - delta);
lastDate = r.date;
}
@@ -202,6 +217,14 @@ function main(): void {
mkdirSync(OUT_DIR, { recursive: true });
writeFileSync(join(OUT_DIR, 'ratings.json'), JSON.stringify(out));
// Per-team Elo trajectories for the team pages.
const eloHistory: Record<string, [string, number][]> = {};
for (const [team, m] of history) {
eloHistory[team] = [...m.entries()].sort((a, b) => a[0].localeCompare(b[0]));
}
writeFileSync(join(OUT_DIR, 'elohistory.json'), JSON.stringify({ recentFrom: RECENT_FROM, teams: eloHistory }));
console.log(`elo history → public/data/elohistory.json (${Object.keys(eloHistory).length} teams)`);
// Compact training rows for the server's nightly in-tournament Team-DC refit
// (absolute dates, so daysAgo can be recomputed as the tournament progresses).
const dcRows = rows