You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

217 lines
6.5 KiB
Python

from __future__ import annotations
from apps.intraday_bt.sweep import SweepConfig, load_sweep_config, run_sweep
from libs.intraday.domain import IntradayConfig, ORBStrategyParams
def test_run_sweep_passes_orb_sector_and_overlay_context(monkeypatch) -> None:
captured: dict[str, object] = {}
def fake_run_orb_simulation(
all_intraday,
trading_days,
params,
enrichment,
ticker_sectors=None,
vix_by_day=None,
overlay_tickers_per_day=None,
):
captured["ticker_sectors"] = ticker_sectors
captured["overlay_tickers_per_day"] = overlay_tickers_per_day
return []
monkeypatch.setattr("libs.intraday.orb_simulator.run_orb_simulation", fake_run_orb_simulation)
config = IntradayConfig(
strategy_mode="orb",
orb_strategy=ORBStrategyParams(),
)
sweep = SweepConfig(base_config=config, sweep_params={"weight_event_catalyst": [0.12]})
run_sweep(
sweep,
all_intraday={},
trading_days=["2026-04-21"],
enrichment={},
ticker_sectors={"MSTR": "Financial Services"},
overlay_tickers_per_day={"2026-04-21": {"MSTR"}},
)
assert captured["ticker_sectors"] == {"MSTR": "Financial Services"}
assert captured["overlay_tickers_per_day"] == {"2026-04-21": {"MSTR"}}
def test_run_sweep_uses_combo_specific_orb_context(monkeypatch) -> None:
seen: list[tuple[int, tuple[str, ...], str, dict[str, set[str]] | None]] = []
def fake_run_orb_simulation(
all_intraday,
trading_days,
params,
enrichment,
ticker_sectors=None,
vix_by_day=None,
overlay_tickers_per_day=None,
):
seen.append(
(
int(params.prior_event_lookback_days),
tuple(params.prior_event_types),
enrichment["marker"],
overlay_tickers_per_day,
)
)
return []
monkeypatch.setattr("libs.intraday.orb_simulator.run_orb_simulation", fake_run_orb_simulation)
config = IntradayConfig(
strategy_mode="orb",
orb_strategy=ORBStrategyParams(
weight_event_catalyst=0.12,
prior_event_lookback_days=7,
prior_event_types=["earnings_release", "guidance_update"],
),
)
sweep = SweepConfig(
base_config=config,
sweep_params={
"prior_event_lookback_days": [5, 7],
"prior_event_types": [
["earnings_release"],
["earnings_release", "guidance_update"],
],
},
)
def orb_context_resolver(combo_config: IntradayConfig):
params = combo_config.orb_strategy or ORBStrategyParams()
marker = f"{params.prior_event_lookback_days}:{','.join(params.prior_event_types)}"
return (
{"marker": marker},
None,
{"2026-04-21": {marker}},
)
run_sweep(
sweep,
all_intraday={},
trading_days=["2026-04-21"],
enrichment={"marker": "base"},
orb_context_resolver=orb_context_resolver,
)
assert seen == [
(5, ("earnings_release",), "5:earnings_release", {"2026-04-21": {"5:earnings_release"}}),
(
5,
("earnings_release", "guidance_update"),
"5:earnings_release,guidance_update",
{"2026-04-21": {"5:earnings_release,guidance_update"}},
),
(7, ("earnings_release",), "7:earnings_release", {"2026-04-21": {"7:earnings_release"}}),
(
7,
("earnings_release", "guidance_update"),
"7:earnings_release,guidance_update",
{"2026-04-21": {"7:earnings_release,guidance_update"}},
),
]
def test_run_sweep_filters_intraday_to_combo_candidate_map(monkeypatch) -> None:
seen: list[list[str]] = []
def fake_run_orb_simulation(
all_intraday,
trading_days,
params,
enrichment,
ticker_sectors=None,
vix_by_day=None,
overlay_tickers_per_day=None,
):
seen.append(sorted(all_intraday.get("2026-04-21", {}).keys()))
return []
monkeypatch.setattr("libs.intraday.orb_simulator.run_orb_simulation", fake_run_orb_simulation)
config = IntradayConfig(
strategy_mode="orb",
orb_strategy=ORBStrategyParams(),
)
sweep = SweepConfig(
base_config=config,
sweep_params={"min_price": [10.0, 15.0]},
)
def orb_context_resolver(combo_config: IntradayConfig):
params = combo_config.orb_strategy or ORBStrategyParams()
if params.min_price >= 15.0:
return {}, None, None, {"2026-04-21": ["BBB", "SPY"]}
return {}, None, None, {"2026-04-21": ["AAA"]}
run_sweep(
sweep,
all_intraday={"2026-04-21": {"AAA": [{}], "BBB": [{}], "SPY": [{}]}},
trading_days=["2026-04-21"],
enrichment={},
orb_context_resolver=orb_context_resolver,
)
assert seen == [["AAA"], ["BBB", "SPY"]]
def test_load_sweep_config_preserves_literal_none_string_enums(tmp_path) -> None:
sweep_path = tmp_path / "sweep.yaml"
sweep_path.write_text(
"\n".join(
[
"sweep:",
" vwap_exit_mode:",
" - none",
" - floor",
" spy_intraday_guard_pct:",
" - none",
" - -0.004",
]
)
)
config = IntradayConfig(
strategy_mode="orb",
orb_strategy=ORBStrategyParams(),
)
sweep = load_sweep_config(str(sweep_path), config)
assert sweep.sweep_params["vwap_exit_mode"] == ["none", "floor"]
assert sweep.sweep_params["spy_intraday_guard_pct"] == [None, -0.004]
def test_load_sweep_config_reads_trade_day_objective(tmp_path) -> None:
sweep_path = tmp_path / "sweep.yaml"
sweep_path.write_text(
"\n".join(
[
"objective:",
" name: trade_day_adjusted",
" target_days_with_trades: 80",
" trade_day_bonus: 0.5",
"sweep:",
" soft_day_vwap_reclaim_size_scale:",
" - 0.05",
" - 0.08",
]
)
)
config = IntradayConfig(
strategy_mode="orb",
orb_strategy=ORBStrategyParams(),
)
sweep = load_sweep_config(str(sweep_path), config)
assert sweep.objective["name"] == "trade_day_adjusted"
assert sweep.objective["target_days_with_trades"] == 80
assert sweep.sweep_params["soft_day_vwap_reclaim_size_scale"] == [0.05, 0.08]