Skip to content

Price history (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.

["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.

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

{"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.

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.

  • 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.
["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.

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

Replies with series for exactly those markets at that range:

{"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 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",{}]
v1.5.9Changelogllms.txtllms-full.txt