first (part 2: other directories)
Deploy Iddaai Backend / build-and-deploy (push) Failing after 18s

This commit is contained in:
2026-04-16 15:11:25 +03:00
parent 7814e0bc6b
commit 2f0b85a0c7
203 changed files with 59989 additions and 0 deletions
+462
View File
@@ -0,0 +1,462 @@
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
/**
* 🎯 SÜRPRİZ AVCISI (Upset Hunter)
* ================================
* Favorilerin kaybettiği maçları analiz ederek,
* gelecekteki sürprizleri önceden tespit etmeye çalışıyoruz.
*/
async function upsetHunter() {
console.log('\n');
console.log(
'╔══════════════════════════════════════════════════════════════════╗',
);
console.log(
'║ 🎯 SÜRPRİZ AVCISI (Upset Hunter) ║',
);
console.log(
'║ Favorilerin kaybettiği maçların analizi ║',
);
console.log(
'╚══════════════════════════════════════════════════════════════════╝',
);
// ═════════════════════════════════════════════════════════════════
// BÖLÜM 1: FAVORİ KAYIPLARINI BUL
// ═════════════════════════════════════════════════════════════════
console.log('\n📊 BÖLÜM 1: FAVORİ KAYIPLARINI TESPİT ET');
console.log('─'.repeat(60));
// Favori: MS oranı 1.60'tan düşük olanlar
// Favori kaybı: Favori takımın kaybettiği maçlar
// Bitmiş maçları ve oranlarını al
const finishedMatches = await prisma.match.findMany({
where: {
sport: 'football',
state: 'postGame',
scoreHome: { not: null },
scoreAway: { not: null },
},
include: {
homeTeam: true,
awayTeam: true,
league: true,
oddCategories: {
include: { selections: true },
},
officials: { include: { role: true } },
},
take: 500,
orderBy: { mstUtc: 'desc' },
});
console.log(`\nAnaliz edilen maç sayısı: ${finishedMatches.length}`);
// Favori kayıplarını tespit et
const upsets = [];
const normalResults = [];
for (const match of finishedMatches) {
// MS oranlarını bul
let msHome = null,
msDraw = null,
msAway = null;
for (const cat of match.oddCategories) {
if (cat.name?.includes('Maç Sonucu') || cat.name === 'MS') {
for (const sel of cat.selections) {
if (sel.name === '1') msHome = parseFloat(sel.oddValue);
if (sel.name === 'X') msDraw = parseFloat(sel.oddValue);
if (sel.name === '2') msAway = parseFloat(sel.oddValue);
}
}
}
if (!msHome || !msDraw || !msAway) continue;
// Sonucu belirle
const result =
match.scoreHome > match.scoreAway
? '1'
: match.scoreHome < match.scoreAway
? '2'
: 'X';
// Favori kim?
const favorite = msHome < msAway ? '1' : msAway < msHome ? '2' : 'draw';
const favoriteOdds = favorite === '1' ? msHome : msAway;
// Sadece net favori olan maçları al (1.60 altı)
if (favoriteOdds < 1.6) {
// Favori kaybetti mi?
const isUpset =
(favorite === '1' && result === '2') ||
(favorite === '2' && result === '1');
// X de favori kaybı sayılır (favori kazanamadı)
const isDrawUpset = result === 'X';
if (isUpset || isDrawUpset) {
upsets.push({
match,
favorite,
favoriteOdds,
result,
msHome,
msDraw,
msAway,
isUpset,
isDrawUpset,
});
} else {
normalResults.push({
match,
favorite,
favoriteOdds,
result,
msHome,
msDraw,
msAway,
});
}
}
}
console.log(`\n📈 Sonuçlar:`);
console.log(` Favori kazandı: ${normalResults.length} maç`);
console.log(
` Favori kaybetti (SÜRPRİZ): ${upsets.filter((u) => u.isUpset).length} maç`,
);
console.log(
` Favori berabere: ${upsets.filter((u) => u.isDrawUpset).length} maç`,
);
// Sürpriz oranı
const totalFavMatches = normalResults.length + upsets.length;
const upsetRate = (
(upsets.filter((u) => u.isUpset).length / totalFavMatches) *
100
).toFixed(1);
console.log(`\n⚠️ Favori kayıp oranı: %${upsetRate}`);
// ═════════════════════════════════════════════════════════════════
// BÖLÜM 2: SÜRPRİZ MAÇLARIN ORTAK ÖZELLİKLERİ
// ═════════════════════════════════════════════════════════════════
console.log('\n\n🔍 BÖLÜM 2: SÜRPRİZ MAÇLARIN ORTAK ÖZELLİKLERİ');
console.log('─'.repeat(60));
// Sadece net sürprizleri (favori kaybı) analiz et
const realUpsets = upsets.filter((u) => u.isUpset);
// 2.1 ORAN ANALİZİ
console.log('\n📊 2.1 ORAN ANALİZİ:');
// Bookmaker margin analizi
let upsetMargins = [];
let normalMargins = [];
for (const upset of realUpsets) {
const margin = 1 / upset.msHome + 1 / upset.msDraw + 1 / upset.msAway - 1;
upsetMargins.push(margin);
}
for (const normal of normalResults) {
const margin =
1 / normal.msHome + 1 / normal.msDraw + 1 / normal.msAway - 1;
normalMargins.push(margin);
}
const avgUpsetMargin =
upsetMargins.length > 0
? (
(upsetMargins.reduce((a, b) => a + b, 0) / upsetMargins.length) *
100
).toFixed(1)
: 0;
const avgNormalMargin =
normalMargins.length > 0
? (
(normalMargins.reduce((a, b) => a + b, 0) / normalMargins.length) *
100
).toFixed(1)
: 0;
console.log(
` Sürpriz maçlarda ortalama bookmaker margin: %${avgUpsetMargin}`,
);
console.log(
` Normal maçlarda ortalama bookmaker margin: %${avgNormalMargin}`,
);
// Margin farkı yüksek mi?
if (parseFloat(avgUpsetMargin) > parseFloat(avgNormalMargin) + 2) {
console.log(` ⚠️ DİKKAT: Sürpriz maçlarda margin YÜKSEK! Şüpheli!`);
}
// 2.2 FAVORİ ORAN ARALIĞI
console.log('\n📊 2.2 FAVORİ ORAN ARALIĞI:');
const upsetOddsRanges = {
'1.10-1.20': 0,
'1.20-1.30': 0,
'1.30-1.40': 0,
'1.40-1.50': 0,
'1.50-1.60': 0,
};
const normalOddsRanges = {
'1.10-1.20': 0,
'1.20-1.30': 0,
'1.30-1.40': 0,
'1.40-1.50': 0,
'1.50-1.60': 0,
};
for (const upset of realUpsets) {
const odds = upset.favoriteOdds;
if (odds >= 1.1 && odds < 1.2) upsetOddsRanges['1.10-1.20']++;
else if (odds >= 1.2 && odds < 1.3) upsetOddsRanges['1.20-1.30']++;
else if (odds >= 1.3 && odds < 1.4) upsetOddsRanges['1.30-1.40']++;
else if (odds >= 1.4 && odds < 1.5) upsetOddsRanges['1.40-1.50']++;
else if (odds >= 1.5 && odds < 1.6) upsetOddsRanges['1.50-1.60']++;
}
for (const normal of normalResults) {
const odds = normal.favoriteOdds;
if (odds >= 1.1 && odds < 1.2) normalOddsRanges['1.10-1.20']++;
else if (odds >= 1.2 && odds < 1.3) normalOddsRanges['1.20-1.30']++;
else if (odds >= 1.3 && odds < 1.4) normalOddsRanges['1.30-1.40']++;
else if (odds >= 1.4 && odds < 1.5) normalOddsRanges['1.40-1.50']++;
else if (odds >= 1.5 && odds < 1.6) normalOddsRanges['1.50-1.60']++;
}
console.log('\n Sürpriz maçlarda favori oran dağılımı:');
for (const [range, count] of Object.entries(upsetOddsRanges)) {
const total = count + normalOddsRanges[range];
const pct = total > 0 ? ((count / total) * 100).toFixed(1) : 0;
console.log(
` ${range}: ${count} sürpriz / ${total} toplam → %${pct} sürpriz oranı`,
);
}
// 2.3 HAKEM ANALİZİ
console.log('\n📊 2.3 HAKEM ANALİZİ:');
const upsetReferees = {};
for (const upset of realUpsets) {
const referee = upset.match.officials?.find(
(o) => o.role?.name === 'Orta Hakem',
);
if (referee) {
if (!upsetReferees[referee.name]) {
upsetReferees[referee.name] = { upsets: 0, total: 0 };
}
upsetReferees[referee.name].upsets++;
}
}
// Tüm maçlarda bu hakemler
const allReferees = {};
for (const match of finishedMatches) {
const referee = match.officials?.find((o) => o.role?.name === 'Orta Hakem');
if (referee) {
if (!allReferees[referee.name]) {
allReferees[referee.name] = 0;
}
allReferees[referee.name]++;
}
}
// En çok sürprize sebep olan hakemler
const sortedUpsetReferees = Object.entries(upsetReferees)
.map(([name, data]) => ({
name,
upsets: data.upsets,
total: allReferees[name] || data.upsets,
rate: ((data.upsets / (allReferees[name] || data.upsets)) * 100).toFixed(
1,
),
}))
.filter((r) => r.total >= 3) // En az 3 maç yönetenler
.sort((a, b) => parseFloat(b.rate) - parseFloat(a.rate));
console.log('\n En çok sürpriz yaşatan hakemler:');
for (const ref of sortedUpsetReferees.slice(0, 5)) {
console.log(
` ${ref.name}: ${ref.upsets}/${ref.total} maç → %${ref.rate} sürpriz`,
);
}
// 2.4 LİG ANALİZİ
console.log('\n📊 2.4 LİG ANALİZİ:');
const upsetLeagues = {};
const allLeagues = {};
for (const upset of realUpsets) {
const leagueName = upset.match.league?.name || 'Bilinmeyen';
if (!upsetLeagues[leagueName]) upsetLeagues[leagueName] = 0;
upsetLeagues[leagueName]++;
}
for (const match of finishedMatches) {
const leagueName = match.league?.name || 'Bilinmeyen';
if (!allLeagues[leagueName]) allLeagues[leagueName] = 0;
allLeagues[leagueName]++;
}
const sortedUpsetLeagues = Object.entries(upsetLeagues)
.map(([name, count]) => ({
name,
upsets: count,
total: allLeagues[name] || count,
rate: ((count / (allLeagues[name] || count)) * 100).toFixed(1),
}))
.filter((l) => l.total >= 5)
.sort((a, b) => parseFloat(b.rate) - parseFloat(a.rate));
console.log('\n En çok sürpriz yaşanan ligler:');
for (const league of sortedUpsetLeagues.slice(0, 5)) {
console.log(
` ${league.name}: ${league.upsets}/${league.total} maç → %${league.rate} sürpriz`,
);
}
// ═════════════════════════════════════════════════════════════════
// BÖLÜM 3: SÜRPRİZ TESPİT MODELİ
// ═════════════════════════════════════════════════════════════════
console.log('\n\n🎯 BÖLÜM 3: SÜRPRİZ TESPİT İŞARETLERİ');
console.log('─'.repeat(60));
console.log(`
┌─────────────────────────────────────────────────────────────────────┐
│ SÜRPRİZ TESPİT İŞARETLERİ (Upset Indicators) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. ⚠️ YÜKSEK MARGIN (%18+) │
│ → Bookmaker kendini koruyor, favori riskli │
│ │
│ 2. 👨‍⚖️ SÜRPRİZ HAKEM │
│ → Bazı hakemler favorilere karşı sert │
│ │
│ 3. 📉 ORAN HAREKETİ │
│ → Favori oranı yükseliyorsa, para dışarı akıyor │
│ │
│ 4. 🏆 DERBİ/ÖZEL MAÇ │
│ → Form tablosu işlemez, motivasyon farkı
│ │
│ 5. 📊 ÇOK DÜŞÜK FAVORİ ORANI (<1.20) │
│ → "Çok iyi görünen" fırsatlar genelde tuzak │
│ │
│ 6. 🔄 H2H SÜRPRİZ GEÇMİŞİ │
│ → Geçmişte sürpriz olmuşsa tekrar edebilir │
│ │
└─────────────────────────────────────────────────────────────────────┘
`);
// Real Madrid örneği
console.log('\n📌 REAL MADRID vs GETAFE ANALİZİ (Sürpriz Neden Oldu?):');
console.log('─'.repeat(60));
const realMadridMatch = finishedMatches.find(
(m) => m.id === '2m4sef2l4im49rda90k3p41lg',
);
if (realMadridMatch) {
let msH = null,
msD = null,
msA = null;
for (const cat of realMadridMatch.oddCategories) {
if (cat.name?.includes('Maç Sonucu') || cat.name === 'MS') {
for (const sel of cat.selections) {
if (sel.name === '1') msH = parseFloat(sel.oddValue);
if (sel.name === 'X') msD = parseFloat(sel.oddValue);
if (sel.name === '2') msA = parseFloat(sel.oddValue);
}
}
}
const margin =
msH && msD && msA
? ((1 / msH + 1 / msD + 1 / msA - 1) * 100).toFixed(1)
: 'N/A';
console.log(`\n 📊 Oranlar: 1=${msH} | X=${msD} | 2=${msA}`);
console.log(
` 📈 Bookmaker Margin: %${margin} ${parseFloat(margin) > 18 ? '⚠️ YÜKSEK!' : ''}`,
);
const referee = realMadridMatch.officials?.find(
(o) => o.role?.name === 'Orta Hakem',
);
if (referee) {
const refUpsetData = upsetReferees[referee.name];
console.log(` 👨‍⚖️ Hakem: ${referee.name}`);
if (refUpsetData) {
console.log(
` Bu hakemde sürpriz oranı: %${((refUpsetData.upsets / allReferees[referee.name]) * 100).toFixed(1)}`,
);
}
}
console.log(`\n ✅ SÜRPRİZ İŞARETLERİ:`);
if (parseFloat(margin) > 18) {
console.log(` ⚠️ Margin yüksek → Bookmaker risk görüyordu`);
}
console.log(` 🏆 Madrid derbisi → Derbide form işlemez`);
console.log(` 📉 Favori oranı çok düşük (1.25) → "Tuzak" oranı`);
}
// ═════════════════════════════════════════════════════════════════
// BÖLÜM 4: SÜRPRİZ TAHMİN FONKSİYONU
// ═════════════════════════════════════════════════════════════════
console.log('\n\n🎯 BÖLÜM 4: SÜRPRİZ TAHMİN FONKSİYONU');
console.log('─'.repeat(60));
console.log(`
// Sürpriz Skoru Hesaplama
function calculateUpsetScore(match, odds, referee, league) {
let score = 0;
// 1. Margin Kontrolü
const margin = (1/odds.ms_h + 1/odds.ms_d + 1/odds.ms_a) - 1;
if (margin > 0.20) score += 15; // Yüksek margin = risk
// 2. Hakem Faktörü
if (referee.upsetRate > 25) score += 20;
else if (referee.upsetRate > 20) score += 10;
// 3. Favori Oran Çok Düşük
if (odds.favorite < 1.20) score += 25; // Tuzak oranı
else if (odds.favorite < 1.30) score += 15;
// 4. Derbi/Özel Maç
if (isDerby(match)) score += 15;
// 5. H2H Sürpriz Geçmişi
if (h2h.upsetCount > 0) score += 10;
// 6. Form Farkı Çok Büyük
if (formDiff > 40) score += 10; // "Çok iyi" görünüyorsa risk
return score; // 0-100 arası
}
// EŞİK: 50+ = Sürpriz bekleniyor, Value bet var
`);
console.log('\n📌 ÖRNEK: Real Madrid vs Getafe için sürpriz skoru:');
console.log(' Margin (%20.1 > %18): +15');
console.log(' Favori oran (1.25 < 1.30): +15');
console.log(' Derbi maçı: +15');
console.log(' Hakem sürpriz oranı yüksek: +10');
console.log(' ─────────────────────────────');
console.log(' TOPLAM: 55 → ⚠️ SÜRPRİZ BEKLENİYOR!');
console.log('\n');
await prisma.$disconnect();
}
upsetHunter().catch(console.error);