""" Backtest for September 13th (Top Leagues Only) ============================================== Simulates the NEW 'Skip Logic' on matches from Sept 13, 2025. """ import os import sys import json import psycopg2 from psycopg2.extras import RealDictCursor from datetime import datetime # Load .env manually to ensure correct DB connection project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, project_root) # Add root to path if needed def get_clean_dsn() -> str: return "postgresql://suggestbet:SuGGesT2026SecuRe@localhost:15432/boilerplate_db" # ─── Configuration ───────── MIN_CONF_THRESHOLDS = { "MS": 45.0, "DC": 40.0, "OU15": 50.0, "OU25": 45.0, "OU35": 45.0, "BTTS": 45.0, "HT": 40.0, } def run_backtest(): print("🚀 Backtest: 13 Eylül 2024 - Top Leagues") print("="*60) # 1. Load Top Leagues leagues_path = os.path.join(project_root, "top_leagues.json") try: with open(leagues_path, 'r') as f: top_leagues = json.load(f) # Ensure they are strings for SQL IN clause league_ids = tuple(str(lid) for lid in top_leagues) print(f"📋 Loaded {len(top_leagues)} top leagues.") except Exception as e: print(f"❌ Error loading top_leagues.json: {e}") return # 2. Define Date Range (Sept 13, 2024 UTC) start_dt = datetime(2024, 9, 13, 0, 0, 0) end_dt = datetime(2024, 9, 13, 23, 59, 59) start_ts = int(start_dt.timestamp() * 1000) end_ts = int(end_dt.timestamp() * 1000) dsn = get_clean_dsn() conn = psycopg2.connect(dsn) cur = conn.cursor(cursor_factory=RealDictCursor) # 3. Fetch Matches & Predictions # We need matches that are FT and have a prediction query = """ SELECT p.match_id, p.prediction_json, m.score_home, m.score_away, m.status, m.league_id FROM predictions p JOIN matches m ON p.match_id = m.id WHERE m.mst_utc BETWEEN %s AND %s AND m.league_id IN %s AND m.status = 'FT' AND p.prediction_json IS NOT NULL """ try: cur.execute(query, (start_ts, end_ts, league_ids)) rows = cur.fetchall() except Exception as e: print(f"❌ DB Error: {e}") cur.close() conn.close() return print(f"📊 Found {len(rows)} matches with predictions on Sept 13, 2024.") if not rows: print("⚠️ No predictions found for this date. The AI Engine might not have processed these historical matches yet.") print("💡 Tip: Run the feeder or AI engine on this date range to generate predictions first.") cur.close() conn.close() return total_bets = 0 winning_bets = 0 skipped_bets = 0 total_profit = 0.0 for row in rows: data = row['prediction_json'] if isinstance(data, str): data = json.loads(data) home_score = row['score_home'] or 0 away_score = row['score_away'] or 0 total_goals = home_score + away_score # Extract Main Pick main_pick = None main_pick_conf = 0.0 main_pick_odds = 0.0 if "main_pick" in data and isinstance(data["main_pick"], dict): mp = data["main_pick"] main_pick = mp.get("pick") main_pick_conf = mp.get("confidence", 0.0) main_pick_odds = mp.get("odds", 0.0) if not main_pick or not main_pick_conf: continue # Determine Market Type pick_str = str(main_pick).upper() market_type = "MS" if "1X" in pick_str or "X2" in pick_str or "12" in pick_str: market_type = "DC" elif "ÜST" in pick_str or "ALT" in pick_str or "OVER" in pick_str or "UNDER" in pick_str: if "1.5" in pick_str: market_type = "OU15" elif "3.5" in pick_str: market_type = "OU35" else: market_type = "OU25" elif "VAR" in pick_str or "YOK" in pick_str or "BTTS" in pick_str: market_type = "BTTS" threshold = MIN_CONF_THRESHOLDS.get(market_type, 45.0) # --- SKIP LOGIC --- # 1. Confidence Gate if main_pick_conf < threshold: skipped_bets += 1 continue # 2. Value Gate if main_pick_odds > 0: implied_prob = 1.0 / main_pick_odds my_prob = main_pick_conf / 100.0 edge = my_prob - implied_prob if edge < -0.03: skipped_bets += 1 continue # --- BET PLAYED --- total_bets += 1 is_won = False # Resolve Result if market_type == "MS": if (main_pick == "1" or main_pick == "MS 1") and home_score > away_score: is_won = True elif (main_pick == "X" or main_pick == "MS X") and home_score == away_score: is_won = True elif (main_pick == "2" or main_pick == "MS 2") and away_score > home_score: is_won = True elif market_type.startswith("OU"): line = 2.5 if "1.5" in pick_str: line = 1.5 elif "3.5" in pick_str: line = 3.5 is_over = total_goals > line is_under = total_goals < line if ("ÜST" in pick_str or "OVER" in pick_str) and is_over: is_won = True elif ("ALT" in pick_str or "UNDER" in pick_str) and is_under: is_won = True elif market_type == "BTTS": if home_score > 0 and away_score > 0: if "VAR" in pick_str: is_won = True else: if "YOK" in pick_str: is_won = True elif market_type == "DC": if "1X" in pick_str and home_score >= away_score: is_won = True elif "X2" in pick_str and away_score >= home_score: is_won = True elif "12" in pick_str and home_score != away_score: is_won = True if is_won: winning_bets += 1 profit = main_pick_odds - 1.0 total_profit += profit else: total_profit -= 1.0 # Report print("\n" + "="*60) print("📈 BACKTEST RESULTS: 13 EYLÜL 2025 (TOP LEAGUES)") print("="*60) print(f"Total Matches Analyzed: {len(rows)}") print(f"🚫 Bets SKIPPED (Low Conf/Bad Value): {skipped_bets}") print(f"✅ Bets PLAYED: {total_bets}") if total_bets > 0: win_rate = (winning_bets / total_bets) * 100 roi = (total_profit / total_bets) * 100 print(f"🏆 Winning Bets: {winning_bets}") print(f"💀 Losing Bets: {total_bets - winning_bets}") print("-" * 40) print(f" Win Rate: {win_rate:.2f}%") print(f"💰 Total Profit (Units): {total_profit:.2f}") print(f"📊 ROI: {roi:.2f}%") if roi > 0: print("🟢 STRATEGY IS PROFITABLE!") else: print("🔴 STRATEGY IS LOSING") else: print("⚠️ No bets were played. Thresholds might be too high or no suitable matches found.") cur.close() conn.close() if __name__ == "__main__": run_backtest()