"""Test dello sleeve VRP01 (options short-vol: put credit spread + gate IV-rank).""" import sys from pathlib import Path import numpy as np import pandas as pd import pytest PROJECT_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(PROJECT_ROOT)) from src.portfolio.sleeves import ( _bs_put, _strike_from_delta, _vrp_combo_returns, vrp_sleeve, _HL_DIR) _HAS_DVOL = (_HL_DIR / "dvol_btc.parquet").exists() and (_HL_DIR / "dvol_eth.parquet").exists() _skip_data = pytest.mark.skipif(not _HAS_DVOL, reason="serve data/raw/dvol_*.parquet (scripts/research/fetch_dvol.py)") def test_bs_put_monotonic_in_strike(): """Put piu' ITM (strike piu' alto) vale di piu'.""" S, T, sig = 100.0, 7 / 365.25, 0.6 vals = [_bs_put(S, K, T, sig) for K in (80, 90, 100, 110)] assert all(b < a for b, a in zip(vals, vals[1:])) # crescente nello strike def test_strike_from_delta_ordering(): """La put venduta delta -0.28 ha strike piu' alto (piu' vicino) della comprata -0.10.""" S, T, sig = 100.0, 7 / 365.25, 0.6 Ks = _strike_from_delta(S, T, sig, -0.28) Kl = _strike_from_delta(S, T, sig, -0.10) assert Kl < Ks < S # entrambe OTM, long piu' lontana @_skip_data def test_sleeve_is_deterministic_and_daily(): a = vrp_sleeve().daily() b = _vrp_combo_returns() assert isinstance(a.index, pd.DatetimeIndex) and a.index.tz is not None assert (a.index.normalize() == a.index).all() # griglia giornaliera # presente ogni giorno nel suo span (nessun buco) -> peso costante nel portafoglio full = pd.date_range(a.index.min(), a.index.max(), freq="1D", tz="UTC") assert len(a) == len(full) np.testing.assert_array_equal(a.values, vrp_sleeve().daily().values) # deterministico @_skip_data def test_gates_reduce_activity(): """I gate (IV-rank/VRP/crash-skip) devono lasciare flat parte delle settimane: i giorni con rendimento != 0 sono molto meno del totale (lump settimanale + settimane saltate).""" s = _vrp_combo_returns() active = float((s != 0).mean()) assert 0.0 < active < 0.25 # ~1/7 (lump weekly) e meno per i gate @_skip_data def test_sleeve_positive_and_capped_tail(): """Lo sleeve e' profittevole e la coda e' tagliata dal long wing (worst-day moderato).""" s = _vrp_combo_returns() nz = s[s != 0] assert s.sum() > 0 # somma rendimenti positiva assert nz.min() > -0.15 # defined-risk: nessuna settimana < -15%