fix: mark Tradier positions off Alpaca real-time trades, not delayed quotes
Exit manager reads position current_price for breakeven/trail ratchets and the stop-breach guard; Tradier sandbox quotes are 15-min delayed, so marks lagged reality. Prefer the Alpaca data client's latest trades (real-time), fall back to Tradier /markets/quotes per missing symbol. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8c62804808
commit
bd7183c861
1 file changed
+24
-10
+24
-10
@@ -339,18 +339,32 @@ class TradierClient:
|
||||
raw = _one_or_many(payload.get("positions"), "position")
|
||||
if not raw:
|
||||
return []
|
||||
# Tradier positions carry no mark — price them off /markets/quotes
|
||||
# Tradier positions carry no mark. Price them off the Alpaca data
|
||||
# client's REAL-TIME trades when available — the exit manager's
|
||||
# breakeven/trail logic reads current_price, and Tradier sandbox
|
||||
# quotes are 15-min delayed (stale marks = lagging trails). Tradier
|
||||
# /markets/quotes is the fallback (real-time on funded live accounts).
|
||||
symbols = sorted({str(p["symbol"]) for p in raw if p.get("symbol")})
|
||||
lasts: dict[str, float] = {}
|
||||
try:
|
||||
qp = self._request("GET", "/markets/quotes",
|
||||
params={"symbols": ",".join(symbols)}) or {}
|
||||
for q in _one_or_many(qp.get("quotes"), "quote"):
|
||||
last = q.get("last")
|
||||
if q.get("symbol") and last is not None:
|
||||
lasts[q["symbol"]] = float(last)
|
||||
except Exception: # noqa: BLE001 — quotes down: positions still report, unmarked
|
||||
logger.warning("tradier quotes lookup failed; positions unmarked")
|
||||
if self.data_client is not None:
|
||||
try:
|
||||
for sym, t in (self.data_client.get_latest_trades(symbols) or {}).items():
|
||||
px = t.get("p")
|
||||
if px is not None:
|
||||
lasts[sym] = float(px)
|
||||
except Exception: # noqa: BLE001 — fall through to Tradier quotes
|
||||
logger.warning("alpaca latest-trades mark failed; falling back to tradier quotes")
|
||||
missing = [s for s in symbols if s not in lasts]
|
||||
if missing:
|
||||
try:
|
||||
qp = self._request("GET", "/markets/quotes",
|
||||
params={"symbols": ",".join(missing)}) or {}
|
||||
for q in _one_or_many(qp.get("quotes"), "quote"):
|
||||
last = q.get("last")
|
||||
if q.get("symbol") and last is not None:
|
||||
lasts[q["symbol"]] = float(last)
|
||||
except Exception: # noqa: BLE001 — quotes down: positions still report, unmarked
|
||||
logger.warning("tradier quotes lookup failed; positions unmarked")
|
||||
return [translate_position(p, lasts.get(str(p.get("symbol") or ""))) for p in raw]
|
||||
|
||||
def get_positions(self, fresh: bool = False) -> list[dict]:
|
||||
|
||||
Reference in new issue
Block a user