Un flux forex en direct par WebSocket
L'achat et la vente en direct, et les bougies à leur clôture, pour 46 paires de devises et 5 métaux, sur un seul WebSocket ouvert depuis votre serveur ou directement depuis un navigateur.
- Se connecter
- Votre serveur échange la clé d'API contre un jeton valable 12 heures. Avec le jeton, POST /v1/ws-ticket donne un ticket à usage unique, proposé comme second sous-protocole à côté de adona.data.v1, jamais dans l'URL. Une trame subscribe nomme ensuite les canaux.
- Les canaux
- quote:SYMBOLE pour l'achat et la vente en direct, candle:SYMBOLE:UNITÉ pour les bougies à leur clôture, sur S1, M1, M5, M15, H1, H4, D1. Jusqu'à 64 canaux par trame subscribe. À l'abonnement, chaque canal de bougies envoie d'abord ses bougies récentes, facturées en tranches comme une page REST : chaque reconnexion les paie à nouveau.
- Les connexions
- Un palier payant ne refuse jamais une connexion : le pic mensuel de connexions simultanées fixe le palier de la facture suivante. L'essai permet 3 connexions simultanées. Une fermeture avec le code 1008 et une raison, comme une clé révoquée ou un essai terminé, est une réponse : ne vous reconnectez pas en boucle dessus. Les autres fermetures sont passagères : reconnectez-vous après une pause.
- Ce que cela coûte
- Vendu à la capacité, dès CHF 500 par mois. La redistribution est permise sur chaque palier : un symbole que vos connexions suivent en direct plus de 80 % de ses heures d'ouverture dans le mois ajoute CHF 150 à la facture, en plus du palier.
Faits à la date du 03.10.2026.
Le code : le flux en direct, en JavaScript
// Node 22+: live quotes and closed bars on one WebSocket. ADONA_API_KEY=... node stream.mjs
// In a browser the WebSocket part is the same; the token comes from your server, never the key.
const API = "https://api.adona-robot.com";
const STREAM = "wss://api.adona-robot.com/v1/stream";
async function post(path, bearer, body) {
const response = await fetch(`${API}${path}`, {
method: "POST",
headers: { Authorization: `Bearer ${bearer}`, "Content-Type": "application/json" },
body: body === undefined ? undefined : JSON.stringify(body),
});
if (!response.ok) throw new Error(`refused: ${response.status} ${await response.text()}`);
return response.json();
}
// your server exchanges the key for a token
const { access_token: token } = await post("/v1/tokens", process.env.ADONA_API_KEY, { end_user_id: "u-42" });
// a single-use ticket, offered as the second subprotocol, never in the URL
const { ticket } = await post("/v1/ws-ticket", token);
const ws = new WebSocket(STREAM, ["adona.data.v1", `ticket.${ticket}`]);
ws.onopen = () =>
ws.send(JSON.stringify({ type: "subscribe", request_id: "sub", payload: { channels: ["quote:EURUSD", "candle:EURUSD:M1"] } }));
ws.onmessage = (event) => {
const { type, payload } = JSON.parse(event.data);
if (type === "quote.update" || type === "quote.snapshot") console.log(type, payload.symbol, payload.bid, payload.ask);
else if (type === "candle.snapshot") console.log(type, payload.channel, payload.candles.length, "bars");
else if (type === "candle.bar") console.log(type, payload.symbol, payload.tf, payload.ts, payload.c); // short keys
else console.log(type, payload);
};
// 1008 with a reason (revoked, trial ended) is an answer: do not reconnect in a loop on it
ws.onclose = (event) => console.log("closed", event.code, event.reason);