Files
fxhnt/tests/unit/test_gate_policy_regression.py
jgrusewski f821929ddf refactor(policy): gate functions take ResolvedPolicy; remove scattered gate constants (B1)
evaluate_reconciliation_gate + evaluate_gate now take a ResolvedPolicy (resolve_policy(POLICY,
gate_spec)); the module constants (MIN_FORWARD_DAYS / RECON_* ) + gate.py inline defaults are
removed — thresholds now come from the single GatePolicy. Verdict logic + reason strings
unchanged → byte-identical verdicts (full suite 1873 green proves it). _gate_verdict resolves once.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 14:16:18 +02:00

32 lines
1.8 KiB
Python

"""Regression anchor for sub-project B1 Task 3: the gate functions take a `ResolvedPolicy` instead of a raw
`gate_spec` dict, but the verdicts must be BYTE-IDENTICAL to the pre-refactor behavior — this is a real-capital
gate, so the consolidation must change no verdict. `POLICY` (gate_policy.py) mirrors the old module constants
exactly, so `resolve_policy(POLICY, gate_spec)` reproduces the same thresholds the old `gate_spec.get(...,
CONSTANT)` calls did."""
from __future__ import annotations
from fxhnt.application.forward_models import ForwardNavRow, ForwardSummary
from fxhnt.application.gate_policy import POLICY
from fxhnt.application.gate_policy_resolve import resolve_policy
from fxhnt.application.reconciliation_gate import BacktestReference, evaluate_reconciliation_gate
def _summary(days, total_return, sid="x"):
return ForwardSummary(strategy_id=sid, as_of="2026-07-01", days=days, nav=1.0 + total_return,
total_return=total_return, sharpe=1.0, maxdd=-0.01)
def test_recon_gate_takes_resolved_policy_and_verdicts_are_unchanged():
pol = resolve_policy(POLICY, {})
rows = [ForwardNavRow(strategy_id="x", date=f"2026-06-{d:02d}", ret=0.001, nav=1.0 + 0.001 * i)
for i, d in enumerate(range(1, 16), 0)]
# 15 days >= 14, forward +1.5% vs expected +1.0% (outperforms → in band), clean → PASS
v = evaluate_reconciliation_gate(_summary(15, 0.015), rows, BacktestReference(expected_window_return=0.01), pol)
assert v.status == "PASS"
# short window → WAIT building
v2 = evaluate_reconciliation_gate(_summary(5, 0.01), rows[:5], BacktestReference(expected_window_return=0.01), pol)
assert v2.status == "WAIT" and "building" in v2.reason
# no backtest ref → WAIT (PASS withheld), unchanged
v3 = evaluate_reconciliation_gate(_summary(15, 0.015), rows, None, pol)
assert v3.status == "WAIT"