fix(feeder): preserve pre-match odds when match goes live
Deploy Iddaai Backend / build-and-deploy (push) Successful in 29s

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
This commit is contained in:
2026-05-02 16:32:42 +03:00
parent 7a8960edb8
commit 145a8b336b
+46 -1
View File
@@ -307,6 +307,9 @@ export class DataFetcherTask {
const allowedLeagueIds = Array.from(new Set(topLeagueIds)); const allowedLeagueIds = Array.from(new Set(topLeagueIds));
// Get matches needing odds (from 12 hours ago onward) // 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 twelveHoursAgo = new Date(Date.now() - 12 * 60 * 60 * 1000);
const matchesToFetch = await this.prisma.liveMatch.findMany({ const matchesToFetch = await this.prisma.liveMatch.findMany({
@@ -315,6 +318,15 @@ export class DataFetcherTask {
...(allowedLeagueIds.length > 0 ...(allowedLeagueIds.length > 0
? { leagueId: { in: allowedLeagueIds } } ? { 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: { include: {
homeTeam: { select: { name: true } }, 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<string, unknown> | 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({ await this.prisma.liveMatch.update({
where: { id: match.id }, where: { id: match.id },
data: { data: {