Update handoff doc + add backtest checkpoint/resume
Deploy Iddaai Backend / build-and-deploy (push) Successful in 4m32s
Deploy Iddaai Backend / build-and-deploy (push) Successful in 4m32s
This commit is contained in:
@@ -579,6 +579,52 @@ def write_text_summary(rows: List[Dict], agg: Dict, diag: Dict,
|
|||||||
|
|
||||||
|
|
||||||
# ── Main loop ─────────────────────────────────────────────────────────
|
# ── Main loop ─────────────────────────────────────────────────────────
|
||||||
|
def _checkpoint_paths(args) -> Tuple[str, str]:
|
||||||
|
"""Stable checkpoint paths derived from the run's date window so a
|
||||||
|
re-run with the same args picks up the same checkpoint."""
|
||||||
|
key = f"{args.start or 'd' + str(args.days)}_{args.end or 'now'}_{args.max_matches}"
|
||||||
|
key = key.replace("-", "").replace(":", "")
|
||||||
|
ckpt_csv = os.path.join(REPORTS_DIR, f"_checkpoint_{key}.csv")
|
||||||
|
ckpt_state = os.path.join(REPORTS_DIR, f"_checkpoint_{key}.state")
|
||||||
|
return ckpt_csv, ckpt_state
|
||||||
|
|
||||||
|
|
||||||
|
def _load_checkpoint(args) -> Tuple[List[Dict], set]:
|
||||||
|
"""Read partial CSV + processed-IDs set if a previous run was interrupted."""
|
||||||
|
ckpt_csv, _ = _checkpoint_paths(args)
|
||||||
|
if not os.path.exists(ckpt_csv):
|
||||||
|
return [], set()
|
||||||
|
import csv
|
||||||
|
rows: List[Dict] = []
|
||||||
|
seen: set = set()
|
||||||
|
try:
|
||||||
|
with open(ckpt_csv, "r", encoding="utf-8", newline="") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
for row in reader:
|
||||||
|
rows.append(row)
|
||||||
|
seen.add(str(row.get("match_id") or ""))
|
||||||
|
except Exception as e:
|
||||||
|
print(f" checkpoint read failed ({e}); starting fresh")
|
||||||
|
return [], set()
|
||||||
|
return rows, seen
|
||||||
|
|
||||||
|
|
||||||
|
def _flush_checkpoint(args, rows: List[Dict]) -> None:
|
||||||
|
"""Atomic-ish overwrite of the partial CSV. Cheap enough at every 100 rows."""
|
||||||
|
if not rows:
|
||||||
|
return
|
||||||
|
ckpt_csv, _ = _checkpoint_paths(args)
|
||||||
|
import csv
|
||||||
|
tmp = ckpt_csv + ".tmp"
|
||||||
|
fields = list(rows[0].keys())
|
||||||
|
with open(tmp, "w", encoding="utf-8", newline="") as f:
|
||||||
|
w = csv.DictWriter(f, fieldnames=fields)
|
||||||
|
w.writeheader()
|
||||||
|
for r in rows:
|
||||||
|
w.writerow(r)
|
||||||
|
os.replace(tmp, ckpt_csv)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument("--days", type=int, default=14,
|
parser.add_argument("--days", type=int, default=14,
|
||||||
@@ -588,6 +634,10 @@ def main():
|
|||||||
parser.add_argument("--start", help="Start date YYYY-MM-DD (overrides --days)")
|
parser.add_argument("--start", help="Start date YYYY-MM-DD (overrides --days)")
|
||||||
parser.add_argument("--end", help="End date YYYY-MM-DD")
|
parser.add_argument("--end", help="End date YYYY-MM-DD")
|
||||||
parser.add_argument("--progress-interval", type=int, default=50)
|
parser.add_argument("--progress-interval", type=int, default=50)
|
||||||
|
parser.add_argument("--checkpoint-every", type=int, default=100,
|
||||||
|
help="Flush partial CSV every N matches (default 100)")
|
||||||
|
parser.add_argument("--no-resume", action="store_true",
|
||||||
|
help="Ignore any prior checkpoint and start fresh")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
print("=" * 70)
|
print("=" * 70)
|
||||||
@@ -614,12 +664,20 @@ def main():
|
|||||||
print("No matches to process. Exiting.")
|
print("No matches to process. Exiting.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# ── Resume from prior checkpoint if available ──
|
||||||
rows: List[Dict[str, Any]] = []
|
rows: List[Dict[str, Any]] = []
|
||||||
|
seen_ids: set = set()
|
||||||
|
if not args.no_resume:
|
||||||
|
rows, seen_ids = _load_checkpoint(args)
|
||||||
|
if rows:
|
||||||
|
print(f" Resuming from checkpoint: {len(rows)} matches already done")
|
||||||
errors: List[Tuple[str, str]] = []
|
errors: List[Tuple[str, str]] = []
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
|
|
||||||
for i, m in enumerate(matches, start=1):
|
for i, m in enumerate(matches, start=1):
|
||||||
mid = str(m["match_id"])
|
mid = str(m["match_id"])
|
||||||
|
if mid in seen_ids:
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
pkg = orch.analyze_match(mid)
|
pkg = orch.analyze_match(mid)
|
||||||
if pkg is None:
|
if pkg is None:
|
||||||
@@ -627,20 +685,26 @@ def main():
|
|||||||
row = capture_bet_row(m, pkg)
|
row = capture_bet_row(m, pkg)
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nInterrupted, writing partial results...")
|
print("\nInterrupted, flushing checkpoint...")
|
||||||
|
_flush_checkpoint(args, rows)
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
errors.append((mid, str(e)))
|
errors.append((mid, str(e)))
|
||||||
if len(errors) <= 5:
|
if len(errors) <= 5:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# ── Periodic checkpoint flush so a crash doesn't lose everything ──
|
||||||
|
if i % args.checkpoint_every == 0:
|
||||||
|
_flush_checkpoint(args, rows)
|
||||||
|
|
||||||
if i % args.progress_interval == 0:
|
if i % args.progress_interval == 0:
|
||||||
elapsed = time.time() - t0
|
elapsed = time.time() - t0
|
||||||
rate = i / elapsed
|
rate = i / elapsed
|
||||||
eta = (n - i) / rate if rate else 0
|
eta = (n - i) / rate if rate else 0
|
||||||
playable_so_far = sum(1 for r in rows if r["playable"])
|
playable_so_far = sum(1 for r in rows if r["playable"])
|
||||||
print(f" [{i}/{n}] rate={rate:.1f}/s eta={eta/60:.1f}min "
|
print(f" [{i}/{n}] rate={rate:.1f}/s eta={eta/60:.1f}min "
|
||||||
f"playable={playable_so_far} errors={len(errors)}")
|
f"playable={playable_so_far} errors={len(errors)} "
|
||||||
|
f"(checkpoint at every {args.checkpoint_every})")
|
||||||
|
|
||||||
print(f"\nProcessed {len(rows)} rows in {(time.time()-t0):.1f}s "
|
print(f"\nProcessed {len(rows)} rows in {(time.time()-t0):.1f}s "
|
||||||
f"({len(errors)} errors)")
|
f"({len(errors)} errors)")
|
||||||
|
|||||||
+45
-1
@@ -1,10 +1,54 @@
|
|||||||
# SESSION HANDOFF — iddaai sistem durumu
|
# SESSION HANDOFF — iddaai sistem durumu
|
||||||
|
|
||||||
**Son güncelleme**: 2026-05-25 ~20:30
|
**Son güncelleme**: 2026-05-25 ~23:00 (Windows'tan Mac'e geçiş öncesi)
|
||||||
**Hedef**: Başka makinede / yeni Claude session'ında bu doc tek başına okunup işin nerede kaldığı anlaşılabilmeli.
|
**Hedef**: Başka makinede / yeni Claude session'ında bu doc tek başına okunup işin nerede kaldığı anlaşılabilmeli.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🚨 EN SON DURUM (Mac'e geçmeden önce oku)
|
||||||
|
|
||||||
|
### Validation backtest ÖLDÜ
|
||||||
|
- Pencere: 2026-05-01 → 2026-05-14, 1500 maç
|
||||||
|
- **1200/1500'de SSH tunnel düşünce process sessizce öldü**
|
||||||
|
- **CSV kayıp** — script eski versiyondu, sadece sonda yazıyordu
|
||||||
|
- Sebep: localhost:5432 erişimi kayboldu, psycopg2 connection error
|
||||||
|
|
||||||
|
### Script DÜZELTILDI (Mac'te kullanılabilir)
|
||||||
|
`scripts/diagnostic_backtest.py` artık **crash-safe**:
|
||||||
|
- `--checkpoint-every 100` → her 100 maçta partial CSV diske yazılır
|
||||||
|
- Crash sonrası tekrar koşulunca **otomatik kaldığı yerden devam**
|
||||||
|
- Checkpoint dosyası: `reports/_checkpoint_<window-key>.csv`
|
||||||
|
- `--no-resume` flag fresh başlamak için
|
||||||
|
|
||||||
|
### Git push BEKLİYOR
|
||||||
|
- 36 dosya **commit edildi (local)** — bkz "Bu seansta yapılan KOD değişiklikleri"
|
||||||
|
- Push **auth hatası** verdi (gitea credentials cached değil)
|
||||||
|
- **User Mac'te push yapacak** (Gitea Personal Access Token gerekli, repo write yetkisi)
|
||||||
|
|
||||||
|
### Mac'te yapılacaklar (öncelikli sırayla)
|
||||||
|
1. Repo'yu clone et veya OneDrive'dan kopyala (eğer Mac OneDrive senkronize ediyorsa)
|
||||||
|
2. `git push origin main` ile pending commit'i remote'a yolla
|
||||||
|
3. SSH tunnel kur (Pi @ 95.70.252.214, port 2222) → DB için tunnel localhost:5432
|
||||||
|
4. Yeni Claude session'ı başlat, bu dosyayı oku, devam et
|
||||||
|
5. Backtest tekrar koştur (çoktan eski versiyondu, şimdi crash-safe)
|
||||||
|
```bash
|
||||||
|
cd ai-engine
|
||||||
|
export DATABASE_URL="postgresql://iddaai_user:IddaA1_S4crET!@localhost:5432/iddaai_db?schema=public"
|
||||||
|
export PYTHONIOENCODING=utf-8
|
||||||
|
python scripts/diagnostic_backtest.py --start 2026-05-01 --end 2026-05-14 --max-matches 1500
|
||||||
|
# ölürse, aynı komutu tekrar koş — checkpoint'ten devam eder
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mevcut sağlam veri
|
||||||
|
Validation kayıp ama elimizde **in-sample backtest** ve **grid search** çıktıları var:
|
||||||
|
- `reports/diagnostic_backtest_20260525_035649.{csv,json,txt}` — 1000 maç, May 11-24
|
||||||
|
- `reports/filter_optimization_patch.json` — grid search winners
|
||||||
|
- Bu data ile in-sample analiz tamamlandı, validation eksik
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🎯 Üst-seviye hedef
|
## 🎯 Üst-seviye hedef
|
||||||
|
|
||||||
Sistem **maç başı-1 saat** kullanıcı tetiklemesiyle çalışacak. Bahis uzmanı seviyesinde:
|
Sistem **maç başı-1 saat** kullanıcı tetiklemesiyle çalışacak. Bahis uzmanı seviyesinde:
|
||||||
|
|||||||
Reference in New Issue
Block a user