charting_library package, not the open-source Lightweight Charts) renders price by calling a datafeed object you implement. This guide wires Struct into that datafeed: historical bars from the candlestick API in getBars, and live bars from the trades WebSocket room fed into the realtime callback.
| Need | Source |
|---|---|
| Historical OHLCV bars for one outcome | client.markets.getPositionCandlestick |
| Historical OHLCV bars for a whole market | client.markets.getCandlestick |
| Live trades to build the forming bar | polymarket_trades (trade_stream_update) |
| Pre-aggregated live OHLC (server-side) | polymarket_position_metrics (position_metrics_update) |
When to use this
- A candlestick chart of a single market outcome (Yes/No price over time) inside your own app.
- A live trading view that backfills history on mount and extends the last bar on every fill.
- Replacing a polling chart with a pushed one, no reconnect bookkeeping.
getAssetCandlestick, not the market candlestick endpoints below.
The candlestick endpoints
getPositionCandlestick returns OHLCV for a single outcome token; getCandlestick returns it for a market by condition ID. Both return the same bar shape.
{ o, h, l, c, v, t, tc }:
| Field | Description |
|---|---|
o h l c | Open, high, low, close (each 0–1, the outcome’s price) |
v | Volume in the bar |
t | Bar start time, Unix milliseconds |
tc | Trade count in the bar |
| Parameter | Description |
|---|---|
position_id | Outcome token ID (getCandlestick takes condition_id instead) |
resolution | Candle interval, TradingView naming: 1S, 5S, 10S, 30S (seconds), 1, 5, 15, 30, 60, 240 (minutes), D / 1D (daily) |
count_back | Number of candles to return (max 2500) |
from / to | Optional Unix second range to bound the window |
pagination_key | Cursor from a previous response to page further back |
Request bounds (
from / to) are in Unix seconds, but each returned bar’s t is in Unix milliseconds. TradingView’s Bar.time wants milliseconds for intraday resolutions, so t maps straight across with no conversion.Step 1: getBars
TradingView callsgetBars on first load and again as the user pans into older data. periodParams gives the window (from, to in Unix seconds), the bar count (countBack), and whether this is the first request. Map the response into the library’s Bar type and sort ascending; out-of-order bars are rejected.
Page older history with the cursor, not the window. Send from / to (or just count_back) on the first request to anchor the visible range, then follow the pagination_key from each response on every subsequent call. The pagination.has_more flag — not the returned row count — tells you when the series is exhausted, so keep a small state object between calls to carry the cursor and the more-data flag.
Step 2: live bars from the trades room
subscribeBars should only register the chart’s realtime callback. Feed updates from a separate handleRealtimeTrade method driven by the polymarket_trades room, so one socket serves the chart, a trade tape, and anything else.
Each trade_stream_update carries price (0–1), shares_amount, side, and confirmed_at (Unix seconds). Bucket each trade into its bar, opening a new bar from the previous bar’s close so the series stays continuous.
time, so the first trade after load lands on the same timestamp as the last historical bar and updates it in place.
By default the trades room sends confirmed on-chain fills. Pass
status: "all" to also receive mempool trades, which arrive before confirmation and carry received_at (Unix milliseconds) instead of confirmed_at. Use them for a faster visual tick and reconcile against confirmed data.Step 3: assemble the datafeed and mount
onReady advertises supported resolutions. resolveSymbol describes the instrument: prices are 0–1, so pricescale: 10000 gives four decimals. Then connect the socket, subscribe to the trades room, and pipe each event into handleRealtimeTrade.
For a browser chart, authenticate with a
pk_jwt_ public key plus the signed-in user’s JWT, as above. The pk_jwt_ key is safe in a frontend bundle because it is useless without a valid JWT from your configured auth provider. On a server, use your sk_ secret key and drop the jwt. See JWT auth.Common combinations
| Goal | Call |
|---|---|
| Backfill one outcome | getPositionCandlestick({ position_id, resolution: "60", count_back: 500 }) |
| Backfill a whole market | getCandlestick({ condition_id, resolution: "60" }) |
| Live bars from trades | subscribe("polymarket_trades", { position_ids: [id] }) → trade_stream_update |
| Live confirmed + pending | subscribe("polymarket_trades", { position_ids: [id], status: "all" }) |
Follow-on
- Trades room for every trade filter and the full
trade_stream_updatepayload. - Position metrics room for live probability and position-level metrics.
- SDK REST reference for the candlestick method signatures.
- Crypto Up/Down feed to chart the crypto spot price behind Up/Down markets.