promote residual momentum ranking
Deploy / lint (push) Successful in 7s
Deploy / test (push) Successful in 1m8s
Deploy / deploy (push) Successful in 35s

This commit is contained in:
2026-07-02 21:00:39 +02:00
parent 849489a4b5
commit aadec7d403
21 changed files with 310 additions and 185 deletions
+32 -16
View File
@@ -118,14 +118,30 @@ def test_assigns_raw_and_residual_percentiles_independently():
assert by_resid[0.10] == 0.0
def test_activation_percentile_prefers_residual_with_raw_fallback():
cands = [
{"momentum_percentile": 80.0, "residual_momentum_percentile": 95.0},
{"momentum_percentile": 70.0, "residual_momentum_percentile": None},
]
bt._assign_activation_momentum_percentiles(cands)
assert cands[0][bt.PRODUCTION_PERCENTILE_KEY] == 95.0
assert cands[1][bt.PRODUCTION_PERCENTILE_KEY] == 70.0
def test_strategy_variants_keep_only_current_research_candidates():
variants = {cfg["variant"]: cfg for cfg in bt.STRATEGY_VARIANTS}
assert "production_raw_80_fixed10" not in variants
assert "raw_80_regime_scaled" not in variants
assert "residual_80_regime_scaled" not in variants
assert "residual_90_fixed10" not in variants
assert variants["raw_90_fixed15"]["max_positions"] == 15
assert variants["residual_80_fixed20"]["max_positions"] == 20
assert "raw_90_fixed15" not in variants
assert "residual_80_fixed20" not in variants
assert variants["production_residual_80_fixed10"]["percentile_key"] == bt.PRODUCTION_PERCENTILE_KEY
assert variants["legacy_raw_80_fixed10"]["percentile_key"] == bt.RAW_PERCENTILE_KEY
assert variants["residual_80_fixed15"]["max_positions"] == 15
assert all(cfg["risk_scale"] is None for cfg in bt.STRATEGY_VARIANTS)
@@ -136,6 +152,7 @@ def test_strategy_variant_sims_emit_fixed_variants_without_mutating_qualified(mo
"direction": "long",
"momentum_percentile": 90.0,
"residual_momentum_percentile": 91.0,
"activation_momentum_percentile": 91.0,
}]
calls = []
@@ -168,34 +185,31 @@ def test_strategy_variant_sims_emit_fixed_variants_without_mutating_qualified(mo
assert [r["variant"] for r in rows] == [cfg["variant"] for cfg in bt.STRATEGY_VARIANTS]
assert all(call["exit_policy"] == "hold" for call in calls)
assert any(call["ranking_key"] == "residual_momentum_percentile" for call in calls)
assert any(call["max_positions"] == 20 for call in calls)
assert any(call["ranking_key"] == bt.PRODUCTION_PERCENTILE_KEY for call in calls)
assert any(call["ranking_key"] == bt.RAW_PERCENTILE_KEY for call in calls)
assert any(call["max_positions"] == 15 for call in calls)
assert cands[0]["qualified"] is False
def test_build_research_recommendation_applies_promotion_rules():
report = {
"strategy_variants": {"variants": [
{"variant": "production_raw_80_fixed10", "label": "Base", "sharpe": 1.20,
"max_drawdown_pct": 20.0, "cagr_pct": 30.0},
{"variant": "residual_80_fixed10", "label": "Residual", "sharpe": 1.35,
"max_drawdown_pct": 21.0, "cagr_pct": 31.0, "risk_scale": None},
{"variant": "residual_80_fixed20", "label": "Residual 20", "sharpe": 1.40,
"max_drawdown_pct": 20.5, "cagr_pct": 32.0, "risk_scale": None},
{"variant": "production_residual_80_fixed10", "label": "Base", "sharpe": 1.40,
"max_drawdown_pct": 20.0, "cagr_pct": 32.0, "skipped_book_full": 7},
{"variant": "residual_80_fixed15", "label": "Capacity", "sharpe": 1.39,
"max_drawdown_pct": 20.0, "cagr_pct": 32.0, "skipped_book_full": 0},
{"variant": "raw_90_fixed10", "label": "Cutoff 90", "sharpe": 1.25,
"max_drawdown_pct": 19.0, "cagr_pct": 28.0},
{"variant": "raw_90_fixed15", "label": "Cutoff 90 / 15", "sharpe": 1.30,
"max_drawdown_pct": 18.0, "cagr_pct": 29.0},
]},
}
rec = bt._build_research_recommendation(report)
by_topic = {item["topic"]: item for item in rec["items"]}
assert by_topic["residual_momentum"]["candidate"] is True
assert "Residual 20" in by_topic["residual_momentum"]["text"]
assert by_topic["cutoff_90"]["candidate"] is True
assert "Cutoff 90 / 15" in by_topic["cutoff_90"]["text"]
assert by_topic["capacity_15"]["candidate"] is False
assert "not needed yet" in by_topic["capacity_15"]["text"]
assert by_topic["cutoff_90"]["candidate"] is False
assert "Cutoff 90" in by_topic["cutoff_90"]["text"]
class TestStopFillR:
@@ -305,6 +319,7 @@ def _acand(
"confidence": conf,
"action": action,
"momentum_percentile": mp,
"activation_momentum_percentile": mp,
"direction": direction,
"meets_core": meets,
"risk_level": "Low",
@@ -380,6 +395,7 @@ def _sim_cand(
"stop": stop,
"target": target,
"momentum_percentile": mp,
"activation_momentum_percentile": mp,
}