#!/usr/bin/env node /** * RUN THE BACKTEST HARNESS (Session 64). * * Joins model_snapshots (the model's INPUTS + prediction) to ledger_entries * (the single source of truth for OUTCOMES) on the natural key, and runs the * harness. Outcomes are NEVER denormalized onto snapshots. * * Join key: (sport, player_key, stat, line, side, game_date). * Verified empirically: 283 clean 1:1 joins, ZERO ambiguity. `game_id` is NOT * usable — 400/550 snapshot rows carry `UNK@UNK` because home/away team names * weren't threaded into the grader until Session 64 Order 1.6. * * Rows that don't join are EXPECTED, not errors: retention stores BOTH sides * of every prop plus refusals, while the ledger keeps only the graded side of * non-refused props. * * node scripts/run-backtest.js # rows exported via SQL */ const harness = require('../src/services/backtestHarness'); const rows = JSON.parse(require('fs').readFileSync(process.argv[2], 'utf8')); const report = harness.runBacktest(rows, {}); const pad = (s, n) => String(s).padEnd(n); console.log('══════════ VYNDR BACKTEST HARNESS ══════════'); console.log(`generated_at : ${report.generated_at}`); console.log(`min_sample : ${report.min_sample}`); console.log(`VERDICT : ${report.verdict} (can_validate=${report.can_validate})`); console.log('\n--- denominator ---'); Object.entries(report.counts).forEach(([k, v]) => console.log(` ${pad(k, 22)} ${v}`)); console.log('\n--- grade buckets (4-letter) ---'); for (const b of report.grade_buckets.sort((a, z) => z.n - a.n)) { console.log(b.status === 'OK' ? ` ${pad(b.bucket, 4)} n=${pad(b.n, 5)} hit=${(b.hit_rate * 100).toFixed(1)}% 95% CI [${(b.ci_low * 100).toFixed(1)}, ${(b.ci_high * 100).toFixed(1)}]` : ` ${pad(b.bucket, 4)} n=${pad(b.n, 5)} INSUFFICIENT — need ${b.need} (short by ${b.short_by})`); } console.log('\n--- grade buckets (11-step) ---'); for (const b of report.grade_11_buckets.sort((a, z) => z.n - a.n)) { console.log(b.status === 'OK' ? ` ${pad(b.bucket, 4)} n=${pad(b.n, 5)} hit=${(b.hit_rate * 100).toFixed(1)}%` : ` ${pad(b.bucket, 4)} n=${pad(b.n, 5)} INSUFFICIENT`); } console.log(`\n--- monotonicity: ${report.monotonicity.verdict} ---`); report.monotonicity.comparisons.forEach((c) => console.log( ` ${c.higher} vs ${c.lower}: ${c.distinguishable ? (c.holds ? 'HOLDS' : 'BROKEN') : 'not distinguishable on this sample'}`, )); console.log('\n--- probability calibration ---'); console.log(report.probability.n ? ` n=${report.probability.n} Brier=${report.probability.brier.toFixed(4)}` : ' n=0 — no stored p_win on any joinable settled row'); console.log('\n--- strata (never mixed) ---'); report.strata.forEach((s) => console.log(` ${pad(s.sport, 6)} ${pad(s.model_version, 24)} n=${s.n}`)); require('fs').writeFileSync( process.argv[3] || '/tmp/backtest-report.json', JSON.stringify(report, null, 2), ); console.log(`\nfull report → ${process.argv[3] || '/tmp/backtest-report.json'}`);