research(v2.0.0): honest harness + fasi 0-3 + ricerca frattale 63 agenti — nessun edge robusto su BTC/ETH

Harness onesto research_lab.py (serie di posizione causale, fee-aware, null model a
rotazione circolare, hold-out 2025+ bloccato; self-test cheat/noise che valida il banco).
- Fase 1: triage superstiti (DIP, shape-ML) -> morti net-fee.
- Fase 2: esplorazione famiglie (reversal morta; solo trend long-only/MA-cross passa i gate base).
- Fase 3: conferma avversariale del trend -> regime-luck del toro, bocciato sul hold-out 2025-26.
- Ricerca frattale multi-agente (Workflow, 63 agenti, 52 ipotesi dai due documenti) con guard
  anti-look-ahead (eval_signal.py) + hold-out + test cross-asset -> 0 edge robusto (l'unico
  "confermato" su ETH fallisce su BTC con lo stesso codice).
- Analisi options: VRP reale +10/+14 vol pt ma finestra 6 sett. regime unico -> non validabile;
  ruolo solo overlay tail-cap, tenere cerbero-bite ad accumulare.

Quinta conferma indipendente: su BTC/ETH-solo-prezzo non c'e' un edge facile. Il processo
disciplinato ha evitato un falso "+49% vs -49%" che sul vecchio feed contaminato sarebbe
finito in produzione. Diari docs/diary/2026-06-19-research-phase0-1 / -phase2-options /
-phase3-confirm / -fractal-multiagent-search.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-19 18:37:05 +00:00
parent 14522262e6
commit 38c8cdf25b
11 changed files with 1194 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
"""ANALISI OPTIONS per BTC/ETH — onesta sui dati REALI disponibili (cerbero-bite mainnet).
Dati: Old/data/options (chain per-strike + dvol + market_snapshots). Finestra ~2026-05-01→06-11
(~6 settimane, REGIME UNICO calmo). NON si può validare OOS un edge su opzioni qui; si possono
MISURARE i livelli reali (VRP, premi put, skew, liquidità) e ragionare sull'USO delle opzioni
per il book BTC/ETH certificato. cerbero-bite è ancora vivo -> la fonte continua ad accumulare.
uv run python scripts/analysis/options_analysis.py
"""
from __future__ import annotations
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
import numpy as np
import pandas as pd
OPT = PROJECT_ROOT / "Old" / "data" / "options"
def load(name):
return pd.read_parquet(OPT / name)
def market_snapshots_analysis():
print("=" * 90)
print(" (1) MARKET SNAPSHOTS — VRP, DVOL, funding, dealer-gamma (livelli reali)")
print("=" * 90)
ms = load("market_snapshots.parquet")
t = pd.to_datetime(ms["timestamp"], utc=True, errors="coerce")
print(f" copertura: {t.min()} -> {t.max()} ({len(ms)} righe)")
for a in ("BTC", "ETH"):
d = ms[ms["asset"] == a].dropna(subset=["iv_minus_rv"])
if len(d) == 0:
print(f" {a}: nessun dato"); continue
vrp = d["iv_minus_rv"].astype(float)
dvol = d["dvol"].astype(float)
rv = d["realized_vol_30d"].astype(float)
fund = d["funding_perp_annualized"].astype(float) if "funding_perp_annualized" in d else pd.Series([np.nan])
gam = d["dealer_net_gamma"].astype(float) if "dealer_net_gamma" in d else pd.Series([np.nan])
print(f"\n {a} (n={len(d)})")
print(f" VRP (IV-RV): media {vrp.mean():+.1f} mediana {vrp.median():+.1f} "
f">0 nel {100*(vrp>0).mean():.0f}% del tempo [IV-RV in punti di vol annua]")
print(f" DVOL: media {dvol.mean():.1f} range [{dvol.min():.1f}, {dvol.max():.1f}]")
print(f" Realized30d: media {rv.mean():.1f}")
print(f" Funding perp: media {fund.mean():+.1f}% annuo")
if gam.notna().any():
print(f" Dealer net-γ: >0 nel {100*(gam>0).mean():.0f}% del tempo (>0 = dealer long gamma = mean-rev)")
def chain_analysis(asset):
print("\n" + "=" * 90)
print(f" (2) CHAIN {asset} — premi put protettivi, skew, liquidità (livelli reali)")
print("=" * 90)
ch = load(f"{asset.lower()}_chain.parquet")
for col in ("strike", "bid", "ask", "mid", "iv", "delta", "gamma"):
if col in ch:
ch[col] = pd.to_numeric(ch[col], errors="coerce")
ch["option_type"] = ch["option_type"].astype(str)
dv = load("dvol_history.parquet")
dv = dv[dv["asset"] == asset][["timestamp", "spot"]].copy()
dv["spot"] = pd.to_numeric(dv["spot"], errors="coerce")
# timestamp -> datetime UTC nativo (sono datetime64[tz], NON ms int: to_numeric li romperebbe)
ch["t"] = pd.to_datetime(ch["timestamp"], utc=True, errors="coerce")
dv["t"] = pd.to_datetime(dv["timestamp"], utc=True, errors="coerce")
ch = ch.dropna(subset=["t"]).sort_values("t").reset_index(drop=True)
dv = dv.dropna(subset=["t", "spot"]).sort_values("t").reset_index(drop=True)
# spot causale per timestamp della chain (merge_asof nearest, tolleranza 1h)
ch = pd.merge_asof(ch, dv[["t", "spot"]], on="t", direction="nearest",
tolerance=pd.Timedelta("1h"))
ch = ch.dropna(subset=["spot", "mid", "strike"])
# days-to-expiry
exp = pd.to_datetime(ch["expiry"], utc=True, errors="coerce")
ch["dte"] = (exp - ch["t"]).dt.total_seconds() / 86_400.0
ch = ch[(ch["dte"] > 0.5) & (ch["dte"] < 90)]
ch["money"] = ch["strike"] / ch["spot"]
ch["prem_pct"] = ch["mid"] * 100 # mid è in COIN (frazione del sottostante) -> %-del-notional
# NB: iv è GIÀ in percento (35.94 = 35.94%, coerente col DVOL ~40) -> non riscalare
ch["spread_pct"] = (ch["ask"] - ch["bid"]) / ch["mid"].replace(0, np.nan) * 100
puts = ch[ch["option_type"].str.lower().str.startswith("p")]
calls = ch[ch["option_type"].str.lower().str.startswith("c")]
def band(df, mlo, mhi, dlo, dhi):
s = df[(df["money"] >= mlo) & (df["money"] <= mhi) & (df["dte"] >= dlo) & (df["dte"] <= dhi)]
return s
print(" PUT protettive — premio reale (mid/spot) e liquidità per tenor/moneyness:")
print(f" {'tenor':<10s}{'moneyness':<14s}{'premio%':>9s}{'/mese%':>9s}{'spread%':>9s}{'n':>7s}{'strike?':>9s}")
for dlo, dhi, tn in [(5, 12, "settim."), (18, 45, "mensile")]:
for mlo, mhi, ml in [(0.97, 1.03, "ATM"), (0.88, 0.93, "~10% OTM"), (0.83, 0.88, "~15% OTM")]:
s = band(puts, mlo, mhi, dlo, dhi)
if len(s) == 0:
print(f" {tn:<10s}{ml:<14s}{'':>9s}{'':>9s}{'':>9s}{0:>7d}{'NO':>9s}")
continue
prem = s["prem_pct"].median()
permonth = prem * 30.0 / s["dte"].median()
print(f" {tn:<10s}{ml:<14s}{prem:>8.2f}%{permonth:>8.2f}%{s['spread_pct'].median():>8.1f}%"
f"{len(s):>7d}{'SI':>9s}")
# skew: IV put 10% OTM vs IV call 10% OTM (stesso tenor mensile)
pv = band(puts, 0.88, 0.93, 12, 50)["iv"].median()
cv = band(calls, 1.07, 1.12, 12, 50)["iv"].median()
atmv = band(ch, 0.98, 1.02, 12, 50)["iv"].median()
if pd.notna(pv) and pd.notna(cv):
print(f" SKEW: IV put 10%OTM {pv:.0f}% vs call 10%OTM {cv:.0f}% vs ATM {atmv:.0f}%"
f" -> skew put {pv-cv:+.0f} pt vol (>0 = put care = paura del crash prezzata)")
def main():
market_snapshots_analysis()
for a in ("BTC", "ETH"):
chain_analysis(a)
print("\n" + "=" * 90)
print(" NB: finestra ~6 settimane, REGIME UNICO calmo -> livelli REALI misurabili, ma NESSUN")
print(" edge su opzioni è validabile OOS qui. Vedi commento finale.")
print("=" * 90)
if __name__ == "__main__":
main()