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]