251 lines
11 KiB
Python
251 lines
11 KiB
Python
"""
|
||
VQWEN Live Prediction Tracker
|
||
=============================
|
||
Predicts today's upcoming matches (from live_matches) and tracks results.
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import json
|
||
import time
|
||
import pickle
|
||
import psycopg2
|
||
import pandas as pd
|
||
import numpy as np
|
||
from psycopg2.extras import RealDictCursor
|
||
|
||
AI_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
ROOT_DIR = os.path.dirname(AI_DIR)
|
||
PROJECT_ROOT = os.path.dirname(ROOT_DIR)
|
||
|
||
def get_clean_dsn() -> str:
|
||
return "postgresql://suggestbet:SuGGesT2026SecuRe@localhost:15432/boilerplate_db"
|
||
|
||
def run_live_predictions():
|
||
print("🔴 VQWEN LIVE PREDICTION TRACKER")
|
||
print("="*60)
|
||
|
||
# Load Models
|
||
mdir = os.path.join(ROOT_DIR, 'models', 'vqwen')
|
||
try:
|
||
with open(os.path.join(mdir, 'vqwen_ms.pkl'), 'rb') as f: model_ms = pickle.load(f)
|
||
with open(os.path.join(mdir, 'vqwen_ou25.pkl'), 'rb') as f: model_ou = pickle.load(f)
|
||
with open(os.path.join(mdir, 'vqwen_btts.pkl'), 'rb') as f: model_btts = pickle.load(f)
|
||
print("✅ VQWEN v3 modelleri yüklendi.")
|
||
except Exception as e:
|
||
print(f"❌ Model hatası: {e}")
|
||
return
|
||
|
||
dsn = get_clean_dsn()
|
||
conn = psycopg2.connect(dsn)
|
||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||
|
||
# 1. Bugünün Maçlarını Çek (NS veya oynanıyor ama henüz bitmemiş olanlar)
|
||
# mst_utc bugün olan maçlar
|
||
start_of_day = int(time.mktime(time.strptime(time.strftime("%Y-%m-%d"), "%Y-%m-%d")) * 1000)
|
||
end_of_day = start_of_day + (24 * 60 * 60 * 1000)
|
||
|
||
print(f"📅 Bugünün maçları taranıyor...")
|
||
|
||
# live_matches veya matches tablosundan bugünkü maçları alıyoruz
|
||
# Önce odds olanları alalım
|
||
cur.execute("""
|
||
SELECT m.id, m.home_team_id, m.away_team_id, m.score_home, m.score_away,
|
||
m.mst_utc, m.status,
|
||
t1.name as home_team, t2.name as away_team,
|
||
l.name as league_name,
|
||
maf.home_elo, maf.away_elo
|
||
FROM live_matches m
|
||
LEFT JOIN teams t1 ON m.home_team_id = t1.id
|
||
LEFT JOIN teams t2 ON m.away_team_id = t2.id
|
||
LEFT JOIN leagues l ON m.league_id = l.id
|
||
LEFT JOIN football_ai_features maf ON maf.match_id = m.id
|
||
WHERE m.mst_utc >= %s AND m.mst_utc <= %s
|
||
ORDER BY m.mst_utc ASC
|
||
""", (start_of_day, end_of_day))
|
||
|
||
rows = cur.fetchall()
|
||
print(f"📊 Bugün için {len(rows)} maç bulundu.")
|
||
|
||
if not rows:
|
||
print("⚠️ Bugün için oranı olan maç bulunamadı.")
|
||
cur.close()
|
||
conn.close()
|
||
return
|
||
|
||
results = []
|
||
total_profit = 0.0
|
||
total_bet = 0
|
||
total_won = 0
|
||
|
||
for i, row in enumerate(rows):
|
||
match_id = str(row['id'])
|
||
home = row['home_team'] or "Home"
|
||
away = row['away_team'] or "Away"
|
||
league = row['league_name'] or "Unknown"
|
||
|
||
# Maç bitmiş mi kontrol et
|
||
is_finished = row['status'] in ['FT', 'AET', 'PEN', 'post', 'postGame'] or (
|
||
row['score_home'] is not None and row['score_away'] is not None and
|
||
row['status'] not in ['NS', 'pre', 'preGame', 'live', 'liveGame']
|
||
)
|
||
|
||
# Oranları al (odd_categories)
|
||
cur.execute("""
|
||
SELECT oc.name as category, 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 = %s AND oc.name ILIKE ANY (ARRAY['%%Maç Sonucu%%', '%%2,5 Alt/Üst%%', '%%Karşılıklı Gol%%'])
|
||
""", (match_id,))
|
||
odds_rows = cur.fetchall()
|
||
|
||
odds_dict = {}
|
||
for o in odds_rows:
|
||
cat = o['category'].lower()
|
||
sel = o['selection'].lower()
|
||
val = float(o['odd_value'])
|
||
if 'maç sonucu' in cat or 'mac sonucu' in cat:
|
||
if sel == '1': odds_dict['ms_h'] = val
|
||
elif sel == 'x': odds_dict['ms_d'] = val
|
||
elif sel == '2': odds_dict['ms_a'] = val
|
||
elif '2,5 alt' in cat or '2.5 alt' in cat:
|
||
if 'alt' in sel: odds_dict['ou25_u'] = val
|
||
elif 'üst' in sel or 'ust' in sel: odds_dict['ou25_o'] = val
|
||
elif 'karşılıklı gol' in cat:
|
||
if 'var' in sel: odds_dict['btts_y'] = val
|
||
elif 'yok' in sel: odds_dict['btts_n'] = val
|
||
|
||
# Eğer oranlar yoksa atla
|
||
if not all(k in odds_dict for k in ['ms_h', 'ms_d', 'ms_a', 'ou25_o', 'btts_y']):
|
||
# print(f"⚠️ {home} vs {away} - Oranlar eksik.")
|
||
continue
|
||
|
||
# Özellikleri Hesapla
|
||
# Form, Rest, Contextual Goals veritabanından çekilmeli (canlı maç için)
|
||
cur.execute("""
|
||
SELECT
|
||
COALESCE((SELECT AVG(m2.score_home) FROM matches m2 WHERE m2.home_team_id = %s AND m2.status = 'FT' AND m2.mst_utc < %s), 1.2) as h_home_goals,
|
||
COALESCE((SELECT AVG(m2.score_away) FROM matches m2 WHERE m2.away_team_id = %s AND m2.status = 'FT' AND m2.mst_utc < %s), 1.2) as a_away_goals,
|
||
COALESCE(EXTRACT(EPOCH FROM (to_timestamp(%s/1000) - (SELECT MAX(to_timestamp(m2.mst_utc/1000)) FROM matches m2 WHERE m2.home_team_id = %s AND m2.status = 'FT' AND m2.mst_utc < %s)) / 86400), 7) as h_rest,
|
||
COALESCE(EXTRACT(EPOCH FROM (to_timestamp(%s/1000) - (SELECT MAX(to_timestamp(m2.mst_utc/1000)) FROM matches m2 WHERE m2.away_team_id = %s AND m2.status = 'FT' AND m2.mst_utc < %s)) / 86400), 7) as a_rest,
|
||
COALESCE((SELECT COUNT(*) FROM match_player_participation mp WHERE mp.match_id = %s AND mp.team_id = %s AND mp.is_starting = true), 11) as h_xi,
|
||
COALESCE((SELECT COUNT(*) FROM match_player_participation mp WHERE mp.match_id = %s AND mp.team_id = %s AND mp.is_starting = true), 11) as a_xi,
|
||
COALESCE((SELECT COUNT(*) FILTER (WHERE m2.score_home > m2.score_away)::float / NULLIF(COUNT(*), 0) FROM matches m2 WHERE m2.home_team_id = %s AND m2.away_team_id = m2.away_team_id AND m2.status = 'FT' AND m2.mst_utc < %s), 0.5) as h2h_h_wr,
|
||
COALESCE((SELECT SUM(pts) FROM (SELECT CASE WHEN m2.score_home > m2.score_away THEN 3 WHEN m2.score_home = m2.score_away THEN 1 ELSE 0 END as pts FROM matches m2 WHERE m2.home_team_id = %s AND m2.status = 'FT' AND m2.mst_utc < %s ORDER BY m2.mst_utc DESC LIMIT 5) sub), 0) as h_form_pts,
|
||
COALESCE((SELECT SUM(pts) FROM (SELECT CASE WHEN m2.score_away > m2.score_home THEN 3 WHEN m2.score_away = m2.score_home THEN 1 ELSE 0 END as pts FROM matches m2 WHERE m2.away_team_id = %s AND m2.status = 'FT' AND m2.mst_utc < %s ORDER BY m2.mst_utc DESC LIMIT 5) sub), 0) as a_form_pts
|
||
""", (
|
||
row['home_team_id'], row['mst_utc'],
|
||
row['away_team_id'], row['mst_utc'],
|
||
row['mst_utc'], row['home_team_id'], row['mst_utc'],
|
||
row['mst_utc'], row['away_team_id'], row['mst_utc'],
|
||
match_id, row['home_team_id'],
|
||
match_id, row['away_team_id'],
|
||
row['home_team_id'], row['away_team_id'], row['mst_utc'],
|
||
row['home_team_id'], row['mst_utc'],
|
||
row['away_team_id'], row['mst_utc']
|
||
))
|
||
stats = cur.fetchone()
|
||
|
||
h_elo = float(row['home_elo'] or 1500)
|
||
a_elo = float(row['away_elo'] or 1500)
|
||
h_home_goals = float(stats['h_home_goals'] or 1.2)
|
||
a_away_goals = float(stats['a_away_goals'] or 1.2)
|
||
h_rest = float(stats['h_rest'] or 7)
|
||
a_rest = float(stats['a_rest'] or 7)
|
||
h_xi = float(stats['h_xi'] or 11)
|
||
a_xi = float(stats['a_xi'] or 11)
|
||
h2h_h_wr = float(stats['h2h_h_wr'] or 0.5)
|
||
h_pts = float(stats['h_form_pts'] or 0)
|
||
a_pts = float(stats['a_form_pts'] or 0)
|
||
|
||
def fatigue(rest):
|
||
if rest < 3: return 0.85
|
||
if rest < 5: return 0.95
|
||
return 1.0
|
||
|
||
h_fat = fatigue(h_rest)
|
||
a_fat = fatigue(a_rest)
|
||
h_xg = h_home_goals * h_fat
|
||
a_xg = a_away_goals * a_fat
|
||
margin = (1/odds_dict['ms_h']) + (1/odds_dict['ms_d']) + (1/odds_dict['ms_a'])
|
||
|
||
features = pd.DataFrame([{
|
||
'elo_diff': h_elo - a_elo,
|
||
'h_xg': h_xg, 'a_xg': a_xg,
|
||
'total_xg': h_xg + a_xg,
|
||
'pow_diff': (h_elo/100)*h_fat - (a_elo/100)*a_fat,
|
||
'rest_diff': h_rest - a_rest,
|
||
'h_fatigue': h_fat, 'a_fatigue': a_fat,
|
||
'imp_h': (1/odds_dict['ms_h'])/margin,
|
||
'imp_d': (1/odds_dict['ms_d'])/margin,
|
||
'imp_a': (1/odds_dict['ms_a'])/margin,
|
||
'h_xi': h_xi, 'a_xi': a_xi,
|
||
'h2h_h_wr': h2h_h_wr,
|
||
'form_diff': h_pts - a_pts
|
||
}])
|
||
|
||
# --- TAHMİNLER ---
|
||
ms_probs = model_ms.predict(features)[0]
|
||
p_over = float(model_ou.predict(features)[0])
|
||
p_btts = float(model_btts.predict(features)[0])
|
||
|
||
# --- EN İYİ VALUE PICK ---
|
||
picks = []
|
||
for pick, prob, odd in zip(['1', 'X', '2'], ms_probs, [odds_dict['ms_h'], odds_dict['ms_d'], odds_dict['ms_a']]):
|
||
edge = prob - (1/odd)
|
||
if edge > 0.05 and prob > 0.45:
|
||
picks.append({"market": "MS", "pick": pick, "prob": prob, "odds": odd})
|
||
|
||
if p_over > 0.55: picks.append({"market": "OU2.5", "pick": "Over", "prob": p_over, "odds": odds_dict.get('ou25_o', 1.85)})
|
||
if p_btts > 0.55: picks.append({"market": "BTTS", "pick": "Var", "prob": p_btts, "odds": odds_dict.get('btts_y', 1.85)})
|
||
|
||
picks.sort(key=lambda x: (x['prob'] + max(0, x['prob'] - 1/x['odds'])*100), reverse=True)
|
||
best_pick = picks[0] if picks else None
|
||
|
||
# --- SONUÇ KONTROLÜ ---
|
||
res_str = "⏳ Oynanıyor/Bekleniyor"
|
||
won = None
|
||
h_score = row['score_home']
|
||
a_score = row['score_away']
|
||
|
||
if is_finished and h_score is not None and a_score is not None:
|
||
res_str = f"🏁 SONUÇ: {h_score}-{a_score}"
|
||
if best_pick:
|
||
p = best_pick['pick']
|
||
if p == '1': won = h_score > a_score
|
||
elif p == 'X': won = h_score == a_score
|
||
elif p == '2': won = a_score > h_score
|
||
elif p == 'Over': won = (h_score + a_score) > 2.5
|
||
elif p == 'Var': won = h_score > 0 and a_score > 0
|
||
|
||
res_str += " | " + ("✅ KAZANDI" if won else "❌ KAYBETTİ")
|
||
if won: total_profit += (best_pick['odds'] - 1.0)
|
||
else: total_profit -= 1.0
|
||
total_bet += 1
|
||
if won: total_won += 1
|
||
|
||
# Çıktı
|
||
match_time = time.strftime("%H:%M", time.gmtime(row['mst_utc']/1000))
|
||
pick_info = f"{best_pick['market']} - {best_pick['pick']} (%{best_pick['prob']*100:.0f} @ {best_pick['odds']:.2f})" if best_pick else "💤 Önerilen Bahis Yok"
|
||
|
||
print(f"\n⚽ [{match_time}] {home} vs {away} ({league})")
|
||
print(f" 🧠 Tahmin: {pick_info}")
|
||
print(f" {res_str}")
|
||
|
||
print("\n" + "="*60)
|
||
print("📊 GÜNLÜK ÖZET")
|
||
print("="*60)
|
||
if total_bet > 0:
|
||
print(f"🎲 Oynanan Bahis: {total_bet}")
|
||
print(f"✅ Kazanan: {total_won}")
|
||
print(f"💰 Toplam Kâr: {total_profit:.2f} Units")
|
||
print(f"📈 ROI: {(total_profit/total_bet)*100:.1f}%")
|
||
else:
|
||
print("📝 Bugün için Value Bahis bulunamadı veya maçlar bitmedi.")
|
||
|
||
cur.close()
|
||
conn.close()
|
||
|
||
if __name__ == "__main__":
|
||
run_live_predictions()
|