research(crypto): 4 filoni 2026-06-29 — ERM lead sub-daily (forward), 3 scartati/deboli
Ricerca onesta su BTC/ETH + universo HL, branch separato (nessun impatto live).
Harness condiviso altlib (causale, fee 0.10% RT, marginal vs TP01, day-boundary,
haircut $600). Test 19/19 verdi.
- A DVOL direzionale -> LEAD hedge/DD-dampener, NON sleeve (buy-the-fear; is_hedge).
- B Intraday ERM 8h -> LEAD forte / forward-monitor: earns_slot=True, ADDS oltre
SKH01 (TP01+SKH+ERM 60/25/15 FULL 1.88/HOLD 1.46/DD 8.9%).
Caveat: plateau hold-out single-row, multiple-testing non
deflazionato, exec 8h. Controllo TOD = FAIL atteso.
- C Cross-sectional non-mom (low-vol HL) -> DEBOLE/forward-monitor (deflated-Sh 0.13,
storia 2.5a, non eseguibile $600) STAT-MODE.
- D Macro regime-gate -> RIDONDANTE col trend (corr->TP01 0.989), SCARTATO.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
"""Test del filone DVOL-DIREZIONALE (scripts/research/dvol_directional.py).
|
||||
|
||||
Verifica le proprieta' che DEVONO valere per ogni segnale del progetto:
|
||||
* il percentile espandente del DVOL e' CAUSALE (rank su un prefisso == rank(full)[:cut]);
|
||||
* il leader (DVOL-fear long-flat) passa causality_ok di altlib (niente future-peeking);
|
||||
* a $600 (eval_weights_smallcap) l'haircut e' trascurabile (segnale eseguibile, low-turnover);
|
||||
* sign-falsification: la tesi (buy-the-fear) batte il suo flip (buy-the-calm) sull'era DVOL.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt"))
|
||||
|
||||
import altlib as al # noqa: E402
|
||||
from scripts.research import dvol_directional as dd # noqa: E402
|
||||
|
||||
|
||||
def test_expanding_rank_is_causal():
|
||||
"""rank calcolato su un prefisso deve coincidere con rank(full) ristretto al prefisso."""
|
||||
x = np.asarray(al.dvol(al.get("BTC", "1d"), "BTC"), float)
|
||||
full = dd._expanding_rank_arr(x)
|
||||
cut = int(len(x) * 0.7)
|
||||
pref = dd._expanding_rank_arr(x[:cut])
|
||||
a, b = full[:cut], pref
|
||||
m = np.isfinite(a) & np.isfinite(b)
|
||||
assert m.sum() > 100
|
||||
assert np.max(np.abs(a[m] - b[m])) < 1e-9
|
||||
|
||||
|
||||
def test_leader_causality_ok():
|
||||
"""Il leader (DVOL-fear q0.4 long-flat) non deve avere look-ahead (altlib causality_ok)."""
|
||||
fn = dd.make_dvol_level(0.4, "fear", True)
|
||||
co = al.causality_ok(fn, tf="1d")
|
||||
assert co["ok"], co
|
||||
assert co["max_tail_diff"] <= 1e-6
|
||||
|
||||
|
||||
@pytest.mark.parametrize("asset", ["BTC", "ETH"])
|
||||
def test_leader_executable_at_600(asset):
|
||||
"""A $600 il segnale e' a basso turnover: haircut Sharpe trascurabile, trade eseguiti reali."""
|
||||
fn = dd.make_dvol_level(0.4, "fear", True)
|
||||
df = al.get(asset, "1d")
|
||||
sc = al.eval_weights_smallcap(df, al._call_target(fn, df, asset), capital=600, min_order=5)
|
||||
assert sc["n_executed_trades"] > 10
|
||||
assert abs(sc["sharpe_haircut"]) < 0.10
|
||||
|
||||
|
||||
def test_sign_falsification():
|
||||
"""La tesi (buy-the-fear) deve battere il flip (buy-the-calm) sull'era DVOL."""
|
||||
thesis = dd.era_full_sharpe(dd.make_dvol_level(0.5, "fear", True))["sharpe"]
|
||||
flip = dd.era_full_sharpe(dd.make_dvol_level(0.5, "calm", True))["sharpe"]
|
||||
assert thesis > flip
|
||||
Reference in New Issue
Block a user