> ## 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 a trader's PnL history

> Build equity curves, daily PnL heatmaps, best and worst period summaries, and drawdown views for any Polymarket wallet from the trader PnL history REST endpoints.

The trader namespace exposes a family of read endpoints that return a wallet's profit and loss in chart-ready shapes: an OHLC candlestick series for an equity curve, a daily calendar for a heatmap, best and worst day/week/month extremes, and drawdown/runup risk metrics. Each endpoint is keyed by the trader's `address` and authenticated with `X-API-Key`. REST responses are wrapped, so the payload lives under `.data`.

These are historical snapshots. For a live view that updates as new fills land, see the realtime companion guide [Live trader PnL dashboard](/guides/live-trader-pnl-dashboard).

## When to use this

* Drawing an equity (total PnL) candlestick chart for a single wallet.
* Rendering a GitHub-style daily PnL heatmap of green and red days.
* Showing a trader's best and worst day, week, and month at a glance.
* Plotting maximum drawdown and runup for a risk panel.
* Listing the markets where a wallet made or lost the most across the platform.

## Equity curve from PnL candles

`getTraderPnlCandles` returns an OHLC series of the wallet's PnL over time, suitable for a candlestick or area chart. Pass a `resolution` (`1m`, `1h`, `4h`, `1d`, or `auto`) and a `timeframe` (`1d`, `7d`, `30d`, or `lifetime`, default `lifetime`). `count_back` defaults to 500 and caps at 2500, and `fill_gaps` carries the last close across empty buckets so the line stays continuous.

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

  const client = new StructClient({ apiKey: "sk_live_xxx" });

  const { data: bars } = await client.trader.getTraderPnlCandles({
    address: "0xTRADER",
    resolution: "1d",
    timeframe: "lifetime",
    count_back: 500,
    fill_gaps: true,
  });
  ```

  ```bash cURL theme={null}
  curl "https://api.struct.to/v1/polymarket/trader/pnl/0xTRADER/candles?resolution=1d&timeframe=lifetime&count_back=500&fill_gaps=true" \
    -H "X-API-Key: YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests

  bars = requests.get(
      "https://api.struct.to/v1/polymarket/trader/pnl/0xTRADER/candles",
      headers={"X-API-Key": "YOUR_API_KEY"},
      params={
          "resolution": "1d",
          "timeframe": "lifetime",
          "count_back": 500,
          "fill_gaps": True,
      },
  ).json()["data"]
  ```
</CodeGroup>

Each element is a `PnlCandlestickBar`. The total-PnL OHLC fields drive an equity candlestick, and parallel realized and unrealized series let you split the line.

| Field               | Description                                                     |
| ------------------- | --------------------------------------------------------------- |
| `t`                 | Bar start time, Unix seconds                                    |
| `o` `h` `l` `c`     | Total PnL open, high, low, close (nullable on empty buckets)    |
| `ro` `rh` `rl` `rc` | Realized PnL open, high, low, close                             |
| `uo` `uh` `ul` `uc` | Unrealized PnL open, high, low, close                           |
| `po` `ph` `pl` `pc` | Portfolio value open, high, low, close                          |
| `ob` `cb` `ub` `pb` | Opening basis, closing basis, unrealized basis, portfolio basis |
| `nop`               | Open position count in the bar                                  |

For a simple equity line, plot `c` per bar. For a candlestick, map `o`/`h`/`l`/`c` straight into the chart and treat null buckets (gaps) as carried-forward closes unless you set `fill_gaps`.

## Daily heatmap from the calendar

`getTraderPnlCalendar` returns one entry per day, ideal for a contribution-style heatmap. `days` defaults to 30 and caps at 30.

<CodeGroup>
  ```typescript SDK theme={null}
  const { data: calendar } = await client.trader.getTraderPnlCalendar({
    address: "0xTRADER",
    days: 30,
  });
  ```

  ```bash cURL theme={null}
  curl "https://api.struct.to/v1/polymarket/trader/pnl/0xTRADER/calendar?days=30" \
    -H "X-API-Key: YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests

  calendar = requests.get(
      "https://api.struct.to/v1/polymarket/trader/pnl/0xTRADER/calendar",
      headers={"X-API-Key": "YOUR_API_KEY"},
      params={"days": 30},
  ).json()["data"]
  ```
</CodeGroup>

Each `PnlCandleEntry` is `{ t, pnl }`: `t` is the day in Unix seconds and `pnl` is that day's PnL in USD. Color each cell on the sign and magnitude of `pnl` to get a green and red heatmap.

## Best and worst periods

`getTraderPnlPeriods` returns the wallet's extreme day, week, and month for both total PnL and portfolio value. Pass a `timeframe` (`1d`, `24h`, `7d`, `30d`, or `lifetime`) to bound the window the extremes are computed over.

<CodeGroup>
  ```typescript SDK theme={null}
  const { data: periods } = await client.trader.getTraderPnlPeriods({
    address: "0xTRADER",
    timeframe: "lifetime",
  });
  ```

  ```bash cURL theme={null}
  curl "https://api.struct.to/v1/polymarket/trader/pnl/0xTRADER/periods?timeframe=lifetime" \
    -H "X-API-Key: YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests

  periods = requests.get(
      "https://api.struct.to/v1/polymarket/trader/pnl/0xTRADER/periods",
      headers={"X-API-Key": "YOUR_API_KEY"},
      params={"timeframe": "lifetime"},
  ).json()["data"]
  ```
</CodeGroup>

The `PnlPeriodsResponse` carries six extremes objects: `total_pnl_day`, `total_pnl_week`, `total_pnl_month`, `portfolio_day`, `portfolio_week`, and `portfolio_month`. Each is a `PnlPeriodExtremes` with `best` and `worst`, and each of those (when present) is a metric of `{ from, to, change, change_pct }` (`from` and `to` are Unix seconds bounding the period, `change` is the PnL over it). Render `total_pnl_day.best` and `total_pnl_day.worst` as the best and worst day, and so on for week and month.

## Drawdown and runup

`getTraderPnlRisk` returns risk metrics for both the total-PnL series and the portfolio series. It takes the same `timeframe` values as periods.

<CodeGroup>
  ```typescript SDK theme={null}
  const { data: risk } = await client.trader.getTraderPnlRisk({
    address: "0xTRADER",
    timeframe: "lifetime",
  });
  ```

  ```bash cURL theme={null}
  curl "https://api.struct.to/v1/polymarket/trader/pnl/0xTRADER/risk?timeframe=lifetime" \
    -H "X-API-Key: YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests

  risk = requests.get(
      "https://api.struct.to/v1/polymarket/trader/pnl/0xTRADER/risk",
      headers={"X-API-Key": "YOUR_API_KEY"},
      params={"timeframe": "lifetime"},
  ).json()["data"]
  ```
</CodeGroup>

`PnlRiskResponse` has `total_pnl` and `portfolio`, each a `PnlRiskMetric` (either may be null when there is no history). The metric fields:

| Field                  | Description                             |
| ---------------------- | --------------------------------------- |
| `max_drawdown`         | Largest peak-to-trough decline          |
| `max_drawdown_pct`     | The same as a percentage (nullable)     |
| `current_drawdown`     | Decline from the most recent peak       |
| `current_drawdown_pct` | The same as a percentage (nullable)     |
| `max_runup`            | Largest trough-to-peak gain             |
| `max_runup_pct`        | The same as a percentage (nullable)     |
| `high` `low` `latest`  | Series high, low, and most recent value |

Each drawdown and runup field also has a matching `*_market` object naming the market that drove it, so you can annotate the risk panel.

## Best and worst markets

`getTopTradesMarkets` ranks markets across the platform by PnL rather than scoping to one wallet. It is global, so it takes no `address`. Pass a `timeframe` (`1d`, `7d`, `30d`, or `lifetime`), plus `limit` (default 50, max 200) and `offset` for paging.

<CodeGroup>
  ```typescript SDK theme={null}
  const { data: markets } = await client.trader.getTopTradesMarkets({
    timeframe: "30d",
    limit: 50,
  });
  ```

  ```bash cURL theme={null}
  curl "https://api.struct.to/v1/polymarket/trader/top_trades_markets?timeframe=30d&limit=50" \
    -H "X-API-Key: YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests

  markets = requests.get(
      "https://api.struct.to/v1/polymarket/trader/top_trades_markets",
      headers={"X-API-Key": "YOUR_API_KEY"},
      params={"timeframe": "30d", "limit": 50},
  ).json()["data"]
  ```
</CodeGroup>

Each `MarketEntry` includes `condition_id`, `title`, `question`, `image_url`, `realized_pnl_usd`, `total_pnl_usd`, `total_volume_usd`, `total_fees`, and a `trader` profile, alongside trade counts and volume breakdowns. Sort or split the rows by `total_pnl_usd` to surface the best and worst markets.

## Follow-on

* [Live trader PnL dashboard](/guides/live-trader-pnl-dashboard) for the realtime companion that pushes PnL updates as fills arrive.
* [SDK REST reference](/sdk/rest-api) for the full trader method signatures.
* The API Reference tab for the underlying operation schemas.
