import { PrismaClient } from '@prisma/client'; import * as dotenv from 'dotenv'; dotenv.config(); (BigInt.prototype as any).toJSON = function () { return this.toString(); }; const prisma = new PrismaClient(); async function main() { console.log('๐Ÿ” ANALYZING HT/FT REVERSAL MATCHES (1/2 & 2/1)'); console.log('='.repeat(80)); // Use raw SQL for performance const matches: any[] = await prisma.$queryRaw` SELECT m.id, m.ht_score_home, m.ht_score_away, m.score_home, m.score_away, m.mst_utc, ht.name as home_team, at.name as away_team, l.name as league FROM matches m LEFT JOIN teams ht ON ht.id = m.home_team_id LEFT JOIN teams at ON at.id = m.away_team_id LEFT JOIN leagues l ON l.id = m.league_id WHERE m.status = 'FT' AND m.ht_score_home IS NOT NULL AND m.ht_score_away IS NOT NULL AND m.score_home IS NOT NULL AND m.score_away IS NOT NULL ORDER BY m.mst_utc DESC `; console.log(`๐Ÿ“Š Total completed matches: ${matches.length}`); let htftCounts: Record = { '1/1': 0, '1/X': 0, '1/2': 0, 'X/1': 0, 'X/X': 0, 'X/2': 0, '2/1': 0, '2/X': 0, '2/2': 0 }; const reversals: any[] = []; for (const m of matches) { const htH = m.ht_score_home; const htA = m.ht_score_away; const ftH = m.score_home; const ftA = m.score_away; const htR = htH > htA ? '1' : htH === htA ? 'X' : '2'; const ftR = ftH > ftA ? '1' : ftH === ftA ? 'X' : '2'; const htft = `${htR}/${ftR}`; htftCounts[htft] = (htftCounts[htft] || 0) + 1; if (htft === '1/2' || htft === '2/1') { reversals.push({ ...m, htft, htH, htA, ftH, ftA }); } } const total = matches.length; console.log('\n๐Ÿ“Š HT/FT DISTRIBUTION:'); for (const [key, count] of Object.entries(htftCounts)) { const pct = (count / total * 100).toFixed(2); const marker = (key === '1/2' || key === '2/1') ? ' โš ๏ธ REVERSAL' : ''; console.log(` ${key}: ${count} (${pct}%)${marker}`); } console.log(`\nโš ๏ธ TOTAL REVERSALS: ${reversals.length} (${(reversals.length / total * 100).toFixed(2)}%)`); // ANALYSIS 1: By League console.log('\n๐Ÿ“ˆ LEAGUE DISTRIBUTION (min 100 matches):'); const leagueMap: Record = {}; for (const m of matches) { const league = m.league || 'Unknown'; if (!leagueMap[league]) leagueMap[league] = { total: 0, rev: 0 }; leagueMap[league].total++; const htH = m.ht_score_home; const htA = m.ht_score_away; const ftH = m.score_home; const ftA = m.score_away; const htR = htH > htA ? '1' : htH === htA ? 'X' : '2'; const ftR = ftH > ftA ? '1' : ftH === ftA ? 'X' : '2'; if ((htR === '1' && ftR === '2') || (htR === '2' && ftR === '1')) { leagueMap[league].rev++; } } const topLeagues = Object.entries(leagueMap) .filter(([_, v]) => v.total >= 100 && v.rev > 0) .sort((a, b) => (b[1].rev / b[1].total) - (a[1].rev / a[1].total)) .slice(0, 15); console.log('\nTop 15 leagues by reversal rate:'); for (const [league, data] of topLeagues) { const rate = (data.rev / data.total * 100).toFixed(2); console.log(` ${league}: ${data.rev}/${data.total} (${rate}%)`); } // ANALYSIS 2: Score patterns console.log('\n๐Ÿ“ˆ HT SCORE PATTERNS IN REVERSALS:'); const htScoreMap: Record = {}; for (const m of reversals) { const key = `${m.htH}-${m.htA}`; htScoreMap[key] = (htScoreMap[key] || 0) + 1; } Object.entries(htScoreMap) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .forEach(([score, count]) => { console.log(` HT ${score}: ${count} matches`); }); console.log('\n๐Ÿ“ˆ FT SCORE PATTERNS IN REVERSALS:'); const ftScoreMap: Record = {}; for (const m of reversals) { const key = `${m.ftH}-${m.ftA}`; ftScoreMap[key] = (ftScoreMap[key] || 0) + 1; } Object.entries(ftScoreMap) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .forEach(([score, count]) => { console.log(` FT ${score}: ${count} matches`); }); // ANALYSIS 3: Comeback magnitude console.log('\n๐Ÿ“ˆ COMEBACK MAGNITUDE:'); let by1 = 0, by2 = 0, by3plus = 0; for (const m of reversals) { const margin = Math.abs((m.ftH - m.ftA)); if (margin === 1) by1++; else if (margin === 2) by2++; else by3plus++; } console.log(` By 1 goal: ${by1} (${(by1/reversals.length*100).toFixed(1)}%)`); console.log(` By 2 goals: ${by2} (${(by2/reversals.length*100).toFixed(1)}%)`); console.log(` By 3+ goals: ${by3plus} (${(by3plus/reversals.length*100).toFixed(1)}%) โš ๏ธ`); // Show extreme comebacks const extreme = reversals .filter(m => Math.abs(m.ftH - m.ftA) >= 2) .sort((a, b) => Math.abs(b.ftH - b.ftA) - Math.abs(a.ftH - a.ftA)) .slice(0, 10); console.log('\nTop 10 extreme comebacks (2+ goal margin):'); for (const m of extreme) { const diff = Math.abs(m.ftH - m.ftA); console.log(` ${m.league}: ${m.home_team} vs ${m.away_team} | HT: ${m.htH}-${m.htA} => FT: ${m.ftH}-${m.ftA} (margin: ${diff})`); } // ANALYSIS 4: 1/2 vs 2/1 split const rev_1_2 = reversals.filter(m => m.htft === '1/2'); const rev_2_1 = reversals.filter(m => m.htft === '2/1'); console.log('\n๐Ÿ“ˆ REVERSAL TYPE SPLIT:'); console.log(` 1/2 (Home leads HT, Away wins FT): ${rev_1_2.length} (${(rev_1_2.length/reversals.length*100).toFixed(1)}%)`); console.log(` 2/1 (Away leads HT, Home wins FT): ${rev_2_1.length} (${(rev_2_1.length/reversals.length*100).toFixed(1)}%)`); // Get odds for a sample of reversals console.log('\n๐Ÿ“ˆ SAMPLE ODDS ANALYSIS (last 100 reversals):'); const sample = reversals.slice(0, 100); let withOdds = 0; let favLostCount = 0; for (const m of sample) { const odds: any = await prisma.$queryRaw` SELECT oc.name, os.name as selection, os.odd_value FROM odd_categories oc JOIN odd_selections os ON os.odd_category_db_id = oc.db_id WHERE oc.match_id = ${m.id} `; if (odds.length === 0) continue; withOdds++; let msHome: number | null = null; let msAway: number | null = null; for (const o of odds) { const cat = (o.name || '').toLowerCase(); if (cat.includes('maรง sonucu')) { const sel = (o.selection || '').toLowerCase(); if (sel === '1') msHome = parseFloat(o.odd_value.toString()); else if (sel === '2') msAway = parseFloat(o.odd_value.toString()); } } if (msHome && msAway) { const favWasHome = msHome < msAway; const actualWinner = m.ftH > m.ftA ? '1' : m.ftA > m.ftH ? '2' : 'X'; if ((favWasHome && actualWinner === '2') || (!favWasHome && actualWinner === '1')) { favLostCount++; } } } console.log(` Reversals with odds: ${withOdds}/${sample.length}`); if (withOdds > 0) { console.log(` Favorite lost: ${favLostCount}/${withOdds} (${(favLostCount/withOdds*100).toFixed(1)}%) โš ๏ธ`); } console.log('\n' + '='.repeat(80)); console.log('โœ… ANALYSIS COMPLETE'); console.log('='.repeat(80)); await prisma.$disconnect(); } main().catch(console.error);