Fix: don't permanently reject events when parking sell fails transiently

- When _parking_liquidate_for_event returns False but parking had shares
  (sell failed due to transient Alpaca error), skip without recording so
  event retries on next run_next_open instead of being permanently blocked

- Improve parking_sell_for_event_failed log: include exc_type, symbol, qty
  so we can see the actual exception (was silently swallowed with empty msg)

- Also deleted AVGO from processed_events so it retries tomorrow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
I Luk Kim 4 months ago
parent 5ea8850ac2
commit ebfada24cb

@ -1596,7 +1596,7 @@ class PaperTradingEngine:
)
return True
except Exception as exc:
logger.warning("parking_sell_for_event_failed", error=str(exc))
logger.warning("parking_sell_for_event_failed", error=str(exc) or repr(exc), exc_type=type(exc).__name__, symbol=sym, qty=shares_to_sell)
return False
def _parking_position_value(
@ -2502,7 +2502,11 @@ class PaperTradingEngine:
engine_daily_new_risk_used=engine_risk_used if engine_cfg else 0.0,
)
freed = False
parking_had_qty = False
if plan.skip_reason == "insufficient_cash":
# Check if parking has shares before attempting liquidation
_pst = self._state.get_parking_state(session_id)
parking_had_qty = _pst is not None and (_pst.get("qty") or 0) > 0
# 1) Attempt to free parking cash before giving up
needed = plan.shares * float(candidate.entry_price_est) if plan.shares else float(candidate.entry_price_est)
freed = self._parking_liquidate_for_event(session_id, today, needed)
@ -2537,9 +2541,7 @@ class PaperTradingEngine:
)
if plan.skip_reason:
# If we just freed parking cash but plan still shows insufficient_cash,
# Alpaca may not have settled the SGOV sell yet → skip without recording
# so the event retries on the next run_next_open.
# Case 1: parking freed but cash not yet reflected in account (Alpaca latency)
if freed and plan.skip_reason == "insufficient_cash":
logger.warning(
"paper_engine_parking_freed_cash_not_settled",
@ -2547,6 +2549,15 @@ class PaperTradingEngine:
hint="SGOV sold but cash not yet reflected in account — will retry on next run",
)
continue
# Case 2: parking existed but sell failed (transient Alpaca error)
# Don't permanently reject — retry next run when sell may succeed.
if not freed and parking_had_qty and plan.skip_reason == "insufficient_cash":
logger.warning(
"paper_engine_parking_sell_failed_skip",
symbol=candidate.symbol,
hint="Parking sell failed (transient?) — skipping without recording to allow retry",
)
continue
self._state.record_processed_event(
session_id, candidate.event_id, today.isoformat(),
"rejected", skip_reason=plan.skip_reason,

Loading…
Cancel
Save