The live loop froze ~40s every discovery pass. py-spy showed the main thread
stuck in upsert_daily_bars -> PgConnection.executemany, which used psycopg2's
cur.executemany — ONE INSERT round-trip per row. A discovery pass upserts
thousands of daily bars across the universe, so over the LAN Postgres that's
thousands of serial round-trips. While frozen the loop emits nothing and can't
service the trigger/exit cadence.
Switch executemany to psycopg2.extras.execute_batch (page_size=200): many
statements per round-trip, same SQL, same ON CONFLICT upsert semantics.
Benchmarked on the live DB: 771-row upsert 1.87s -> 0.06s (~31x); a real
discovery pass now finishes in one cycle (ingest 6803 rows, no stall).
Helps every bulk writer (daily/minute bars, scanner snapshots).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>