This commit is contained in:
2026-04-22 02:17:02 +03:00
parent 2ccd6831eb
commit df428ed1e8
19 changed files with 6436 additions and 9 deletions
+317
View File
@@ -0,0 +1,317 @@
"""
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ı!")