The streaming WebSocket API is currently under development (a work in progress). Below are key design considerations on supporting WebSockets in ccxt library.
In general, not all exchanges offer WebSockets, but many of them do. Exchanges' WebSocket APIs can be classified into two different categories:
- sub or subscribe allows receiving only
- pub or publish allows sending and receiving
A sub interface usually allows to subscribe to a stream of data and listen for it to income. Most of exchanges that do support WebSockets will offer a sub type of API only. The sub type includes streaming public market data. Sometimes exchanges also allow subcribing to private user data. After the user subscribes to any data feed the channel effectively starts working one-way and sending updates from the exchange towards the user continuously.
- Commonly appearing types of public market data streams:
- order book (most common) - updates on added, edited and deleted orders (aka change deltas)
- ticker- updates upon a change of 24 hour stats
- fills feed (also common) - a live stream of public trades
- exchange chat
- Less common types of private user data streams:
- the stream of trades of the user
- balance updates
- custom streams
- exchange-specific and other streams
A pub interface usually allows users to send data requests towards the server. This usually includes common user actions, like:
- placing and canceling orders
- placing withdrawal requests
- posting chat messages
- etc
Most exchanges do not offer a pub WS API, they will offer sub WS API only. However, there are some exchanges that have a complete WebSocket API.
In most cases a user cannot operate effectively having just the WebSocket API. Exchanges will stream public market data sub, and the HTTP REST API will still be required for the pub part (where missing).
The goal of ccxt is to seamlessly combine in a unified interface all available types of networking, possibly, without introducing backward-incompatible changes.
The WebSocket API in ccxt consists of the following:
- the pull (on-demand) interface
- the push (notification-based) interface
The pull WebSocket interface replicates the async REST interface one-to-one. So, in order to switch from REST
to pull WebSocket + REST
, the user is only required to submit a { ws: true }
option in constructor params. From there any call to the unified API will be switched to WebSockets, where available (where supported by the exchange).
The pull interface means the user pulling data from the library by calling its methods, whereas the data is fetched and merged in background. For example, whevener the user calls the fetchOrderBook (symbol, params)
method, the following sequence of events takes place:
- If the user is already subscribed to the orderbook updates feed for that particular symbol, the returned value would represent the current state of that orderbook in memory with all updates up to the moment of the call.
- If the user is not subscribed to the orderbook updates for that symbol yet, the library will subscribe the user to it upon first call.
- After subscribing, the library will receive a snapshot of current orderbook. This is returned to the caller right away.
- It will continue to receive partial updates just in time from the exchange, merging all updates with the orderbook in memory. Each incoming update is called a delta. Deltas represent changes to the orderbook (order added, edited or deleted) that have to be merged on top of the last known snapshot of the orderbook. These update-deltas are incoming continuously as soon as the exchange sends them.
- The ccxt library merges deltas to the orderbook in background.
- If the user calls the same
fetchOrderBook
method again – the library will return the up-to-date orderbook with all current deltas merged into it (return to step 1 at this point).
The above behaviour is the same for all methods that can get data feeds from exchanges websocket. The library will fallback to HTTP REST and will send a HTTP request if the exchange does not offer streaming of this or that type of data.
The list of related unified API methods is:
- fetchOrderBook
- fetchOrderBooks
- fetchTicker
- fetchTickers
- fetchTrades
- fetchBalances
- fetchOrders
- fetchOpenOrders
- fetchClosedOrders
- fetchMyTrades
- fetchTransactions
- fetchDeposits
- fetchWithdrawals
The push interface contains all of the above methods, but works in reverse, the library pushes the updates to the user. This is done in two ways:
- callbacks (JS, Python 2 & 3, PHP)
- async generators (JS, Python 3.5+)
The async generators is the prefered modern way of reading and writing streams of data. They do the work of callbacks in much more natural syntax that is built into the language itself. A callback is a mechanism of an inverted flow control. An async generator, on the other hand, is a mechanism of a direct flow control. Async generators make code much cleaner and sometimes even faster in both development and production.
I haven't used it but the PECL event extension seems like it might add the async to a PHP implementation.
It seems like adding that "ws: true" command would make things more complicated to deal with. Basically a hidden parameter that would have to be checked and passed in every method. And wouldn't reveal which are available as a socket compared to only as rest. Which would affect coding decisions.
Wouldn't it be better if the rest and socket classes were separate and extended from a base class per exchange?