FMZ Cryptocurrency Quantitative Platform WebSocket Usage Guide (Detailed Explanation of the Upgraded Dial Function)

Author: Lydia, Created: 2023-07-13 14:03:32, Updated: 2024-01-03 21:05:36

img

FMZ Cryptocurrency Quantitative Platform WebSocket Usage Guide (Detailed Explanation of the Upgraded Dial Function)

Most cryptocurrency exchanges support sending market data via WebSocket, and some exchanges also support updating account information via WebSocket. Compared to REST API, WebSocket generally has lower latency, higher frequency, and is not subject to platform’s rest API rate limits. However, it has the drawback of potential interruptions and less intuitive handling.

This article will mainly introduce the usage of the Dial function, which is encapsulated in the FMZ Quant Platform using the JavaScript language. The specific instructions and parameters can be found in the documentation by searching for “Dial”. In order to achieve various functionalities, the Dial function has undergone several updates, which will be covered in this article. Additionally, it will also discuss the event-driven strategy based on WebSocket Secure (wss) and the issue of connecting to multiple exchanges.

1. Websocket connection:

A direct connection is usually sufficient, such as getting a coin security ticker push:

var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")

When the returned data is in compressed format, it needs to be specified during the connection. “compress” is used to specify the compression format, and “mode” represents which part of the data (either sending or returning) needs to be compressed. For example, when connecting to OKEX, the following can be used:

var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")

The Dial function supports reconnection, which is handled by the underlying Go language. It automatically reconnects when a connection is detected to be disconnected. For cases where the request data is already included in the URL, such as the previous example with Binance, it is very convenient and recommended to use. However, for cases where sending subscription messages is required, it is recommended to maintain the reconnection mechanism manually.

var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")

Subscribe to wss messages, some exchanges have requests in the url, and there are also channels that you need to send your own subscriptions to, such as coinbase:

client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')

2. Websocket Read

Generally, it can be read continuously in an infinite loop. The code is as follows:

function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
    while (true) {
        var msg = client.read()
        var data = JSON.parse(msg) // Parse json strings into quotable objects 
// Process data 
    }
}

The wss data push speed is very fast. The underlayer of Golang will cache all the data in the queue, and when the program calls read, the data will be returned in turn. However, operations such as placing an order on the bot will cause delays, which may result in the accumulation of data. For information such as trading execution push, account push, and depth interpolation push, we need the history data. For market quote data, in most cases, we only care about the latest data, not history data.

If read() adds no parameters, it will return the oldest data, and block until return when there is no data. If you want the latest data, you can use client.read(-2) to return the latest data immediately, but when there is no data, it will return null, which needs to be judged before reference.

Depending on how to deal with the old cached data and whether it is blocked when there is no data, “read” has different parameters, as shown in the table below, which looks complicated, but makes the program more flexible.

img

3. Connecting with Multiple platforms by Websocket

In this case, it is obvious that simply using “read()” does not work in the program, because one platform will block waiting messages, and another platform will not receive even if there are new messages. The general processing method is:

    function main() {
        var binance = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
        var coinbase = Dial("wss://ws-feed.pro.coinbase.com", 60)
        coinbase.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
        while (true) {
            var msgBinance = binance.read(-1) // Parameter -1 represents no data and return null immediately; it will not occur that being blocked before there is data to be returned 
            var msgCoinbase = coinbase.read(-1)
            if(msgBinance){
                // at this time, Binance has data to return 
            }
            if(msgCoinbase){
                // at this time, coinbase has data to return 
            }
            Sleep(1) // Sleep for 1 millisecond
        }
    }

4. Disconnection & Reconnection Problems

This part of the processing is more troublesome, because the push data may be interrupted, or the push delay is extremely long. Even if the heartbeat can be received, it does not mean the data is still being pushed. You can set an event interval; if no update is received after the interval, reconnect; it is best to compare the results returned by “rest” after a period of time, to see if the data is accurate. For the special cases of Binance, you can directly set automatic reconnection.

5. Using the General Program Frame of Websocket

For the push data has been used, the program will naturally be written as event-triggered; pay attention to the frequency of pushing data, because high-frequency requests will lead to being blocked; generally you can write:

    var tradeTime = Date.now()
    var accountTime = Date.now()
    function trade(data){
        if(Date.now() - tradeTime > 2000){//Here it limits only one trade in 2 seconds 
            tradeTime = Date.now()
            // Trading logic
        }
    }
    function GetAccount(){
        if(Date.now() - accountTime > 5000){//Here it limits GetAccount only once in 5 seconds 
            accountTime = Date.now()
            return exchange.GetAccount()
        }
    }
    function main() {
        var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true");
        while (true) {
            var msg = client.read()
            var data = JSON.parse(msg)
            var account = GetAccount()
            trade(data)
        }
    }

6. Conclusion

The connection method, data transmission method, subscribed content and data format of the websocket on each platform are often different, so the platform does not encapsulate it and needs to use the Dial function to connect by itself. This article basically covers some basic precautions. If you have any more questions, please feel free to ask.

PS: Although some platforms do not provide websocket quotes, in fact, when you log in to the website to use the debugging function, you will find that they are all using the websocket push. After researching, you will find out that some subscription formats and return formats seem to be encrypted, which can be seen by decoding and decompressing with base64.


More