"""End-to-end test del gate IV-RV adattivo + Vol-of-Vol guard via Repository.""" from __future__ import annotations from datetime import UTC, datetime, timedelta from decimal import Decimal import pytest from cerbero_bite.state.db import connect, run_migrations from cerbero_bite.state.models import MarketSnapshotRecord from cerbero_bite.state.repository import Repository def _seed_history( conn, repo: Repository, asset: str, base: datetime, n_ticks: int, iv_rv_value: Decimal, dvol_value: Decimal, ) -> None: for i in range(n_ticks): repo.record_market_snapshot( conn, MarketSnapshotRecord( timestamp=base + timedelta(minutes=15 * i), asset=asset, spot=Decimal("2000"), dvol=dvol_value, realized_vol_30d=Decimal("48"), iv_minus_rv=iv_rv_value, funding_perp_annualized=Decimal("0"), funding_cross_annualized=Decimal("0"), dealer_net_gamma=Decimal("0"), gamma_flip_level=None, oi_delta_pct_4h=None, liquidation_long_risk="low", liquidation_short_risk="low", macro_days_to_event=None, fetch_ok=True, fetch_errors_json=None, ), ) conn.commit() @pytest.fixture def db_30d(tmp_path): """30 giorni di storia con IV-RV bimodale: prima metà 1.0, seconda metà 5.0.""" db_path = tmp_path / "e2e.sqlite" conn = connect(str(db_path)) run_migrations(conn) repo = Repository() base = datetime(2026, 4, 1, 0, 0, tzinfo=UTC) _seed_history(conn, repo, "ETH", base, 1440, Decimal("1.0"), Decimal("50")) _seed_history( conn, repo, "ETH", base + timedelta(days=15), 1440, Decimal("5.0"), Decimal("50"), ) return conn, repo def test_iv_rv_history_p25_picks_up_recent_regime(db_30d) -> None: """Sanity: con bimodale 1.0/5.0 e finestra 30g, P25 di tutta la storia è 1.0 (il 25° centile è ancora nella metà bassa).""" conn, repo = db_30d history = repo.iv_rv_history( conn, asset="ETH", max_days=60, as_of=datetime(2026, 5, 1, 0, 0, tzinfo=UTC), ) assert len(history) == 2880 from cerbero_bite.core.adaptive_threshold import compute_adaptive_threshold threshold = compute_adaptive_threshold( history=history, percentile=Decimal("0.25"), absolute_floor=Decimal("0"), min_days=30, target_days=60, ) assert threshold == Decimal("1.0") def test_dvol_lookback_within_tolerance(db_30d) -> None: conn, repo = db_30d base = datetime(2026, 4, 1, 0, 0, tzinfo=UTC) out = repo.dvol_lookback(conn, asset="ETH", reference=base + timedelta(hours=24)) assert out == Decimal("50") def test_dvol_lookback_returns_none_outside_tolerance(db_30d) -> None: conn, repo = db_30d out = repo.dvol_lookback( conn, asset="ETH", reference=datetime(2025, 1, 1, tzinfo=UTC), tolerance_minutes=15, ) assert out is None