# QFEX — public market data

This file covers the public market-data socket, quote and band decoding, REST contract summaries, and order-book fallback. Endpoint shapes were observed on 2026-09-30; consumers must validate current data rather than assume a remembered listing.

## TL;DR

1. MDS is public and its subscribe frame is flat, unlike the trade socket's `params` envelope.
2. BBO price and size are decimal strings; validate both sides before deriving a midpoint.
3. The live band is `minmax_price`; invalidate it on disconnection.
4. REST books may contain many zero-size padded rows.
5. `last_price` has no freshness proof and is weaker evidence than a timestamped healthy quote.

## 1. Stream contract

Connect to `wss://mds.qfex.com` and send `{type: 'subscribe', channels: ['bbo'], symbols: ['EXAMPLE-USD']}`. The synthetic symbol illustrates the shape and is not a listing claim. Acknowledgement contains `type: 'subscribed'`, `channels`, and `symbols`.

Channels documented or returned by the server include `level2`, `trade`, `underlier`, `candle`, `funding`, `mark_price`, `open_interest`, `minmax_price`, `bbo`, and `market_stats`. The toolkit's main data workflow consumes BBO and live bands; listing a channel here does not imply a complete implementation for it.

Observed BBO frames contained `sequence`, `type`, ISO `time`, `symbol`, and `bid`/`ask` arrays of `[price, size]` strings. The pulsed interval was about 500 ms per subscribed symbol. Subscription breadth and per-frame symbol count are caller policies; select them explicitly.

The MDS error form is flat, with `error_code: 'InvalidJSON'` observed for an unsupported channel. The trade socket uses different codes and envelopes. Decode by surface.

## 2. Live band freshness

`minmax_price` frames contain `symbol`, `min_price`, `max_price`, and `time`. The stream decoder requires finite nonnegative boundaries and a positive-width interval. Keep the raw server time and receipt time separately when the consuming application needs both.

The stream's resend cadence is unverified: a band may be emitted on change rather than on a periodic timer. A band-selection helper therefore needs explicit freshness policy and connection health. Core `createMarketDataStream` ages a band from its own frame. Experimental `createStreamAgeMarketData` extends that age from other symbol frames; those frames cannot prove that the old band is unchanged. Disconnect clears stream-backed confidence. A reference-data fallback must identify its age; it is not equivalent to a live band.

IoC band usage is described in [markets-and-numbers.md](markets-and-numbers.md) §4. Band validity is separate from whether executable liquidity exists at the boundary.

## 3. REST contracts and books

`GET /md/contracts` returns summary rows including ticker identity, base/target currencies, last price, volume, open interest, index price, funding rate, next rate, and next-funding timestamp. Most numeric values are decimal strings. It supplies no bid/ask pair, and a last trade can be old on an inactive book.

`GET /md/orderbook/{ticker_id}` returns millisecond timestamp, bids, and asks as string pairs. Public responses observed 2026-09-30 included padded zero-size levels. Remove zero-size rows, reject malformed remaining rows, sort bids descending and asks ascending, and reject crossed or one-sided books as a midpoint source.

The existing symbol-price path uses a fresh BBO midpoint, then a validated REST-book midpoint, then a contract last price with explicitly weaker freshness. The package exposes the quote and book decoders separately; the caller supplies freshness and fallback policy.

## 4. Funding data

Funding documentation describes hourly calculation and settlement while the product's funding session is open; funding can be zero outside those hours. The live funding channel conveys the implied rate for its current window. Fees, funding cash flows, and trading PnL are distinct quantities; account mechanics are in [account-and-leverage.md](account-and-leverage.md) §4.

## Pitfalls

| What breaks | Why | Correct approach |
|---|---|---|
| Subscription is rejected | Trade and MDS envelopes differ | Use flat MDS fields |
| Midpoint is zero or crossed | Padding or malformed sides | Filter size zero, validate both sides |
| A band survives a dead stream | Receipt time alone was retained | Include connection health |
| Last trade is treated as current liquidity | Summary data has no bid/ask or age proof | Prefer fresh quotes |

## Open questions / not verified

- Band push cadence and server sequence-reset semantics across reconnects.
- Public REST and handshake throttling ceilings.
- Browser use outside the venue origin: observed CORS responses allowed the venue website, so a separate origin must verify its own access.

## Sources

[Pulsed BBO](https://docs.qfex.com/websocket/channels/mds/bbo); [Minmax price](https://docs.qfex.com/websocket/channels/mds/minmax_price); [Contracts](https://docs.qfex.com/api-reference/rest/market-data/contracts); [Order book](https://docs.qfex.com/api-reference/rest/market-data/order-book); [Funding](https://docs.qfex.com/qfex/funding). Response-shape observations: 2026-09-30. Freshness and fallback rules are implementation safeguards.

<!-- license-footer -->
_© markpaper authors. Licensed under [CC BY 4.0](LICENSE.md): when publishing or adapting this material, credit “markpaper — QFEX knowledge base” and link to the original and the license._
