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
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]
|