11 KiB
Missed Opportunity Analysis Script
Dosya:
ai-engine/scripts/missed_opportunities.pyTarih: 2026-03-18 Amaç: PASS grade'li ama gerçekte tutan pick'leri tespit ederek grading threshold'larını optimize etmek
1. Ne Yapıyor?
Bu script, AI Engine'in "oynamayın" (PASS) dediği ama aslında tutan pick'leri bulur. Böylece:
- Hangi PASS gate'inin en çok "kaçırılmış fırsat" ürettiğini gösterir
- Threshold'ların gevşetilmesi gereken yerleri tespit eder
- Edge bucket analizi ile hangi aralıklardaki PASS'lerin tuttuğunu gösterir
- Potansiyel kaybedilen kârı hesaplar
2. Kullanım
cd ai-engine
# Son 5 gün (varsayılan)
python scripts/missed_opportunities.py
# Son 10 gün
python scripts/missed_opportunities.py --days 10
# Belirli tarih aralığı
python scripts/missed_opportunities.py --date 2026-03-01 --end-date 2026-03-15
# Günlük max maç limitini değiştir (varsayılan: 15)
python scripts/missed_opportunities.py --days 7 --max-per-day 25
⚠️ Script salt okunurdur — DB'ye hiçbir şey yazmaz, yalnızca rapor üretir.
3. Çalışma Akışı
1. top_leagues.json → sadece ana ligler
2. DB'den FT (Full Time) maçları çeker
3. Her maçı SingleMatchOrchestrator ile analiz eder
4. bet_summary'deki PASS pick'leri filtreler
5. actual_outcome() ile gerçek sonuçla karşılaştırır
6. Tutanları "missed opportunity" olarak kaydeder
7. 6 farklı rapor tablosu üretir
4. Dosya Yapısı ve Önemli Fonksiyonlar
actual_outcome(sh, sa, market, pick) → bool
Gerçek skor ile pick'in doğruluğunu kontrol eder.
| Market | Açıklama | Kontrol Mantığı |
|---|---|---|
MS |
Maç Sonucu (1X2) | sh > sa → "1", sh < sa → "2", sh == sa → "X" |
DC |
Çifte Şans (1X, X2, 12) | İki sonucun birini kapsar |
OU15 |
Üst/Alt 1.5 | total > 1.5 |
OU25 |
Üst/Alt 2.5 | total > 2.5 |
OU35 |
Üst/Alt 3.5 | total > 3.5 |
BTTS |
Karşılıklı Gol | sh > 0 AND sa > 0 |
OE |
Tek/Çift | total % 2 == 1 |
Atlanan market'ler: HT, HT_OU05, HTFT (ilk yarı market'leri, skor ayrıştırması yok)
run_analysis(start_date, end_date, max_per_day)
Ana analiz fonksiyonu. Her maç için:
SingleMatchOrchestrator.analyze_match(match_id)çağrısıbet_summarydizisindeki her item'ı kontrolplayable=Falseveyabet_grade="PASS"olanları filtreleractual_outcome()ile tutan PASS pick'leri toplar
Her missed opportunity entry'si şu alanları içerir:
{
"date": "2026-03-15",
"match": "Fenerbahçe vs Galatasaray",
"score": "2-1",
"market": "MS",
"pick": "1",
"odds": 1.85,
"ev_edge": 0.045,
"confidence": 62.5,
"grade": "PASS",
"stake": 0.0,
"playable": False,
"reasons": ["insufficient_play_score", "lineup_not_confirmed"]
}
5. Rapor Bölümleri (6 Tablo)
5.1. MATCH-BY-MATCH DETAIL
Her maç için tutan PASS pick'leri ayrıntılı gösterir. Her satırın altında PASS sebebi belirtilir:
2026-03-15 | Fenerbahçe vs Galatasaray (2-1)
✅ HIT (PASS): MS → 1 odds=1.85 edge=+0.045 conf=62.5% grade=PASS
└─ PASS reason: insufficient_play_score
5.2. MARKET SUMMARY
Market bazlı aggregate istatistikler:
MARKET HIT AVG_EDGE AVG_ODDS AVG_CONF
MS 12 +0.038 1.92 55.3%
OU25 8 +0.041 1.78 58.1%
BTTS 5 +0.029 2.05 51.2%
Kullanım: Hangi market'te en çok fırsat kaçırılıyor? OU25'te edge ortalaması yüksekse → OU25 threshold'u gevşetilebilir.
5.3. EDGE BUCKET ANALYSIS
Edge değerine göre PASS pick'lerin dağılımı:
EDGE_RANGE HIT AVG_ODDS AVG_CONF NOTE
edge < 0% 3 2.10 45.2%
0% to +2% 12 1.75 52.8%
+2% to +5% 18 1.90 56.1% ← potansiyel grade upgrade adayı
+5% to +10% 7 2.15 60.3% ← potansiyel grade upgrade adayı
+10%+ 2 1.65 68.4% ← neden PASS? kontrol et!
Akıllı notlar:
+2% to +5%ve+5% to +10%aralığı → pozitif edge var, playable yapılabilir+10%+→ ciddi edge olmasına rağmen PASS → muhtemelen konfidans veya kadro gate'i
5.4. PASS REASON BREAKDOWN (Yeni Eklenen)
En kritik tablo. Hangi PASS gate'inin en çok missed opportunity ürettiğini gösterir:
REASON HIT AVG_EDGE AVG_ODDS AVG_CONF NOTE
insufficient_play_score 42 +0.045 1.85 58.2% ← threshold gevşetilebilir
lineup_not_confirmed 28 +0.032 2.10 52.1% ← post-match kadro bilgisi mevcut
below_calibrated_conf_threshold 15 +0.061 1.72 48.5% ← edge yüksek, conf threshold düşürülebilir
lineup_insufficient_for_market 8 +0.028 1.95 55.0% ← post-match kadro bilgisi mevcut
PASS reasons field'ı nereden geliyor?
SingleMatchOrchestrator → apply_grading() → bet_summary[].reasons listesine PASS sebepleri yazılır.
Bilinen reason key'leri:
| Reason Key | Açıklama | Aksiyon Önerisi |
|---|---|---|
insufficient_play_score |
Toplam play skoru threshold'un altı | Threshold'u düşür |
below_calibrated_conf_threshold |
Kalibrasyon konfidansı düşük | Conf threshold'u market bazlı ayarla |
lineup_not_confirmed |
Kadro onaylanmamış | Post-match veriler için ihmal edilebilir |
lineup_insufficient_for_market |
Bu market için kadro yetersiz | Post-match veriler için ihmal edilebilir |
insufficient_edge |
EV edge threshold altı | Edge threshold'u incele |
odds_out_of_range |
Odds kabul edilen aralığın dışı | Odds range'i genişlet |
Akıllı notlar mantığı:
insufficient_play_score+avg_edge > 3%→ "threshold gevşetilebilir"lineup_*reason'lar → "post-match kadro bilgisi mevcut" (tarihsel analizde kadro zaten belli)below_calibrated_conf_threshold+avg_edge > 5%→ "edge yüksek, conf threshold düşürülebilir"
5.5. TOP 15 MISSED (Highest Edge)
En yüksek edge'e sahip 15 tutan PASS pick. Her birinin altında PASS sebebi:
1. Fenerbahçe vs Galatasaray 2-1 MS 1 odds=1.85 edge=+0.120 conf=62.5%
└─ insufficient_play_score, lineup_not_confirmed
2. Barcelona vs Real Madrid 3-2 OU25 Üst 2.5 odds=1.72 edge=+0.095 conf=58.1%
└─ below_calibrated_conf_threshold
Kullanım: Bu listedeki pattern'leri incele. Hep aynı reason mı tekrarlıyor? → O gate'i gevşetmek en büyük getiriyi sağlar.
5.6. POTENTIAL PROFIT LOST
Flat 1-unit stake ile ne kadar kâr kaçırıldığının özeti:
Tutan PASS pick sayısı: 87
Kaçırılan toplam kâr: +72.35 units
Ortalama odds: 1.83
Ortalama edge: +0.041
6. Veri Akışı Diyagramı
┌───────────────┐ ┌──────────────────────┐
│ DB (matches) │──────▶│ SingleMatchOrchestrator│
│ FT + top_league│ │ .analyze_match() │
└───────────────┘ └───────┬──────────────┘
│
┌───────▼──────────────┐
│ bet_summary[] │
│ ├─ market │
│ ├─ pick │
│ ├─ playable │
│ ├─ bet_grade │
│ ├─ ev_edge │
│ ├─ odds │
│ ├─ calibrated_conf │
│ └─ reasons[] │◀── PASS sebebi
└───────┬──────────────┘
│
grade=="PASS" || !playable
│
┌───────▼──────────────┐
│ actual_outcome() │
│ score vs pick check │
└───────┬──────────────┘
│
correct == true
│
┌───────▼──────────────┐
│ missed[] │
│ 6 rapor tablosu │
└──────────────────────┘
7. Threshold Tuning Rehberi
Bu raporu çalıştırdıktan sonra şu adımları izle:
- PASS REASON BREAKDOWN tablosuna bak → En çok hangi reason üretiyor?
- O reason'ın avg_edge'ine bak → Pozitif ve anlamlıysa threshold gevşetilebilir
- TOP 15 listesini incele → Tekrar eden pattern var mı?
- Edge Bucket'ta +5%+ bölümüne bak → Burada PASS olan pick'ler ciddi fırsat kaçırması
- Market Summary'de en çok kaçıran market'e bak → O market'in threshold'unu öncelikli ayarla
Threshold Değiştirme Noktaları
| Parametre | Dosya | Açıklama |
|---|---|---|
play_score_threshold |
config/grading.py |
Minimum play skoru |
calibrated_conf_threshold |
config/grading.py |
Minimum kalibrasyon konfidansı |
min_edge |
config/grading.py |
Minimum EV edge |
odds_range |
config/grading.py |
Kabul edilen odds aralığı |
lineup_required_pct |
config/grading.py |
Minimum kadro onay yüzdesi |
8. Yapılan Değişiklikler (Change Log)
2026-03-18 — PASS Reason Breakdown Eklentisi
4 değişiklik noktası:
| # | Satır | Değişiklik | Açıklama |
|---|---|---|---|
| 1 | 153 | pass_reasons = item.get("reasons", []) |
bet_summary'den reasons field'ı okunuyor |
| 2 | 181 | "reasons": pass_reasons |
Entry dict'ine PASS sebepleri ekleniyor |
| 3 | 226-232 | Match detail'de reason gösterimi | Her HIT satırının altında └─ PASS reason: |
| 4 | 281-310 | Yeni PASS REASON BREAKDOWN tablosu | Reason bazlı count, avg_edge, avg_odds, avg_conf + akıllı notlar |
| 5 | 319-326 | TOP 15'te reason | Her pick'in altında └─ reason_str |
Eklenen importlar: Yok (mevcut defaultdict, Dict, List kullanıldı)
Yeni fonksiyon: Yok — tüm değişiklikler run_analysis() raporlama bölümünde