Align backtest production sim with live runtime config

The portfolio monitor's Production row now replays the live qualification
flag and the Admin exit policy (mode/ATR multiplier/hold days) instead of a
frozen research-variant gate, so Admin tuning is reflected in the next run.
Single-source the 80/20 strategy_rank weights in momentum_service and pin
every dual-defined constant with a parity test. Behavior-preserving today:
the production sim reproduces the README baseline exactly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 14:28:50 +02:00
co-authored by Claude Fable 5
parent 65d2dae62a
commit ae1aeb3c84
4 changed files with 169 additions and 10 deletions
+88
View File
@@ -0,0 +1,88 @@
"""Parity guards: the backtest's production strategy must equal the live setup.
The portfolio monitor's production row replays the live qualification flag and
the runtime Admin exit policy, but several constants are still defined on both
sides (defaults, trail width, ordering weights). These tests fail if the two
sides drift, so a change to the live strategy forces the backtest — and vice
versa — to move with it.
"""
import pytest
from app.services import paper_trade_service
from app.services.admin_service import ACTIVATION_DEFAULTS
from app.services.backtest_service import (
ATR_TRAIL_MULTIPLIER,
LIVE_EXIT_MODE_TO_SIM,
PORTFOLIO_MONITOR_STRATEGIES,
PRODUCTION_PERCENTILE_KEY,
RESIDUAL_HIGH_VOL_BLEND_80_20_KEY,
TIME_EXIT_DAYS,
_entry_variant_config,
_momentum_qualifies,
_qualifies_strategy_variant,
)
from app.services.momentum_service import (
STRATEGY_RANK_MOMENTUM_WEIGHT,
STRATEGY_RANK_VOL_WEIGHT,
)
def _production_monitor_row() -> dict:
return next(s for s in PORTFOLIO_MONITOR_STRATEGIES if s.get("is_production"))
def test_exit_defaults_match_the_simulated_exit() -> None:
assert paper_trade_service.DEFAULT_EXIT_MODE == "atr_trailing"
assert LIVE_EXIT_MODE_TO_SIM[paper_trade_service.DEFAULT_EXIT_MODE] == "atr_trail3"
assert paper_trade_service.DEFAULT_ATR_MULTIPLIER == ATR_TRAIL_MULTIPLIER
assert paper_trade_service.DEFAULT_HOLD_DAYS == max(TIME_EXIT_DAYS)
def test_every_live_exit_mode_has_a_sim_mapping() -> None:
assert set(paper_trade_service._VALID_EXIT_MODES) == set(LIVE_EXIT_MODE_TO_SIM)
def test_gate_default_matches_the_promoted_cutoff() -> None:
prod = _production_monitor_row()
entry_cfg = _entry_variant_config(str(prod["entry_variant"]))
assert entry_cfg is not None
assert float(entry_cfg["cutoff"]) == float(ACTIVATION_DEFAULTS["min_momentum_percentile"])
def test_production_ordering_weights_are_single_sourced() -> None:
# The promoted ordering is 80/20 momentum/vol; the backtest imports the
# weight, so equality here pins the *value* the promotion was validated at.
assert STRATEGY_RANK_MOMENTUM_WEIGHT == 0.8
assert STRATEGY_RANK_VOL_WEIGHT == pytest.approx(0.2)
prod = _production_monitor_row()
entry_cfg = _entry_variant_config(str(prod["entry_variant"]))
assert entry_cfg is not None
assert entry_cfg["ranking_key"] == RESIDUAL_HIGH_VOL_BLEND_80_20_KEY
def test_production_monitor_row_replays_the_live_config() -> None:
prod = _production_monitor_row()
assert prod.get("use_live_config") is True
assert prod["exit_policy"] == "atr_trail3"
def test_live_gate_equals_the_production_variant_gate() -> None:
"""The monitor's live-gate switch relies on the runtime `qualified` flag
(_momentum_qualifies) selecting exactly what the frozen production variant
gate selects at the default cutoff."""
prod = _production_monitor_row()
entry_cfg = _entry_variant_config(str(prod["entry_variant"]))
assert entry_cfg is not None
cutoff = float(ACTIVATION_DEFAULTS["min_momentum_percentile"])
for cand in (
{"meets_core": True, "direction": "long", PRODUCTION_PERCENTILE_KEY: 92.0},
{"meets_core": True, "direction": "long", PRODUCTION_PERCENTILE_KEY: 80.0},
{"meets_core": True, "direction": "long", PRODUCTION_PERCENTILE_KEY: 79.9},
{"meets_core": True, "direction": "long", PRODUCTION_PERCENTILE_KEY: None},
{"meets_core": True, "direction": "short", PRODUCTION_PERCENTILE_KEY: 95.0},
{"meets_core": False, "direction": "long", PRODUCTION_PERCENTILE_KEY: 95.0},
):
assert _momentum_qualifies(cand, cutoff) == _qualifies_strategy_variant(
cand, entry_cfg
), cand