Quick backtest mode for image builds (identical published numbers)
BACKTEST_QUICK=1 skips the hyperparameter grid and scores only the shipped + previous configs — same test metrics, a fraction of the build time on the 1-CPU VPS. The full grid search stays a local activity. Verified byte-level: quick output matches the grid run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+4
-1
@@ -4,7 +4,10 @@ WORKDIR /app
|
|||||||
COPY package.json bun.lock* bun.lockb* ./
|
COPY package.json bun.lock* bun.lockb* ./
|
||||||
RUN bun install
|
RUN bun install
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN bun run data:build # icons + fixtures.json + ratings.json → public/
|
# Quick backtest in image builds: score the shipped config only — the published
|
||||||
|
# numbers are identical; the hyperparam grid search is a local-dev activity.
|
||||||
|
ENV BACKTEST_QUICK=1
|
||||||
|
RUN bun run data:build # icons + fixtures.json + ratings.json + backtest → public/
|
||||||
RUN bun run build # tsc -b && vite build → /app/dist
|
RUN bun run build # tsc -b && vite build → /app/dist
|
||||||
RUN bun run build:server # esbuild → /app/server/dist/index.js
|
RUN bun run build:server # esbuild → /app/server/dist/index.js
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ const FORM_WINDOW = 10; // last N non-friendly matches feeding the form signal
|
|||||||
const MIN_VAL_GAIN = 0.0005; // candidate must beat the previous config by this much
|
const MIN_VAL_GAIN = 0.0005; // candidate must beat the previous config by this much
|
||||||
/** the previously shipped v3 config — the bar every candidate has to clear */
|
/** the previously shipped v3 config — the bar every candidate has to clear */
|
||||||
const PREV = { xi: 0.5, k: 5, w: 0.75, m: 2, beta: 0, gamma: 0 };
|
const PREV = { xi: 0.5, k: 5, w: 0.75, m: 2, beta: 0, gamma: 0 };
|
||||||
|
/** the v4 winner (from the full grid run) — keep in sync with buildRatings.ts */
|
||||||
|
const SHIPPED = { xi: 0.25, k: 3, w: 0.45, m: 3, beta: 0.3, gamma: 0 };
|
||||||
|
/** BACKTEST_QUICK=1 (Docker builds): skip the grid search and just score the
|
||||||
|
* shipped + previous configs — the published test numbers are identical, the
|
||||||
|
* hyperparam hunt only happens locally. */
|
||||||
|
const QUICK = process.env.BACKTEST_QUICK === '1';
|
||||||
|
|
||||||
interface Row { date: string; home: string; away: string; hs: number; as: number; tournament: string; neutral: boolean; t: number }
|
interface Row { date: string; home: string; away: string; hs: number; as: number; tournament: string; neutral: boolean; t: number }
|
||||||
type Probs = { h: number; d: number; a: number };
|
type Probs = { h: number; d: number; a: number };
|
||||||
@@ -241,11 +247,23 @@ function main(): void {
|
|||||||
return W_STEPS.map((w, wi) => ({ w, rps: acc[wi]! / preds.length }));
|
return W_STEPS.map((w, wi) => ({ w, rps: acc[wi]! / preds.length }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let best = { xi: PREV.xi, k: PREV.k, w: PREV.w, m: PREV.m, beta: 0, gamma: 0, rps: Infinity };
|
||||||
|
let prevValRps = Infinity; // validation RPS of the previously shipped config
|
||||||
|
|
||||||
|
if (QUICK) {
|
||||||
|
// ---------- quick mode: score only shipped + previous on validation ----------
|
||||||
|
console.log('quick mode — scoring shipped + previous configs on validation…');
|
||||||
|
const predsShipped = walkTeamDc(SHIPPED.xi, SHIPPED.k, tValFrom, tTestFrom);
|
||||||
|
const predsPrev = SHIPPED.xi === PREV.xi && SHIPPED.k === PREV.k
|
||||||
|
? predsShipped
|
||||||
|
: walkTeamDc(PREV.xi, PREV.k, tValFrom, tTestFrom);
|
||||||
|
const shippedRps = sweepW(predsShipped, SHIPPED.beta, SHIPPED.m, SHIPPED.gamma).find((x) => x.w === SHIPPED.w)!.rps;
|
||||||
|
prevValRps = sweepW(predsPrev, PREV.beta, PREV.m, PREV.gamma).find((x) => x.w === PREV.w)!.rps;
|
||||||
|
best = { ...SHIPPED, rps: shippedRps };
|
||||||
|
} else {
|
||||||
// ---------- validation stage 1: tune ξ, k, w (β=0, γ=0) ----------
|
// ---------- validation stage 1: tune ξ, k, w (β=0, γ=0) ----------
|
||||||
console.log('validation stage 1 — ξ/k/w grid (β=0, γ=0)…');
|
console.log('validation stage 1 — ξ/k/w grid (β=0, γ=0)…');
|
||||||
const valWalks = new Map<string, Pred[]>();
|
const valWalks = new Map<string, Pred[]>();
|
||||||
let best = { xi: PREV.xi, k: PREV.k, w: PREV.w, m: PREV.m, beta: 0, gamma: 0, rps: Infinity };
|
|
||||||
let prevValRps = Infinity; // validation RPS of the previously shipped config
|
|
||||||
for (const xi of XI_GRID) {
|
for (const xi of XI_GRID) {
|
||||||
for (const k of K_GRID) {
|
for (const k of K_GRID) {
|
||||||
const preds = walkTeamDc(xi, k, tValFrom, tTestFrom);
|
const preds = walkTeamDc(xi, k, tValFrom, tTestFrom);
|
||||||
@@ -279,6 +297,7 @@ function main(): void {
|
|||||||
if (r < best.rps) best = { ...best, gamma, w, rps: r };
|
if (r < best.rps) best = { ...best, gamma, w, rps: r };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
console.log(` champion: ξ=${best.xi} k=${best.k} w=${best.w} β=${best.beta} m=${best.m} γ=${best.gamma} rps=${best.rps.toFixed(4)}`);
|
console.log(` champion: ξ=${best.xi} k=${best.k} w=${best.w} β=${best.beta} m=${best.m} γ=${best.gamma} rps=${best.rps.toFixed(4)}`);
|
||||||
|
|
||||||
// ---------- decision rule: ship only a validated improvement ----------
|
// ---------- decision rule: ship only a validated improvement ----------
|
||||||
|
|||||||
Reference in New Issue
Block a user