feat(run): --start-at HH:MM + --stop-at HH:MM for wall-clock scheduling
Usage: atm run --start-at 16:30 --stop-at 23:00 Sleeps until next occurrence of the start time, runs until stop-at. If start-at is in the past today, rolls over to tomorrow. Duration flag is overridden when --stop-at is given. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,16 @@ def main(argv=None) -> None:
|
|||||||
"--startup-delay", type=float, default=5.0, metavar="SEC",
|
"--startup-delay", type=float, default=5.0, metavar="SEC",
|
||||||
help="Seconds to wait before the loop starts (bring TradeStation to front). Default 5.",
|
help="Seconds to wait before the loop starts (bring TradeStation to front). Default 5.",
|
||||||
)
|
)
|
||||||
|
p_run.add_argument(
|
||||||
|
"--start-at", metavar="HH:MM", default=None,
|
||||||
|
help="Sleep until local wall-clock time HH:MM before starting. If the "
|
||||||
|
"time is in the past today, waits until tomorrow.",
|
||||||
|
)
|
||||||
|
p_run.add_argument(
|
||||||
|
"--stop-at", metavar="HH:MM", default=None,
|
||||||
|
help="Stop at local HH:MM (overrides --duration). If the time is in "
|
||||||
|
"the past when the loop starts, rolls over to tomorrow.",
|
||||||
|
)
|
||||||
|
|
||||||
# journal
|
# journal
|
||||||
p_journal = sub.add_parser("journal", help="Add a trade journal entry interactively")
|
p_journal = sub.add_parser("journal", help="Add a trade journal entry interactively")
|
||||||
@@ -141,9 +151,32 @@ def _cmd_dryrun(args) -> None:
|
|||||||
|
|
||||||
def _cmd_run(args) -> None:
|
def _cmd_run(args) -> None:
|
||||||
cfg = Config.load_current(Path("configs"))
|
cfg = Config.load_current(Path("configs"))
|
||||||
duration_s = args.duration * 3600 if args.duration is not None else None
|
|
||||||
capture_stub = args.capture_stub or bool(os.environ.get("ATM_STUB_CAPTURE"))
|
capture_stub = args.capture_stub or bool(os.environ.get("ATM_STUB_CAPTURE"))
|
||||||
|
|
||||||
|
# --start-at HH:MM: sleep until the next occurrence of that local wall-clock time
|
||||||
|
if args.start_at:
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
hh, mm = (int(p) for p in args.start_at.split(":"))
|
||||||
|
now = datetime.now()
|
||||||
|
target = now.replace(hour=hh, minute=mm, second=0, microsecond=0)
|
||||||
|
if target <= now:
|
||||||
|
target += timedelta(days=1)
|
||||||
|
wait = (target - now).total_seconds()
|
||||||
|
print(f"Waiting until {target:%Y-%m-%d %H:%M} (in {wait/60:.1f} min) before starting...", flush=True)
|
||||||
|
time.sleep(wait)
|
||||||
|
|
||||||
|
# --stop-at HH:MM: compute duration from NOW to the next HH:MM; overrides --duration
|
||||||
|
duration_s = args.duration * 3600 if args.duration is not None else None
|
||||||
|
if args.stop_at:
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
hh, mm = (int(p) for p in args.stop_at.split(":"))
|
||||||
|
now = datetime.now()
|
||||||
|
stop = now.replace(hour=hh, minute=mm, second=0, microsecond=0)
|
||||||
|
if stop <= now:
|
||||||
|
stop += timedelta(days=1)
|
||||||
|
duration_s = (stop - now).total_seconds()
|
||||||
|
print(f"Will stop at {stop:%Y-%m-%d %H:%M} (duration {duration_s/3600:.2f}h)", flush=True)
|
||||||
|
|
||||||
delay = getattr(args, "startup_delay", 0.0)
|
delay = getattr(args, "startup_delay", 0.0)
|
||||||
if delay > 0 and not capture_stub:
|
if delay > 0 and not capture_stub:
|
||||||
print(f"Bring TradeStation to front, minimize PowerShell/VS Code. Starting in {delay:.0f}s...", flush=True)
|
print(f"Bring TradeStation to front, minimize PowerShell/VS Code. Starting in {delay:.0f}s...", flush=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user