Three fixes from the first live session:
1) Naked-stop bug (critical): the entry-timeout guard cancelled an entry it
thought was unfilled AFTER it had really filled (our order_filled events lag
the 60s sync). Cancelling a bracket parent cancels its stop/TP legs, so the
position went NAKED and bled 22% with no floor. Guard now checks the broker's
LIVE positions (get_positions) before cancelling and never cancels a filled
name. Regression test added.
2) Speed: ingest re-fetched a 240-min window for ~80 symbols every minute (~46s
measured), and the single-threaded loop froze the 4s trigger that whole time
-> breakouts caught once a minute, not in real time. Ingest is now
incremental (heavy first pass covers the session so the opening range is
present; then ~20-min refreshes, closed bars never change). Loop passes
dropped from ~70s to ~2-5s.
3) Threaded trigger: the fast breakout detector/fire loop now runs on its own
thread, fully decoupled from the slow scheduler. EventStore, the execution
service, and the trigger book are made thread-safe (locks) so the thread and
main loop can't race the shared psycopg2 connection or in-memory state.
Verified live: fired EZGO/NEOV, all positions retain active stops.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>