Isolate residual S/R target selection

This commit is contained in:
2026-07-13 09:05:28 +02:00
parent 04ef7f44a2
commit e31d1704a2
7 changed files with 109 additions and 11 deletions
+22 -8
View File
@@ -94,9 +94,14 @@ HORIZON = 30 # trading days to resolve an outcome (matches the evaluat
ATR_MULTIPLIER = 1.5 ATR_MULTIPLIER = 1.5
RANGE_FACTOR_LOOKBACK = 504 RANGE_FACTOR_LOOKBACK = 504
RANGE_FACTOR_MIN_LOG = 1.0 # approximately a 2.7x high/low span RANGE_FACTOR_MIN_LOG = 1.0 # approximately a 2.7x high/low span
RANGE_RESIDUAL_VARIANTS = {
"rewrite_range504_structural_legacy_primary",
"rewrite_range504_structural_primary2",
}
RANGE_FACTOR_VARIANTS = { RANGE_FACTOR_VARIANTS = {
"production_range504", "production_range504",
"rewrite_range504_legacy_primary", "rewrite_range504_legacy_primary",
*RANGE_RESIDUAL_VARIANTS,
} }
# Cross-sectional signal evaluation (factor IC). Each candidate signal is a # Cross-sectional signal evaluation (factor IC). Each candidate signal is a
@@ -169,7 +174,10 @@ def _sr_detector_variant(sr_variant: str) -> str:
"""Map factor-gated research arms to the detector they hold fixed.""" """Map factor-gated research arms to the detector they hold fixed."""
if sr_variant == "production_range504": if sr_variant == "production_range504":
return "production_control" return "production_control"
if sr_variant == "rewrite_range504_legacy_primary": if (
sr_variant == "rewrite_range504_legacy_primary"
or sr_variant in RANGE_RESIDUAL_VARIANTS
):
return "rewrite" return "rewrite"
return sr_variant.removesuffix("_legacy_primary") return sr_variant.removesuffix("_legacy_primary")
@@ -195,6 +203,17 @@ def _range_factor_allows(sr_variant: str, range_504_log: float) -> bool:
) )
def _primary_min_rr_for_variant(sr_variant: str, activation: dict) -> float:
"""Preserve the deployed selector except in explicit primary-2 research."""
if (
sr_variant in {"production_control", "production_range504"}
or sr_variant.endswith("_legacy_primary")
or sr_variant.startswith("legacy_")
):
return 1.5
return float(activation.get("min_rr", 0.0))
def _apply_zone_strength_variant(zone_levels: list[Any], sr_variant: str) -> list[Any]: def _apply_zone_strength_variant(zone_levels: list[Any], sr_variant: str) -> list[Any]:
"""Apply post-cluster research controls without changing zone geometry.""" """Apply post-cluster research controls without changing zone geometry."""
if sr_variant in {"legacy_geometry_neutral", "legacy_range_grid_neutral"}: if sr_variant in {"legacy_geometry_neutral", "legacy_range_grid_neutral"}:
@@ -363,6 +382,7 @@ def _window_setups(
gate_levels = _gate_eligible_levels( gate_levels = _gate_eligible_levels(
sr_levels, sr_levels,
confirmed_rounds_only=detector_variant in {"confirmed_rounds", "gate_v2"}, confirmed_rounds_only=detector_variant in {"confirmed_rounds", "gate_v2"},
exclude_standalone_rounds=sr_variant in RANGE_RESIDUAL_VARIANTS,
) )
zone_strength_mode = ( zone_strength_mode = (
"soft" "soft"
@@ -406,13 +426,7 @@ def _window_setups(
# Collapse duplicate floor-pinned lottery targets (parity with # Collapse duplicate floor-pinned lottery targets (parity with
# enhance_trade_setup). # enhance_trade_setup).
targets = _prune_floor_pinned_targets(targets) targets = _prune_floor_pinned_targets(targets)
primary_min_rr = ( primary_min_rr = _primary_min_rr_for_variant(sr_variant, activation)
1.5
if sr_variant in {"production_control", "production_range504"}
or sr_variant.endswith("_legacy_primary")
or sr_variant.startswith("legacy_")
else float(activation.get("min_rr", 0.0))
)
primary = _select_primary_target( primary = _select_primary_target(
targets, targets,
min_rr=primary_min_rr, min_rr=primary_min_rr,
+6 -2
View File
@@ -60,6 +60,7 @@ def _gate_eligible_levels(
sr_levels: list[Any], sr_levels: list[Any],
*, *,
confirmed_rounds_only: bool = False, confirmed_rounds_only: bool = False,
exclude_standalone_rounds: bool = False,
min_round_rejections: int = 2, min_round_rejections: int = 2,
) -> list[Any]: ) -> list[Any]:
"""Return structures allowed to influence entry qualification. """Return structures allowed to influence entry qualification.
@@ -69,7 +70,7 @@ def _gate_eligible_levels(
either confluence with a pivot/volume source or distinct rejection clusters either confluence with a pivot/volume source or distinct rejection clusters
before such a level is allowed to manufacture a gate target. before such a level is allowed to manufacture a gate target.
""" """
if not confirmed_rounds_only: if not confirmed_rounds_only and not exclude_standalone_rounds:
return list(sr_levels) return list(sr_levels)
eligible: list[Any] = [] eligible: list[Any] = []
@@ -79,7 +80,10 @@ def _gate_eligible_levels(
]) ])
is_round_only = sources == {"round_number"} is_round_only = sources == {"round_number"}
rejections = int(getattr(level, "rejection_count", 0) or 0) rejections = int(getattr(level, "rejection_count", 0) or 0)
if not is_round_only or rejections >= min_round_rejections: if exclude_standalone_rounds and is_round_only:
continue
if confirmed_rounds_only and is_round_only and rejections < min_round_rejections:
continue
eligible.append(level) eligible.append(level)
return eligible return eligible
+35
View File
@@ -585,6 +585,41 @@ Run on pre-2024 training data only:
.venv/bin/python scripts/run_sr_v2_matrix.py factor --workers 14 .venv/bin/python scripts/run_sr_v2_matrix.py factor --workers 14
``` ```
Result:
| training arm | qualified | Sharpe | CAGR | MaxDD | net avg R | ex-top-5% |
|---|---:|---:|---:|---:|---:|---:|
| production control | 676 | 1.28 | 28.8% | 21.4% | 0.230 | 0.066 |
| production + range504 | 180 | **1.65** | 31.4% | **15.9%** | **0.476** | **0.250** |
| clean rewrite | 1,200 | 0.96 | 21.0% | 22.2% | 0.037 | -0.102 |
| clean rewrite + range504 | 291 | 1.46 | **31.5%** | 18.4% | 0.292 | 0.158 |
The scalar recovers most of the detector rewrite's regression. The clean arm now
beats the original production baseline on Sharpe, CAGR, drawdown, and robust
expectancy. Old target geometry retains a smaller edge over the equally filtered
clean arm.
The residual cohort is target selection, not another range feature. The clean arm
retains 63 exceptionally strong common setups (+0.665R ex-top-5%), adds 228 weak
setups (+0.018R), and misses 117 strong old-geometry setups (+0.175R). Of the
clean-only additions, 156 have a standalone round-number primary; all 162
round-only qualified setups have fewer than two observed rejections and return
-0.024R ex-top-5%. Structural-only and round-confluent primaries return +0.394R
and +0.369R.
For 113 of the 117 missed setups, the clean detector does produce a target, but
its selected primary averages 1.56R; 77 land in the 1.5-2R veto band. A final
two-arm residual matrix therefore holds the clean detector and range factor fixed:
- `rewrite_range504_structural_legacy_primary`: exclude standalone round targets,
retain the deployed 1.5 primary floor;
- `rewrite_range504_structural_primary2`: identical, but select the primary from
targets clearing 2.0R.
```bash
.venv/bin/python scripts/run_sr_v2_matrix.py residual --workers 14
```
The post-2024 window has been opened and is now analysis data, not a valid final The post-2024 window has been opened and is now analysis data, not a valid final
promotion holdout. These arms can isolate mechanism, but neither may ship without promotion holdout. These arms can isolate mechanism, but neither may ship without
new future data or a separately pre-registered walk-forward protocol. new future data or a separately pre-registered walk-forward protocol.
+2
View File
@@ -57,6 +57,8 @@ def _parse_args() -> argparse.Namespace:
"legacy_traffic_grid_only", "legacy_range_grid_touch", "legacy_traffic_grid_only", "legacy_range_grid_touch",
"legacy_range_grid_neutral", "legacy_range_grid_neutral",
"production_range504", "rewrite_range504_legacy_primary", "production_range504", "rewrite_range504_legacy_primary",
"rewrite_range504_structural_legacy_primary",
"rewrite_range504_structural_primary2",
), ),
default=None, default=None,
help="Research-only S/R detector/gate arm.", help="Research-only S/R detector/gate arm.",
+18
View File
@@ -38,6 +38,10 @@ RANGE_FACTOR_ARMS = (
"production_range504", "production_range504",
"rewrite_range504_legacy_primary", "rewrite_range504_legacy_primary",
) )
RANGE_RESIDUAL_ARMS = (
"rewrite_range504_structural_legacy_primary",
"rewrite_range504_structural_primary2",
)
def _add_common(parser: argparse.ArgumentParser) -> None: def _add_common(parser: argparse.ArgumentParser) -> None:
@@ -76,6 +80,17 @@ def _args() -> argparse.Namespace:
default=None, default=None,
help="Run one range-factor arm without repeating the pair.", help="Run one range-factor arm without repeating the pair.",
) )
residual = commands.add_parser(
"residual",
help="Isolate standalone rounds and the 1.5-2R primary-target veto.",
)
_add_common(residual)
residual.add_argument(
"--only-arm",
choices=RANGE_RESIDUAL_ARMS,
default=None,
help="Run one residual target-geometry arm without repeating the pair.",
)
validate = commands.add_parser( validate = commands.add_parser(
"validate", "validate",
help="Run production control and one locked arm from 2024-07-01.", help="Run production control and one locked arm from 2024-07-01.",
@@ -166,6 +181,9 @@ def main() -> None:
elif args.command == "factor": elif args.command == "factor":
arms = (args.only_arm,) if args.only_arm else RANGE_FACTOR_ARMS arms = (args.only_arm,) if args.only_arm else RANGE_FACTOR_ARMS
_train(args, arms, "backtest-sr-range-factor-train") _train(args, arms, "backtest-sr-range-factor-train")
elif args.command == "residual":
arms = (args.only_arm,) if args.only_arm else RANGE_RESIDUAL_ARMS
_train(args, arms, "backtest-sr-range-residual-train")
else: else:
_validate(args) _validate(args)
+22
View File
@@ -765,6 +765,8 @@ def test_sr_research_variant_is_explicit_and_validated(monkeypatch):
"legacy_range_grid_neutral", "legacy_range_grid_neutral",
"production_range504", "production_range504",
"rewrite_range504_legacy_primary", "rewrite_range504_legacy_primary",
"rewrite_range504_structural_legacy_primary",
"rewrite_range504_structural_primary2",
): ):
monkeypatch.setenv("BACKTEST_SR_VARIANT", variant) monkeypatch.setenv("BACKTEST_SR_VARIANT", variant)
assert bt._sr_research_variant() == variant assert bt._sr_research_variant() == variant
@@ -776,6 +778,10 @@ def test_sr_research_variant_is_explicit_and_validated(monkeypatch):
def test_range_factor_detector_mapping_is_explicit(): def test_range_factor_detector_mapping_is_explicit():
assert bt._sr_detector_variant("production_range504") == "production_control" assert bt._sr_detector_variant("production_range504") == "production_control"
assert bt._sr_detector_variant("rewrite_range504_legacy_primary") == "rewrite" assert bt._sr_detector_variant("rewrite_range504_legacy_primary") == "rewrite"
assert bt._sr_detector_variant(
"rewrite_range504_structural_legacy_primary"
) == "rewrite"
assert bt._sr_detector_variant("rewrite_range504_structural_primary2") == "rewrite"
assert bt._sr_detector_variant("soft_zones_legacy_primary") == "soft_zones" assert bt._sr_detector_variant("soft_zones_legacy_primary") == "soft_zones"
@@ -789,6 +795,10 @@ def test_range_factor_gate_is_research_arm_only():
below = bt.RANGE_FACTOR_MIN_LOG - 0.01 below = bt.RANGE_FACTOR_MIN_LOG - 0.01
assert not bt._range_factor_allows("production_range504", below) assert not bt._range_factor_allows("production_range504", below)
assert not bt._range_factor_allows("rewrite_range504_legacy_primary", below) assert not bt._range_factor_allows("rewrite_range504_legacy_primary", below)
assert not bt._range_factor_allows(
"rewrite_range504_structural_primary2",
below,
)
assert bt._range_factor_allows( assert bt._range_factor_allows(
"production_range504", "production_range504",
bt.RANGE_FACTOR_MIN_LOG, bt.RANGE_FACTOR_MIN_LOG,
@@ -796,6 +806,18 @@ def test_range_factor_gate_is_research_arm_only():
assert bt._range_factor_allows("production_control", below) assert bt._range_factor_allows("production_control", below)
def test_residual_arms_change_only_the_primary_rr_floor():
activation = {"min_rr": 2.0}
assert bt._primary_min_rr_for_variant(
"rewrite_range504_structural_legacy_primary",
activation,
) == 1.5
assert bt._primary_min_rr_for_variant(
"rewrite_range504_structural_primary2",
activation,
) == 2.0
@pytest.mark.parametrize( @pytest.mark.parametrize(
("variant", "neutral_strength"), ("variant", "neutral_strength"),
[ [
@@ -374,3 +374,6 @@ def test_gate_requires_confirmation_for_standalone_round_number():
assert _gate_eligible_levels( assert _gate_eligible_levels(
[untouched, confirmed, confluent], confirmed_rounds_only=True [untouched, confirmed, confluent], confirmed_rounds_only=True
) == [confirmed, confluent] ) == [confirmed, confluent]
assert _gate_eligible_levels(
[untouched, confirmed, confluent], exclude_standalone_rounds=True
) == [confluent]