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>
32 lines
1.8 KiB
Python
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"
|