Fix naked-stop regression: speed cache made the guard strip live brackets
The get_positions TTL cache (added for the speed fix) silently broke the naked-stop guard: _broker_held_symbols read 2.5s-stale positions, so a just-filled entry wasn't seen as held, the guard thought it was unfilled, and cancelled its bracket on a price dip -> NAKED position (APWC bled -12.8% unprotected today). Fixes: - client.get_positions(fresh=True) bypasses the cache; the guard uses it. - entry_grace_seconds (default 5): never cancel an entry within N seconds of arming, so a marketable fill can confirm before the bracket could be stripped. Regression test added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
27b0748b71
commit
1cd82de6a8
5 files changed
+43
-4
No files matched your search
@@ -151,7 +151,14 @@ class AlpacaPaperClient:
|
||||
def get_account(self) -> dict:
|
||||
return self._cached("account", lambda: self._trading("GET", "/account"))
|
||||
|
||||
def get_positions(self) -> list[dict]:
|
||||
def get_positions(self, fresh: bool = False) -> list[dict]:
|
||||
# fresh=True bypasses the TTL cache — REQUIRED for the naked-stop guard,
|
||||
# which must never decide an order is unfilled off a stale snapshot.
|
||||
if fresh:
|
||||
val = self._trading("GET", "/positions")
|
||||
with self._cache_lock:
|
||||
self._cache["positions"] = (val, time.monotonic())
|
||||
return val
|
||||
return self._cached("positions", lambda: self._trading("GET", "/positions"))
|
||||
|
||||
def get_orders(
|
||||
|
||||
Reference in new issue
Block a user