"""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