써포츠
혼자도 좋지만, 함께라면 더 빛나는 우리들의 플레이. 모임·뽐·대회·구장을 한 앱에서.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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.