Files
iddaai-be/scripts/match-prediction-v2.js
T
fahricansecer 2f0b85a0c7
Deploy Iddaai Backend / build-and-deploy (push) Failing after 18s
first (part 2: other directories)
2026-04-16 15:11:25 +03:00

656 lines
25 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
async function analyzeMatch() {
const matchId = '2ivwkprq3bnkkp2bfa2cdintg';
console.log('\n');
console.log(
'╔══════════════════════════════════════════════════════════════════╗',
);
console.log(
'║ GLM-5 TAHMİN & SÜRPRİZ ANALİZİ ║',
);
console.log(
'║ Maç ID: ' +
matchId.substring(0, 20) +
'... ║',
);
console.log(
'╚══════════════════════════════════════════════════════════════════╝',
);
const match = await prisma.match.findUnique({
where: { id: matchId },
include: {
homeTeam: true,
awayTeam: true,
league: { include: { country: true } },
oddCategories: { include: { selections: true } },
officials: { include: { role: true } },
aiFeatures: true,
},
});
if (!match) {
console.log('❌ Maç bulunamadı!');
await prisma.$disconnect();
return;
}
console.log(
'\n═══════════════════════════════════════════════════════════════',
);
console.log('⚽ MAÇ BİLGİLERİ');
console.log(
'═══════════════════════════════════════════════════════════════',
);
console.log(`\n🏠 Ev Sahibi: ${match.homeTeam?.name}`);
console.log(`✈️ Deplasman: ${match.awayTeam?.name}`);
console.log(
`🏆 Lig: ${match.league?.name} (${match.league?.country?.name || ''})`,
);
console.log(
`📅 Tarih: ${new Date(Number(match.mstUtc)).toLocaleString('tr-TR', { timeZone: 'Europe/Istanbul' })}`,
);
console.log(`📊 Durum: ${match.state} / ${match.status}`);
console.log(`🔢 İddaa Kodu: ${match.iddaaCode || 'Yok'}`);
// SKORU GİZLE - Sona saklayacağız
// ═════════════════════════════════════════════════════════════════
// 1. ORAN ANALİZİ
// ═════════════════════════════════════════════════════════════════
console.log(
'\n═══════════════════════════════════════════════════════════════',
);
console.log('📊 1. ORAN ANALİZİ');
console.log(
'═══════════════════════════════════════════════════════════════',
);
const odds = {
ms_h: null,
ms_d: null,
ms_a: null,
dc_1x: null,
dc_x2: null,
dc_12: null,
ou25_o: null,
ou25_u: null,
btts_y: null,
btts_n: null,
ht_h: null,
ht_d: null,
ht_a: null,
};
console.log('\n📋 Tüm Oran Kategorileri:\n');
for (const cat of match.oddCategories) {
const selections = cat.selections
.map((s) => `${s.name}: ${s.oddValue}`)
.join(' | ');
console.log(` ${cat.name}: ${selections}`);
if (cat.name?.includes('Maç Sonucu') || cat.name === 'MS') {
for (const sel of cat.selections) {
if (sel.name === '1') odds.ms_h = parseFloat(sel.oddValue);
if (sel.name === 'X') odds.ms_d = parseFloat(sel.oddValue);
if (sel.name === '2') odds.ms_a = parseFloat(sel.oddValue);
}
}
if (cat.name?.includes('Çifte Şans')) {
for (const sel of cat.selections) {
if (sel.name === '1-X') odds.dc_1x = parseFloat(sel.oddValue);
if (sel.name === 'X-2') odds.dc_x2 = parseFloat(sel.oddValue);
if (sel.name === '1-2') odds.dc_12 = parseFloat(sel.oddValue);
}
}
if (cat.name?.includes('2,5') || cat.name?.includes('2.5')) {
for (const sel of cat.selections) {
if (sel.name === 'Alt' || sel.name === 'A')
odds.ou25_u = parseFloat(sel.oddValue);
if (sel.name === 'Üst' || sel.name === 'Ü')
odds.ou25_o = parseFloat(sel.oddValue);
}
}
if (
cat.name?.toLowerCase().includes('karşılıklı') ||
cat.name?.includes('KG')
) {
for (const sel of cat.selections) {
if (sel.name === 'Var' || sel.name === 'Evet')
odds.btts_y = parseFloat(sel.oddValue);
if (sel.name === 'Yok' || sel.name === 'Hayır')
odds.btts_n = parseFloat(sel.oddValue);
}
}
if (cat.name?.includes('1. Yarı') && cat.name?.includes('Sonuc')) {
for (const sel of cat.selections) {
if (sel.name === '1') odds.ht_h = parseFloat(sel.oddValue);
if (sel.name === 'X') odds.ht_d = parseFloat(sel.oddValue);
if (sel.name === '2') odds.ht_a = parseFloat(sel.oddValue);
}
}
}
let margin = null;
let normHome = 33,
normDraw = 33,
normAway = 33;
let favorite = null,
favoriteOdds = null;
if (odds.ms_h && odds.ms_d && odds.ms_a) {
const rawHome = (1 / odds.ms_h) * 100;
const rawDraw = (1 / odds.ms_d) * 100;
const rawAway = (1 / odds.ms_a) * 100;
const total = rawHome + rawDraw + rawAway;
margin = total - 100;
normHome = (rawHome / total) * 100;
normDraw = (rawDraw / total) * 100;
normAway = (rawAway / total) * 100;
// Favori kim?
if (odds.ms_h < odds.ms_a) {
favorite = 'home';
favoriteOdds = odds.ms_h;
} else if (odds.ms_a < odds.ms_h) {
favorite = 'away';
favoriteOdds = odds.ms_a;
} else {
favorite = 'draw';
favoriteOdds = odds.ms_d;
}
console.log(
`\n📊 MS Oranları: 1=${odds.ms_h} | X=${odds.ms_d} | 2=${odds.ms_a}`,
);
console.log(`📈 Bookmaker Margin: %${margin.toFixed(1)}`);
console.log(`🎯 Normalize Olasılık:`);
console.log(
` 1 (${match.homeTeam?.name?.substring(0, 15)}): %${normHome.toFixed(1)}`,
);
console.log(` X (Beraberlik): %${normDraw.toFixed(1)}`);
console.log(
` 2 (${match.awayTeam?.name?.substring(0, 15)}): %${normAway.toFixed(1)}`,
);
console.log(
`\n🏆 Favori: ${favorite === 'home' ? match.homeTeam?.name + ' (1)' : favorite === 'away' ? match.awayTeam?.name + ' (2)' : 'Beraberlik (X)'} @ ${favoriteOdds}`,
);
}
// ═════════════════════════════════════════════════════════════════
// 2. TAKIM FORM ANALİZİ
// ═════════════════════════════════════════════════════════════════
console.log(
'\n═══════════════════════════════════════════════════════════════',
);
console.log('📈 2. TAKIM FORM ANALİZİ');
console.log(
'═══════════════════════════════════════════════════════════════',
);
// Ev sahibi son 10 maç
const homeMatches = await prisma.match.findMany({
where: {
OR: [{ homeTeamId: match.homeTeamId }, { awayTeamId: match.homeTeamId }],
sport: 'football',
state: 'postGame',
scoreHome: { not: null },
scoreAway: { not: null },
},
orderBy: { mstUtc: 'desc' },
take: 10,
});
let homeWins = 0,
homeDraws = 0,
homeLosses = 0;
let homeGoalsFor = 0,
homeGoalsAgainst = 0;
for (const m of homeMatches) {
const isHome = m.homeTeamId === match.homeTeamId;
const gf = isHome ? m.scoreHome : m.scoreAway;
const ga = isHome ? m.scoreAway : m.scoreHome;
homeGoalsFor += gf || 0;
homeGoalsAgainst += ga || 0;
if (gf > ga) homeWins++;
else if (gf < ga) homeLosses++;
else homeDraws++;
}
const homeFormScore =
homeMatches.length > 0
? ((homeWins * 3 + homeDraws) / (homeMatches.length * 3)) * 100
: 50;
console.log(`\n🏠 ${match.homeTeam?.name}:`);
console.log(
` Son ${homeMatches.length} Maç: ${homeWins}G ${homeDraws}B ${homeLosses}M`,
);
console.log(` Gol: ${homeGoalsFor} attı, ${homeGoalsAgainst} yedi`);
console.log(` Form Skoru: ${homeFormScore.toFixed(1)}/100`);
// Deplasman son 10 maç
const awayMatches = await prisma.match.findMany({
where: {
OR: [{ homeTeamId: match.awayTeamId }, { awayTeamId: match.awayTeamId }],
sport: 'football',
state: 'postGame',
scoreHome: { not: null },
scoreAway: { not: null },
},
orderBy: { mstUtc: 'desc' },
take: 10,
});
let awayWins = 0,
awayDraws = 0,
awayLosses = 0;
let awayGoalsFor = 0,
awayGoalsAgainst = 0;
for (const m of awayMatches) {
const isHome = m.homeTeamId === match.awayTeamId;
const gf = isHome ? m.scoreHome : m.scoreAway;
const ga = isHome ? m.scoreAway : m.scoreHome;
awayGoalsFor += gf || 0;
awayGoalsAgainst += ga || 0;
if (gf > ga) awayWins++;
else if (gf < ga) awayLosses++;
else awayDraws++;
}
const awayFormScore =
awayMatches.length > 0
? ((awayWins * 3 + awayDraws) / (awayMatches.length * 3)) * 100
: 50;
console.log(`\n✈️ ${match.awayTeam?.name}:`);
console.log(
` Son ${awayMatches.length} Maç: ${awayWins}G ${awayDraws}B ${awayLosses}M`,
);
console.log(` Gol: ${awayGoalsFor} attı, ${awayGoalsAgainst} yedi`);
console.log(` Form Skoru: ${awayFormScore.toFixed(1)}/100`);
const formDiff = homeFormScore - awayFormScore;
console.log(
`\n📈 Form Farkı: ${formDiff > 0 ? '+' : ''}${formDiff.toFixed(1)}`,
);
// ═════════════════════════════════════════════════════════════════
// 3. HEAD-TO-HEAD
// ═════════════════════════════════════════════════════════════════
console.log(
'\n═══════════════════════════════════════════════════════════════',
);
console.log('🔄 3. HEAD-TO-HEAD');
console.log(
'═══════════════════════════════════════════════════════════════',
);
const h2hMatches = await prisma.match.findMany({
where: {
OR: [
{ homeTeamId: match.homeTeamId, awayTeamId: match.awayTeamId },
{ homeTeamId: match.awayTeamId, awayTeamId: match.homeTeamId },
],
sport: 'football',
state: 'postGame',
},
orderBy: { mstUtc: 'desc' },
take: 5,
});
let h2hHomeWins = 0,
h2hAwayWins = 0,
h2hDraws = 0;
if (h2hMatches.length > 0) {
for (const m of h2hMatches) {
const homeTeamIsHome = m.homeTeamId === match.homeTeamId;
const result =
m.scoreHome > m.scoreAway
? homeTeamIsHome
? '1'
: '2'
: m.scoreHome < m.scoreAway
? homeTeamIsHome
? '2'
: '1'
: 'X';
if (result === '1') h2hHomeWins++;
else if (result === '2') h2hAwayWins++;
else h2hDraws++;
console.log(
` ${m.homeTeam?.name} ${m.scoreHome}-${m.scoreAway} ${m.awayTeam?.name} [${result}]`,
);
}
console.log(
`\n H2H: ${match.homeTeam?.name} ${h2hHomeWins}G, ${h2hDraws}B, ${match.awayTeam?.name} ${h2hAwayWins}G`,
);
} else {
console.log(' Karşılıklı maç bulunamadı');
}
// ═════════════════════════════════════════════════════════════════
// 4. HAKEM ANALİZİ
// ═════════════════════════════════════════════════════════════════
console.log(
'\n═══════════════════════════════════════════════════════════════',
);
console.log('👨‍⚖️ 4. HAKEM ANALİZİ');
console.log(
'═══════════════════════════════════════════════════════════════',
);
let refUpsetRate = 0;
const mainReferee = match.officials?.find(
(o) => o.role?.name === 'Orta Hakem',
);
if (mainReferee) {
console.log(`\n👤 Hakem: ${mainReferee.name}`);
const refereeMatches = await prisma.matchOfficial.findMany({
where: { name: mainReferee.name, roleId: 1 },
include: { match: true },
take: 30,
});
let refHomeWins = 0,
refDraws = 0,
refAwayWins = 0;
let refUpsets = 0,
refTotal = 0;
for (const rm of refereeMatches) {
if (rm.match?.scoreHome !== null && rm.match?.scoreAway !== null) {
refTotal++;
// Bu maç için favori belirle (basit: ev sahibi favori varsay)
// Gerçek hayatta oranlar kontrol edilmeli
if (rm.match.scoreHome > rm.match.scoreAway) refHomeWins++;
else if (rm.match.scoreHome < rm.match.scoreAway) {
refAwayWins++;
refUpsets++; // Ev sahibi kaybetti = sürpriz (basitleştirilmiş)
} else refDraws++;
}
}
if (refTotal > 0) {
console.log(` Yönettiği maçlar: ${refTotal}`);
console.log(
` Ev kazanma: %${((refHomeWins / refTotal) * 100).toFixed(1)}`,
);
console.log(
` Beraberlik: %${((refDraws / refTotal) * 100).toFixed(1)}`,
);
console.log(
` Deplasman: %${((refAwayWins / refTotal) * 100).toFixed(1)}`,
);
refUpsetRate = (refAwayWins / refTotal) * 100;
console.log(
` ⚠️ Sürpriz (ev kaybı) oranı: %${refUpsetRate.toFixed(1)}`,
);
}
} else {
console.log(' Hakem bilgisi yok');
}
// ═════════════════════════════════════════════════════════════════
// 5. SÜRPRİZ SKORU HESAPLAMA
// ═════════════════════════════════════════════════════════════════
console.log(
'\n═══════════════════════════════════════════════════════════════',
);
console.log('🎯 5. SÜRPRİZ SKORU HESAPLAMA');
console.log(
'═══════════════════════════════════════════════════════════════',
);
let upsetScore = 0;
const upsetReasons = [];
console.log('\n📊 Sürpriz Skoru Hesaplaması:\n');
// 1. Margin
if (margin !== null) {
if (margin > 20) {
upsetScore += 15;
upsetReasons.push('Margin yüksek (%20+)');
console.log(` ✓ Margin %${margin.toFixed(1)} > %20 → +15 puan`);
} else if (margin > 18) {
upsetScore += 10;
upsetReasons.push('Margin orta-yüksek');
console.log(` ✓ Margin %${margin.toFixed(1)} > %18 → +10 puan`);
} else {
console.log(` ✗ Margin %${margin.toFixed(1)} < %18 → +0 puan`);
}
}
// 2. Favori Oran
if (favoriteOdds !== null) {
if (favoriteOdds >= 1.5 && favoriteOdds < 1.6) {
upsetScore += 25;
upsetReasons.push('Favori oran 1.50-1.60 arası (riskli)');
console.log(` ✓ Favori oran ${favoriteOdds} (1.50-1.60) → +25 puan`);
} else if (favoriteOdds >= 1.4 && favoriteOdds < 1.5) {
upsetScore += 20;
upsetReasons.push('Favori oran 1.40-1.50 arası');
console.log(` ✓ Favori oran ${favoriteOdds} (1.40-1.50) → +20 puan`);
} else if (favoriteOdds >= 1.3 && favoriteOdds < 1.4) {
upsetScore += 15;
upsetReasons.push('Favori oran 1.30-1.40 arası');
console.log(` ✓ Favori oran ${favoriteOdds} (1.30-1.40) → +15 puan`);
} else if (favoriteOdds < 1.2) {
upsetScore += 20;
upsetReasons.push('Favori oran çok düşük (tuzak şüphesi)');
console.log(
` ✓ Favori oran ${favoriteOdds} (<1.20, tuzak?) → +20 puan`,
);
} else {
console.log(
` ✗ Favori oran ${favoriteOdds} (güvenli aralık) → +0 puan`,
);
}
}
// 3. Hakem
if (refUpsetRate > 30) {
upsetScore += 20;
upsetReasons.push(
`Hakem sürpriz oranı yüksek (%${refUpsetRate.toFixed(0)})`,
);
console.log(
` ✓ Hakem sürpriz oranı %${refUpsetRate.toFixed(0)} > %30 → +20 puan`,
);
} else if (refUpsetRate > 20) {
upsetScore += 10;
upsetReasons.push('Hakem sürpriz oranı orta');
console.log(
` ✓ Hakem sürpriz oranı %${refUpsetRate.toFixed(0)} > %20 → +10 puan`,
);
} else {
console.log(
` ✗ Hakem sürpriz oranı %${refUpsetRate.toFixed(0)} < %20 → +0 puan`,
);
}
// 4. Form Farkı
if (Math.abs(formDiff) > 40) {
upsetScore += 15;
upsetReasons.push(
`Form farkı çok büyük (${formDiff > 0 ? '+' : ''}${formDiff.toFixed(0)})`,
);
console.log(
` ✓ Form farkı ${formDiff.toFixed(0)} > 40 (tuzak?) → +15 puan`,
);
} else {
console.log(` ✗ Form farkı ${formDiff.toFixed(0)} < 40 → +0 puan`);
}
// 5. H2H Sürpriz
if (h2hAwayWins > 0 && favorite === 'home') {
upsetScore += 10;
upsetReasons.push('H2H geçmişinde deplasman galibiyeti var');
console.log(` ✓ H2H'de deplasman ${h2hAwayWins} galibiyet → +10 puan`);
} else if (h2hHomeWins > 0 && favorite === 'away') {
upsetScore += 10;
upsetReasons.push('H2H geçmişinde ev sahibi galibiyeti var');
console.log(` ✓ H2H'de ev sahibi ${h2hHomeWins} galibiyet → +10 puan`);
} else {
console.log(` ✗ H2H'de sürpriz yok → +0 puan`);
}
console.log(`\n ─────────────────────────────`);
console.log(` 📊 SÜRPRİZ SKORU: ${upsetScore}/100`);
// ═════════════════════════════════════════════════════════════════
// 6. FİNAL TAHMİN
// ═════════════════════════════════════════════════════════════════
console.log('\n');
console.log(
'╔══════════════════════════════════════════════════════════════════╗',
);
console.log(
'║ 🎯 FİNAL TAHMİN ║',
);
console.log(
'╚══════════════════════════════════════════════════════════════════╝',
);
// Normal tahmin
const w = { odds: 0.4, form: 0.3, home: 0.15, league: 0.15 };
const homeScore = normHome * w.odds + homeFormScore * w.form + 8 * w.home;
const awayScore = normAway * w.odds + awayFormScore * w.form + 0 * w.home;
const drawScore =
normDraw * w.odds + (100 - Math.abs(formDiff)) * 0.1 * w.form;
const total = homeScore + drawScore + awayScore;
const finalHome = (homeScore / total) * 100;
const finalDraw = (drawScore / total) * 100;
const finalAway = (awayScore / total) * 100;
let prediction, confidence;
if (finalHome > finalDraw && finalHome > finalAway) {
prediction = '1';
confidence = finalHome;
} else if (finalAway > finalDraw) {
prediction = '2';
confidence = finalAway;
} else {
prediction = 'X';
confidence = finalDraw;
}
// Alt/Üst
const avgGoals =
(homeGoalsFor + homeGoalsAgainst + awayGoalsFor + awayGoalsAgainst) /
(homeMatches.length + awayMatches.length) || 2.5;
const ou25 = avgGoals > 2.5 ? 'ÜST' : 'ALT';
console.log(`\n📊 NORMAL TAHMİN (Oran + Form):`);
console.log(` 1: %${finalHome.toFixed(1)}`);
console.log(` X: %${finalDraw.toFixed(1)}`);
console.log(` 2: %${finalAway.toFixed(1)}`);
console.log(`\n 🏆 TAHMİN: ${prediction}`);
console.log(` 📊 Güven: %${confidence.toFixed(1)}`);
console.log(` ⚽ 2.5 ${ou25}`);
// Sürpriz tahmini
console.log(`\n⚠️ SÜRPRİZ ANALİZİ:`);
console.log(` Sürpriz Skoru: ${upsetScore}/100`);
if (upsetScore >= 50) {
console.log(`\n 🔴 YÜKSEK SÜRPRİZ RİSKİ!`);
console.log(` Sürpriz işaretleri:`);
for (const reason of upsetReasons) {
console.log(`${reason}`);
}
// Value bet önerisi
const upsetPrediction =
prediction === '1' ? '2' : prediction === '2' ? '1' : 'X';
const upsetOdds =
prediction === '1'
? odds.ms_a
: prediction === '2'
? odds.ms_h
: odds.ms_d;
console.log(`\n 💰 VALUE BET ÖNERİSİ:`);
console.log(
` Normal tahmin: ${prediction} @ ${prediction === '1' ? odds.ms_h : prediction === '2' ? odds.ms_a : odds.ms_d}`,
);
console.log(
` Sürpriz tahmin: ${upsetPrediction} @ ${upsetOdds} ← VALUE BET!`,
);
} else if (upsetScore >= 30) {
console.log(`\n 🟡 ORTA SÜRPRİZ RİSKİ`);
console.log(` Dikkatli olun, çifte şans düşünülebilir`);
} else {
console.log(`\n 🟢 DÜŞÜK SÜRPRİZ RİSKİ`);
console.log(` Normal tahmin güvenle oynanabilir`);
}
// ═════════════════════════════════════════════════════════════════
// GERÇEK SONUÇ (SON)
// ═════════════════════════════════════════════════════════════════
console.log('\n');
console.log(
'╔══════════════════════════════════════════════════════════════════╗',
);
console.log(
'║ 📊 GERÇEK SONUÇ ║',
);
console.log(
'╚══════════════════════════════════════════════════════════════════╝',
);
if (match.scoreHome !== null && match.scoreAway !== null) {
const actual =
match.scoreHome > match.scoreAway
? '1'
: match.scoreHome < match.scoreAway
? '2'
: 'X';
const actualGoals = match.scoreHome + match.scoreAway;
const actualOU = actualGoals > 2.5 ? 'ÜST' : 'ALT';
console.log(
`\n Skor: ${match.homeTeam?.name} ${match.scoreHome} - ${match.scoreAway} ${match.awayTeam?.name}`,
);
console.log(` Sonuç: ${actual}`);
console.log(` Alt/Üst: ${actualOU} (${actualGoals} gol)`);
console.log(`\n ─────────────────────────────`);
console.log(
` 🎯 Normal Tahmin (${prediction}): ${prediction === actual ? '✅ DOĞRU' : '❌ YANLIŞ'}`,
);
console.log(
` 🎯 2.5 ${ou25}: ${ou25 === actualOU ? '✅ DOĞRU' : '❌ YANLIŞ'}`,
);
if (upsetScore >= 50) {
const upsetPrediction =
prediction === '1' ? '2' : prediction === '2' ? '1' : 'X';
console.log(
` 🎯 Sürpriz Tahmin (${upsetPrediction}): ${upsetPrediction === actual ? '✅ DOĞRU' : '❌ YANLIŞ'}`,
);
}
} else {
console.log(`\n Maç henüz oynanmamış veya skor yok`);
}
console.log('\n');
await prisma.$disconnect();
}
analyzeMatch().catch(console.error);