# 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