What you’ll build: a live “my positions” view that stays in sync as a wallet trades, as outcome prices move, and as markets resolve. You seed it once from REST, then keep it warm over a websocket so the open and closed sections never go stale.
polymarket_trader_positions pushes per-position updates for a set of wallets as they happen, batched per block. This recipe seeds a portfolio map from REST, then keeps it warm with three envelope types so the UI reflects new trades, moving prices, and market resolutions without polling.
When to use this
- A live “my positions” view with open and closed sections.
- A portfolio value ticker that moves with outcome prices between trades.
- Surfacing redeemable and mergeable flags so a user knows when they can claim or convert.
The three envelope types
| Event | Fires on | Shape |
|---|---|---|
trader_position_batch | A trade landed (buy, sell, merge, split, redemption, NegRisk convert). | Full position rows. |
trader_position_price_batch | Outcome prices moved. | Compact price ticks (dirty_kinds is ["price"]). |
trader_position_resolution_batch | The position’s market resolved. | Resolution ticks (dirty_kinds is ["position_resolved"]). |
position_id and merge each envelope in place.
Note that timestamps in this room (first_trade_at, last_trade_at) are Unix milliseconds, unlike the trader PnL room where they are seconds.
Step 1: seed from REST
Pull the current position book so the portfolio is painted before the first block arrives. Use a trader outcome PnL call withmin_shares: 0 to include fully exited positions, then build a map keyed by position_id.
Step 2: subscribe and merge full rows
traders is the only required filter. Each trader_position_batch carries full rows; upsert them by position_id.
current_shares_balance > 0 and closed when it reaches 0. The full row also carries realized_pnl_usd, realized_pnl_pct, avg_entry_price, and the descriptive fields (title, question, outcome, image_url) you need to render a card.
Step 3: patch the mark-to-market value from price ticks
Between trades, prices move, andtrader_position_price_batch is how the live value tracks them. Each tick carries current_price, current_value, and the refreshed realized_pnl_usd for one position_id. The unrealized value of an open position is current_price times current_shares_balance, and the tick’s current_value is exactly that product, so you can use it directly.
current_value across open positions plus realized PnL on closed ones, and the ticker moves smoothly with the market.
Step 4: handle resolutions
When a position’s market resolves,trader_position_resolution_batch delivers the verdict: resolved, won, and the final realized_pnl_usd. Apply it, then re-check the redeemable flag.
Surfacing redeemable and mergeable flags
The full row carries two action flags worth surfacing in the UI:redeemableis true when the market has resolved and the wallet still holds shares, meaning there is a payout to claim.mergeableis true for an unresolved NegRisk market where the wallet holds shares, meaning positions can be merged back to collateral.
trader_position_batch after a trade or, for resolution-driven redeemability, after you apply a resolution tick and re-evaluate.
Cutting message volume
This room bills per message. Thedirty_kinds filter (a subset of ["trade","price","position_resolved"] or ["all"]) lets you drop families you do not render. A portfolio view that only redraws on trades and resolutions, not on every price move, can omit price:
Follow-on
- For aggregate realized PnL summary cards alongside the position list, see Building a live trader PnL dashboard.
- To annotate a PnL chart with the moment each position closed, see Charting PnL with exit markers.
- Full payload schemas live on the Trader positions room page.