Exit manager: scope get_orders to held symbols (stop-leg truncation)

Alpaca's orders `limit` keeps only the most-RECENT orders and counts child
legs toward the cap. On a busy day the order list runs to hundreds (every
backed-out entry is an order), so the exit manager's limit=100 snapshot
silently dropped the stop legs of older OPEN positions. A stop-leg lookup then
read "no stop" on a fully-protected position -> broken trailing/profit-lock for
that name (and false-naked alarms in monitoring).

Fix: get_orders gains a `symbols` filter; the exit manager scopes its snapshot
to the handful of held names (no truncation possible). Verified live: an
unscoped limit=100 query reported PLUG stop=None while the scoped query
correctly surfaced PLUG's stop @2.65.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
momentum devandClaude Opus 4.8 committed 2026-06-18 12:34:43 -04:00
1 parent 1cd82de6a8
commit 11ab6c7b56
2 files changed
+24 -9

No files matched your search

+15 -5
View File
@@ -162,14 +162,24 @@ class AlpacaPaperClient:
return self._cached("positions", lambda: self._trading("GET", "/positions"))
def get_orders(
self, status: str = "all", limit: int = 100, nested: bool = True
self, status: str = "all", limit: int = 500, nested: bool = True,
symbols: list[str] | None = None,
) -> list[dict]:
# nested=true includes bracket child legs (stop/target) so working risk
# isn't under-reported in the orders snapshot.
return self._trading(
"GET", "/orders",
params={"status": status, "limit": limit, "nested": str(nested).lower()},
)
#
# IMPORTANT: Alpaca's `limit` truncates to the most-RECENT orders (and
# counts child legs toward the cap). On a busy day the order list runs to
# hundreds (every backed-out entry is an order), so a small limit silently
# drops the stop legs of older OPEN positions — a stop-leg lookup then
# reads "no stop" on a fully-protected position. Pass `symbols` to scope
# the query to the handful of held names (no truncation risk); that's what
# the exit manager / safety checks must do.
params: dict = {"status": status, "limit": min(limit, 500),
"nested": str(nested).lower()}
if symbols:
params["symbols"] = ",".join(symbols)
return self._trading("GET", "/orders", params=params)
def submit_order(
self,