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.
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.
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 nativeWebSocketAPI 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:
- 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.
- 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.