chore(reset): v2.0.0 — storico certificato Deribit mainnet, ripartenza pulita

Reset del progetto su fondamenta verificate dopo la scoperta che l'intera
libreria "validata OOS" era artefatto di feed contaminato (print fantasma del
feed Cerbero TESTNET + storico Binance/USDT).

- Storico ricostruito da Deribit MAINNET (ccxt pubblico, tokenless) e
  CERTIFICATO (certify_feed.py): BTC/ETH puliti su TUTTA la storia
  (mediana 2-6 bps vs Coinbase USD), integrita' OHLC + coerenza resample
  (maxΔ 0.00) + cross-venue OK. Alt esclusi (illiquidi/divergenti: LTC/DOGE
  50-82% barre flat; XRP/BNB non certificabili).
- Verdetto sul feed pulito: FADE / PAIRS / XS01 / TSM01 morti (ogni
  portafoglio Sharpe -2.3..-3.0, DD ~40%); solo SH01 e frammenti HONEST
  con segnale residuo, da ri-validare in isolamento.
- Cleanup "restart pulito": strategie, stack live (src/live, src/portfolio,
  runner/executor, yml, docker), ~100 script ricerca/gate, waste/games/
  portfolios, dati non certificati + cache e 60+ diari -> archiviati in Old/
  (preservati, non cancellati). Diario consolidato in un unico documento.
- Skeleton ricerca tenuto: Strategy ABC + indicatori + src/fractal +
  src/backtest/engine + load_data; tool dati certificati (rebuild_history,
  certify_feed, audit_feed, multi_source_check).
- Universo dati ATTIVO: solo BTC/ETH (5m/15m/1h); guardrail fisico
  (load_data su alt -> FileNotFoundError). Esecuzione DISABILITATA, conto flat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-19 15:16:03 +00:00
