From 145a8b336b29b6e45164b89705b1a84b718ced44 Mon Sep 17 00:00:00 2001 From: Fahri Can Date: Sat, 2 May 2026 16:32:42 +0300 Subject: [PATCH] fix(feeder): preserve pre-match odds when match goes live MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live odds have missing selections (e.g. '1' key removed from Maç Sonucu after kickoff), causing the AI model to produce wildly incorrect predictions (e.g. 3.5% home win for Bristol City). Two guards added: 1. fetchOddsForMatches: Exclude live/finished matches from odds fetch query 2. processMatchOdds: Skip odds/lineups/sidelined overwrite if match already has pre-match odds and is live/finished --- src/tasks/data-fetcher.task.ts | 47 +++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/tasks/data-fetcher.task.ts b/src/tasks/data-fetcher.task.ts index 82da6e1..01533cd 100755 --- a/src/tasks/data-fetcher.task.ts +++ b/src/tasks/data-fetcher.task.ts @@ -307,6 +307,9 @@ export class DataFetcherTask { const allowedLeagueIds = Array.from(new Set(topLeagueIds)); // Get matches needing odds (from 12 hours ago onward) + // CRITICAL: Only fetch odds/lineups for NOT STARTED matches. + // Once a match goes live, odds selections disappear or change, + // corrupting the pre-match data the AI model relies on. const twelveHoursAgo = new Date(Date.now() - 12 * 60 * 60 * 1000); const matchesToFetch = await this.prisma.liveMatch.findMany({ @@ -315,6 +318,15 @@ export class DataFetcherTask { ...(allowedLeagueIds.length > 0 ? { leagueId: { in: allowedLeagueIds } } : {}), + // Exclude live and finished matches — preserve their pre-match odds + NOT: { + OR: [ + { status: { in: FINISHED_STATUS_VALUES_FOR_DB } }, + { state: { in: FINISHED_STATE_VALUES_FOR_DB } }, + { status: { in: LIVE_STATUS_VALUES_FOR_DB } }, + { state: { in: LIVE_STATE_VALUES_FOR_DB } }, + ], + }, }, include: { homeTeam: { select: { name: true } }, @@ -891,7 +903,40 @@ export class DataFetcherTask { } } - // ALWAYS update oddsUpdatedAt to ensure rotation + // Guard: If match already has pre-match odds and is now live/finished, + // do NOT overwrite odds/lineups/sidelined — the model needs stable pre-match data. + const matchState = match.state?.toLowerCase() ?? ''; + const matchStatus = match.status?.toLowerCase() ?? ''; + const liveStates = LIVE_STATE_VALUES_FOR_DB.map((s) => s.toLowerCase()); + const liveStatuses = LIVE_STATUS_VALUES_FOR_DB.map((s) => s.toLowerCase()); + const finishedStates = FINISHED_STATE_VALUES_FOR_DB.map((s) => s.toLowerCase()); + const finishedStatuses = FINISHED_STATUS_VALUES_FOR_DB.map((s) => s.toLowerCase()); + + const isLiveOrFinished = + liveStates.includes(matchState) || + liveStatuses.includes(matchStatus) || + finishedStates.includes(matchState) || + finishedStatuses.includes(matchStatus); + + const existingOdds = match.odds as Record | null; + const hasExistingOdds = + !!existingOdds && + typeof existingOdds === 'object' && + Object.keys(existingOdds).length > 0; + + if (isLiveOrFinished && hasExistingOdds) { + // Match is live/finished and already has pre-match odds — skip data update + this.logger.debug( + `🛡️ Preserving pre-match data for ${match.matchName} (status: ${matchStatus}, state: ${matchState})`, + ); + await this.prisma.liveMatch.update({ + where: { id: match.id }, + data: { oddsUpdatedAt: new Date() }, + }); + return; + } + + // Update odds/lineups/sidelined for pre-match (NS) matches await this.prisma.liveMatch.update({ where: { id: match.id }, data: {