""" Detailed Backtest with 50 Top League Matches ============================================ Runs AI Engine predictions on 50 real historical matches and shows exactly which predictions were correct and which were skipped. Usage: python ai-engine/scripts/backtest_50_detailed.py """ import os import sys import json import time import psycopg2 from psycopg2.extras import RealDictCursor # Add paths AI_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT_DIR = os.path.dirname(AI_DIR) sys.path.insert(0, ROOT_DIR) if "scripts" in os.path.basename(AI_DIR): ROOT_DIR = os.path.dirname(ROOT_DIR) from services.single_match_orchestrator import get_single_match_orchestrator def get_clean_dsn() -> str: return "postgresql://suggestbet:SuGGesT2026SecuRe@localhost:15432/boilerplate_db" # 50 Match IDs from the query MATCH_IDS = [ "v2ljcst50nk37x04xwimpi50", "7gz0bhb5yvdssazl3y5946kno", "7ftj7kbu4rzpewxravf3luuc4", "7f1z4e8ch1dm5q677644cky6s", "7ffq3aq3so22iymfdzch63nys", "rrkmeuymz7gzvoz8mplikzdg", "7hegc9covicy699bxsi81xkb8", "7gl7rpr1hjayk3e5ut0gr613o", "7g7d86i3738287xfvyfeffcwk", "7hs4boe4hv80muawocevvx2j8", "7ijhsloieg4t9yp5cxp0duln8", "7ixaiiptli5ek32kuybuni4gk", "7i5sfh41cjpwg4l972dm487x0", "eo7g4wunxxxr8uv45q8p5x638", "7dinds2937w4645wva2rddlas", "7b5ukdhvqh62wtndeqfg01ixg", "7bjptsj24gndoydn7n0202g44", "7cqxf3vo58ewrwmoom5xiyexg", "7bxjl9h2hnf165rlp3o1vfztg", "7eo8zrez08c342rqsezpvq39w", "7as1muhs98vdarlhsean4bspg", "7dwhj8cfxv6v6bzxpu5e3h05w", "7d4vq4417ps84yjzh95bnvvv8", "7ea9z501jgp9kxw3gay4myrkk", "7cd3401itlty6ded7c1wct0yc", "ebgpz9mcije2snv986n6587pw", "i7ar1dkhvcwpxmkyks65ib6c", "lyek7tyy6qk2xjs9vblucnx0", "hdn9qtyn3ysjwbc3i2trantg", "3y2bnssfqlajosiz2gpkn6xhw", "40pehd14s9djjtycujavbex3o", "3xnbfjznzmnwml20akbgnis5w", "2eovi2rcc2l4ha7fpb2w7e1hw", "2bwuikdjyyuithhru8ka8o00k", "2d3pcd76ya9ihi9yotxc553is", "1e9it04z4epy2etdxsffe7m6s", "7af49jgo4iulv1k8cplj9smj8", "5k3vrz619hdu9nx4rnx6uim1g", "amjppgpetnyr0iisi241kgkyc", "coqrhq09kxd16iejvgtzj3mz8", "d8ysan1qdctmkvjaz2adw7aqc", "9ttciz0gtb0z09ev1q5fe0ro4", "9u720o37yaddqu1w6hlszpnh0", "7ijezdjp8t0rjti91ac63hyxg", "72gvdvztbb3dn79jidzzxzcb8", "6uof1v2s6vrpieeml2bwo9tlg", "91dd8ia3m0bxoqzjgyo3ptsk", "3tj1nt3udsbvb9soqn2cs6gpg", "1br5g88o5idtjxka1fr6zg4k4", "akuesquthbmxlzckvnqmgles4" ] def run_detailed_backtest(): print("πŸš€ DETAILED BACKTEST: 50 Top League Matches") print("🧠 Engine: V30 Ensemble (V20+V25) + Skip Logic") print("="*80) dsn = get_clean_dsn() conn = psycopg2.connect(dsn) cur = conn.cursor(cursor_factory=RealDictCursor) # Fetch match details with odds placeholders = ','.join(['%s'] * len(MATCH_IDS)) cur.execute(f""" SELECT m.id, m.match_name, m.home_team_id, m.away_team_id, m.score_home, m.score_away, m.league_id, t1.name as home_team, t2.name as away_team, l.name as league_name FROM 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 WHERE m.id IN ({placeholders}) AND m.status = 'FT' ORDER BY m.mst_utc DESC """, MATCH_IDS) rows = cur.fetchall() print(f"πŸ“Š Found {len(rows)} matches. Starting AI Analysis...") if not rows: print("⚠️ No matches found.") cur.close() conn.close() return # Initialize AI Engine try: orchestrator = get_single_match_orchestrator() print("βœ… AI Engine Loaded.\n") except Exception as e: print(f"❌ Failed to load AI Engine: {e}") cur.close() conn.close() return # ─── Backtest Loop ─── results = [] total_skipped = 0 total_played = 0 total_won = 0 total_profit = 0.0 MIN_CONF = 45.0 start_time = time.time() for i, row in enumerate(rows): match_id = str(row['id']) home_team = row['home_team'] or "Unknown" away_team = row['away_team'] or "Unknown" league = row['league_name'] or "Unknown" home_score = row['score_home'] or 0 away_score = row['score_away'] or 0 total_goals = home_score + away_score print(f"[{i+1}/{len(rows)}] {home_team} vs {away_team} ({league}) ... ", end="", flush=True) try: prediction = orchestrator.analyze_match(match_id) if not prediction: print("⚠️ No prediction") continue # Extract Main Pick main_pick = prediction.get("main_pick") or {} pick_name = main_pick.get("pick", "") confidence = main_pick.get("confidence", 0) odds = main_pick.get("odds", 0) # Apply Skip Logic if confidence < MIN_CONF: print(f"🚫 SKIP (Conf {confidence:.0f}%)") total_skipped += 1 results.append({"match": f"{home_team} vs {away_team}", "pick": pick_name, "conf": confidence, "odds": odds, "result": "SKIPPED", "profit": 0}) continue if odds > 0: implied_prob = 1.0 / odds my_prob = confidence / 100.0 if my_prob - implied_prob < -0.03: print(f"🚫 SKIP (Bad Value)") total_skipped += 1 results.append({"match": f"{home_team} vs {away_team}", "pick": pick_name, "conf": confidence, "odds": odds, "result": "SKIPPED", "profit": 0}) continue # Bet Played total_played += 1 won = False # Resolve pick_clean = str(pick_name).upper() if pick_clean in ["1", "MS 1", "Δ°Y 1"] and home_score > away_score: won = True elif pick_clean in ["X", "MS X", "Δ°Y X"] and home_score == away_score: won = True elif pick_clean in ["2", "MS 2", "Δ°Y 2"] and away_score > home_score: won = True elif pick_clean in ["1X", "X2"] or ("1X" in pick_clean or "X2" in pick_clean): if "1X" in pick_clean and home_score >= away_score: won = True elif "X2" in pick_clean and away_score >= home_score: won = True elif pick_clean in ["12"] and home_score != away_score: won = True elif "ÜST" in pick_clean or "OVER" in pick_clean: line = 2.5 if "1.5" in pick_clean: line = 1.5 elif "3.5" in pick_clean: line = 3.5 if total_goals > line: won = True elif "ALT" in pick_clean or "UNDER" in pick_clean: line = 2.5 if "1.5" in pick_clean: line = 1.5 elif "3.5" in pick_clean: line = 3.5 if total_goals < line: won = True elif "VAR" in pick_clean and home_score > 0 and away_score > 0: won = True elif "YOK" in pick_clean and (home_score == 0 or away_score == 0): won = True if won: total_won += 1 profit = odds - 1.0 print(f"βœ… WON ({pick_name} @ {odds:.2f}, +{profit:.2f})") else: profit = -1.0 print(f"❌ LOST ({pick_name} @ {odds:.2f})") total_profit += profit results.append({"match": f"{home_team} vs {away_team}", "pick": pick_name, "conf": confidence, "odds": odds, "result": "WON" if won else "LOST", "profit": profit, "score": f"{home_score}-{away_score}"}) except Exception as e: print(f"πŸ’₯ Error: {e}") elapsed = time.time() - start_time # ─── DETAILED REPORT ─── print("\n" + "="*80) print("πŸ“ˆ DETAILED BACKTEST RESULTS") print(f"⏱️ Time: {elapsed:.1f}s") print("="*80) print(f"πŸ“Š Total Matches: {len(rows)}") print(f"🚫 Skipped: {total_skipped}") print(f"🎲 Played: {total_played}") print(f"βœ… Won: {total_won}") print(f"πŸ’€ Lost: {total_played - total_won}") print(f"πŸ’° Profit: {total_profit:+.2f} units") if total_played > 0: win_rate = (total_won / total_played) * 100 roi = (total_profit / total_played) * 100 print(f"πŸ“Š Win Rate: {win_rate:.1f}%") print(f"πŸ“Š ROI: {roi:.1f}%") if roi > 0: print("🟒 STRATEGY IS PROFITABLE!") else: print("πŸ”΄ STRATEGY IS LOSING") # ─── TABLE OF ALL RESULTS ─── print("\n" + "="*80) print("πŸ“‹ DETAILED MATCH RESULTS") print("="*80) print(f"{'Match':<40} {'Pick':<15} {'Conf':<6} {'Odds':<6} {'Result':<8} {'Score':<6}") print("-"*80) for r in results: match_str = r['match'][:38] pick_str = str(r['pick'])[:13] conf_str = f"{r['conf']:.0f}%" odds_str = f"{r['odds']:.2f}" if r['odds'] > 0 else "N/A" res_str = r['result'] score_str = r.get('score', '') # Color coding if res_str == "WON": res_display = f"βœ… {res_str}" elif res_str == "LOST": res_display = f"❌ {res_str}" else: res_display = f"🚫 {res_str}" print(f"{match_str:<40} {pick_str:<15} {conf_str:<6} {odds_str:<6} {res_display:<12} {score_str:<6}") cur.close() conn.close() if __name__ == "__main__": run_detailed_backtest()