""" Strategy Generator — Senin Excel mantığını DB üzerinde otomatize eder. Mantık: 1. Ev sahibi takım X, evinde oran bandı Y'de oynadığında → OU1.5/OU2.5/BTTS oranları 2. Deplasman takım Z, deplasmanda oran bandı W'de oynadığında → OU1.5/OU2.5/BTTS oranları 3. İkisi de yüksekse → STRATEJİ ÜRET Çıktı: Her maç için hangi bahis oynanabilir, neden, ve geçmiş başarı oranı """ import psycopg2 import pandas as pd import numpy as np from collections import defaultdict from datetime import datetime # DB connection conn = psycopg2.connect( host="localhost", port=15432, dbname="boilerplate_db", user="suggestbet", password="SuGGesT2026SecuRe" ) print("=" * 70) print(" STRATEGY GENERATOR — Veritabanından Strateji Üretimi") print("=" * 70) # 1. Tüm biten maçları, takım adları ve MS oranlarıyla çek query = """ SELECT m.id as match_id, m.home_team_id, m.away_team_id, m.league_id, m.score_home, m.score_away, m.mst_utc, ht.name as home_team, at.name as away_team, l.name as league_name FROM matches m JOIN teams ht ON m.home_team_id = ht.id JOIN teams at ON m.away_team_id = at.id JOIN leagues l ON m.league_id = l.id WHERE m.status = 'FT' AND m.score_home IS NOT NULL ORDER BY m.mst_utc ASC """ df = pd.read_sql(query, conn) print(f"\nToplam biten maç: {len(df):,}") # 2. Tüm oranları çek (MS, OU25, BTTS, OU15) odds_query = """ SELECT oc.match_id, oc.name as market, os.name as selection, CAST(os.odd_value AS DECIMAL) as odds FROM odd_categories oc JOIN odd_selections os ON os.odd_category_db_id = oc.db_id WHERE oc.name IN ( 'Maç Sonucu', '2,5 Alt/Üst', '1,5 Alt/Üst', '3,5 Alt/Üst', 'Karşılıklı Gol' ) """ odds_df = pd.read_sql(odds_query, conn) print(f"Toplam oran kaydı: {len(odds_df):,}") # Pivot: her maç için oranları sütunlara çevir def get_odds(match_id, market, selection): mask = (odds_df.match_id == match_id) & (odds_df.market == market) & (odds_df.selection == selection) vals = odds_df.loc[mask, 'odds'] return float(vals.iloc[0]) if len(vals) > 0 else None # Daha verimli: oran lookup dict oluştur print("Oran lookup oluşturuluyor...") odds_lookup = {} for _, row in odds_df.iterrows(): key = (row.match_id, row.market, row.selection) odds_lookup[key] = float(row.odds) def get_o(mid, market, sel): return odds_lookup.get((mid, market, sel)) # 3. Her maça oranları ekle print("Maçlara oranlar ekleniyor...") df['odds_ms_h'] = df.match_id.map(lambda x: get_o(x, 'Maç Sonucu', '1')) df['odds_ms_a'] = df.match_id.map(lambda x: get_o(x, 'Maç Sonucu', '2')) df['odds_ms_d'] = df.match_id.map(lambda x: get_o(x, 'Maç Sonucu', '0')) df['odds_ou25_o'] = df.match_id.map(lambda x: get_o(x, '2,5 Alt/Üst', 'Üst')) df['odds_ou25_u'] = df.match_id.map(lambda x: get_o(x, '2,5 Alt/Üst', 'Alt')) df['odds_ou15_o'] = df.match_id.map(lambda x: get_o(x, '1,5 Alt/Üst', 'Üst')) df['odds_ou15_u'] = df.match_id.map(lambda x: get_o(x, '1,5 Alt/Üst', 'Alt')) df['odds_ou35_o'] = df.match_id.map(lambda x: get_o(x, '3,5 Alt/Üst', 'Üst')) df['odds_ou35_u'] = df.match_id.map(lambda x: get_o(x, '3,5 Alt/Üst', 'Alt')) df['odds_btts_y'] = df.match_id.map(lambda x: get_o(x, 'Karşılıklı Gol', 'Var')) df['odds_btts_n'] = df.match_id.map(lambda x: get_o(x, 'Karşılıklı Gol', 'Yok')) # Sonuç hesapla df['total_goals'] = df.score_home + df.score_away df['ou15'] = (df.total_goals > 1).astype(int) df['ou25'] = (df.total_goals > 2).astype(int) df['ou35'] = (df.total_goals > 3).astype(int) df['btts'] = ((df.score_home > 0) & (df.score_away > 0)).astype(int) print(f"Oranı olan maç sayısı: {df.odds_ms_h.notna().sum():,}") # 4. ORAN BANDI fonksiyonu def odds_band(odds): if pd.isna(odds): return None if odds < 1.30: return '1.00-1.30' if odds < 1.50: return '1.30-1.50' if odds < 1.80: return '1.50-1.80' if odds < 2.20: return '1.80-2.20' if odds < 2.80: return '2.20-2.80' if odds < 4.00: return '2.80-4.00' if odds < 6.00: return '4.00-6.00' return '6.00+' # 5. STRATEJİ: Expanding window — sadece geçmiş veriye bakarak tahmin print("\n" + "=" * 70) print(" STRATEJİ BACKTEST — Expanding Window") print("=" * 70) # Ev sahibi geçmişi: {team_id: {odds_band: [ou15, ou25, btts, ou35, ...]}} home_history = defaultdict(lambda: defaultdict(list)) away_history = defaultdict(lambda: defaultdict(list)) MIN_MATCHES = 8 # Minimum geçmiş maç sayısı TEST_PCT = 0.30 # Son %30 test N = len(df) test_start = int(N * (1 - TEST_PCT)) results = { 'ou15_over': [], 'ou25_over': [], 'ou35_over': [], 'btts_yes': [], 'btts_no': [], 'ou25_under': [], 'ou15_under': [], 'ms_home': [] } for i in range(N): row = df.iloc[i] h_odds = row.odds_ms_h a_odds = row.odds_ms_a if pd.isna(h_odds) or pd.isna(a_odds): continue h_band = odds_band(h_odds) a_band = odds_band(a_odds) # TEST: sadece test bölümünde bahis yap if i >= test_start: h_hist = home_history[row.home_team_id][h_band] a_hist = away_history[row.away_team_id][a_band] if len(h_hist) >= MIN_MATCHES and len(a_hist) >= MIN_MATCHES: # Ev sahibi bu oran bandında ne yapmış? h_ou15 = np.mean([x[0] for x in h_hist]) h_ou25 = np.mean([x[1] for x in h_hist]) h_ou35 = np.mean([x[2] for x in h_hist]) h_btts = np.mean([x[3] for x in h_hist]) h_win = np.mean([x[4] for x in h_hist]) # Deplasman bu oran bandında ne yapmış? a_ou15 = np.mean([x[0] for x in a_hist]) a_ou25 = np.mean([x[1] for x in a_hist]) a_ou35 = np.mean([x[2] for x in a_hist]) a_btts = np.mean([x[3] for x in a_hist]) a_loss = np.mean([x[4] for x in a_hist]) # deplasman kaybetme oranı # KOMBİNE SİNYAL sig_ou15 = (h_ou15 + a_ou15) / 2 sig_ou25 = (h_ou25 + a_ou25) / 2 sig_ou35 = (h_ou35 + a_ou35) / 2 sig_btts = (h_btts + a_btts) / 2 sig_hw = (h_win + a_loss) / 2 # ev kazanma + deplasman kaybetme base = { 'match': f"{row.home_team} vs {row.away_team}", 'league': row.league_name, 'home_team': row.home_team, 'away_team': row.away_team, 'h_band': h_band, 'a_band': a_band, 'h_n': len(h_hist), 'a_n': len(a_hist), } # OU 1.5 OVER if sig_ou15 >= 0.85 and row.odds_ou15_o and row.odds_ou15_o > 1.01: results['ou15_over'].append({ **base, 'signal': sig_ou15, 'odds': row.odds_ou15_o, 'won': row.ou15 == 1, 'actual_goals': row.total_goals, 'h_sig': h_ou15, 'a_sig': a_ou15 }) # OU 2.5 OVER if sig_ou25 >= 0.70 and row.odds_ou25_o and row.odds_ou25_o > 1.10: results['ou25_over'].append({ **base, 'signal': sig_ou25, 'odds': row.odds_ou25_o, 'won': row.ou25 == 1, 'actual_goals': row.total_goals, 'h_sig': h_ou25, 'a_sig': a_ou25 }) # OU 3.5 OVER if sig_ou35 >= 0.60 and row.odds_ou35_o and row.odds_ou35_o > 1.20: results['ou35_over'].append({ **base, 'signal': sig_ou35, 'odds': row.odds_ou35_o, 'won': row.ou35 == 1, 'actual_goals': row.total_goals, 'h_sig': h_ou35, 'a_sig': a_ou35 }) # BTTS YES if sig_btts >= 0.70 and row.odds_btts_y and row.odds_btts_y > 1.10: results['btts_yes'].append({ **base, 'signal': sig_btts, 'odds': row.odds_btts_y, 'won': row.btts == 1, 'actual_goals': row.total_goals, 'h_sig': h_btts, 'a_sig': a_btts }) # OU 2.5 UNDER (düşük gol beklentisi) if sig_ou25 <= 0.30 and row.odds_ou25_u and row.odds_ou25_u > 1.10: results['ou25_under'].append({ **base, 'signal': 1-sig_ou25, 'odds': row.odds_ou25_u, 'won': row.ou25 == 0, 'actual_goals': row.total_goals, 'h_sig': 1-h_ou25, 'a_sig': 1-a_ou25 }) # MS HOME WIN (ev sahibi kazanma) if sig_hw >= 0.75 and row.odds_ms_h and 1.10 < row.odds_ms_h < 3.50: results['ms_home'].append({ **base, 'signal': sig_hw, 'odds': row.odds_ms_h, 'won': row.score_home > row.score_away, 'actual_goals': row.total_goals, 'h_sig': h_win, 'a_sig': a_loss }) # History güncelle (her zaman) home_history[row.home_team_id][h_band].append(( row.ou15, row.ou25, row.ou35, row.btts, int(row.score_home > row.score_away) )) away_history[row.away_team_id][a_band].append(( row.ou15, row.ou25, row.ou35, row.btts, int(row.score_away < row.score_home) # deplasman kaybetme )) # 6. SONUÇLARI YAZIDIR print(f"\nTest bölümü: son {TEST_PCT*100:.0f}% ({N - test_start:,} maç)") print(f"Minimum geçmiş: {MIN_MATCHES} maç\n") for market_name, bets in results.items(): if not bets: print(f"\n {market_name}: sinyal yok") continue bdf = pd.DataFrame(bets) total = len(bdf) wins = bdf.won.sum() hit = wins / total * 100 pnl = (bdf.won * (bdf.odds - 1) - (~bdf.won) * 1).sum() roi = pnl / total * 100 avg_odds = bdf.odds.mean() print(f"\n{'='*60}") print(f" {market_name.upper()}") print(f"{'='*60}") print(f" Toplam bahis: {total}") print(f" Kazanan: {wins} ({hit:.1f}%)") print(f" Ortalama odds: {avg_odds:.2f}") print(f" PnL: {pnl:+.1f} birim") print(f" ROI: {roi:+.1f}%") # Farklı sinyal eşiklerinde performans print(f"\n Sinyal eşik analizi:") for threshold in [0.70, 0.75, 0.80, 0.85, 0.90, 0.95]: sub = bdf[bdf.signal >= threshold] if len(sub) < 5: continue w = sub.won.sum() p = (sub.won * (sub.odds - 1) - (~sub.won) * 1).sum() r = p / len(sub) * 100 star = ' ✅ PROFIT' if r > 0 else (' ⚖️ BE' if r > -3 else '') print(f" ≥{threshold:.2f}: {len(sub):5d} bahis, hit={w/len(sub)*100:.1f}%, ROI={r:+.1f}%{star}") # En iyi 10 örnek (kazanan) if wins > 0: best = bdf[bdf.won].nlargest(min(5, wins), 'signal') print(f"\n Örnek kazanan bahisler:") for _, b in best.iterrows(): print(f" {b.home_team} vs {b.away_team} ({b.league})") print(f" Ev {b.h_band} ({b.h_sig:.0%}) + Dep {b.a_band} ({b.a_sig:.0%}) → sinyal={b.signal:.0%}, odds={b.odds:.2f}, gol={b.actual_goals:.0f}") # 7. ÖZET TABLO print("\n\n" + "=" * 70) print(" ÖZET TABLO") print("=" * 70) print(f"{'Market':<15} {'Bahis':>6} {'Hit':>7} {'ROI':>8} {'Avg Odds':>9}") print("-" * 50) for market_name, bets in results.items(): if not bets: continue bdf = pd.DataFrame(bets) total = len(bdf) wins = bdf.won.sum() hit = wins / total * 100 pnl = (bdf.won * (bdf.odds - 1) - (~bdf.won) * 1).sum() roi = pnl / total * 100 avg_odds = bdf.odds.mean() print(f"{market_name:<15} {total:>6} {hit:>6.1f}% {roi:>+7.1f}% {avg_odds:>8.2f}") conn.close() print("\n✅ Tamamlandı!")