This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
# Missed Opportunity Analysis Script
|
||||
|
||||
> **Dosya:** `ai-engine/scripts/missed_opportunities.py`
|
||||
> **Tarih:** 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
|
||||
|
||||
```bash
|
||||
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:
|
||||
1. `SingleMatchOrchestrator.analyze_match(match_id)` çağrısı
|
||||
2. `bet_summary` dizisindeki her item'ı kontrol
|
||||
3. `playable=False` veya `bet_grade="PASS"` olanları filtreler
|
||||
4. `actual_outcome()` ile tutan PASS pick'leri toplar
|
||||
|
||||
Her missed opportunity entry'si şu alanları içerir:
|
||||
|
||||
```python
|
||||
{
|
||||
"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:
|
||||
|
||||
1. **PASS REASON BREAKDOWN tablosuna bak** → En çok hangi reason üretiyor?
|
||||
2. **O reason'ın avg_edge'ine bak** → Pozitif ve anlamlıysa threshold gevşetilebilir
|
||||
3. **TOP 15 listesini incele** → Tekrar eden pattern var mı?
|
||||
4. **Edge Bucket'ta +5%+ bölümüne bak** → Burada PASS olan pick'ler ciddi fırsat kaçırması
|
||||
5. **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
|
||||
Reference in New Issue
Block a user