WebSocket API
One socket for live market data. The private socket adds your balances, positions, orders and order submission.
Connect
Section titled “Connect”wss://sockets.cloud9trader.com/The public socket needs no key and spends no credits. Connect from a script or from a web page.
import WebSocket from "ws"
const socket = new WebSocket("wss://sockets.cloud9trader.com/")
socket.on("open", () => { socket.send(JSON.stringify(["subscribe", "price.EUR_USD:OANDA"]))})
socket.on("message", (raw) => { const [topic, ...args] = JSON.parse(raw.toString()) console.log(topic, ...args)})import asyncio, json, websockets
async def main(): async with websockets.connect("wss://sockets.cloud9trader.com/") as socket: await socket.send(json.dumps(["subscribe", "price.EUR_USD:OANDA"])) async for raw in socket: topic, *args = json.loads(raw) print(topic, *args)
asyncio.run(main())In JavaScript, the client library does this for you, and reconnects.
Messages
Section titled “Messages”Every message, both ways, is a JSON array. The first element says what the message is; the rest are its arguments.
What you send
Section titled “What you send”| Message | What it does |
|---|---|
["ping"] |
Checks the connection. The server answers ["pong"]. |
["request", topic, requestId?] |
Asks for the current value of a topic, once. |
["subscribe", topic] |
Starts streaming a topic. The first message is a snapshot. |
["unsubscribe", topic] |
Stops streaming a topic. |
["submit", type, payload, requestId?] |
Sends an order or closes a position. Private socket only: see Submit an order. |
What you receive
Section titled “What you receive”| Message | When |
|---|---|
[topic, data] |
A snapshot or update for a topic you subscribed to, or the answer to a request sent without a requestId. |
[requestId, error, data] |
The answer to a request or submit sent with a requestId. error is null when it worked. |
["error", message] |
Something you sent could not be done. See Error messages. |
["pong"] |
The answer to ["ping"]. |
Use a requestId (any string of your own) when you need to match an answer to its request. Without one, answers arrive on the topic, and failures arrive as ["error", message].
Public topics
Section titled “Public topics”An instrument id is the symbol, a colon and the broker, as in EUR_USD:OANDA or BTCUSD:Bitfinex. List instruments gives every id.
| Topic | Request | Subscribe | Data |
|---|---|---|---|
price.<instrumentId> |
Yes | Yes | Bid and ask |
ticker.<instrumentId> |
No | Yes | 24-hour summary |
order-book.<instrumentId> |
Yes | Yes | Order book snapshot, then changes |
trades.<instrumentId> |
Yes | Yes | Recent trades, then new ones |
currency.<symbol> |
Yes | Yes | A currency’s US dollar rate |
Live streams come from the brokers that publish them: OANDA for forex and CFDs, and Bitfinex for crypto. FXCM gives price history only. See Brokers.
["price.EUR_USD:OANDA", { "time": "2024-04-02T14:00:00.123Z", "instrumentId": "EUR_USD:OANDA", "bid": 1.07412, "ask": 1.07425 }]| Field | Type | Meaning |
|---|---|---|
time |
string | Time of the price, ISO 8601 |
instrumentId |
string | The instrument |
bid |
number | Best bid |
ask |
number | Best ask |
ticker
Section titled “ticker”Subscribe only. Bitfinex.
| Field | Type | Meaning |
|---|---|---|
time |
string | Time of the update, ISO 8601 |
symbol |
string | The exchange’s symbol |
priceChange |
number | Change over 24 hours |
priceChangePercent |
number | Change over 24 hours, as a fraction of the price 24 hours ago |
bid, ask |
number | Best bid and ask |
bidQuantity, askQuantity |
number | Size at the best bid and ask |
high, low |
number | 24-hour high and low |
volume |
number | 24-hour volume, in the base currency |
order-book
Section titled “order-book”A snapshot has the top 50 levels on each side, best first:
["order-book.BTCUSD:Bitfinex", { "sequence": 1234567, "bids": [[66010, 0.5], [66009, 1.2]], "asks": [[66011, 0.3], [66012, 2.0]] }]Each update carries the next sequence and a list of changes, at most four a second:
["order-book.BTCUSD:Bitfinex", { "sequence": 1234568, "deltas": [ { "action": "update", "side": "bids", "data": [66010, 0.7] }, { "action": "insert", "side": "asks", "data": [66010.5, 0.1] }, { "action": "delete", "side": "asks", "data": 66012 }] }]| Action | Apply it by |
|---|---|
insert |
Adding the [price, quantity] level to side in price order: bids high to low, asks low to high. |
update |
Replacing the quantity of the level at that price. |
delete |
Removing the level at the price in data. |
refresh |
Replacing the whole book with data, which has bids and asks. |
To keep a correct book:
- Subscribe and request at the same time.
- Ignore updates until the snapshot arrives.
- Apply only an update whose
sequenceis one more than the last you applied. - On a gap, request a new snapshot and start again from step 2.
trades
Section titled “trades”A snapshot of the last 100 trades, then new trades as they happen, at most four messages a second. Each message is an array of trades, oldest first.
| Field | Type | Meaning |
|---|---|---|
id |
number | The exchange’s trade id |
time |
string | Time of the trade, ISO 8601 |
price |
number | Price traded at |
quantity |
number | Amount traded |
side |
string | BUY or SELL: the side of the taker |
currency
Section titled “currency”["currency.BTC", { "price": 66159.73, "percentChange24h": 1.59, "volume24h": 31200000000, "updated": "2024-04-02T13:59:59.874Z" }]| Field | Type | Meaning |
|---|---|---|
price |
number | Price in US dollars |
percentChange24h |
number | Change over 24 hours, in percent |
volume24h |
number | 24-hour volume, in US dollars |
updated |
string | When the rate was last updated, ISO 8601 |
Stay connected
Section titled “Stay connected”- The server does not ping you. Send
["ping"]every 30 seconds or so. If no["pong"]comes back within a few seconds, close the socket and reconnect. - Expect disconnects. Sockets are closed when Cloud9Trader deploys, and networks drop. Reconnect with a growing delay, for example 1, 2, 4, 8, then 15 seconds, and reset the delay once connected.
- Subscriptions do not survive a reconnect. After reconnecting, subscribe again to every topic, and request fresh snapshots. Discard any order book you were keeping.
- Subscribe once per topic. A second subscribe to the same topic gets
["error", "Topic is already subscribed"].
Error messages
Section titled “Error messages”These arrive as ["error", message], or as the error in [requestId, error, data]. <topic> stands for the topic you sent.
| Message | Meaning |
|---|---|
Message contained malformed JSON |
The message was not valid JSON. |
Message must contain arguments as array |
The message was JSON, but not an array. |
Instruction '<instruction>' is not supported |
The first element was not ping, request, subscribe, unsubscribe or submit. |
Topic is required |
The message had no topic. |
Topic '<topic>' is not available. Private topics require authentication |
Unknown topic, a private topic on the public socket, or request on ticker. |
Topic is already subscribed |
You are already subscribed to this topic. |
Topic is not subscribed |
You unsubscribed from a topic you were not subscribed to. |
Could not find broker connection for topic "<topic>" |
No live feed from the broker in that instrument id. Check the broker name after the colon. |
Could not fetch "<topic>": <reason> |
The broker failed to answer a request. Try again. |
Read next
Section titled “Read next”- Private WebSocket: your account, orders and positions.
- WebSocket client library: the JavaScript client.