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,261 @@
"""VERIFY EXIT-16 close_confirm_sl — lente STRESS (avversariale).
Ipotesi nulla: l'edge della close-confirm-SL e' fragile a frizioni reali.
Quattro stress, tutti su segnali cache (params LIVE, hurst_max=0.55):
(1) FEE 2x: FEE_RT=0.002 (vs 0.001). Penalizza le policy che girano piu' capitale.
(2) BEAR/CRASH 2021-01..2022-12 (LUNA/FTX/19-mag-21): worst-trade + 5 peggiori
trade della policy vs base. Lo SL disattivato lascia correre le perdite?
(3) SLIPPAGE AVVERSO 20bps sulle uscite della policy: ogni fill di USCITA paga
+20bps contro la posizione (prezzo di uscita peggiorato). L'edge regge?
NB: lo applico SOLO alle uscite della POLICY (la sua tesi e' "esco al close":
il close-fill e' market, paga slippage; la base esce a livelli limite sl0/tp0).
(4) OVERLAP/TURNOVER: la policy allunga la permanenza (no stop intrabar). Conto
i segnali SALTATI per non-overlap (i <= last_exit) base vs policy, e quanto
capitale-tempo (somma bars in posizione) gira in piu'.
Tutto via simulate() con monkeypatch di FEE_RT e una sottoclasse engine per lo
slippage. Niente modifiche ad altri file.
"""
import sys
from pathlib import Path
import numpy as np
import pandas as pd
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
import exit_lab # noqa: E402
from exit_lab import ExitPolicy, simulate, OOS_START_MS, HARD_CAP, LEV, POS # noqa: E402
import importlib.util
spec = importlib.util.spec_from_file_location(
"cc16", str(Path(__file__).resolve().parent / "16_close_confirm_sl.py"))
cc16 = importlib.util.module_from_spec(spec)
spec.loader.exec_module(cc16)
CloseConfirmSl = cc16.CloseConfirmSl
BUF = 0.5 # train-pick
def fmt(r):
if not r:
return " n/a"
return (f"ret{r['ret_pct']:>8.0f}% dd{r['dd_pct']:>5.1f} sh{r['sharpe_t']:>5.2f} "
f"n{r['trades']:>4} win{r['win_pct']:>4.0f} bars{r['avg_bars']:>5.1f}")
def sub(cls, sleeve, g, s, e):
return simulate(cls, sleeve, g, start_ms=s, end_ms=e)
def ms(d):
return int(pd.Timestamp(d, tz="UTC").value // 1e6)
# ===========================================================================
# Engine "instrumented" che riproduce simulate() ma:
# - applica uno slippage avverso (bps) su OGNI fill di USCITA (solo se policy)
# - raccoglie la lista dei ret per-trade e i segnali SALTATI per non-overlap
# - raccoglie capital-time (somma bars)
# Lo tengo allineato 1:1 con exit_lab.simulate (stesso ordine SL-prima-di-TP).
# ===========================================================================
def simulate_instr(policy_cls, sleeve, params=None, start_ms=None, end_ms=None,
exit_slip_bps=0.0):
params = params or {}
h, l, c, ts = sleeve["high"], sleeve["low"], sleeve["close"], sleeve["ts_ms"]
n = len(c)
ctx = dict(sleeve)
policy_cls.prepare(ctx, **params)
fee = exit_lab.FEE_RT * LEV
slip = exit_slip_bps * 1e-4
capital = peak = 1000.0
max_dd = 0.0
last_exit = -1
trades = wins = 0
bars_tot = 0
skipped_overlap = 0
rets = [] # (ret, ts_entry, bars)
for (i, d, tp0, sl0, mb) in sleeve["signals"]:
if start_ms is not None and ts[i] < start_ms:
continue
if end_ms is not None and ts[i] >= end_ms:
continue
if i + 1 >= n:
continue
if i <= last_exit:
skipped_overlap += 1
continue
entry = c[i]
pol = policy_cls(ctx, i, d, entry, tp0, sl0, mb, **params)
horizon = min(int(pol.horizon), HARD_CAP)
fills = []
remaining = 1.0
j = i
for step in range(1, horizon + 1):
j = i + step
if j >= n:
j = n - 1
fills.append((remaining, c[j])); remaining = 0.0
break
tp, sl, tpfrac = pol.levels(j)
hit_sl = sl is not None and ((d == 1 and l[j] <= sl) or (d == -1 and h[j] >= sl))
hit_tp = tp is not None and ((d == 1 and h[j] >= tp) or (d == -1 and l[j] <= tp))
if hit_sl:
fills.append((remaining, sl)); remaining = 0.0
break
if hit_tp:
f = min(max(tpfrac, 0.0), 1.0) * remaining
if f > 0:
fills.append((f, tp)); remaining -= f
if remaining <= 1e-9:
break
pol.on_partial(j, tp, remaining)
if pol.after_bar(j):
fills.append((remaining, c[j])); remaining = 0.0
break
if step == horizon:
fills.append((remaining, c[j])); remaining = 0.0
if remaining > 1e-9:
fills.append((remaining, c[j]))
# slippage avverso sull'uscita: il prezzo di uscita peggiora di slip,
# cioe' si vende piu' basso (long) / si ricompra piu' alto (short).
def adj(p):
return p * (1.0 - slip) if d == 1 else p * (1.0 + slip)
ret = sum(f * (adj(p) - entry) for f, p in fills) / entry * d * LEV - fee
capital = max(capital + capital * POS * ret, 10.0)
peak = max(peak, capital)
max_dd = max(max_dd, (peak - capital) / peak)
last_exit = j
trades += 1
wins += ret > 0
bars_tot += j - i
rets.append((ret, int(ts[i]), j - i))
if trades == 0:
return {}
r = np.array([x[0] for x in rets])
return {
"ret_pct": (capital / 1000.0 - 1) * 100,
"dd_pct": max_dd * 100,
"trades": trades,
"win_pct": wins / trades * 100,
"sharpe_t": float(r.mean() / r.std() * np.sqrt(len(r))) if r.std() else 0.0,
"avg_bars": bars_tot / trades,
"bars_tot": bars_tot,
"skipped_overlap": skipped_overlap,
"rets": rets,
"worst5": sorted(r.tolist())[:5],
}
def main():
data = exit_lab.load_sleeves()
# ===================================================================
print("=" * 104)
print("TEST 1 — FEE 2x (FEE_RT 0.001 -> 0.002). base vs policy buffer=0.5 (OOS, dopo 2023-11)")
print("=" * 104)
orig_fee = exit_lab.FEE_RT
survive_fee = True
for fee in (0.001, 0.002):
exit_lab.FEE_RT = fee
print(f"\n--- FEE_RT={fee} ---")
for (code, asset), sleeve in data.items():
key = f"{code.split('_')[0]} {asset}"
b = sub(ExitPolicy, sleeve, {}, OOS_START_MS, None)
p = sub(CloseConfirmSl, sleeve, {"buffer": BUF}, OOS_START_MS, None)
tag = ""
if fee == 0.002 and b and p:
# regge se sharpe policy >= base (la tesi e' che migliora)
ok = p["sharpe_t"] >= b["sharpe_t"] - 0.10
survive_fee &= ok
tag = "OK" if ok else "WORSE"
print(f" {key:<10} base {fmt(b)}")
print(f" {'':<10} pol {fmt(p)} {tag}")
exit_lab.FEE_RT = orig_fee
print(f"\nFEE 2x: policy regge (>= base-0.10 sh su tutti gli sleeve OOS)? {survive_fee}")
# ===================================================================
print("\n" + "=" * 104)
print("TEST 2 — BEAR/CRASH 2021-01..2022-12 (LUNA/FTX/19-mag): worst-trade + 5 peggiori")
print("=" * 104)
s2, e2 = ms("2021-01-01"), ms("2023-01-01")
tail_worse = 0
tail_total = 0
for (code, asset), sleeve in data.items():
key = f"{code.split('_')[0]} {asset}"
b = simulate_instr(ExitPolicy, sleeve, {}, s2, e2)
p = simulate_instr(CloseConfirmSl, sleeve, {"buffer": BUF}, s2, e2)
print(f"\n{key}")
print(f" base {fmt(b)}")
print(f" pol {fmt(p)}")
if b and p:
bw = [f"{x*100:+.1f}%" for x in b["worst5"]]
pw = [f"{x*100:+.1f}%" for x in p["worst5"]]
print(f" base 5 peggiori (ret netto): {bw}")
print(f" pol 5 peggiori (ret netto): {pw}")
tail_total += 1
# la policy peggiora la coda se il worst-trade e' piu' negativo
if p["worst5"][0] < b["worst5"][0] - 0.005:
tail_worse += 1
print(f" -> CODA PEGGIORE: worst {p['worst5'][0]*100:+.1f}% < base {b['worst5'][0]*100:+.1f}%")
else:
print(f" -> coda OK: worst {p['worst5'][0]*100:+.1f}% vs base {b['worst5'][0]*100:+.1f}%")
print(f" DD bear: base {b['dd_pct']:.1f}% pol {p['dd_pct']:.1f}%")
print(f"\nBEAR: sleeve con coda PEGGIORE (worst-trade > 0.5pt sotto base): {tail_worse}/{tail_total}")
# ===================================================================
print("\n" + "=" * 104)
print("TEST 3 — SLIPPAGE AVVERSO 20bps sulle uscite della POLICY (OOS). base senza slippage")
print("(la tesi della policy e' 'esco al close' = market fill -> paga slippage)")
print("=" * 104)
survive_slip = True
for (code, asset), sleeve in data.items():
key = f"{code.split('_')[0]} {asset}"
b = simulate_instr(ExitPolicy, sleeve, {}, OOS_START_MS, None, exit_slip_bps=0.0)
p0 = simulate_instr(CloseConfirmSl, sleeve, {"buffer": BUF}, OOS_START_MS, None, exit_slip_bps=0.0)
p20 = simulate_instr(CloseConfirmSl, sleeve, {"buffer": BUF}, OOS_START_MS, None, exit_slip_bps=20.0)
ok = p20 and b and p20["sharpe_t"] >= b["sharpe_t"] - 0.10
survive_slip &= bool(ok)
print(f"\n{key}")
print(f" base (no slip) {fmt(b)}")
print(f" pol (no slip) {fmt(p0)}")
print(f" pol (+20bps exit) {fmt(p20)} {'OK' if ok else 'WORSE vs base'}")
print(f"\nSLIPPAGE 20bps: policy ancora >= base-0.10 sh su tutti? {survive_slip}")
print("(test severo: lo slippage colpisce la policy ma NON la base — asimmetria pessimistica)")
# severita' extra: slippage anche sulla base (entrambe market) per fairness
print("\n--- fairness: 20bps anche sulle uscite della BASE ---")
fair = True
for (code, asset), sleeve in data.items():
key = f"{code.split('_')[0]} {asset}"
b20 = simulate_instr(ExitPolicy, sleeve, {}, OOS_START_MS, None, exit_slip_bps=20.0)
p20 = simulate_instr(CloseConfirmSl, sleeve, {"buffer": BUF}, OOS_START_MS, None, exit_slip_bps=20.0)
ok = p20 and b20 and p20["sharpe_t"] >= b20["sharpe_t"] - 0.10
fair &= bool(ok)
print(f" {key:<10} base+20 {fmt(b20)}")
print(f" {'':<10} pol +20 {fmt(p20)} {'OK' if ok else 'WORSE'}")
print(f"fairness (entrambe +20bps): policy >= base-0.10 sh? {fair}")
# ===================================================================
print("\n" + "=" * 104)
print("TEST 4 — OVERLAP/TURNOVER: segnali saltati per non-overlap + capital-time (OOS)")
print("=" * 104)
for (code, asset), sleeve in data.items():
key = f"{code.split('_')[0]} {asset}"
b = simulate_instr(ExitPolicy, sleeve, {}, OOS_START_MS, None)
p = simulate_instr(CloseConfirmSl, sleeve, {"buffer": BUF}, OOS_START_MS, None)
if b and p:
dskip = p["skipped_overlap"] - b["skipped_overlap"]
dbars = p["bars_tot"] - b["bars_tot"]
print(f" {key:<10} base: trades {b['trades']:>4} skip-overlap {b['skipped_overlap']:>4} "
f"bars_tot {b['bars_tot']:>6} avg {b['avg_bars']:.1f}")
print(f" {'':<10} pol : trades {p['trades']:>4} skip-overlap {p['skipped_overlap']:>4} "
f"bars_tot {p['bars_tot']:>6} avg {p['avg_bars']:.1f}")
print(f" {'':<10} -> +{dskip} segnali persi per overlap, "
f"+{dbars} bars in posizione ({dbars/max(b['bars_tot'],1)*100:+.0f}% capital-time)")
if __name__ == "__main__":
main()