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:
@@ -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
|
||||
Reference in New Issue
Block a user