Spaces:
Running
Running
Khanna, Videh Rakesh Rakesh
Add bear-direction fixes, research scripts, market_calendar, and gitignore cleanup
74d4035 | #!/usr/bin/env python3 | |
| """Schedule loop_backtest.py to start once after N hours (macOS launchd).""" | |
| from __future__ import annotations | |
| import argparse | |
| import datetime as dt | |
| import os | |
| import plistlib | |
| import subprocess | |
| import sys | |
| from pathlib import Path | |
| def _pick_python(repo_root: Path) -> str: | |
| venv_python = repo_root / ".venv" / "bin" / "python" | |
| if venv_python.exists(): | |
| return str(venv_python) | |
| return sys.executable | |
| def _write_plist(plist_path: Path, label: str, run_at: dt.datetime, repo_root: Path, reset: bool) -> dict: | |
| python_bin = _pick_python(repo_root) | |
| script_path = repo_root / "research" / "loop_backtest.py" | |
| args = [python_bin, str(script_path)] | |
| if reset: | |
| args.append("--reset") | |
| payload = { | |
| "Label": label, | |
| "ProgramArguments": args, | |
| "WorkingDirectory": str(repo_root), | |
| "StartCalendarInterval": { | |
| "Year": run_at.year, | |
| "Month": run_at.month, | |
| "Day": run_at.day, | |
| "Hour": run_at.hour, | |
| "Minute": run_at.minute, | |
| }, | |
| "RunAtLoad": False, | |
| "StandardOutPath": "/tmp/papertrade_loop_backtest_delayed.log", | |
| "StandardErrorPath": "/tmp/papertrade_loop_backtest_delayed.err", | |
| } | |
| plist_path.parent.mkdir(parents=True, exist_ok=True) | |
| with plist_path.open("wb") as f: | |
| plistlib.dump(payload, f) | |
| return { | |
| "python": python_bin, | |
| "script": str(script_path), | |
| "stdout": payload["StandardOutPath"], | |
| "stderr": payload["StandardErrorPath"], | |
| } | |
| def _launch_load(plist_path: Path) -> tuple[bool, str]: | |
| uid = os.getuid() | |
| target = f"gui/{uid}" | |
| subprocess.run( | |
| ["launchctl", "bootout", target, str(plist_path)], | |
| stdout=subprocess.DEVNULL, | |
| stderr=subprocess.DEVNULL, | |
| check=False, | |
| ) | |
| bootstrap = subprocess.run( | |
| ["launchctl", "bootstrap", target, str(plist_path)], | |
| capture_output=True, | |
| text=True, | |
| check=False, | |
| ) | |
| if bootstrap.returncode == 0: | |
| return True, "bootstrap" | |
| legacy = subprocess.run( | |
| ["launchctl", "load", "-w", str(plist_path)], | |
| capture_output=True, | |
| text=True, | |
| check=False, | |
| ) | |
| if legacy.returncode == 0: | |
| return True, "load" | |
| err = (bootstrap.stderr or "").strip() or (legacy.stderr or "").strip() or "unknown launchctl error" | |
| return False, err | |
| def main() -> int: | |
| parser = argparse.ArgumentParser(description="Schedule loop_backtest.py after a delay") | |
| parser.add_argument("--hours", type=float, default=5.0, help="Delay before running loop_backtest (default: 5)") | |
| parser.add_argument("--reset", action="store_true", help="Pass --reset to loop_backtest.py") | |
| parser.add_argument("--label", default="com.papertrade.loopbacktest.once", help="launchd label") | |
| args = parser.parse_args() | |
| repo_root = Path(__file__).resolve().parents[1] | |
| run_at = dt.datetime.now() + dt.timedelta(hours=args.hours) | |
| plist_path = Path.home() / "Library" / "LaunchAgents" / f"{args.label}.plist" | |
| details = _write_plist(plist_path, args.label, run_at, repo_root, args.reset) | |
| ok, method_or_err = _launch_load(plist_path) | |
| if not ok: | |
| print(f"FAILED to load launch agent: {method_or_err}") | |
| return 1 | |
| print("Scheduled delayed loop backtest") | |
| print(f"label={args.label}") | |
| print(f"plist={plist_path}") | |
| print(f"run_at_local={run_at.strftime('%Y-%m-%d %H:%M')}") | |
| print(f"python={details['python']}") | |
| print(f"script={details['script']}") | |
| print(f"stdout={details['stdout']}") | |
| print(f"stderr={details['stderr']}") | |
| print(f"load_method={method_or_err}") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |