from __future__ import annotations from pathlib import Path from apps.web.routers import intraday MOMENTUM_CONFIG = """\ _meta: name: Leader Intraday Momentum description: Test momentum config id: 7 strategy_mode: momentum strategy: entry_minutes_after_open: 10 exit_minutes_before_close: 5 stop_loss_pct: null trailing_stop_pct: -0.06 min_morning_gain_pct: 0.02 max_morning_gain_pct: null min_entry_volume: 250000 ticker_cooldown_days: 0 top_n: 5 initial_capital: 10000.0 slippage_bps: 5.0 market_regime_spy_threshold: null universe: source: midcap min_price: 10.0 backtest: lookback_trading_days: 200 pre_screen_threshold: 0.02 cache: enabled: true dir: data/cache/intraday output: dir: runs/intraday verbose: false """ MOMENTUM_YAML_UNIVERSE_CONFIG = """\ _meta: name: Leader Intraday Momentum description: Test momentum config id: 7 strategy_mode: momentum strategy: entry_minutes_after_open: 10 exit_minutes_before_close: 5 stop_loss_pct: null trailing_stop_pct: -0.06 min_morning_gain_pct: 0.02 max_morning_gain_pct: null min_entry_volume: 250000 ticker_cooldown_days: 0 top_n: 5 initial_capital: 10000.0 slippage_bps: 5.0 market_regime_spy_threshold: null universe: source: yaml symbols_file: configs/symbols_midcap_smallmid.yaml min_price: 10.0 backtest: lookback_trading_days: 200 pre_screen_threshold: 0.02 cache: enabled: true dir: data/cache/intraday output: dir: runs/intraday verbose: false """ def _write_config(tmp_path: Path, slug: str, text: str = MOMENTUM_CONFIG) -> Path: strategies_dir = tmp_path / "configs" / "intraday" / "strategies" strategies_dir.mkdir(parents=True, exist_ok=True) path = strategies_dir / f"{slug}.yaml" path.write_text(text) return path def test_load_user_strategy_supports_momentum(monkeypatch, tmp_path: Path) -> None: _write_config(tmp_path, "leader_intraday_momentum") monkeypatch.setattr(intraday, "get_project_root", lambda: tmp_path) payload = intraday._load_user_strategy("leader_intraday_momentum") assert payload is not None assert payload["strategy_mode"] == "momentum" assert payload["output_dir"] == "runs/intraday" assert payload["top_n"] == 5 assert payload["min_morning_gain_pct"] == 0.02 def test_submit_intraday_backtest_uses_config_strategy_mode(monkeypatch, tmp_path: Path) -> None: _write_config(tmp_path, "leader_intraday_momentum") monkeypatch.setattr(intraday, "get_project_root", lambda: tmp_path) monkeypatch.setattr(intraday, "get_runs_dir", lambda: tmp_path / "runs") intraday._tasks.clear() intraday._tasks_initialized = True captured: dict[str, object] = {} class DummyProc: pid = 4242 def fake_popen(cmd, stdout=None, stderr=None, cwd=None): # noqa: ANN001 captured["cmd"] = cmd captured["cwd"] = cwd return DummyProc() class DummyThread: def __init__(self, target=None, args=None, daemon=None): # noqa: ANN001 captured["thread_target"] = target captured["thread_args"] = args def start(self) -> None: captured["thread_started"] = True monkeypatch.setattr(intraday.subprocess, "Popen", fake_popen) monkeypatch.setattr(intraday.threading, "Thread", DummyThread) req = intraday.IntradayBacktestRequest( config="leader_intraday_momentum", universe="midcap", start_date="2026-01-02", end_date="2026-03-31", compound_returns=False, ) resp = intraday.submit_intraday_backtest(req) task = intraday._tasks[resp["task_id"]] cmd = captured["cmd"] assert task["strategy_mode"] == "momentum" assert task["output_dir"] == "runs/intraday" assert "--strategy" in cmd assert cmd[cmd.index("--strategy") + 1] == "momentum" assert "--output-dir" in cmd assert cmd[cmd.index("--output-dir") + 1] == str(tmp_path / "runs" / "intraday") def test_submit_intraday_backtest_preserves_yaml_strategy_universe(monkeypatch, tmp_path: Path) -> None: _write_config(tmp_path, "leader_intraday_momentum_yaml", MOMENTUM_YAML_UNIVERSE_CONFIG) monkeypatch.setattr(intraday, "get_project_root", lambda: tmp_path) monkeypatch.setattr(intraday, "get_runs_dir", lambda: tmp_path / "runs") intraday._tasks.clear() intraday._tasks_initialized = True captured: dict[str, object] = {} class DummyProc: pid = 4242 def fake_popen(cmd, stdout=None, stderr=None, cwd=None): # noqa: ANN001 captured["cmd"] = cmd captured["cwd"] = cwd return DummyProc() class DummyThread: def __init__(self, target=None, args=None, daemon=None): # noqa: ANN001 captured["thread_target"] = target captured["thread_args"] = args def start(self) -> None: captured["thread_started"] = True monkeypatch.setattr(intraday.subprocess, "Popen", fake_popen) monkeypatch.setattr(intraday.threading, "Thread", DummyThread) req = intraday.IntradayBacktestRequest( config="leader_intraday_momentum_yaml", universe="yaml", start_date="2026-01-02", end_date="2026-03-31", compound_returns=False, ) resp = intraday.submit_intraday_backtest(req) task = intraday._tasks[resp["task_id"]] cmd = captured["cmd"] assert task["universe"] == "yaml" assert task["universe_label"] == "yaml:symbols_midcap_smallmid.yaml" assert "--strategy" in cmd assert cmd[cmd.index("--strategy") + 1] == "momentum" assert "--universe" not in cmd def test_submit_intraday_backtest_accepts_broad_universe(monkeypatch, tmp_path: Path) -> None: _write_config(tmp_path, "leader_intraday_momentum") monkeypatch.setattr(intraday, "get_project_root", lambda: tmp_path) monkeypatch.setattr(intraday, "get_runs_dir", lambda: tmp_path / "runs") intraday._tasks.clear() intraday._tasks_initialized = True captured: dict[str, object] = {} class DummyProc: pid = 4242 def fake_popen(cmd, stdout=None, stderr=None, cwd=None): # noqa: ANN001 captured["cmd"] = cmd captured["cwd"] = cwd return DummyProc() class DummyThread: def __init__(self, target=None, args=None, daemon=None): # noqa: ANN001 captured["thread_target"] = target captured["thread_args"] = args def start(self) -> None: captured["thread_started"] = True monkeypatch.setattr(intraday.subprocess, "Popen", fake_popen) monkeypatch.setattr(intraday.threading, "Thread", DummyThread) req = intraday.IntradayBacktestRequest( config="leader_intraday_momentum", universe="broad", start_date="2026-01-02", end_date="2026-03-31", compound_returns=False, ) resp = intraday.submit_intraday_backtest(req) task = intraday._tasks[resp["task_id"]] cmd = captured["cmd"] assert task["universe"] == "broad" assert task["universe_label"] == "broad" assert "--universe" in cmd assert cmd[cmd.index("--universe") + 1] == "broad" def test_result_summary_from_file_includes_loss_containment_fields(tmp_path: Path) -> None: result_path = tmp_path / "intraday_result.json" result_path.write_text( """{ "metrics": { "total_return_pct": 0.0688, "max_drawdown_pct": -0.1325, "sharpe_ratio": 1.23, "win_rate": 0.569, "total_trades": 116, "final_equity": 10688.0, "calmar_ratio": 0.52, "loss_containment_score": 43.78, "avg_loss_day_pct": -0.0168, "tail_loss_20_pct": -0.0377, "worst_day_return_pct": -0.0672 } }""" ) summary = intraday._result_summary_from_file(result_path) assert summary is not None assert summary["return_pct"] == 0.0688 assert summary["loss_containment_score"] == 43.78 assert summary["avg_loss_day_pct"] == -0.0168 assert summary["tail_loss_20_pct"] == -0.0377 assert summary["worst_day_return_pct"] == -0.0672