parent 8401a280b9
commit 14522262e6
383 changed files with 1971 additions and 779 deletions
@@ -0,0 +1,163 @@
"""GATE PORT06: trend_max=3.0/ema_long=200 sulle 6 fade LIVE (improvement-sweep punto 7).
Contesto. Il backtest canonico (build_everything) applica il filtro trend alle fade,
ma in produzione le SleeveSpec di _defs.py NON lo passano -> le fade live fadano anche
i trend/crolli estesi (es. MR01/MR02_ETH long ripetuti nel crash ETH del 2026-06-05).
Anche il test PORT06 del loss-guard Hurst (fade_lossguard_port_test) fu misurato su
entries GIA' trend-filtrate -> la config live attuale (hurst SENZA trend) non e' mai
stata gateata. hurst e trend si sovrappongono (entrambi tagliano il regime trending):
il delta marginale va misurato sul path live, non assunto dai numeri canonici.
Confronto, a livello PORT06 (stessa matematica pesi cap di Portfolio.backtest):
LIVE = fade con hurst_max=0.55 + EXIT-16 (sl_confirm 0.5 ATR), trend OFF [prod oggi]
CANDIDATO = LIVE + trend_max=3.0 / ema_long=200 [proposta]
TREND-ONLY= EXIT-16 + trend, senza hurst [diagnostica overlap]
Parita' preliminare: il replay (mode=orig, trend ON, no hurst, no EXIT-16) deve
riprodurre le equity fade canoniche (come exit16_port06_impact).
La maschera Hurst e' quella ESATTA del live: fade_base.hurst_skip_mask (close-only,
window=100, step=6) — non la cache di regime_lab.
GATE (sweep): PROMOSSO se OOS Sharpe non peggiora (>= base-0.02) E il DD scende,
e in FULL non degrada.
uv run python scripts/analysis/trendmax_port06_impact.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))
from src.data.downloader import load_data
from src.strategies.fade_base import hurst_skip_mask
from scripts.analysis.risk_management import strats_for
from scripts.analysis.combine_portfolio import OOS_DATE
from scripts.analysis._port06_gate_common import (
build_trades_variant, equity_from_trades, port_metrics, dd as _dd,
)
from scripts.portfolios._defs import PORTFOLIOS
HURST_MAX = 0.55 # loss-guard live
TREND_MAX = 3.0
def main():
p = PORTFOLIOS["PORT06"]
fade_ids = [s.sid for s in p.sleeves if s.sid.startswith("MR")]
print("=" * 100)
print(" GATE PORT06 — trend_max=3.0/ema200 sulle fade LIVE (hurst 0.55 + EXIT-16 gia' attivi)")
print(f" fade sleeve: {fade_ids} | caps={p.caps}")
print("=" * 100)
print("\n[1] build_everything() canonico (cache)...")
from src.portfolio.sleeves import all_sleeve_equities
eq_base = dict(all_sleeve_equities())
print(f" sleeve totali: {len(eq_base)}")
# dati + entries + maschera hurst (identica al live: close-only, w=100, step=6)
dfs, masks, entries = {}, {}, {}
for asset in ("BTC", "ETH"):
dfs[asset] = load_data(asset, "1h")
masks[asset] = hurst_skip_mask(dfs[asset], HURST_MAX)
print(f" {asset}: {len(dfs[asset])} barre, hurst-skip {masks[asset].mean()*100:.1f}% delle barre")
for nm, (fn, params) in strats_for(asset).items():
sid = f"{nm}_{asset}"
if sid in fade_ids:
entries[sid] = (asset, fn(dfs[asset], **params))
# --- [2] PARITA': mode=orig, trend ON, no hurst, no exit16 == canonico ---
print("\n[2] PARITA' replay (orig, trend ON, no hurst) vs canonico:")
print(f" {'sleeve':<10s}{'corr':>10s}{'ret_canon%':>14s}{'ret_replay%':>14s}{'diff%':>9s}")
parity_ok = True
for sid in fade_ids:
asset, ents = entries[sid]
rep = equity_from_trades(dfs[asset], build_trades_variant(
ents, dfs[asset], mode="orig", trend_max=TREND_MAX))
base = eq_base[sid]
corr = base.pct_change().fillna(0).corr(rep.pct_change().fillna(0))
rb = (base.iloc[-1] / base.iloc[0] - 1) * 100
rr = (rep.iloc[-1] / rep.iloc[0] - 1) * 100
flag = "" if (corr > 0.999 and abs(rr - rb) <= max(1.0, abs(rb) * 0.01)) else " <-- MISMATCH"
if flag:
parity_ok = False
print(f" {sid:<10s}{corr:>10.5f}{rb:>14.1f}{rr:>14.1f}{rr-rb:>+9.2f}{flag}")
print(f"\n PARITA' {'OK' if parity_ok else 'FALLITA'}.")
if not parity_ok:
print(" >>> STOP: non procedo col gate su un engine non in parita'.")
return
# --- [3] varianti live-path delle 6 fade ---
VARIANTS = {
"LIVE": dict(trend_max=None, hurst=True), # produzione oggi
"CANDIDATO": dict(trend_max=TREND_MAX, hurst=True), # + trend filter
"TREND-ONLY": dict(trend_max=TREND_MAX, hurst=False), # swap hurst->trend
"NESSUNO": dict(trend_max=None, hurst=False), # solo EXIT-16 (baseline filtri)
"TREND-2.5": dict(trend_max=2.5, hurst=False), # sensibilita' soglia
"TREND-3.5": dict(trend_max=3.5, hurst=False),
}
eqs = {v: {} for v in VARIANTS}
ntr = {v: {} for v in VARIANTS}
for sid in fade_ids:
asset, ents = entries[sid]
for v, cfg in VARIANTS.items():
tr = build_trades_variant(ents, dfs[asset], mode="exit16",
trend_max=cfg["trend_max"],
hurst_mask=masks[asset] if cfg["hurst"] else None)
eqs[v][sid] = equity_from_trades(dfs[asset], tr)
ntr[v][sid] = len(tr)
# --- [4] PORT06 per variante ---
print("\n" + "=" * 100)
print(f" [4] PORT06 (pesi cap, OOS da {OOS_DATE}) — fade in path LIVE (exit16+hurst)")
print("=" * 100)
print(f" {'variante':<12s}{'FULL Sh':>9s}{'FULL DD%':>10s}{'FULL CAGR':>11s}"
f" | {'OOS Sh':>8s}{'OOS DD%':>9s}{'OOS CAGR':>10s}")
print(" " + "-" * 94)
res = {}
for v in VARIANTS:
members = dict(eq_base)
for sid in fade_ids:
members[sid] = eqs[v][sid]
f, o = port_metrics(members, p)
res[v] = (f, o)
print(f" {v:<12s}{f['sharpe']:>9.2f}{f['dd']:>10.2f}{f['cagr']:>10.0f}%"
f" | {o['sharpe']:>8.2f}{o['dd']:>9.2f}{o['cagr']:>9.0f}%")
f_l, o_l = res["LIVE"]
f_c, o_c = res["CANDIDATO"]
print(" " + "-" * 94)
print(f" {'DELTA C-L':<12s}{f_c['sharpe']-f_l['sharpe']:>+9.2f}{f_c['dd']-f_l['dd']:>+10.2f}"
f"{f_c['cagr']-f_l['cagr']:>+10.0f}% | {o_c['sharpe']-o_l['sharpe']:>+8.2f}"
f"{o_c['dd']-o_l['dd']:>+9.2f}{o_c['cagr']-o_l['cagr']:>+9.0f}%")
# --- [5] per-sleeve ---
print("\n Per-sleeve (FULL): ret% | DD% | n trade [LIVE -> CANDIDATO]")
print(f" {'sleeve':<10s}{'ret L%':>10s}{'ret C%':>10s}{'DD L%':>8s}{'DD C%':>8s}"
f"{'ntr L':>7s}{'ntr C':>7s}")
for sid in fade_ids:
el_, ec = eqs["LIVE"][sid], eqs["CANDIDATO"][sid]
rl = (el_.iloc[-1] / el_.iloc[0] - 1) * 100
rc = (ec.iloc[-1] / ec.iloc[0] - 1) * 100
print(f" {sid:<10s}{rl:>10.1f}{rc:>10.1f}{_dd(el_):>8.1f}{_dd(ec):>8.1f}"
f"{ntr['LIVE'][sid]:>7d}{ntr['CANDIDATO'][sid]:>7d}")
# --- GATE ---
print("\n" + "=" * 100)
print(" GATE (sweep): PROMOSSO se OOS Sharpe >= LIVE-0.02 E OOS DD scende, e FULL non degrada")
print("=" * 100)
oos_sh_ok = o_c["sharpe"] >= o_l["sharpe"] - 0.02
oos_dd_ok = o_c["dd"] < o_l["dd"]
full_ok = f_c["sharpe"] >= f_l["sharpe"] - 0.02 and f_c["dd"] <= f_l["dd"] + 0.20
promoted = oos_sh_ok and oos_dd_ok and full_ok
print(f" OOS Sharpe {o_l['sharpe']:.2f} -> {o_c['sharpe']:.2f} ({'OK' if oos_sh_ok else 'KO'})")
print(f" OOS DD% {o_l['dd']:.2f} -> {o_c['dd']:.2f} ({'OK' if oos_dd_ok else 'KO'})")
print(f" FULL Sharpe {f_l['sharpe']:.2f} -> {f_c['sharpe']:.2f} | "
f"FULL DD {f_l['dd']:.2f} -> {f_c['dd']:.2f} ({'OK' if full_ok else 'KO'})")
print("\n VERDETTO: " + (">>> PROMOSSO <<<" if promoted else ">>> BOCCIATO <<<"))
if __name__ == "__main__":
main()