# Price history (market_stats)


Source: https://docs.stxapp.io/websockets/channels/market-stats/

Topic: `market_stats`

Price over time for the markets you name, as a series of buckets. This is the
feed behind a price chart: the whole history arrives in the join reply, then only
changed buckets are pushed.

One topic covers every market, narrowed by the join payload. Ten markets is one
join, not ten.

:::note
Unrelated to `GET /api/v1/account/market_stats`, which returns your own position
and P&L statistics. This channel carries prices, not your account.
:::

## Joining

```
["1","1","market_stats","phx_join",{"market_ids":["<uuid>","<uuid>"]}]
["1","1","market_stats","phx_join",{"market_ids":["<uuid>"],"range":"week"}]
```

**At least one valid `market_id` is required.** Unlike the per-account filters on
this socket, an absent or unusable list is an error rather than "no filter": the
full history of every market on the exchange is not something this serves.

```json
{"status":"error","response":{"reason":"market_ids_required"}}
```

`range` sets how far back the join snapshot reaches: `"day"`, `"week"`, `"month"`
or `"all"` (the default). An unknown value falls back to `"all"`. The reply echoes
the filter and range, and carries the series:

```json
{"status":"ok","response":{
  "selected_market_ids":["<uuid>"],
  "range":"all",
  "markets":[{"market_id":"<uuid>","points":[
    {"timestamp_us":1789671360000000,"price_percent":43.5}]}]}}
```

A market id naming no market is echoed in `selected_market_ids` but contributes
no entry to `markets`.

## Points

`price_percent` is the bucket's closing price as a **percent of that market's
`max_price`**, so a plain JSON number from `0.0` to `100.0` rather than money.
It is not `probability` from the market payload, which is a modeled value from
the pricing feed.

`timestamp_us` is the bucket's start, in Unix microseconds. Points ascend by it,
and a bucket with no price is omitted rather than sent as zero.

No volume is carried: this is a price series. Per-market traded volume is
`total_volume` on [`ticker`](/websockets/channels/market-data/).

## Server pushes

- `market_stats` carries `{"market_id":"...","points":[point]}`, the buckets that
  changed. **A delta, not a snapshot.** Buckets are 60 seconds wide but flush
  every 2 seconds or so, so the same `timestamp_us` is re-sent as the current
  minute fills: **upsert by `timestamp_us` rather than appending.**
- `market_stats_snapshot` has the same shape and replaces that market's series
  wholesale. Sent after STX cancels a trade, which rewrites buckets already
  delivered and cannot be reconciled from a delta. Sent even when the series is
  now empty.

## Changing which markets stream

```
["1","2","market_stats","select_market_ids",{"market_ids":["<uuid>"]}]
```

The same non-empty requirement applies; an unusable list leaves the current
selection in place and replies with an error. This changes the subscription only
and sends no series, so adding a market cannot overwrite a window you set with
`request_series`. Fetch the new market's history with that instead.

## Fetching history at another range

```
["1","3","market_stats","request_series",{"market_ids":["<uuid>"],"range":"day"}]
```

Replies with series for exactly those markets at that range:

```json
{"status":"ok","response":{"range":"day","markets":[{"market_id":"<uuid>","points":[]}]}}
```

The subscription is untouched. Range belongs to one history request rather than
to the socket, so a client drawing three markets over three windows is one join
plus three of these. The ids need not be subscribed, since price history is the
same for every participant.

Live deltas are range-independent: every bucket update for a subscribed market is
pushed whatever range was last requested.

## Use Cases

| Use case | Message to send |
| --- | --- |
| Join for two markets | `["3","3","market_stats","phx_join",{"market_ids":["<uuid>","<uuid>"]}]` |
| Join with a week of history | `["3","3","market_stats","phx_join",{"market_ids":["<uuid>"],"range":"week"}]` |
| Change which markets stream | `["3","4","market_stats","select_market_ids",{"market_ids":["<uuid>"]}]` |
| Fetch one day for a chart | `["3","5","market_stats","request_series",{"market_ids":["<uuid>"],"range":"day"}]` |
| Keep the channel alive | `["3","6","market_stats","ping",{}]` |
