Skip to content

Wire format

The account channels and the three market-data feeds write money and contract counts as strings, in the same format as the REST API. One parser and one set of fixtures cover both surfaces.

The markets and market_updates metadata feeds are the exception: their prices are JSON numbers in cents, not dollar strings. Their own pages give the units field by field.

Money is a string of dollars, with at least four decimal places:

{ "price": "0.6700", "amount": "39.0000", "max_price": "1.0000" }

Quantities and contract counts are strings, with at least two decimal places:

{ "quantity": "100.00", "filled": "50.00" }

A null stays null; it never becomes "0.0000".

Decimal places are a minimum, not a fixed width

Section titled “Decimal places are a minimum, not a fixed width”

Almost every money field is exactly four decimal places. Order price is the exception on the channels: it is stored to seven, so "0.0125432" is a valid price.

Parse money with a decimal type that accepts a variable scale. A parser hard-coded to four places breaks on that field and nowhere else. Over REST, GET /api/v1/fills carries one more such field, unrounded_trade_fee, at up to nine places.

Only amounts of money and numbers of contracts become strings. These stay JSON numbers:

  • percentages: filled_percentage, price_change24h
  • counts of objects: open_order_count, trade_count, settlements_count
  • fee factors: base_fee_percent, taker_factor, maker_factor
  • loyalty points

Timestamps come in two shapes and neither is a money string: time, settled_at and timestamp are ISO 8601, while inserted_at, accepted_at, delayed_until, expiration_time, expires_at and timestamp_us are integers of Unix microseconds.

On both fills and GET /api/v1/fills, total_fee is the trade fee plus the settlement fee: what the trade has actually cost you. trade_fee is the on-trade component alone, and is also sent, so total_fee - trade_fee is the settlement part. The two surfaces agree, so a REST snapshot and a fills delta can be mixed freely.

orders, fills, positions, settlements and account take an optional market_ids filter in the join payload, so a client watching a few markets is not sent every order, fill, position and settlement on the account.

["1","1","orders:<user_id>","phx_join",{"market_ids":["<uuid>","<uuid>"]}]

The reply echoes what was actually applied:

{"status":"ok","response":{"selected_market_ids":["<uuid>","<uuid>"]}}

It applies to both the snapshot you get on join and every push afterwards.

Omit market_ids, or send null or [], and nothing is filtered; selected_market_ids comes back null. Ids you send that are not valid UUIDs are dropped rather than rejected, and if none of them are valid you get no filter at all, not an empty one. That is why the applied set is echoed: compare it against what you sent to catch a typo, rather than silently receiving everything.

Ids are case-insensitive.

Snapshots are filtered differently from deltas

Section titled “Snapshots are filtered differently from deltas”

A snapshot the filter empties is still sent: {"orders": []} on join tells you there is nothing in those markets, which is worth knowing.

A delta the filter empties is not sent at all, rather than arriving as an empty list, because {"positions": []} would read as “your positions are gone”. updated_positions and new_settlements are the deltas.

["1","2","orders:<user_id>","select_market_ids",{"market_ids":["<uuid>"]}]

The reply carries the new selected_market_ids. Send null to clear the filter.

balances does not take this parameter; it is scoped to one account, not to markets. It takes an optional account_id instead.

The market-data feeds narrow differently again: orderbook requires market_ids and changes it with select_market_ids, while ticker and trades filter on other fields and use select_filters. See Order book, ticker and trades.

v1.5.9Changelogllms.txtllms-full.txt