Files
iddaai-be/scripts/glm5-prediction-approach.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

458 lines
17 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();
/**
* GLM-5 Tahmin Yaklaşımı
* ======================
* Bir maç için eldeki veriler:
* - Takım isimleri (ev sahibi, deplasman)
* - Oranlar (MS, Alt/Üst, BTTS, DC)
* - Sakatlar/Cezalılar
* - Lig
* - Hakem
*
* Hedef: Maç sonucunu tahmin etmek (1/X/2)
*/
async function glm5PredictionApproach() {
console.log('\n');
console.log(
'╔══════════════════════════════════════════════════════════════════╗',
);
console.log(
'║ GLM-5 TAHMİN YAKLAŞIMI - VERİTABANI ANALİZİ ║',
);
console.log(
'╚══════════════════════════════════════════════════════════════════╝',
);
console.log('\n');
// =====================================
// BÖLÜM 1: VERİTABANI HAZİNESİ
// =====================================
console.log('📦 BÖLÜM 1: VERİTABANI HAZİNESİ');
console.log('─'.repeat(50));
const stats = {
matches: await prisma.match.count({
where: { sport: 'football', state: 'postGame' },
}),
odds: await prisma.oddSelection.count(),
events: await prisma.matchPlayerEvents.count(),
teamStats: await prisma.matchTeamStats.count(),
officials: await prisma.matchOfficial.count(),
};
console.log(`\nKullanılabilir Veri Miktarı:`);
console.log(`${stats.matches.toLocaleString()} bitmiş futbol maçı`);
console.log(` 📊 ${stats.odds.toLocaleString()} oran kaydı`);
console.log(
`${stats.events.toLocaleString()} maç olayı (gol, kart, değişiklik)`,
);
console.log(` 📈 ${stats.teamStats.toLocaleString()} takım istatistiği`);
console.log(` 👨‍⚖️ ${stats.officials.toLocaleString()} hakem kaydı`);
// =====================================
// BÖLÜM 2: TAHMİN FAKTÖRLERİ
// =====================================
console.log('\n\n🧠 BÖLÜM 2: TAHMİN FAKTÖRLERİ');
console.log('─'.repeat(50));
console.log(`
Bir maç sonucunu etkileyen faktörler ve veritabanından nasıl çıkarılır:
┌─────────────────────────────────────────────────────────────────────┐
│ FAKTÖR │ VERİTABANI KAYNAĞI │
├─────────────────────────────────────────────────────────────────────┤
│ 1. Takım Gücü │ Son maçlarda gol atma/yeme ortalaması
│ 2. Ev/Deplasman Avantajı │ Ev sahibi %52, Deplasman %28, Berabere %20│
│ 3. Form Durumu │ Son 5 maçta alınan puanlar │
│ 4. Oranlar │ Bookmaker'ın fiyatlaması (implied prob) │
│ 5. Sakat/Cezalı │ sidelined_data JSON alanı
│ 6. Hakem Etkisi │ Hakemin istatistikleri │
│ 7. Lig Özelliği │ Gol ortalamaları, ev avantajı
│ 8. Head-to-Head │ Karşılıklı geçmiş maçlar │
└─────────────────────────────────────────────────────────────────────┘
`);
// =====================================
// BÖLÜM 3: ÖRNEK ANALİZ
// =====================================
console.log('\n\n🎯 BÖLÜM 3: ÖRNEK MAÇ ANALİZİ');
console.log('─'.repeat(50));
// preGame durumundaki bir maç bul
const match = await prisma.match.findFirst({
where: { sport: 'football', state: 'preGame' },
include: {
homeTeam: true,
awayTeam: true,
league: true,
oddCategories: { include: { selections: true } },
officials: { include: { role: true } },
},
});
if (!match) {
// Son bitmiş maçı kullan
const finishedMatch = await prisma.match.findFirst({
where: { sport: 'football', state: 'postGame', scoreHome: { not: null } },
include: {
homeTeam: true,
awayTeam: true,
league: true,
oddCategories: { include: { selections: true } },
officials: { include: { role: true } },
},
orderBy: { mstUtc: 'desc' },
});
if (finishedMatch) {
await analyzeMatch(finishedMatch, true);
}
} else {
await analyzeMatch(match, false);
}
await prisma.$disconnect();
}
async function analyzeMatch(match, isFinished) {
console.log(`\n⚽ MAÇ: ${match.homeTeam?.name} vs ${match.awayTeam?.name}`);
console.log(`🏆 Lig: ${match.league?.name}`);
console.log(`📅 Tarih: ${new Date(Number(match.mstUtc)).toISOString()}`);
if (isFinished) {
console.log(`📊 Gerçek Skor: ${match.scoreHome} - ${match.scoreAway}`);
}
// ═════════════════════════════════════════════════════════════════
// ADIM 1: ORANLARI OKU VE IMPLIED PROBABILITY HESAPLA
// ═════════════════════════════════════════════════════════════════
console.log('\n📊 ADIM 1: ORAN ANALİZİ');
console.log('─'.repeat(40));
const odds = {};
for (const cat of match.oddCategories) {
for (const sel of cat.selections) {
if (cat.name?.includes('Maç Sonucu') || cat.name === 'MS') {
if (sel.name === '1') odds.ms_home = parseFloat(sel.oddValue);
if (sel.name === 'X') odds.ms_draw = parseFloat(sel.oddValue);
if (sel.name === '2') odds.ms_away = parseFloat(sel.oddValue);
}
if (cat.name?.includes('2,5') || cat.name?.includes('2.5')) {
if (sel.name === 'Alt') odds.ou25_under = parseFloat(sel.oddValue);
if (sel.name === 'Üst') odds.ou25_over = parseFloat(sel.oddValue);
}
if (cat.name?.includes('Karşılıklı') || cat.name?.includes('KG')) {
if (sel.name === 'Var' || sel.name === 'Evet')
odds.btts_yes = parseFloat(sel.oddValue);
if (sel.name === 'Yok' || sel.name === 'Hayır')
odds.btts_no = parseFloat(sel.oddValue);
}
}
}
// Implied probability hesapla
if (odds.ms_home && odds.ms_draw && odds.ms_away) {
const rawHome = (1 / odds.ms_home) * 100;
const rawDraw = (1 / odds.ms_draw) * 100;
const rawAway = (1 / odds.ms_away) * 100;
const total = rawHome + rawDraw + rawAway;
console.log(
`\nOranlar: 1=${odds.ms_home} | X=${odds.ms_draw} | 2=${odds.ms_away}`,
);
console.log(
`Ham Implied Probability: 1=%${rawHome.toFixed(1)} | X=%${rawDraw.toFixed(1)} | 2=%${rawAway.toFixed(1)}`,
);
console.log(`Bookmaker Margin: %${(total - 100).toFixed(1)}`);
// Normalize edilmiş
const normHome = (rawHome / total) * 100;
const normDraw = (rawDraw / total) * 100;
const normAway = (rawAway / total) * 100;
console.log(
`Normalize: 1=%${normHome.toFixed(1)} | X=%${normDraw.toFixed(1)} | 2=%${normAway.toFixed(1)}`,
);
odds.normHome = normHome;
odds.normDraw = normDraw;
odds.normAway = normAway;
}
// ═════════════════════════════════════════════════════════════════
// ADIM 2: TAKIM GEÇMİŞ PERFORMANSI
// ═════════════════════════════════════════════════════════════════
console.log('\n📈 ADIM 2: TAKIM PERFORMANS ANALİZİ');
console.log('─'.repeat(40));
// 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,
});
const homeStats = calculateTeamStats(homeMatches, match.homeTeamId);
console.log(`\n🏠 ${match.homeTeam?.name}:`);
console.log(
` Son 10: ${homeStats.wins}G ${homeStats.draws}B ${homeStats.losses}M`,
);
console.log(
` Gol: ${homeStats.goalsFor} attı, ${homeStats.goalsAgainst} yedi`,
);
console.log(` Ortalama: ${(homeStats.goalsFor / 10).toFixed(2)} gol/maç`);
// 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,
});
const awayStats = calculateTeamStats(awayMatches, match.awayTeamId);
console.log(`\n✈️ ${match.awayTeam?.name}:`);
console.log(
` Son 10: ${awayStats.wins}G ${awayStats.draws}B ${awayStats.losses}M`,
);
console.log(
` Gol: ${awayStats.goalsFor} attı, ${awayStats.goalsAgainst} yedi`,
);
console.log(` Ortalama: ${(awayStats.goalsFor / 10).toFixed(2)} gol/maç`);
// ═════════════════════════════════════════════════════════════════
// ADIM 3: HAKEM ANALİZİ
// ═════════════════════════════════════════════════════════════════
console.log('\n👨‍⚖️ ADIM 3: HAKEM ANALİZİ');
console.log('─'.repeat(40));
const mainReferee = match.officials?.find(
(o) => o.role?.name === 'Orta Hakem',
);
if (mainReferee) {
console.log(`\nHakem: ${mainReferee.name}`);
// Bu hakemin yönettiği maçları bul
const refereeMatches = await prisma.matchOfficial.findMany({
where: { name: mainReferee.name, roleId: 1 },
include: { match: true },
take: 20,
});
if (refereeMatches.length > 0) {
let homeWins = 0,
draws = 0,
awayWins = 0;
let totalCards = 0;
for (const rm of refereeMatches) {
if (rm.match?.scoreHome !== null && rm.match?.scoreAway !== null) {
if (rm.match.scoreHome > rm.match.scoreAway) homeWins++;
else if (rm.match.scoreHome < rm.match.scoreAway) awayWins++;
else draws++;
}
}
const total = homeWins + draws + awayWins;
if (total > 0) {
console.log(` Yönettiği maçlar: ${total}`);
console.log(
` Ev sahibi kazanma: %${((homeWins / total) * 100).toFixed(1)}`,
);
console.log(` Beraberlik: %${((draws / total) * 100).toFixed(1)}`);
console.log(
` Deplasman kazanma: %${((awayWins / total) * 100).toFixed(1)}`,
);
}
}
} else {
console.log('Hakem bilgisi yok');
}
// ═════════════════════════════════════════════════════════════════
// ADIM 4: LİG ÖZELLİKLERİ
// ═════════════════════════════════════════════════════════════════
console.log('\n🏆 ADIM 4: LİG ANALİZİ');
console.log('─'.repeat(40));
const leagueMatches = await prisma.match.findMany({
where: { leagueId: match.leagueId, sport: 'football', state: 'postGame' },
take: 100,
});
let leagueHomeWins = 0,
leagueDraws = 0,
leagueAwayWins = 0;
let leagueGoals = 0;
for (const lm of leagueMatches) {
if (lm.scoreHome !== null && lm.scoreAway !== null) {
leagueGoals += lm.scoreHome + lm.scoreAway;
if (lm.scoreHome > lm.scoreAway) leagueHomeWins++;
else if (lm.scoreHome < lm.scoreAway) leagueAwayWins++;
else leagueDraws++;
}
}
const leagueTotal = leagueHomeWins + leagueDraws + leagueAwayWins;
if (leagueTotal > 0) {
console.log(`\nLig: ${match.league?.name}`);
console.log(
` Ev sahibi kazanma: %${((leagueHomeWins / leagueTotal) * 100).toFixed(1)}`,
);
console.log(
` Beraberlik: %${((leagueDraws / leagueTotal) * 100).toFixed(1)}`,
);
console.log(
` Deplasman kazanma: %${((leagueAwayWins / leagueTotal) * 100).toFixed(1)}`,
);
console.log(
` Ortalama gol: ${(leagueGoals / leagueTotal).toFixed(2)}/maç`,
);
}
// ═════════════════════════════════════════════════════════════════
// ADIM 5: GLM-5 TAHMİN MODELİ
// ═════════════════════════════════════════════════════════════════
console.log('\n\n🤖 ADIM 5: GLM-5 TAHMİN MODELİ');
console.log('═'.repeat(50));
// Ağırlıklar
const weights = {
odds: 0.4, // Bookmaker en güvenilir
form: 0.25, // Son performans
homeAdvantage: 0.15, // Ev sahibi avantajı
league: 0.1, // Lig eğilimleri
referee: 0.1, // Hakem etkisi
};
console.log(`\nAğırlıklar:`);
console.log(` Oranlar: %${weights.odds * 100}`);
console.log(` Form: %${weights.form * 100}`);
console.log(` Ev Avantajı: %${weights.homeAdvantage * 100}`);
console.log(` Lig: %${weights.league * 100}`);
console.log(` Hakem: %${weights.referee * 100}`);
// Base skorlar (oranlardan)
let homeScore = odds.normHome || 33;
let drawScore = odds.normDraw || 33;
let awayScore = odds.normAway || 33;
// Form düzeltmesi
const homeFormScore = ((homeStats.wins * 3 + homeStats.draws) / 30) * 100;
const awayFormScore = ((awayStats.wins * 3 + awayStats.draws) / 30) * 100;
const formDiff = homeFormScore - awayFormScore;
console.log(`\nForm Skorları:`);
console.log(` Ev Sahibi: ${homeFormScore.toFixed(1)}`);
console.log(` Deplasman: ${awayFormScore.toFixed(1)}`);
console.log(` Fark: ${formDiff.toFixed(1)} (ev lehine pozitif)`);
// Ev sahibi avantajı (genel istatistik)
const homeAdvantageBonus = 8; // %8 ev sahibi avantajı
// Final hesaplama
homeScore =
(odds.normHome || 33) * weights.odds +
homeFormScore * 0.5 * weights.form +
homeAdvantageBonus * weights.homeAdvantage +
(leagueHomeWins / leagueTotal) * 100 * weights.league;
awayScore =
(odds.normAway || 33) * weights.odds +
awayFormScore * 0.5 * weights.form +
0 * weights.homeAdvantage + // Deplasman avantajı yok
(leagueAwayWins / leagueTotal) * 100 * weights.league;
drawScore =
(odds.normDraw || 33) * weights.odds +
(100 - Math.abs(formDiff)) * 0.1 * weights.form +
(leagueDraws / leagueTotal) * 100 * weights.league;
// Normalize
const totalScore = homeScore + drawScore + awayScore;
const finalHome = (homeScore / totalScore) * 100;
const finalDraw = (drawScore / totalScore) * 100;
const finalAway = (awayScore / totalScore) * 100;
console.log(`\n🎯 FINAL TAHMİN:`);
console.log('─'.repeat(40));
console.log(` 1 (Ev Sahibi): %${finalHome.toFixed(1)}`);
console.log(` X (Beraberlik): %${finalDraw.toFixed(1)}`);
console.log(` 2 (Deplasman): %${finalAway.toFixed(1)}`);
// Kazanan belirle
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;
}
console.log(`\n🏆 TAHMİN: ${prediction}`);
console.log(` Güven: %${confidence.toFixed(1)}`);
// Alt/Üst tahmini
const avgGoals =
(homeStats.goalsFor +
homeStats.goalsAgainst +
awayStats.goalsFor +
awayStats.goalsAgainst) /
20;
const ou25Prediction = avgGoals > 2.5 ? 'ÜST' : 'ALT';
console.log(`\n⚽ 2.5 ${ou25Prediction} (Ort: ${avgGoals.toFixed(2)} gol)`);
if (isFinished) {
console.log(`\n✅ GERÇEK SONUÇ: ${match.scoreHome} - ${match.scoreAway}`);
const actual =
match.scoreHome > match.scoreAway
? '1'
: match.scoreHome < match.scoreAway
? '2'
: 'X';
console.log(` Tahmin ${prediction === actual ? 'DOĞRU ✓' : 'YANLIŞ ✗'}`);
}
}
function calculateTeamStats(matches, teamId) {
let wins = 0,
draws = 0,
losses = 0;
let goalsFor = 0,
goalsAgainst = 0;
for (const m of matches) {
const isHome = m.homeTeamId === teamId;
const gf = isHome ? m.scoreHome : m.scoreAway;
const ga = isHome ? m.scoreAway : m.scoreHome;
goalsFor += gf || 0;
goalsAgainst += ga || 0;
if (gf > ga) wins++;
else if (gf < ga) losses++;
else draws++;
}
return { wins, draws, losses, goalsFor, goalsAgainst };
}
glm5PredictionApproach().catch(console.error);