Accept paper MOC orders IBKR holds as PendingSubmit; stop retrying after the order window
On 2026-10-13 the 09:53 ET run placed six MOC orders and failed: the paper account reports MOC orders as PendingSubmit until the closing auction and sent no acknowledgement within 30 s, so confirm_orders treated them as rejected. IBKR listed all six with permanent ids and filled them at the close, as it did the 14 PendingSubmit orders of 2026-09-29. Every retry then stopped at "6 open orders already exist", and after 15:45 ET at the order window, and the start limit's 6-hour window reset at 15:50 ET, so the job restarted every 10 minutes into the evening (42 restarts by 17:28 ET). - confirm_orders accepts a PendingSubmit order once IBKR lists it with a permanent id, re-reading this client's open orders every 5 s for up to 60 s. - rebalance --submit succeeds without sending anything when every open order comes from today's runs and IBKR still holds every order those runs sent; a missing one still fails. - The trade unit skips starts after 15:45 ET (ExecCondition), which systemd does not restart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -190,6 +190,17 @@ async def rebalance(cfg: Config, submit: bool) -> None:
|
||||
raise SystemExit(f"{now:%H:%M} ET is outside the order window {ORDER_WINDOW[0]}-{ORDER_WINDOW[1]}")
|
||||
|
||||
if open_trades := await broker.open_trades():
|
||||
# A run whose confirmation timed out exits non-zero and the trade service retries it;
|
||||
# if IBKR holds every order that run sent, the session's orders are in.
|
||||
refs = {t.order.orderRef for t in open_trades}
|
||||
sent = [cfg.root / "runs" / ref.removeprefix("ibkr-quant ") / "orders.csv" for ref in refs]
|
||||
if all(ref.startswith(f"ibkr-quant {now:%Y%m%d}-") for ref in refs) and all(p.exists() for p in sent):
|
||||
sent_ids = set(pd.concat(pd.read_csv(p) for p in sent)["order_id"])
|
||||
if missing := sent_ids - {t.order.orderId for t in open_trades}:
|
||||
raise SystemExit(f"today's orders {sorted(missing)} are no longer open at IBKR "
|
||||
"(rejected, cancelled or filled); see `make check`")
|
||||
print(f"{len(open_trades)} orders from {', '.join(sorted(refs))} are open at IBKR; not submitting again")
|
||||
return
|
||||
raise SystemExit(f"{len(open_trades)} open orders already exist; cancel or let them finish first")
|
||||
contracts, unknown = await broker.qualify_stocks([o.symbol for o in orders])
|
||||
if unknown:
|
||||
|
||||
Reference in New Issue
Block a user