Files
iddaai-be/mds/MISSED_OPPORTUNITIES_SCRIPT.md
T
fahricansecer 2f0b85a0c7
Deploy Iddaai Backend / build-and-deploy (push) Failing after 18s
first (part 2: other directories)
2026-04-16 15:11:25 +03:00

279 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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