> ## Documentation Index
> Fetch the complete documentation index at: https://docs.struct.to/llms.txt
> Use this file to discover all available pages before exploring further.

# Trader Positions

> Stream full position snapshots, price ticks, and resolutions for tracked traders.

<Note>
  **Room ID:** `polymarket_trader_positions` \
  **Endpoint:** `wss://api.struct.to/ws` \
  **Rate:** 0.005 credits per message
</Note>

Stream per-position updates for tracked traders, batched per block. The room emits three envelope types: full rows on trades, compact price ticks on mark-to-market refreshes, and resolution ticks when a position's market resolves.

<Tip>
  **Related guide:** [Real-time positions tracking](/guides/real-time-positions-tracking) seeds a portfolio from REST and keeps it live through this room.
</Tip>

## Envelope types

| Event                              | Trigger                        | dirty\_kinds                     |
| ---------------------------------- | ------------------------------ | -------------------------------- |
| `trader_position_batch`            | A trade landed                 | `["trade"]` (may include others) |
| `trader_position_price_batch`      | Mark-to-market refresh         | `["price"]`                      |
| `trader_position_resolution_batch` | The position's market resolved | `["position_resolved"]`          |

Each envelope is shaped `{ type, room_id, block, data: [ ...rows ] }`. Empty batches are not sent. Resolution rows cover lifetime non-redeemers as well as fresh resolutions.

### dirty\_kinds

| Value               | Meaning                                                          |
| ------------------- | ---------------------------------------------------------------- |
| `trade`             | A buy, sell, merge, split, redemption, or NegRisk convert landed |
| `price`             | An outcome price moved (mark-to-market refresh)                  |
| `position_resolved` | The position's market resolved                                   |

## Subscribe

The `traders` filter is required.

### Filters

| Filter        | Type       | Required | Description                                                                                                                        |
| ------------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `traders`     | `string[]` | Yes      | EVM wallet addresses (0x-prefixed), non-empty                                                                                      |
| `dirty_kinds` | `string[]` | No       | Subset of `["trade","price","position_resolved"]`, or `["all"]`. A row is delivered only if its `dirty_kinds` intersects this set. |

### Example

```json theme={null}
{
  "type": "join_room",
  "payload": {
    "room_id": "polymarket_trader_positions"
  }
}
```

```json theme={null}
{
  "type": "room_message",
  "payload": {
    "room_id": "polymarket_trader_positions",
    "message": {
      "action": "subscribe",
      "traders": ["0x1234567890abcdef1234567890abcdef12345678"]
    }
  }
}
```

### Response

```json theme={null}
{
  "type": "trader_positions_stream_subscribe_response",
  "room_id": "polymarket_trader_positions",
  "data": {
    "traders": ["0x1234567890abcdef1234567890abcdef12345678"],
    "rejected": [],
    "error": null
  }
}
```

## Events

### `trader_position_batch`

Full position rows, fired on trades.

```json theme={null}
{
  "type": "trader_position_batch",
  "room_id": "polymarket_trader_positions",
  "block": 68420001,
  "data": [
    {
      "trader": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "position_id": "12345678901234567",
      "condition_id": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
      "market_slug": "will-bitcoin-hit-100k",
      "event_slug": "bitcoin-price-markets",
      "title": "Will Bitcoin hit $100k?",
      "question": "Will Bitcoin hit $100k by end of year?",
      "image_url": "https://polymarket.com/images/market.png",
      "outcome": "Yes",
      "outcome_index": 0,
      "open": true,
      "won": null,
      "total_buys": 3,
      "total_sells": 1,
      "converted_count": 0,
      "converted_shares_gained": 0.0,
      "converted_shares_lost": 0.0,
      "total_shares_bought": 1500.0,
      "total_shares_sold": 500.0,
      "total_buy_usd": 975.0,
      "total_sell_usd": 350.0,
      "redemption_usd": 0.0,
      "merge_usd": 0.0,
      "avg_entry_price": 0.65,
      "avg_exit_price": 0.7,
      "avg_price": 0.66,
      "realized_pnl_usd": 125.5,
      "realized_pnl_pct": 12.87,
      "total_fees": 2.65,
      "first_trade_at": 1742000000000,
      "last_trade_at": 1743500000000,
      "current_price": 0.72,
      "current_shares_balance": 1000.0,
      "current_value": 720.0,
      "last_traded_price": 0.71,
      "end_date": 1751328000,
      "is_neg_risk": false,
      "redeemable": false,
      "mergeable": false,
      "dirty_kinds": ["trade"]
    }
  ]
}
```

All fields are nullable.

