Files
fahricansecer f8599bdb9a
Deploy Iddaai Backend / build-and-deploy (push) Failing after 2m1s
gg
2026-05-11 23:11:41 +03:00

207 lines
7.0 KiB
Python

"""
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()