Skip to content

Developers

WebSocket API

One socket for live market data. The private socket adds your balances, positions, orders and order submission.

wss://sockets.cloud9trader.com/

The public socket needs no key and spends no credits. Connect from a script or from a web page.

TypeScript (Node.js, ws)
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)
})
Python (websockets)
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.

Every message, both ways, is a JSON array. The first element says what the message is; the rest are its arguments.

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.
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].

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

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

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:

  1. Subscribe and request at the same time.
  2. Ignore updates until the snapshot arrives.
  3. Apply only an update whose sequence is one more than the last you applied.
  4. On a gap, request a new snapshot and start again from step 2.

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.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
  • 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"].

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.