9d586eeb58
Angolo ESEGUIBILE (tocca il book live): DVOL (vol implicita forward-looking) come denominatore del vol-target invece della realizzata. Finestra comune 2021-2026. Le varianti DVOL abbassano DD (12.3->9.2%) ma anche Sharpe FULL (0.75->0.70) e CAGR. Controllo decisivo: realized @ vol-tgt 15% eguaglia quel DD (9.4%) a Sharpe piu' alto (0.75) -> il taglio di DD del DVOL e' solo DE-LEVERING, replicabile meglio con un target_vol piu' basso. Hold-out +0.06 = single-window (storia DVOL <5y), sotto la soglia multi-cut. Gate DVOL-spike ridondante col trend (TP01 gia' flat nei crash). Lezione: per meno DD sul live la leva e' target_vol, non un overlay DVOL. - scripts/research/tp01_dvol_overlay.py (realized/dvol/blend/max/derisk + controllo target_vol) - tests/test_tp01_dvol_overlay.py - docs/diary/2026-06-26-tp01-dvol-overlay.md - CLAUDE.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
47 lines
2.1 KiB
Python
47 lines
2.1 KiB
Python
"""Lock della conclusione 2026-06-26: il DVOL come denominatore del vol-target NON migliora TP01
|
|
risk-adjusted. Il taglio di DD delle varianti DVOL è solo de-levering, replicabile MEGLIO con un
|
|
target_vol più basso sul realizzato. Diario docs/diary/2026-06-26-tp01-dvol-overlay.md."""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pandas as pd
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
|
|
|
from tp01_dvol_overlay import portfolio, metrics, RAW, VW # type: ignore
|
|
|
|
|
|
def _dstart():
|
|
s = max(pd.read_parquet(RAW / f"dvol_{a.lower()}.parquet")["timestamp"].min() for a in ("BTC", "ETH"))
|
|
return pd.Timestamp(s, unit="ms", tz="UTC") + pd.Timedelta(days=VW)
|
|
|
|
|
|
def test_dvol_does_not_beat_realized_risk_adjusted():
|
|
"""Sulla finestra comune, il realized batte (o eguaglia) le varianti DVOL sullo Sharpe FULL."""
|
|
lo = _dstart()
|
|
base = metrics(portfolio("realized")[portfolio("realized").index >= lo])
|
|
mx = metrics(portfolio("max")[portfolio("max").index >= lo])
|
|
dv = metrics(portfolio("dvol")[portfolio("dvol").index >= lo])
|
|
assert base["sharpe"] >= mx["sharpe"] - 1e-9
|
|
assert base["sharpe"] >= dv["sharpe"] - 1e-9
|
|
|
|
|
|
def test_lower_target_vol_replicates_dd_cut_at_better_sharpe():
|
|
"""Il taglio di DD del DVOL è solo de-levering: realized @ vol-tgt 15% eguaglia il DD del
|
|
max-DVOL a Sharpe FULL non inferiore (anzi superiore)."""
|
|
lo = _dstart()
|
|
mx = metrics(portfolio("max")[portfolio("max").index >= lo])
|
|
lo_tv = metrics(portfolio("realized", tvol=0.15)[portfolio("realized", tvol=0.15).index >= lo])
|
|
assert lo_tv["dd"] <= mx["dd"] + 0.01 # stesso DD (entro 1pp)
|
|
assert lo_tv["sharpe"] >= mx["sharpe"] # a Sharpe non inferiore
|
|
|
|
|
|
def test_dvol_spike_gate_is_redundant_with_trend():
|
|
"""Il gate DVOL-spike de-risk non cambia nulla: TP01 è già flat nei crash (momentum<0)."""
|
|
lo = _dstart()
|
|
base = portfolio("realized"); dr = portfolio("derisk")
|
|
base = base[base.index >= lo]; dr = dr[dr.index >= lo]
|
|
assert abs(metrics(base)["sharpe"] - metrics(dr)["sharpe"]) < 0.02
|