Skip to main content
QFEX provides a 500ms pulsed stream of the order book up to 20 levels deep via websocket.

How it works

Connect to the public websocket:
  • URL: wss://mds.qfex.com
Then send a subscribe message:
{
  "type": "subscribe",
  "channels": ["level2"],
  "symbols": ["AAPL-USD", "US500-USD"]
}
You can also use a wildcard to subscribe to all symbols:
{ "type": "subscribe", "channels": ["level2"], "symbols": ["*"] }

Significant Figures (Optional)

You can optionally specify sig_figs to aggregate order book levels:
{
  "type": "subscribe",
  "channels": ["level2"],
  "symbols": ["AAPL-USD"],
  "sig_figs": [1]
}
  • Default: 0 (no aggregation)
  • Valid values: 0, 1, or 2
  • Purpose: Aggregates individual levels to the next significant figure from the tick size
    • Bids round down
    • Offers round up
  • Response format: Unchanged (same structure as without sig_figs)

Sample Code

# Python (websocket-client)
# pip install websocket-client
import json
import websocket

def on_open(ws):
    sub = {
        "type": "subscribe",
        "channels": ["level2"],
        "symbols": ["US500-USD"]  # or ["*"]
    }
    ws.send(json.dumps(sub))

def on_message(ws, message):
    data = json.loads(message)
    print("OrderBook update:", json.dumps(data, indent=2))

def on_error(ws, error):
    print("Error:", error)

def on_close(ws, close_status_code, close_msg):
    print("Closed:", close_status_code, close_msg)

if __name__ == "__main__":
    ws = websocket.WebSocketApp(
        "wss://mds.qfex.com",
        on_open=on_open,
        on_message=on_message,
        on_error=on_error,
        on_close=on_close,
    )
    ws.run_forever()
// Node.js (ws)
// npm i ws
import WebSocket from "ws";

const ws = new WebSocket("wss://mds.qfex.com");

ws.on("open", () => {
  const sub = {
    type: "subscribe",
    channels: ["level2"],
    symbols: ["US500-USD"], // or ["*"]
  };
  ws.send(JSON.stringify(sub));
});

ws.on("message", (msg) => {
  try {
    const data = JSON.parse(msg.toString());
    console.log("OrderBook update:", data);
  } catch (e) {
    console.error("Parse error:", e);
  }
});

ws.on("error", (err) => console.error("WS error:", err));
ws.on("close", (code, reason) =>
  console.log("Closed:", code, reason?.toString())
);
// Go (gorilla/websocket)
// go get github.com/gorilla/websocket
package main

import (
	"fmt"
	"log"
	"os"
	"os/signal"

	"github.com/gorilla/websocket"
)

func main() {
	c, _, err := websocket.DefaultDialer.Dial("wss://mds.qfex.com", nil)
	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c.Close()

	sub := map[string]interface{}{
		"type":     "subscribe",
		"channels": []string{"level2"},
		"symbols":  []string{"US500-USD"}, // or []string{"*"}
	}
	if err := c.WriteJSON(sub); err != nil {
		log.Fatal("write:", err)
	}

	done := make(chan struct{})
	go func() {
		defer close(done)
		for {
			_, message, err := c.ReadMessage()
			if err != nil {
				log.Println("read:", err)
				return
			}
			fmt.Printf("OrderBook update: %s\n", message)
		}
	}()

	// keep running until Ctrl+C
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)
	<-interrupt
}
// Java (OkHttp WebSocket)
// Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0")
import java.util.concurrent.TimeUnit;
import okhttp3.*;

public class OrderBookWs {
  public static void main(String[] args) {
    OkHttpClient client = new OkHttpClient.Builder()
        .pingInterval(20, TimeUnit.SECONDS)
        .build();

    Request request = new Request.Builder()
        .url("wss://mds.qfex.com")
        .build();

    WebSocketListener listener = new WebSocketListener() {
      @Override public void onOpen(WebSocket webSocket, Response response) {
        String sub = "{\"type\":\"subscribe\",\"channels\":[\"level2\"],\"symbols\":[\"US500-USD\"]}";
        webSocket.send(sub);
      }
      @Override public void onMessage(WebSocket webSocket, String text) {
        System.out.println("OrderBook update: " + text);
      }
      @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) {
        System.err.println("WS error: " + t.getMessage());
      }
    };

    client.newWebSocket(request, listener);

    // Keep JVM alive
    try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {}
  }
}

Example Response

{
  "type": "level2",
  "symbol": "AAPL-USD",
  "bid": [
    ["10101.10", "0.45054140"],
    ["10101.00", "0.55054140"]
  ],
  "ask": [
    ["10102.55", "0.57753524"],
    ["10102.65", "0.57753524"]
  ],
  "sequence": 30272539,
  "time": "2025-09-04T09:26:39.545268322Z"
}

Unsubscribe (optional)

{ "type": "unsubscribe", "channels": ["level2"], "symbols": ["US500-USD"] }

Notes
  • Pulsed at 50ms intervals.
  • Depth: up to 20 levels.
  • symbols accepts a wildcard * to stream all symbols.