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

# Trade Events

> Real-time confirmed order fills and fee refunds

The trades channel (`"orders"`) streams real-time trade events from Polymarket. By default, you receive **confirmed** events - trades that have been mined on-chain. Covers both V1 and V2 Polymarket contracts.

<Info>
  Want even earlier signals? [Pending trade events](/websocket/pending-trades) deliver trades **3–5 seconds before** confirmation by decoding transactions from the Polygon mempool. Use `status: "all"` to receive both.
</Info>

<Note>
  **V1 vs V2:** Every `order_filled` event has a `version` field (`1` or `2`). V2 trades add `builder` and `metadata` fields (bytes32 hex, currently zero). `fee_refund` events are **V1-only** - V2 emits net fees directly on `order_filled` (no refund flow). See the [V2 Migration Guide](/websocket/v2-migration).
</Note>

## Event Types

| Event          | Description                      |
| -------------- | -------------------------------- |
| `order_filled` | An order was filled on-chain     |
| `fee_refund`   | A maker fee rebate was processed |

***

## order\_filled

```json theme={null}
{
  "type": "event",
  "subscription_id": "sub_2f4b15b33798",
  "data": {
    "status": "confirmed",
    "event_type": "order_filled",
    "token_id": "61192765571543...",
    "token_label": "Up",
    "side": "SELL", // [!code highlight]
    "market_slug": "btc-updown-15m-1770244200",
    "condition_id": "0x04f954e4f30f...",
    "shares": 2000000,
    "shares_normalized": 2, // [!code highlight]
    "price": 0.04, // [!code highlight]
    "tx_hash": "0xf30a29f249...",
    "log_index": "0x21e",
    "title": "Bitcoin Up or Down - February 4, 5:30PM-5:45PM ET",
    "timestamp": 1770244731,
    "order_hash": "0x8bf54f44e5d7...",
    "user": "0xe9cbb1c9b3f7f411...", // [!code highlight]
    "taker": "0x98f36c3d6300b905...",
    "role": "maker", // [!code highlight]
    "outcome": "Up",
    "outcome_index": 0,
    "complement_token_id": "9876543210...",
    "complement_token_label": "Down",
    "is_neg_risk": false,
    "market_id": "1329542",
    "image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/BTC+fullsize.png",
    "fee": 0.008,
    "version": 2,
    "builder": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "metadata": "0x0000000000000000000000000000000000000000000000000000000000000000"
  }
}
```

### Field Reference

