This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user