feat(core): Vol-of-Vol guard in validate_entry + tests

Blocca entry se |DVOL_now - DVOL_24h_ago| >= threshold (default
5 pt). Fail-open quando dvol_24h_ago è None (gap dati). Independente
dal gate IV-RV: i due gate sono additivi.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-05-08 22:36:59 +00:00
parent 3a5cf2554b
commit d36cdff609
2 changed files with 65 additions and 0 deletions
+51
View File
@@ -430,3 +430,54 @@ def test_iv_minus_rv_none_skips_gate_in_both_modes() -> None:
cfg,
)
assert decision.accepted is True
# ---------------------------------------------------------------------------
# Vol-of-Vol guard
# ---------------------------------------------------------------------------
def _vov_cfg(threshold: Decimal = Decimal("5")) -> StrategyConfig:
return golden_config(entry={
"vol_of_vol_guard_enabled": True,
"vol_of_vol_threshold_pt": threshold,
"vol_of_vol_lookback_hours": 24,
})
def test_vov_guard_blocks_on_large_dvol_shift() -> None:
cfg = _vov_cfg()
decision = validate_entry(
_good_ctx(dvol_now=Decimal("56"), dvol_24h_ago=Decimal("50")), cfg
)
assert decision.accepted is False
assert any("DVOL shifted" in r for r in decision.reasons)
def test_vov_guard_passes_on_small_dvol_shift() -> None:
cfg = _vov_cfg()
decision = validate_entry(
_good_ctx(dvol_now=Decimal("52"), dvol_24h_ago=Decimal("50")), cfg
)
assert decision.accepted is True
def test_vov_guard_passes_when_lookback_missing() -> None:
"""fail-open su gap dati: se dvol_24h_ago=None il guard non scatta."""
cfg = _vov_cfg()
decision = validate_entry(
_good_ctx(dvol_now=Decimal("99"), dvol_24h_ago=None), cfg
)
# dvol_now=99 sarebbe oltre dvol_max=90; testiamo solo l'effetto VoV
# consultando le reasons (dvol_now potrebbe avere altre reason ma non
# quella VoV).
assert not any("DVOL shifted" in r for r in decision.reasons)
def test_vov_guard_disabled_does_nothing() -> None:
cfg = golden_config(entry={"vol_of_vol_guard_enabled": False})
decision = validate_entry(
_good_ctx(dvol_now=Decimal("55"), dvol_24h_ago=Decimal("50")), cfg
)
# Nessuna reason VoV (il delta=5 sarebbe oltre soglia se attivo)
assert not any("DVOL shifted" in r for r in decision.reasons)