Add GTL cohort composition backtest
This commit is contained in:
@@ -805,6 +805,7 @@ def test_sr_research_variant_is_explicit_and_validated(monkeypatch):
|
||||
"production_structural_overlay",
|
||||
"explicit_target_ladder",
|
||||
"gtl_tuning",
|
||||
"gtl_confirmation",
|
||||
):
|
||||
monkeypatch.setenv("BACKTEST_SR_VARIANT", variant)
|
||||
assert bt._sr_research_variant() == variant
|
||||
@@ -864,6 +865,7 @@ def test_residual_arms_change_only_the_primary_rr_floor():
|
||||
activation,
|
||||
) == 1.5
|
||||
assert bt._primary_min_rr_for_variant("gtl_tuning", activation) == 1.5
|
||||
assert bt._primary_min_rr_for_variant("gtl_confirmation", activation) == 1.5
|
||||
|
||||
|
||||
def test_gtl_research_config_parses_and_rejects_unknown_fields():
|
||||
@@ -883,6 +885,29 @@ def test_gtl_research_config_parses_and_rejects_unknown_fields():
|
||||
bt._parse_gtl_research_config("{")
|
||||
|
||||
|
||||
def test_gtl_confirmation_config_parses_and_validates_composition():
|
||||
config = bt._parse_gtl_confirmation_config(json.dumps({
|
||||
"name": "touch_strength_intersection",
|
||||
"mode": "intersection",
|
||||
"confirmations": [
|
||||
{"name": "touch", "touch_tolerance": 0.0025},
|
||||
{"name": "strength", "strength_scale": 1000.0},
|
||||
],
|
||||
}))
|
||||
assert config.name == "touch_strength_intersection"
|
||||
assert config.mode == "intersection"
|
||||
assert len(config.confirmations) == 2
|
||||
assert config.confirmations[0].touch_tolerance == 0.0025
|
||||
assert config.confirmations[1].strength_scale == 1000.0
|
||||
|
||||
with pytest.raises(ValueError, match="exactly one tuned variant"):
|
||||
bt._parse_gtl_confirmation_config(json.dumps({
|
||||
"name": "invalid_union",
|
||||
"mode": "union",
|
||||
"confirmations": [],
|
||||
}))
|
||||
|
||||
|
||||
def test_structural_overlay_tags_production_geometry_without_replacing_it(monkeypatch):
|
||||
production = {
|
||||
"direction": "long",
|
||||
@@ -1031,6 +1056,77 @@ def test_window_setups_routes_gtl_tuning_config(monkeypatch):
|
||||
assert captured["config"].grid_bins == 12
|
||||
|
||||
|
||||
def test_gtl_confirmation_intersection_keeps_control_geometry(monkeypatch):
|
||||
production = [{
|
||||
"direction": "long",
|
||||
"target": 111.0,
|
||||
"rr": 2.2,
|
||||
"meets_core": True,
|
||||
"sr_variant": bt.EXPLICIT_TARGET_LADDER_VARIANT,
|
||||
}]
|
||||
|
||||
def fake_window_setups(*args, sr_variant=None, gtl_research_config=None, **kwargs):
|
||||
if sr_variant == bt.EXPLICIT_TARGET_LADDER_VARIANT:
|
||||
return production
|
||||
assert sr_variant == bt.GTL_TUNING_VARIANT
|
||||
return [{
|
||||
"direction": "long",
|
||||
"target": 115.0,
|
||||
"rr": 3.0,
|
||||
"meets_core": gtl_research_config.name == "pass",
|
||||
}]
|
||||
|
||||
monkeypatch.setattr(bt, "_window_setups", fake_window_setups)
|
||||
research = bt.GTLConfirmationConfig(
|
||||
name="two_filters",
|
||||
mode="intersection",
|
||||
confirmations=(
|
||||
bt.GTLResearchConfig(name="pass"),
|
||||
bt.GTLResearchConfig(name="fail"),
|
||||
),
|
||||
)
|
||||
rows = bt._gtl_confirmation_window_setups(
|
||||
[], {}, {}, confirmation_config=research
|
||||
)
|
||||
|
||||
assert len(rows) == 1
|
||||
assert rows[0]["target"] == 111.0
|
||||
assert rows[0]["rr"] == 2.2
|
||||
assert rows[0]["meets_core"] is False
|
||||
assert rows[0]["gtl_confirmation_passes"] == [True, False]
|
||||
assert production[0]["meets_core"] is True
|
||||
|
||||
|
||||
def test_gtl_confirmation_union_uses_tuned_geometry_only_for_addition(monkeypatch):
|
||||
production = [
|
||||
{"direction": "long", "target": 111.0, "meets_core": False},
|
||||
{"direction": "short", "target": 90.0, "meets_core": True},
|
||||
]
|
||||
tuned = [
|
||||
{"direction": "long", "target": 115.0, "meets_core": True},
|
||||
{"direction": "short", "target": 85.0, "meets_core": False},
|
||||
]
|
||||
|
||||
def fake_window_setups(*args, sr_variant=None, **kwargs):
|
||||
return production if sr_variant == bt.EXPLICIT_TARGET_LADDER_VARIANT else tuned
|
||||
|
||||
monkeypatch.setattr(bt, "_window_setups", fake_window_setups)
|
||||
research = bt.GTLConfirmationConfig(
|
||||
name="strength_union",
|
||||
mode="union",
|
||||
confirmations=(bt.GTLResearchConfig(name="strength"),),
|
||||
)
|
||||
rows = bt._gtl_confirmation_window_setups(
|
||||
[], {}, {}, confirmation_config=research
|
||||
)
|
||||
by_direction = {row["direction"]: row for row in rows}
|
||||
|
||||
assert by_direction["long"]["target"] == 115.0
|
||||
assert by_direction["long"]["gtl_confirmation_source"] == "tuned_addition"
|
||||
assert by_direction["short"]["target"] == 90.0
|
||||
assert by_direction["short"]["gtl_confirmation_source"] == "control"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"variant",
|
||||
["legacy_geometry_neutral", "legacy_range_grid_neutral"],
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Tests for the evidence-selected GTL composition matrix."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from scripts import run_gtl_confirmation_matrix as matrix
|
||||
|
||||
|
||||
def test_confirmation_matrix_is_pre_registered_and_well_formed():
|
||||
assert len(matrix.GTL_CONFIRMATION_ARMS) == 13
|
||||
assert matrix.GTL_CONFIRMATION_ARMS[0]["name"] == "control"
|
||||
names = [arm["name"] for arm in matrix.GTL_CONFIRMATION_ARMS]
|
||||
assert len(names) == len(set(names))
|
||||
|
||||
for arm in matrix.GTL_CONFIRMATION_ARMS:
|
||||
config = matrix._config(arm)
|
||||
assert config["mode"] in {"intersection", "union"}
|
||||
if config["mode"] == "union":
|
||||
assert len(config["confirmations"]) == 1
|
||||
for confirmation in config["confirmations"]:
|
||||
assert confirmation["name"] in {
|
||||
"touch_0_25pct",
|
||||
"strength_1000",
|
||||
"merge_0_25pct",
|
||||
"pivots_none",
|
||||
}
|
||||
|
||||
|
||||
def test_signature_uses_only_control_parity_fields():
|
||||
arm = {
|
||||
"candidates": 100,
|
||||
"qualified": 10,
|
||||
"qualified_net_avg_r": 0.2,
|
||||
"qualified_net_avg_r_ex_top5": 0.05,
|
||||
"full_book": {"sharpe": 2.0},
|
||||
"holdout": {"train": {"sharpe": 1.0}},
|
||||
"unrelated": "ignored",
|
||||
}
|
||||
assert "unrelated" not in matrix._signature(arm)
|
||||
assert matrix._signature(arm)["full_book"] == {"sharpe": 2.0}
|
||||
Reference in New Issue
Block a user