TICKERALL!
← All posts
August 23, 20264 min read

Streaming MetaTrader 5 over a Raw WebSocket (Any Language)

Connect to the TickerAll MetaTrader 5 WebSocket from any language — auth, subscribe frames, and tick and position events, with Node and browser examples.

MetaTrader 5WebSocketJavaScriptMarket Data

There's a Python SDK for the TickerAll MetaTrader 5 API, and if you're writing Python you should use it to stream ticks — it handles reconnects and event parsing for you. But the SDK is a convenience, not a requirement. Underneath it is a plain WebSocket that speaks JSON text frames, and you can talk to it directly from Node, the browser, Go, Rust, or anything with a WebSocket client and a JSON parser.

This post documents that raw protocol so you can stream live MT5 data in whatever language your stack is written in — no SDK needed.

Your client subscribe tick / position events /v1/stream wss://api.tickerall.com
One WebSocket carries every channel: you send subscribe frames up, the server pushes tick and position events down.

Connect and authenticate

The endpoint is:

wss://api.tickerall.com/v1/stream

Authentication uses the same API key as the REST API. You have two ways to present it:

  • Authorization: Bearer <API_KEY> header on the upgrade request. This is the cleanest option and works from any server-side client (Node, Go, Python, curl-style tooling).
  • ?token=<API_KEY> query param on the URL. Use this from the browser, where the native WebSocket API doesn't let you set custom headers.

You also need an accountId before you can subscribe to anything — it identifies which connected broker account the stream should follow. You get one by starting a session through the REST API (POST /v1/sessions with your broker, server, account, and password), which returns the same accountId the REST endpoints use. See the REST API guide for the session call; from here on we assume you have an accountId in hand.

Subscribe

Once the socket is open, send a JSON text frame telling the server what you want. One connection can carry multiple channels — you list them in the channels array:

{"type":"subscribe","channels":[{"kind":"ticks","accountId":"<ACCOUNT_ID>","symbols":["BTCUSDm","ETHUSDm"]}]}

There are four channel kinds:

kind fields delivers
ticks accountId, symbols live bid/ask per symbol
positions accountId open/modify/close position events
account accountId account balance/equity updates
orders accountId full pending-order snapshots

ticks needs a symbols array; the other three take just accountId. To stop a subscription, send the same shape with type set to unsubscribe:

{"type":"unsubscribe","channels":[{"kind":"ticks","accountId":"<ACCOUNT_ID>","symbols":["BTCUSDm"]}]}

Handle events

Everything the server pushes is a JSON text frame with a type field you switch on:

{"type":"tick","symbol":"BTCUSDm","bid":68000.1,"ask":68000.6,"timestamp":"..."}
{"type":"position","event":"open","position":{"ticket":123,"symbol":"BTCUSDm","side":"BUY","volume":0.1,"profit":12.3}}

For position events, event is one of open, modify, or close. The account event carries account fields inline:

{"type":"account", ...account fields...}

And orders sends a full snapshot of your pending orders each time — it's not a delta, so replace your local list wholesale rather than merging:

{"type":"orders","orders":[ ...full pending-order snapshot... ]}

Node.js example

Using the ws package, connect with the Bearer header, subscribe on open, and dispatch on type:

import WebSocket from "ws";

const API_KEY = process.env.TICKERALL_API_KEY;
const ACCOUNT_ID = process.env.TICKERALL_ACCOUNT_ID;

const ws = new WebSocket("wss://api.tickerall.com/v1/stream", {
  headers: { Authorization: `Bearer ${API_KEY}` },
});

ws.on("open", () => {
  ws.send(JSON.stringify({
    type: "subscribe",
    channels: [
      { kind: "ticks", accountId: ACCOUNT_ID, symbols: ["BTCUSDm", "ETHUSDm"] },
      { kind: "positions", accountId: ACCOUNT_ID },
    ],
  }));
});