<Expandable title="Trade fields" defaultOpen>
  | Field               | Type                         | Description                                                                                   |
  | ------------------- | ---------------------------- | --------------------------------------------------------------------------------------------- |
  | `status`            | `"pending"` \| `"confirmed"` | Event source - see [Pending Trades](/websocket/pending-trades)                                |
  | `event_type`        | `"order_filled"`             |                                                                                               |
  | `user`              | string                       | Maker address (order placer)                                                                  |
  | `taker`             | string                       | Taker address (order filler) - equals an exchange contract address on `taker`-role rows       |
  | `role`              | `"maker"` \| `"taker"`       | Which OrderFilled emission this is. See [Maker vs Taker emissions](#maker-vs-taker-emissions) |
  | `side`              | `"BUY"` \| `"SELL"`          | Maker's side of the trade                                                                     |
  | `price`             | number                       | Execution price in USDC per share                                                             |
  | `shares`            | number                       | Raw share amount (6 decimal places)                                                           |
  | `shares_normalized` | number                       | Human-readable share amount                                                                   |
  | `fee`               | number                       | Fee charged in USDC                                                                           |
  | `version`           | number                       | `1` for V1 contracts, `2` for V2 contracts                                                    |
  | `builder`           | string \| null               | bytes32 hex tag (V2 only, currently zero)                                                     |
  | `metadata`          | string \| null               | bytes32 hex tag (V2 only, currently zero)                                                     |
</Expandable>

<Expandable title="Market metadata">
  | Field                    | Type            | Description                                          |
  | ------------------------ | --------------- | ---------------------------------------------------- |
  | `token_id`               | string          | Outcome token ID                                     |
  | `token_label`            | string \| null  | Outcome label (e.g. "Yes", "Up")                     |
  | `outcome`                | string \| null  | Same as `token_label`                                |
  | `outcome_index`          | number \| null  | `0` = first outcome (Yes/Up), `1` = second (No/Down) |
  | `complement_token_id`    | string \| null  | Token ID of the opposite outcome                     |
  | `complement_token_label` | string \| null  | Label of the opposite outcome                        |
  | `is_neg_risk`            | boolean \| null | `true` for neg-risk markets                          |
  | `condition_id`           | string \| null  | Market condition ID                                  |
  | `market_slug`            | string          | Market URL slug                                      |
  | `market_id`              | string \| null  | Internal market ID                                   |
  | `title`                  | string \| null  | Market title                                         |
  | `image`                  | string \| null  | Market image URL                                     |
</Expandable>

<Expandable title="Transaction fields">
  | Field        | Type           | Description                            |
  | ------------ | -------------- | -------------------------------------- |
  | `order_hash` | string \| null | Order hash                             |
  | `tx_hash`    | string         | Transaction hash                       |
  | `log_index`  | string \| null | Log index within the transaction (hex) |
  | `timestamp`  | number         | Unix timestamp in seconds              |
</Expandable>

***

## Maker vs Taker emissions

A single `matchOrders` transaction emits **multiple `order_filled` events** - one per maker matched, plus one synthetic taker-aggregate emission summarizing the taker's overall fill. The `role` field tells you which one you're looking at:

| `role`    | Description                                                                                                                       | When to use                                                                                                                                    |
| --------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `"maker"` | Per-maker fill. One row per maker order matched in the tx. `taker` is the real taker user address.                                | Default for almost all use cases - counting fills, attributing volume to maker wallets, copytrading specific makers.                           |
| `"taker"` | Synthetic taker-aggregate. One row per `matchOrders` tx, summing across all makers. `taker` equals the exchange contract address. | Use when you want one row per taker action - e.g. dashboards keyed on the taker, or to avoid double-counting volume across the per-maker rows. |

<Note>
  Pending events set `role` directly from the maker/taker builder; confirmed events derive it from the chain log (`taker == exchange contract` ⇒ `"taker"`, else `"maker"`). Empirically the two agree 100%, so you can dedupe pending → confirmed by `tx_hash + order_hash` regardless of role.
</Note>

<Warning>
  **Don't sum volume across both roles** - the taker-aggregate row covers the same shares as the per-maker rows. Pick one set: filter on `role === "maker"` for maker-attribution, or `role === "taker"` for taker-attribution.
</Warning>

***

## fee\_refund

<Note>
  `fee_refund` events are emitted for **V1 trades only**. V2 has no refund flow - the net fee is emitted directly on `order_filled` at match time.
</Note>

Emitted when a maker fee rebate is processed on-chain.

```json theme={null}
{
  "type": "event",
  "subscription_id": "sub_2f4b15b33798",
  "data": {
    "event_type": "fee_refund",
    "user": "0x47a51f21d742...",
    "token_id": "53031995840519...",
    "condition_id": "0xdfb2f9d3ed88...",
    "market_slug": "btc-updown-15m-1770242400",
    "title": "Bitcoin Up or Down - February 4, 5:00PM-5:15PM ET",
    "order_hash": "0xbda8ab86c90f...", // [!code highlight]
    "tx_hash": "0x97b2ae331881...",
    "refund": 9.9904, // [!code highlight]
    "fee_charged": 0.0096 // [!code highlight]
  }
}
```

<Expandable title="Field reference">
  | Field          | Type           | Description                           |
  | -------------- | -------------- | ------------------------------------- |
  | `event_type`   | `"fee_refund"` |                                       |
  | `user`         | string         | Address that received the refund      |
  | `token_id`     | string         | Outcome token ID                      |
  | `condition_id` | string \| null | Market condition ID                   |
  | `market_slug`  | string \| null | Market URL slug                       |
  | `title`        | string \| null | Market title                          |
  | `order_hash`   | string         | The order hash this refund applies to |
  | `tx_hash`      | string         | Transaction hash                      |
  | `refund`       | number         | USDC amount refunded to the maker     |
  | `fee_charged`  | number         | Final net fee after refund            |
</Expandable>

***

## Understanding Fee Refunds

Polymarket implements a maker fee rebate program. Here's how it works:

<Steps>
  <Step title="Trade executes">
    An `order_filled` event arrives with a `fee` field representing the gross fee charged.
  </Step>

  <Step title="Rebate processes">
    A `fee_refund` event for the same `order_hash` arrives 2–5ms later (same block, usually same tx). Rarely up to 50ms.
  </Step>

  <Step title="Calculate net fee">
    The maker's actual net fee is `fee_refund.fee_charged` (or equivalently `order_filled.fee - fee_refund.refund`).
  </Step>
</Steps>

<Note>
  If you don't need exact fee accounting, you can ignore `fee_refund` events. The `fee` field on `order_filled` is directionally correct for most purposes. Fee refunds are only necessary for precise PnL tracking or cost basis accounting.
</Note>

***

## Example: Processing Trades

```javascript theme={null}
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === 'event') {
    const data = msg.data;

    if (data.event_type === 'order_filled') {
      const value = data.shares_normalized * data.price; // [!code highlight]
      console.log(`${data.side} ${data.outcome} ${data.shares_normalized} @ ${data.price}`);
      console.log(`Market: ${data.title}`);
      console.log(`Value: $${value.toFixed(2)}`);
      console.log(`Maker: ${data.user}`);
      console.log(`Taker: ${data.taker}`);
    }

    if (data.event_type === 'fee_refund') {
      console.log(`Fee refund: $${data.refund} for order ${data.order_hash}`);
      console.log(`Net fee: $${data.fee_charged}`); // [!code highlight]
    }
  }
};
```
