vv
Deploy Iddaai Backend / build-and-deploy (push) Successful in 1m7s

This commit is contained in:
2026-06-02 03:37:00 +03:00
parent 671979b07d
commit 4e563e996e
10 changed files with 708 additions and 0 deletions
+65
View File
@@ -399,6 +399,12 @@ export class DataFetcherTask {
const closingOddsSnapshot = await this.getClosingOddsSnapshot(
row.matchId,
);
// ── Per-market settlement (V31e forward-test) ────────────────
// Score EVERY captured market (not just main_pick) against reality,
// so the admin Model Performance page can compute per-market
// calibration (model% → actual%) and ROI. won=null → push (skip).
const marketsSettled = this.settleAllMarkets(row);
const settlementSummary = {
settled_at: new Date().toISOString(),
model_version: row.engineVersion,
@@ -413,6 +419,7 @@ export class DataFetcherTask {
away: row.htScoreAway,
},
closing_odds_snapshot: closingOddsSnapshot,
markets_settled: marketsSettled,
};
await this.prisma.$executeRawUnsafe(
@@ -538,6 +545,64 @@ export class DataFetcherTask {
};
}
/**
* V31e forward-test: settle EVERY captured market (payload_summary.markets_full)
* against the final score. Produces one compact record per market with its
* shown probability, the real outcome, and flat profit — the raw material for
* per-market calibration (model% vs actual%) on the admin dashboard.
*/
private settleAllMarkets(
row: PendingPredictionRunForSettlement,
): Array<Record<string, unknown>> {
const summary = this.asRecord(row.payloadSummary);
const markets = Array.isArray(summary.markets_full)
? (summary.markets_full as unknown[])
: [];
const out: Array<Record<string, unknown>> = [];
for (const raw of markets) {
const m = this.asRecord(raw);
const market = typeof m.market === "string" ? m.market : "";
const pick = typeof m.pick === "string" ? m.pick : "";
if (!market || !pick) continue;
const won = this.isPredictionPickWon({
market,
pick,
scoreHome: row.scoreHome,
scoreAway: row.scoreAway,
htScoreHome: row.htScoreHome,
htScoreAway: row.htScoreAway,
});
if (won === null) continue; // push / unresolvable → exclude from stats
const odds = Number(m.odds || 0);
const hasOdds = Number.isFinite(odds) && odds > 1.01;
out.push({
market,
pick,
won,
// shown probability for calibration (0100). Prefer calibrated_confidence.
shown_confidence:
m.calibrated_confidence != null
? Number(m.calibrated_confidence)
: m.model_probability != null
? Number(m.model_probability) * 100
: null,
model_probability:
m.model_probability != null ? Number(m.model_probability) : null,
odds: hasOdds ? odds : null,
playable: m.playable === true,
bet_grade: typeof m.bet_grade === "string" ? m.bet_grade : null,
action: typeof m.action === "string" ? m.action : null,
value_tier: typeof m.value_tier === "string" ? m.value_tier : null,
// flat 1u profit if a real price existed (for per-market ROI)
flat_profit: hasOdds ? Number((won ? odds - 1 : -1).toFixed(4)) : null,
});
}
return out;
}
private isPredictionPickWon(input: {
market: string;
pick: string;