Skip to main content
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

Full rows are the complete record of a position. Price ticks and resolution ticks are light updates that patch the live fields of a row you already hold, so you keep a single map keyed by 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 with min_shares: 0 to include fully exited positions, then build a map keyed by position_id.
See Showing a trader’s open and closed positions for more on the REST shape and the open versus closed split.

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.
A position is open when 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, and trader_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.
Recompute the portfolio total as the sum of 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:
  • redeemable is true when the market has resolved and the wallet still holds shares, meaning there is a payout to claim.
  • mergeable is true for an unresolved NegRisk market where the wallet holds shares, meaning positions can be merged back to collateral.
Both flags live on full rows, so they update on the next 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. The dirty_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

Last modified on July 13, 2026