> ## 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.

# Mirroring a trader from the mempool

> Mirror a target trader's order flow in real time using the trades room's status filter for mempool (pending) detection or block-confirmed signals.

The `polymarket_trades` room exposes a `status` filter with three values: `confirmed` (default, post-block), `pending` (mempool, pre-confirmation), or `all` (both). Pending trades arrive seconds before they land on-chain, so a copy-trading pipeline can react before the original fill is confirmed and reconcile against the confirmed event afterwards.

## When to use this

* **`pending`**: fastest reaction, mirrors a watched wallet from the mempool. Trades may not land, so size mirrors conservatively.
* **`confirmed`**: safe but slower. Use when you only want to react to fills that actually settled.
* **`all`**: hybrid pipelines that act on pending and reconcile when the confirmed event arrives.

For the underlying schema, see [WebSockets: trades room](/websockets/rooms/trades).

## Subscribe to a target trader's trades

Pass `traders` (lowercase 0x-prefixed) and `status` to `polymarket_trades`. The room accepts up to 500 total filters per client.

<CodeGroup>
  ```typescript SDK theme={null}
  import { StructWebSocket } from "@structbuild/sdk";

  const ws = new StructWebSocket({ apiKey: "sk_live_xxx" });
  await ws.connect();

  await ws.subscribe("polymarket_trades", {
    traders: ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"],
    status: "pending",
    trade_types: ["OrderFilled", "OrdersMatched"],
  });

  ws.on("trade_stream_update", (event) => {
    if (event.data.trade_type !== "OrderFilled" && event.data.trade_type !== "OrdersMatched") return;
    const { trader, side, condition_id, position_id, outcome, usd_amount, price } = event.data;
    console.log(event.status, trader.address, side, outcome, usd_amount, price);
  });
  ```

  ```bash cURL theme={null}
  wscat -c "wss://api.struct.to/ws?api-key=YOUR_API_KEY"

  > {"type":"join_room","payload":{"room_id":"polymarket_trades"}}
  > {"type":"room_message","payload":{"room_id":"polymarket_trades","message":{"action":"subscribe","traders":["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"],"status":"pending","trade_types":["OrderFilled","OrdersMatched"]}}}
  ```

  ```python Python theme={null}
  import json
  import websockets

  async with websockets.connect("wss://api.struct.to/ws?api-key=YOUR_API_KEY") as ws:
      await ws.send(json.dumps({"type": "join_room", "payload": {"room_id": "polymarket_trades"}}))
      await ws.send(json.dumps({
          "type": "room_message",
          "payload": {
              "room_id": "polymarket_trades",
              "message": {
                  "action": "subscribe",
                  "traders": ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"],
                  "status": "pending",
                  "trade_types": ["OrderFilled", "OrdersMatched"],
              },
          },
      }))

      async for raw in ws:
          msg = json.loads(raw)
          if msg.get("type") == "trade_stream_update":
              print(msg["status"], msg["data"]["trader"]["address"], msg["data"].get("usd_amount"))
  ```
</CodeGroup>

## Reading a `trade_stream_update`

The payload is a discriminated union narrowed by `trade_type`. For copy trading, the two types you care about are `OrderFilled` and `OrdersMatched`. Both expose `condition_id`, `position_id`, `outcome`, `outcome_index`, `side` (`Buy` / `Sell`), `usd_amount`, `shares_amount`, `price`, and `probability`.

Pending trades omit fields that only exist after a block lands: `block`, `confirmed_at`, `log_index`, `block_index`, `order_hash`, `taker`, `fee`, `fee_shares`, `fee_pct`. They include `received_at` (Unix milliseconds) instead. Use `received_at` for ordering pending events.

See [Trade Types](/sdk/trade-types) for the full discriminated union.

## A copy-mirror loop

Once a watched trader's `OrderFilled` arrives, place a proportional order on the same `condition_id` and `outcome` via your own execution path. A minimal sketch:

```typescript theme={null}
const SIZE_RATIO = 0.05;

ws.on("trade_stream_update", async (event) => {
  if (event.status === "pending" && (event.data.trade_type === "OrderFilled" || event.data.trade_type === "OrdersMatched")) {
    await placeMirrorOrder({
      condition_id: event.data.condition_id,
      position_id: event.data.position_id,
      side: event.data.side,
      usd_amount: event.data.usd_amount * SIZE_RATIO,
      max_price: event.data.side === "Buy" ? event.data.price * 1.02 : event.data.price * 0.98,
    });
  }
});
```

The room itself does not filter by trade size; clamp `usd_amount` client-side if you only want to mirror whale fills.

## Common combinations

| Goal                                            | Subscribe payload                                                                    |
| ----------------------------------------------- | ------------------------------------------------------------------------------------ |
| Mirror one trader from the mempool              | `traders=[0x...]&status=pending`                                                     |
| Confirmed-only mirror for safer fills           | `traders=[0x...]&status=confirmed`                                                   |
| Hybrid (act on pending, reconcile on confirmed) | `traders=[0x...]&status=all`                                                         |
| Watch multiple wallets                          | `traders=[0x...,0x...]&trade_types=[OrderFilled,OrdersMatched]`                      |
| Whale-only mirror (size in client)              | `traders=[0x...]&status=all`, then drop trades below a USD threshold in your handler |

## Reconciling pending against confirmed

With `status: "all"` you receive each trade up to twice: once as pending, once as confirmed. Key by trade `id` (or `hash`) and track which side has arrived. Pending events that never see a confirmed counterpart within a window (typically 30 to 60 seconds on Polygon) should be treated as dropped.

```typescript theme={null}
const seen = new Map<string, { pending: boolean; confirmed: boolean }>();

ws.on("trade_stream_update", (event) => {
  if (event.data.trade_type !== "OrderFilled" && event.data.trade_type !== "OrdersMatched") return;
  const id = event.data.id;
  const entry = seen.get(id) ?? { pending: false, confirmed: false };
  if (event.status === "pending") entry.pending = true;
  if (event.status === "confirmed") entry.confirmed = true;
  seen.set(id, entry);
});
```

## Verifying the trader actually held the position

After mirroring, confirm the position landed on the trader's book by subscribing to [`polymarket_trader_positions`](/websockets/rooms/trader-positions) with the same `traders` filter. The next `trader_position_update` for that wallet will reflect the new `current_shares_balance` once the trade clears.
