"""Direct backtest subprocess runner. Invoked by the web server as a subprocess: python -m apps.web.direct_runner TASK_ID CONFIG_PATH CAPITAL START_DATE END_DATE RESULT_FILE [--parking PRESET] [--idle-alpha PRESET] [--form4-sleeve PRESET] [--ownership-sleeve PRESET] [--risk-off-sleeve PRESET] [--non-core-allocator-v2-mode MODE] Runs run_backtest_session_sync() and saves the result JSON to RESULT_FILE. Exits 0 on success, non-zero on failure. """ from __future__ import annotations import datetime as dt import json import sys from pathlib import Path def _json_default(obj): if isinstance(obj, (dt.date, dt.datetime)): return obj.isoformat() raise TypeError(f"Object of type {type(obj)} is not JSON serializable") def main(): if len(sys.argv) < 7: print( "Usage: direct_runner TASK_ID CONFIG_PATH CAPITAL START_DATE END_DATE RESULT_FILE [--parking PRESET] [--idle-alpha PRESET] [--form4-sleeve PRESET] [--ownership-sleeve PRESET] [--risk-off-sleeve PRESET] [--non-core-allocator-v2-mode MODE]", file=sys.stderr, ) sys.exit(2) task_id = sys.argv[1] config_path = sys.argv[2] capital = float(sys.argv[3]) start_date = dt.date.fromisoformat(sys.argv[4]) end_date = dt.date.fromisoformat(sys.argv[5]) result_file = Path(sys.argv[6]) # Optional flags parking_preset = None idle_alpha_preset = None form4_sleeve_preset = None ownership_sleeve_preset = None risk_off_alpha_sleeve_preset = None non_core_allocator_v2_mode = None snapshot_id_override = None fixed_capital = False remaining = sys.argv[7:] i = 0 while i < len(remaining): if remaining[i] == "--parking" and i + 1 < len(remaining): parking_preset = remaining[i + 1] i += 2 elif remaining[i] == "--idle-alpha" and i + 1 < len(remaining): idle_alpha_preset = remaining[i + 1] i += 2 elif remaining[i] == "--form4-sleeve" and i + 1 < len(remaining): form4_sleeve_preset = remaining[i + 1] i += 2 elif remaining[i] == "--ownership-sleeve" and i + 1 < len(remaining): ownership_sleeve_preset = remaining[i + 1] i += 2 elif remaining[i] == "--risk-off-sleeve" and i + 1 < len(remaining): risk_off_alpha_sleeve_preset = remaining[i + 1] i += 2 elif remaining[i] == "--non-core-allocator-v2-mode" and i + 1 < len(remaining): non_core_allocator_v2_mode = remaining[i + 1] i += 2 elif remaining[i] == "--snapshot-id" and i + 1 < len(remaining): snapshot_id_override = remaining[i + 1] i += 2 elif remaining[i] == "--fixed-capital": fixed_capital = True i += 1 else: i += 1 print(f"[direct] {task_id} · {Path(config_path).stem} · {start_date}→{end_date}" + (f" · parking={parking_preset}" if parking_preset else "") + (f" · idle_alpha={idle_alpha_preset}" if idle_alpha_preset else "") + (f" · form4={form4_sleeve_preset}" if form4_sleeve_preset else "") + (f" · ownership={ownership_sleeve_preset}" if ownership_sleeve_preset else "") + (f" · risk_off={risk_off_alpha_sleeve_preset}" if risk_off_alpha_sleeve_preset else "") + (f" · ncav2={non_core_allocator_v2_mode}" if non_core_allocator_v2_mode else "") + (f" · snapshot={snapshot_id_override}" if snapshot_id_override else "") + (" · fixed_capital" if fixed_capital else "")) sys.stdout.flush() from apps.paper_trader.backtest_sim import run_backtest_session_sync result = run_backtest_session_sync( session_name=Path(config_path).stem, config_path=config_path, initial_equity=capital, start_date=start_date, end_date=end_date, parking_preset=parking_preset, idle_alpha_preset=idle_alpha_preset, form4_sleeve_preset=form4_sleeve_preset, ownership_sleeve_preset=ownership_sleeve_preset, risk_off_alpha_sleeve_preset=risk_off_alpha_sleeve_preset, non_core_allocator_v2_mode=non_core_allocator_v2_mode, snapshot_id_override=snapshot_id_override, fixed_capital_sizing=fixed_capital, ) result_file.parent.mkdir(parents=True, exist_ok=True) result_file.write_text(json.dumps(result, default=_json_default, indent=2)) s = result.get("summary", {}) print( f"[direct] done · return={s.get('return_pct', 0):+.2f}% " f"trades={s.get('trade_count', 0)} " f"sharpe={s.get('sharpe', 0):.2f}" ) sys.stdout.flush() if __name__ == "__main__": main()