| Field                     | Type       | Description                                    |
| ------------------------- | ---------- | ---------------------------------------------- |
| `position_id`             | `string`   | ERC-1155 token ID (decimal string)             |
| `condition_id`            | `string`   | Condition ID                                   |
| `market_slug`             | `string`   | Market slug                                    |
| `event_slug`              | `string`   | Owning event slug                              |
| `title`                   | `string`   | Market title                                   |
| `question`                | `string`   | Market question                                |
| `image_url`               | `string`   | Market image URL                               |
| `outcome`                 | `string`   | Outcome label (for example `Yes`)              |
| `outcome_index`           | `int`      | Outcome index                                  |
| `open`                    | `bool`     | Whether the position is open                   |
| `won`                     | `bool`     | Set once the market resolves                   |
| `total_buys`              | `int64`    | Buy count                                      |
| `total_sells`             | `int64`    | Sell count                                     |
| `converted_count`         | `int64`    | NegRisk conversion count                       |
| `converted_shares_gained` | `number`   | Shares gained via conversion                   |
| `converted_shares_lost`   | `number`   | Shares lost via conversion                     |
| `total_shares_bought`     | `number`   | Cumulative shares bought                       |
| `total_shares_sold`       | `number`   | Cumulative shares sold                         |
| `total_buy_usd`           | `number`   | Buy spend in USD                               |
| `total_sell_usd`          | `number`   | Sell proceeds in USD                           |
| `redemption_usd`          | `number`   | Redemption proceeds in USD                     |
| `merge_usd`               | `number`   | Merge proceeds in USD                          |
| `avg_entry_price`         | `number`   | Volume-weighted entry price (0-1)              |
| `avg_exit_price`          | `number`   | Volume-weighted exit price (0-1)               |
| `avg_price`               | `number`   | Volume-weighted price across buys and sells    |
| `realized_pnl_usd`        | `number`   | Realized PnL in USD                            |
| `realized_pnl_pct`        | `number`   | Realized PnL as a percentage                   |
| `total_fees`              | `number`   | Total fees in USD                              |
| `first_trade_at`          | `int64`    | First trade (**Unix milliseconds**)            |
| `last_trade_at`           | `int64`    | Last trade (**Unix milliseconds**)             |
| `current_price`           | `number`   | Current outcome price                          |
| `current_shares_balance`  | `number`   | Current shares held                            |
| `current_value`           | `number`   | `current_price` times `current_shares_balance` |
| `last_traded_price`       | `number`   | Last on-chain traded price                     |
| `end_date`                | `int64`    | Resolution deadline (Unix seconds)             |
| `is_neg_risk`             | `bool`     | Whether the market is NegRisk                  |
| `redeemable`              | `bool`     | Resolved and still holding shares              |
| `mergeable`               | `bool`     | NegRisk, unresolved, and holding shares        |
| `dirty_kinds`             | `string[]` | What triggered this row                        |

### `trader_position_price_batch`

Compact price ticks. `dirty_kinds` is always `["price"]`.

```json theme={null}
{
  "type": "trader_position_price_batch",
  "room_id": "polymarket_trader_positions",
  "block": 68420002,
  "data": [
    {
      "trader": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "position_id": "12345678901234567",
      "condition_id": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
      "current_price": 0.74,
      "current_value": 740.0,
      "realized_pnl_usd": 125.5,
      "realized_pnl_pct": 12.87,
      "last_block": 68420002,
      "last_trade_at": 1743500000000,
      "dirty_kinds": ["price"]
    }
  ]
}
```

| Field              | Type       | Description                        |
| ------------------ | ---------- | ---------------------------------- |
| `trader`           | `string`   | Wallet address                     |
| `position_id`      | `string`   | ERC-1155 token ID (decimal string) |
| `condition_id`     | `string`   | Condition ID                       |
| `current_price`    | `number`   | Current outcome price              |
| `current_value`    | `number`   | Current position value in USD      |
| `realized_pnl_usd` | `number`   | Realized PnL in USD                |
| `realized_pnl_pct` | `number`   | Realized PnL as a percentage       |
| `last_block`       | `int64`    | Block of this tick                 |
| `last_trade_at`    | `int64`    | Last trade (Unix milliseconds)     |
| `dirty_kinds`      | `string[]` | Always `["price"]`                 |

### `trader_position_resolution_batch`

Resolution ticks. `dirty_kinds` is always `["position_resolved"]`.

```json theme={null}
{
  "type": "trader_position_resolution_batch",
  "room_id": "polymarket_trader_positions",
  "block": 68420003,
  "data": [
    {
      "trader": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "position_id": "12345678901234567",
      "condition_id": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
      "resolved": true,
      "won": true,
      "realized_pnl_usd": 350.25,
      "realized_pnl_pct": 35.9,
      "last_block": 68420003,
      "last_trade_at": 1743500000000,
      "dirty_kinds": ["position_resolved"]
    }
  ]
}
```

| Field              | Type           | Description                        |
| ------------------ | -------------- | ---------------------------------- |
| `trader`           | `string`       | Wallet address                     |
| `position_id`      | `string`       | ERC-1155 token ID (decimal string) |
| `condition_id`     | `string`       | Condition ID                       |
| `resolved`         | `bool`         | Whether the market resolved        |
| `won`              | `bool \| null` | Whether the position won           |
| `realized_pnl_usd` | `number`       | Realized PnL in USD                |
| `realized_pnl_pct` | `number`       | Realized PnL as a percentage       |
| `last_block`       | `int64`        | Block of this tick                 |
| `last_trade_at`    | `int64`        | Last trade (Unix milliseconds)     |
| `dirty_kinds`      | `string[]`     | Always `["position_resolved"]`     |
