research(tp01): DVOL vol-targeting non migliora TP01 (de-levering, non timing)

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>
This commit is contained in:
Adriano Dal Pastro
2026-06-26 18:56:56 +00:00
parent 69e3517f1b
commit 9d586eeb58
4 changed files with 281 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
"""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