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.

96 lines
3.3 KiB
Python

"""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]
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]",
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 --parking PRESET, --idle-alpha PRESET, --form4-sleeve PRESET, and --snapshot-id ID
parking_preset = None
idle_alpha_preset = None
form4_sleeve_preset = None
snapshot_id_override = None
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] == "--snapshot-id" and i + 1 < len(remaining):
snapshot_id_override = remaining[i + 1]
i += 2
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" · snapshot={snapshot_id_override}" if snapshot_id_override 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,
snapshot_id_override=snapshot_id_override,
)
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()