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

# Charting PnL with exit markers

> Plot a trader's realized PnL over time and overlay every position close as an annotation, colored and shaped by exit reason, using the trader exit markers room.

<Info>
  **What you'll build:** a realized-PnL chart with a marker pinned on every closed position, labelled by how the trade ended (won or lost, held to resolution or sold early). Useful for a trader performance chart or a trade journal of wins and losses.
</Info>

`polymarket_trader_pnl_exits` emits one marker per position close, designed to overlay exits on a PnL chart. Each marker says how a position ended (`pnl_usd`, `ts`, and a `reason`), which is exactly what an annotation layer needs. This recipe builds a realized-PnL line and pins a marker at every exit, styled by whether the trader won or lost and whether they held to resolution or sold early.

## When to use this

* A trader performance chart where each closed bet is a labelled point.
* A trade journal that lists wins and losses with the dollar result of each exit.
* An overlay on top of a running PnL line that shows when realized PnL stepped.

## The exit reasons

Every marker carries one of four reasons. They split on two axes: held to resolution versus sold early, and won versus lost.

| Reason          | Meaning                                               | Suggested style         |
| --------------- | ----------------------------------------------------- | ----------------------- |
| `resolved_win`  | Held to market resolution and the verdict was a win.  | Green, filled triangle. |
| `resolved_loss` | Held to market resolution and the verdict was a loss. | Red, filled triangle.   |
| `sold_win`      | Closed before resolution with positive realized PnL.  | Green, hollow circle.   |
| `sold_loss`     | Closed before resolution with negative realized PnL.  | Red, hollow circle.     |

Using shape for the close type (triangle for resolution, circle for a sale) and color for the outcome lets a reader decode an exit at a glance.

## Step 1: subscribe with a reasons filter

`traders` is required. The optional `reasons` filter trims the stream to the exits you chart; passing it cuts message volume since the room bills per message. To plot only the losses, for example, pass the two loss reasons.

<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_trader_pnl_exits", {
    traders: ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"],
    reasons: ["resolved_win", "resolved_loss", "sold_win", "sold_loss"],
  });
  ```

  ```json Subscribe message theme={null}
  {
    "type": "room_message",
    "payload": {
      "room_id": "polymarket_trader_pnl_exits",
      "message": {
        "action": "subscribe",
        "traders": ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"],
        "reasons": ["resolved_win", "resolved_loss", "sold_win", "sold_loss"]
      }
    }
  }
  ```
</CodeGroup>

Omit `reasons` (or pass `["all"]`) to receive every exit.

## Step 2: map markers to annotations

Each `trader_exit_marker_batch` envelope carries an array of marker rows. A marker has `pnl_usd` (realized at exit), `ts` (Unix seconds), `reason`, and the descriptive fields (`title`, `outcome`, `market_slug`) for a tooltip. Map each one to your chart's annotation shape.

```typescript theme={null}
type Annotation = {
  x: number;
  y: number;
  color: "green" | "red";
  shape: "triangle" | "circle";
  label: string;
};

const STYLE: Record<string, Pick<Annotation, "color" | "shape">> = {
  resolved_win: { color: "green", shape: "triangle" },
  resolved_loss: { color: "red", shape: "triangle" },
  sold_win: { color: "green", shape: "circle" },
  sold_loss: { color: "red", shape: "circle" },
};

const annotations: Annotation[] = [];

ws.on("trader_exit_marker_batch", (event) => {
  for (const marker of event.data) {
    const style = STYLE[marker.reason];
    annotations.push({
      x: marker.ts * 1000,
      y: marker.pnl_usd,
      ...style,
      label: `${marker.title}: ${marker.pnl_usd >= 0 ? "+" : ""}${marker.pnl_usd.toFixed(2)}`,
    });
  }
  redrawAnnotations(annotations);
});
```

The marker `ts` is in seconds; most charting libraries expect milliseconds, so multiply by 1000 when placing the point on a time axis.

## Step 3: draw the underlying PnL line

Markers are the overlay; the line beneath them is the trader's realized PnL over time. Drive it from the trader PnL tick stream so the curve moves continuously and each marker lands on the line at the moment of the exit. Subscribe to `polymarket_trader_pnl` for the same wallet and append the global realized figure as it ticks.

```typescript theme={null}
const series: { x: number; y: number }[] = [];

await ws.subscribe("polymarket_trader_pnl", {
  traders: ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"],
  update_types: ["global"],
});

ws.on("trader_global_tick_batch", (event) => {
  for (const row of event.data) {
    series.push({ x: Date.now(), y: row.realized_pnl_usd });
  }
  redrawLine(series);
});
```

Realized PnL steps at each exit, so a marker drawn at `(ts, pnl_usd)` sits on the riser of the step it caused. See [Building a live trader PnL dashboard](/guides/live-trader-pnl-dashboard) for the full PnL stream model, including the difference between full-row, tick, and resolution families.

## Backfilling the chart

The room streams exits going forward. To paint historical markers when the chart first loads, seed from your own records or a REST PnL pull, then attach the live stream for new exits on top, the same seed-then-stream pattern used across these guides.

## Follow-on

* For the realized and unrealized numbers behind the line, see [Building a live trader PnL dashboard](/guides/live-trader-pnl-dashboard).
* To track the positions that produce these exits, see [Tracking positions in real time](/guides/real-time-positions-tracking).
* Full payload schemas live on the [Trader exit markers room](/websockets/rooms/trader-exit-markers) page.
