A Telegram alert that fires when top cohort traders EXIT a bet. It reads the existing pmv2_position_events EXITED feed (logged each cycle by the position watcher), runs as a non-fatal scheduled step after the trending alert, and shows the trader plus the position’s last-seen P/L. Shipped 2026-06-04.

For Agents

  • Source of EXITED events: the first-mover-overlap-watcher position watcher (run_pmv2_positions → append-only pmv2_position_events, kind 'EXITED'). The exit-alert step is a read over that feed — it does not detect exits itself.
  • Module: crates/polymarket-fetch/src/pmv2/exits.rs.
  • Cursor key pmv2_exit_alert in pm_detection_cursors (dedup).
  • Runs as a non-fatal scheduled step right after the trending alert — a failure here never aborts the scheduled run.
  • pmv2-exits CLI = read-only 24h preview (no alerts sent).

What Shipped

When a top-tier cohort trader leaves a position, the bot pushes a 🔴 TRADE EXITED Telegram alert showing:

  • Trader name, score / band / rank
  • Market + outcome
  • P/L (last seen) — the position’s last-snapshot cash_pnl

Gating + filters

  • Top-trader gated — only traders with live score > copy threshold qualify (same threshold as the rest of the pmv2 alerting).
  • Cursor-dedupedpm_detection_cursors detector key pmv2_exit_alert, so each EXITED event alerts at most once.
  • Dust-filtered — ≥ $50 stake.
  • Telegram-budget capped — bounded number of alerts per cycle.

P/L caveat

Exit P/L is the LAST-SEEN unrealized cash_pnl, not a real fill

Exits are snapshot-detected (the position is simply gone from the next fetch), so the actual exit fill price is unknown. The displayed “P/L (last seen)” is the position’s last-snapshot cash_pnl — its last observed unrealized cash P/L before it disappeared. It is labeled (last seen) precisely because it is not a realized fill. Treat it as “where the position stood when last seen,” not “what the trader booked.”

Migration 20260604000000_pmv2_event_pnl.sql

Added P/L-context columns to pmv2_position_events:

  • cash_pnl
  • initial_value
  • current_value
  • cur_price
  • outcome
  • title

These are captured in insert_position_event from the prior PositionRow at log time — no call-site change was needed; the event-logging function reads them off the position it’s already handling. This denormalizes the P/L context onto the event so the exit alert can render P/L without a re-join (same denormalize-at-log-time pattern the first-mover-overlap-watcher uses for trader_score).

CLI

pmv2-exitsread-only 24h preview of cohort exits. Sends no alerts; for eyeballing what the live alert would have fired on over the last day.