尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Simulating end of MLS season results, standings, and playoff picture
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Verde Run-In, model v2.3
Austin FC Western Conference playoff forecast. All figures are experimental model estimates.
The snapshot date lives in data.json (asOf) and the page renders it; this README no longer
hardcodes it, because the snapshot now refreshes automatically.
Reproduce the forecast
The site has no external JavaScript dependencies. Save model.js and data.json in one directory, then run with Node:
Use node tests.js to run invariant tests against the saved snapshot.
Use node refresh.js after updating the input snapshot to regenerate report.json and index.html.檢視完整 README ↗Austin FC Western Conference playoff forecast. All figures are experimental model estimates.
The snapshot date lives in data.json (asOf) and the page renders it; this README no longer
hardcodes it, because the snapshot now refreshes automatically.
The site has no external JavaScript dependencies. Save model.js and data.json in one directory, then run with Node:
const fs = require('node:fs');
const model = require('./model.js');
const data = JSON.parse(fs.readFileSync('./data.json', 'utf8'));
model.validate(data);
const report = model.simulate(data, {iterations: 50000, seed: Number(data.asOf.replaceAll('-', ''))});
console.log(report.atx, report.cutoff, report.unresolved);
Use node tests.js to run invariant tests against the saved snapshot.
Use node refresh.js after updating the input snapshot to regenerate report.json and index.html.
nextMatch (Austin's odds after each result
of its next match), rootingGuide (every fixture in the first week of remaining play × home win /
draw / away win → Austin's odds, ranked by swing), schedule (each Western club's remaining
matches and expected points), and baseline.pointsCurve (P(top 9 | exact final total)).League-average goals per team-game = total GF / total GP. For each team, season GF/GP and GA/GP are shrunk toward that league average with 8 pseudo-games.
With recentWeight set, every club's rates are first blended with its last six completed matches
(data.recentForm); clubs without a form sample fall back to season rates. Expected home goals = home attack × away concession / league average × exp(0.13). Expected away goals use exp(-0.13). Scores follow two Poisson distributions with a Dixon–Coles low-score correction: the joint probabilities of 0–0, 1–0, 0–1 and 1–1 are scaled by 1−λμρ, 1+μρ, 1+λρ and 1−ρ and the table is renormalised. Independent Poissons under-produce draws (this season: 95 real draws in 372 matches vs ~85 modelled), and a negative ρ repairs exactly that. ρ is fitted at every ingest so the model reproduces the season's observed draw count on the season's actual fixtures, and stored in data.calibration; the model reads it from there, so snapshots without the block fall back to ρ = 0. One shared score per fixture updates both teams' points, wins and goals. Strengths are fixed. East-only matches therefore cannot affect Western results and are omitted.
Ranking priority: points, wins, GD, GF. Remaining ties are randomly ordered with the fixed seed, with an explicit Austin best/worst tie bound. Historical H2H, discipline and venue tiebreakers are not implemented. This is disclosed; do not describe all tiebreakers as exact.
The headline presents marginal medians of Austin points and Western place. These do not assert that the median point total necessarily yields the median rank in the same season.
Beyond the headline odds: every club's finishing-position heatmap; standings with GD, GF, matches left and expected points from them; a weekly rooting guide (which results this week move Austin's odds, and which way); the points curve (how often each exact final total was enough); and Austin's magic and tragic numbers, which are arithmetic rather than simulation. Conditional cells in the rooting guide use 20,000 iterations under the same seed as everything else, so they are comparable with each other; the headline stays at 50,000.
simulate() accepts fixed results for any fixture: "H", "D", "A" from the home side's view
on any match, or "W", "D", "L" from Austin's view on Austin's own matches.
Baseline: 50,000 iterations, seed from asOf, priorGames 8, homeLog .13, calibrated drawRho, no form override.
Sensitivity: low-score correction off (ρ = 0); recent weight .2; priorGames 4 and 16; homeLog .08 and .18. Change one at a time.
Recent scenario blends every club's last six completed MLS matches, computed from the results feed rather than hand-entered.
User scenarios: 20,000 iterations with selected Austin W/D/L results. Scorelines are sampled conditional on each chosen result; rivals receive the same fixture outcome.
No validation against held-out seasons has been performed. These are not calibrated betting odds.
The forecast re-runs whenever any club we are competing with finishes a match, not just Austin. A rival dropping points moves Austin's odds as much as Austin winning does.
EventBridge (hourly) -> Lambda -> ASA results -> rebuild data.json
-> refresh.js -> commit to GitHub -> Vercel redeploys
Run it locally:
node ingest --dry-run # report what would change, write nothing
node ingest # rebuild data.json and regenerate the forecast
node tests.js # invariants against the new snapshot
Results come from the American Soccer Analysis MLS API:
free, keyless, documented, and the same endpoint their itscalledsoccer client libraries use. We
send an identifying User-Agent as their policy asks. Every run rebuilds all 30 clubs' records from
the full season game log rather than patching the previous snapshot, so a missed hour or a corrected
scoreline self-heals on the next run.
ASA publishes completed games only, so the remaining-fixture list cannot be refreshed
automatically. Ingestion only ever removes fixtures that have been played, matched on their
(home, away) pair. If MLS reschedules a match into or out of the run-in, model.validate() fails
the run with Incomplete schedule <TEAM> rather than publishing a wrong forecast, and data.json
needs a manual fixture edit. Postponements past the data boundary are moved to the next date both
clubs have free and disclosed in data.notes.
Needs a GitHub fine-grained PAT with contents:write on this repository, stored in Secrets Manager:
AWS_PROFILE=personal aws secretsmanager create-secret \
--name verde-run-in/github-token --secret-string 'github_pat_...'
AWS_PROFILE=personal ./deploy.sh
deploy.sh refuses to run against any account but the personal one, and fails before building if
the token secret is missing — an hourly job whose only output is a commit is worthless without it.
Redeploy after any change to the model, template, or hand-edited fixtures. The function runs
the copy of this repository that was packaged at deploy time, not the current main. Left stale,
the next result would republish the site from the old model and overwrite whatever was pushed.
The schedule is hardcoded in template.yaml rather than parameterised, because sam deploy --parameter-overrides splits values on spaces and rate(1 hour) cannot survive that.
Force a publish even when nothing has changed:
aws lambda invoke --function-name verde-run-in-refresh \
--payload '{"force":true}' --cli-binary-format raw-in-base64-out /dev/stdout
The function is a cheap no-op on most invocations, alarms after three consecutive failing hours, and commits with a message naming the results that triggered it.