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>
This commit is contained in:
Adriano Dal Pastro
2026-06-21 07:05:04 +00:00
parent f5d30d88b9
commit 1afb1014c9
63 changed files with 6660 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
"""make_blind — export the CERTIFIED BTC/ETH 1d feed as ANONYMIZED, OVERLAID curves.
The blind-signal fleet (~50 "signal expert" agents) must NOT know the series are
BTC/ETH crypto — otherwise they pattern-match the 2020 covid crash / 2022 bear /
2024 halving from memory instead of finding a real, transferable timing edge.
So we strip every tell:
* relabel BTC->"A", ETH->"B" (no ticker anywhere)
* REBASE each series to 100 at its first bar (multiply all OHLC by 100/open[0]) ->
constant rescale, returns/backtest UNCHANGED, but the price LEVEL no longer says
"this is $60k bitcoin". Both curves now start at 100 = literally "curve sovrapposte".
* synthetic DAILY calendar starting 2001-01-01 (so 1 bar = 1 day for annualization,
but no 2020/2022 era to recognize).
* normalize volume to its own median (=1) -> shape kept, scale anonymized.
Split: first SPLIT_FRAC of bars = TRAIN (handed to the agents), the rest = TEST
(held out; only the orchestrator ever evaluates on it -> a true out-of-sample PnL/DD).
Outputs (data/blind/, gitignored-friendly):
blind_A_train.parquet blind_B_train.parquet <- agent-visible
blind_A_full.parquet blind_B_full.parquet <- orchestrator-only (full series, for
OOS eval with proper warmup)
blind_meta.json <- split index, lengths (NO mapping to BTC/ETH in plain sight)
overlay.png <- the two overlaid anonymized curves (for the human)
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import numpy as np
import pandas as pd
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt")
import altlib as al # noqa: E402
OUT = Path("/opt/docker/PythagorasGoal/data/blind")
SPLIT_FRAC = 0.70
SYNTH_START = "2001-01-01"
# mapping kept OUT of the agent-visible meta; only here in source for our own audit.
_REAL = {"A": "BTC", "B": "ETH"}
def _anonymize(df: pd.DataFrame, n_bars: int) -> pd.DataFrame:
df = df.reset_index(drop=True).copy()
base = float(df["open"].iloc[0])
scale = 100.0 / base
out = pd.DataFrame()
synth = pd.date_range(SYNTH_START, periods=len(df), freq="1D", tz="UTC")
out["timestamp"] = (synth.view("int64") // 1_000_000).astype("int64")
for col in ("open", "high", "low", "close"):
out[col] = df[col].values.astype(float) * scale
vmed = float(np.nanmedian(df["volume"].values)) or 1.0
out["volume"] = df["volume"].values.astype(float) / vmed
out["datetime"] = synth
return out
def main() -> None:
OUT.mkdir(parents=True, exist_ok=True)
meta = {"split_frac": SPLIT_FRAC, "series": {}}
curves = {}
for label, asset in _REAL.items():
raw = al.get(asset, "1d")
anon = _anonymize(raw, len(raw))
n = len(anon)
cut = int(n * SPLIT_FRAC)
anon.to_parquet(OUT / f"blind_{label}_full.parquet", index=False)
anon.iloc[:cut].reset_index(drop=True).to_parquet(
OUT / f"blind_{label}_train.parquet", index=False)
meta["series"][label] = {"n_bars": n, "train_bars": cut, "test_bars": n - cut}
curves[label] = anon["close"].values
print(f" Series {label}: {n} bars train={cut} test={n-cut} "
f"(rebased start=100, level now {anon['close'].iloc[-1]:.0f})")
(OUT / "blind_meta.json").write_text(json.dumps(meta, indent=2))
# overlay chart for the human (agents work on the numbers, not the png)
try:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(12, 5))
for label, c in curves.items():
ax.plot(np.arange(len(c)), c, label=f"Series {label}", lw=0.8)
ax.axvline(int(min(len(c) for c in curves.values()) * SPLIT_FRAC),
ls="--", color="k", alpha=0.4, label="train/test cut")
ax.set_yscale("log")
ax.set_title("Anonymized overlaid curves (rebased to 100) — train | held-out test")
ax.legend()
fig.tight_layout()
fig.savefig(OUT / "overlay.png", dpi=110)
print(f" overlay.png written")
except Exception as e:
print(f" (chart skipped: {e})")
print(f"\n wrote -> {OUT}")
if __name__ == "__main__":
main()