ws.on("message", (raw) => {
  const msg = JSON.parse(raw);
  switch (msg.type) {
    case "tick":
      console.log(`${msg.symbol}  bid ${msg.bid}  ask ${msg.ask}`);
      break;
    case "position":
      console.log(`position ${msg.event}:`, msg.position);
      break;
    case "account":
      console.log("account update:", msg);
      break;
    case "orders":
      console.log(`${msg.orders.length} pending orders`);
      break;
  }
});

Browser example

In the browser you can't set request headers on a WebSocket, so pass the key as a ?token= query param and use the native WebSocket:

<script>
  const API_KEY = "cf_api_your_key_here";     // see the caveat below
  const ACCOUNT_ID = "your-account-id";

  const ws = new WebSocket(
    `wss://api.tickerall.com/v1/stream?token=${encodeURIComponent(API_KEY)}`
  );

  ws.addEventListener("open", () => {
    ws.send(JSON.stringify({
      type: "subscribe",
      channels: [{ kind: "ticks", accountId: ACCOUNT_ID, symbols: ["BTCUSDm"] }],
    }));
  });

  ws.addEventListener("message", (ev) => {
    const msg = JSON.parse(ev.data);
    if (msg.type === "tick") {
      console.log(`${msg.symbol}  ${msg.bid} / ${msg.ask}`);
    }
  });
</script>

Caveat: a token in a URL is visible to the browser (history, dev tools, any proxy in front of the page). The ?token= form is great for your own dashboards and internal tools, but don't ship your key inside a page you hand to third parties — for that, keep the key server-side and proxy the stream, or issue a scoped key you're comfortable exposing.

Reconnects

Two behaviors to build around:

  1. The server does not remember your subscriptions across a reconnect. When the socket drops and you reopen it, you must re-send your subscribe frames — nothing resumes automatically.
  2. Reconnect with exponential backoff. Don't hammer the endpoint in a tight loop after a drop; back off and cap the delay.

Here's the Node example wrapped in a reconnect loop that re-subscribes every time:

import WebSocket from "ws";

const URL = "wss://api.tickerall.com/v1/stream";
const API_KEY = process.env.TICKERALL_API_KEY;
const ACCOUNT_ID = process.env.TICKERALL_ACCOUNT_ID;

const SUBSCRIBE = {
  type: "subscribe",
  channels: [{ kind: "ticks", accountId: ACCOUNT_ID, symbols: ["BTCUSDm", "ETHUSDm"] }],
};

let backoff = 1000; // start at 1s, cap at 30s

function connect() {
  const ws = new WebSocket(URL, { headers: { Authorization: `Bearer ${API_KEY}` } });

  ws.on("open", () => {
    backoff = 1000;                    // reset once we're up
    ws.send(JSON.stringify(SUBSCRIBE)); // ALWAYS re-subscribe on (re)connect
  });

  ws.on("message", (raw) => {
    const msg = JSON.parse(raw);
    if (msg.type === "tick") console.log(msg.symbol, msg.bid, msg.ask);
  });

  ws.on("close", () => {
    setTimeout(connect, backoff);
    backoff = Math.min(backoff * 2, 30_000);
  });

  ws.on("error", () => ws.close());
}

connect();

One more thing worth internalizing: ticks are irregular and bursty. A symbol can print several times in a second and then go quiet for several seconds when the market is calm. A quiet second is not a broken feed — don't treat gaps as a failure signal or you'll reconnect for no reason.

Wrapping up

That's the whole protocol: one long-lived WebSocket, an API key via header or ?token=, subscribe frames listing ticks / positions / account / orders channels, and JSON events discriminated by type. Re-subscribe on every reconnect, back off on failures, and you have a live MT5 feed in any language.

If you're in Python, the SDK does all of this for you. For everything else — and for the session setup that gives you an accountId — see the full REST API guide and the docs at tickerall.com/docs.

Was this useful?

Comments

Leave a comment

Comments are public.
Streaming MetaTrader 5 over a Raw WebSocket (Any Language) · Ticker All!