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.
120 lines
4.6 KiB
Python
120 lines
4.6 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] [--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()
|