Skip to main content

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.

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.

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.
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);
});

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 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:
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

GoalSubscribe payload
Mirror one trader from the mempooltraders=[0x...]&status=pending
Confirmed-only mirror for safer fillstraders=[0x...]&status=confirmed
Hybrid (act on pending, reconcile on confirmed)traders=[0x...]&status=all
Watch multiple walletstraders=[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.
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 with the same traders filter. The next trader_position_update for that wallet will reflect the new current_shares_balance once the trade clears.
Last modified on April 28, 2026