Virtually all digital currency exchanges support websocket sending, and some exchanges support websocket updates of account information. Compared to rest API, websocket generally has low latency, high frequency, and is not subject to platform rest API frequency limitations.https://zhuanlan.zhihu.com/p/22693475
This article will mainly cover the FMZ inventor quantization platform, using the JavaScript language, connecting using the platform-wrapped Dial function, specifications and parameters in the documentation, searching Dial, several updates of the Dial function to implement various functions, and introducing the strategy of event-driven connection based on wss, as well as the problem of connecting multiple exchanges.
In general, a direct connection is possible, such as getting a security ticker pushed:
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
For the returned data to be compressed in the format that is required at the connection, compress specifies the compression format, the mode represents sending back the data that needs to be compressed, such as connection OKEX:
var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
The Dial function supports reconnection, which is done by the underlying Go language, the detected connection disconnection will reconnect, for the requested data content already in the url, such as the example of Binance, it is very convenient, recommended to use. For those who need to send an order message, you can maintain the reconnection mechanism yourself.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
Subscribe to the WS messages, some exchange requests are in the url, and some channels require you to send your own subscription, such as Coinbase:
client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
The code is usually read continuously during the death cycle, 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) //把json字符串解析为可引用的object
// 处理data数据
}
}
WSS data is pushed very fast, the bottom layer of Go will delay all data in the queue, and when the program calls read, it will return in turn. But the robot's downorder and other operations will cause delays, which can cause data accumulation. For transaction push, account push, depth push, etc. information, we need historical data, for market data, we are mostly concerned only with the latest, not with historical data.
read ((() returns the oldest data if no parameters are added, and blocks it from returning when no data is available. If you want the latest data, you can use client.read (((-2) to immediately return the latest data, but when no data is available, it returns null, and you need to decide to refer again.
Read has different parameters depending on how old data is treated in the cache, and whether it gets clogged when there is no data, specifically as shown below, which looks complicated but makes the program more flexible.
In this case, it is obvious that the procedure cannot use a simple read (), because one exchange will block the waiting message, at which point the other exchange will not receive it even if there is a new message.
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) // 参数-1代表无数据立即返回null,不会阻塞到有数据返回
var msgCoinbase = coinbase.read(-1)
if(msgBinance){
// 此时币安有数据返回
}
if(msgCoinbase){
// 此时coinbase有数据返回
}
Sleep(1) // 可以休眠1ms
}
}
This part of the process is more troublesome, because the push data can be interrupted, or the push delay is extremely high, even if the heartbeat is not received, it does not mean that the data is still being pushed, you can set an event interval, reconnect if you do not receive an update if you exceed the interval, and it is preferable to compare the results of the rest return with the rest of the time to see if the data is accurate.
Since the push data has already been used, the program will naturally also be written as an event driver, pay attention to pushing data frequently, so as not to use too many requests that will lead to being closed, generally you can write:
var tradeTime = Date.now()
var accountTime = Date.now()
function trade(data){
if(Date.now() - tradeTime > 2000){//这里即限制了2s内只交易一次
tradeTime = Date.now()
//交易逻辑
}
}
function GetAccount(){
if(Date.now() - accountTime > 5000){//这里即限制了5s内只获取账户一次
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)
}
}
The way the websockets are connected, the way data is sent, the content that can be subscribed to, and the data format are often different, so the platform is not wrapped and needs to connect itself with the Dial function. This article basically covers some basic precautions, and if there are any questions, please ask.
PS. Some exchanges do not offer a websocket market, but they actually use a websocket push function to land on a website. Research shows that the subscription format and return format are used. Some look like they are encrypted, and can be seen by decrypting them with base64 decryption.
xaifer48wss connection running for a while, reports json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
haohaoWhy is Binance a perpetual contract? After connecting, the read account information changes, and after a minute read returns empty.
xaifer48Received, thank you.
The grassYou can solve errors, print out messages, and make mistakes.
Difficult to quantifySince Binance says that it only pushes messages when there is a change in account information.