Files
Adriano Dal Pastro 1afb1014c9 research(blind): 52 agenti ciechi su curve anonime BTC/ETH — orchestratore valuta PnL/maxDD, niente di nuovo regge
Flotta di 52 subagenti "esperti di segnali" su storico BTC/ETH ANONIMIZZATO (Series A/B
rebased a 100, calendario sintetico, split 70/30) — non sanno cosa siano. Ognuno scrive un
signal(df)->position causale (script o ML), tunato solo sul train. Orchestratore valuta su
PnL e maxDD nel test held-out.

Harness cieco leak-free (riusabile):
- make_blind.py: export anonimo + overlay; blindlib.py: evaluator con shift della posizione +
  GUARDIA DI CAUSALITA' online (squalifica ogni look-ahead, ML incluso); blind_eval.py CLI;
  score_all.py giudice OOS; verify_top.py (corr-al-trend, fee-stress, jackknife).
- 52/52 passano la guardia (zero leak su tutta la flotta).

Esito OOS (benchmark buy&hold: -7% PnL, 68% DD):
- top = macd (+21%, DD 11%, Sh 0.84), accel, vol_of_vol, regime_switch, rf, obv — tutti
  trend/vol-regime. Sharpe OOS ~0.84 decade dal train ~1.4. Mean-rev e ML in fondo.
- 3 scettici indipendenti: REFUTED. regime-luck (top-5 bar = 67-102% del PnL); trend-redundancy
  (HAC alpha t=+0.9..+1.5, nessuno >1.96 — TSMOM travestito); overfit (accel/vov knife-edge).

Verdetto: ri-conferma CIECA e indipendente del soffitto direzionale ~1.3. macd = classe-TP01,
forward-monitor non deploy. Diario 2026-06-21-blind-signal-fleet.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 07:05:04 +00:00

127 lines
4.9 KiB
Python

"""score_all — the ORCHESTRATOR's authoritative, single-scorer leaderboard.
After the fleet writes its modules into agents/, this script is the judge. For every
agent_*.py it:
1. runs the CAUSALITY guard (a leaky signal is disqualified, no matter its PnL),
2. evaluates on the HELD-OUT TEST tail (true out-of-sample) for Series A and B,
3. evaluates on FULL for context,
and prints a leaderboard sorted by out-of-sample risk-adjusted quality, always showing
PnL and max drawdown side by side, against the buy&hold benchmark.
uv run python scripts/research/blind/score_all.py [--split test|full]
Writes results to scripts/research/blind/leaderboard.json
"""
from __future__ import annotations
import argparse
import importlib.util
import json
import sys
import traceback
from pathlib import Path
import numpy as np
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE))
import blindlib as bl # noqa: E402
AGENTS = HERE / "agents"
def _load_signal(path: Path):
spec = importlib.util.spec_from_file_location(path.stem, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod.signal
def _benchmark(split: str) -> dict:
bh = lambda df: np.ones(len(df))
out = {}
for s in ("A", "B"):
out[s] = bl.evaluate(bh, s, split)
out["combined"] = {
"pnl_mean": round(float(np.mean([out[s]["pnl"] for s in ("A", "B")])), 4),
"maxdd_worst": round(float(np.max([out[s]["maxdd"] for s in ("A", "B")])), 4),
"sharpe_mean": round(float(np.mean([out[s]["sharpe"] for s in ("A", "B")])), 3),
}
return out
def score_one(path: Path, split: str) -> dict:
rec = {"name": path.stem, "path": str(path)}
try:
signal = _load_signal(path)
except Exception as e:
rec.update(error=f"import: {e}", causal=False)
return rec
try:
caus = bl.causality_ok(signal)
rec["causal"] = bool(caus.get("ok"))
rec["causality"] = caus
except Exception as e:
rec.update(error=f"causality: {e}", causal=False)
return rec
per = {}
try:
for s in ("A", "B"):
per[s] = bl.evaluate(signal, s, split)
rec["A"], rec["B"] = per["A"], per["B"]
rec["pnl_mean"] = round(float(np.mean([per[s]["pnl"] for s in ("A", "B")])), 4)
rec["pnl_min"] = round(float(np.min([per[s]["pnl"] for s in ("A", "B")])), 4)
rec["maxdd_worst"] = round(float(np.max([per[s]["maxdd"] for s in ("A", "B")])), 4)
rec["maxdd_mean"] = round(float(np.mean([per[s]["maxdd"] for s in ("A", "B")])), 4)
rec["sharpe_mean"] = round(float(np.mean([per[s]["sharpe"] for s in ("A", "B")])), 3)
rec["sharpe_min"] = round(float(np.min([per[s]["sharpe"] for s in ("A", "B")])), 3)
# return-per-unit-drawdown (robust to the buy&hold "huge PnL, huge DD" trap)
dd = max(rec["maxdd_worst"], 1e-6)
rec["calmar"] = round(rec["pnl_mean"] / dd, 3)
except Exception as e:
rec.update(error=f"eval: {e}\n{traceback.format_exc()[-400:]}")
return rec
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--split", default="test", choices=["test", "full"])
args = ap.parse_args()
mods = sorted(p for p in AGENTS.glob("agent_*.py"))
bench = _benchmark(args.split)
rows = [score_one(p, args.split) for p in mods]
valid = [r for r in rows if r.get("causal") and "sharpe_mean" in r]
leaks = [r for r in rows if r.get("causal") is False]
broke = [r for r in rows if "error" in r and r.get("causal") is not False]
valid.sort(key=lambda r: r["sharpe_min"], reverse=True)
bh = bench["combined"]
print(f"\n{'='*100}")
print(f" BLIND-SIGNAL LEADERBOARD — split={args.split.upper()} "
f"({len(mods)} modules: {len(valid)} valid, {len(leaks)} leak-flagged, {len(broke)} broken)")
print(f" BENCHMARK buy&hold: PnL {bh['pnl_mean']*100:+.0f}% maxDD {bh['maxdd_worst']*100:.0f}% "
f"Sharpe {bh['sharpe_mean']:.2f}")
print(f"{'='*100}")
print(f" {'#':>2} {'strategy':<34} {'PnL_A':>7} {'PnL_B':>7} {'PnLmin':>7} "
f"{'DDworst':>7} {'Sh_min':>6} {'Calmar':>6}")
print(f" {'-'*92}")
for i, r in enumerate(valid[:30], 1):
print(f" {i:>2} {r['name'][:34]:<34} {r['A']['pnl']*100:>+6.0f}% {r['B']['pnl']*100:>+6.0f}% "
f"{r['pnl_min']*100:>+6.0f}% {r['maxdd_worst']*100:>6.0f}% "
f"{r['sharpe_min']:>6.2f} {r['calmar']:>6.2f}")
if leaks:
print(f"\n LEAK-FLAGGED (disqualified): {', '.join(r['name'] for r in leaks[:20])}")
if broke:
print(f" BROKEN: {', '.join(r['name'] for r in broke[:20])}")
out = {"split": args.split, "benchmark": bench, "valid": valid,
"leaks": leaks, "broken": broke, "n_modules": len(mods)}
(HERE / "leaderboard.json").write_text(json.dumps(out, indent=2, default=str))
print(f"\n -> {HERE/'leaderboard.json'}")
if __name__ == "__main__":
main()