Loading ...

发明者数字货币量化平台websocket使用指南(Dial函数升级后详解)

Author: botvsing, Created: 2019-04-01 11:06:09, Updated: 2019-04-15 11:22:21

基本上所有数字货币交易所都支持websocket发送行情,部分交易所支持websocket更新账户信息。相比于rest API, websocket一般具有延时低,频率高,不受平台rest API频率限制等有带你,缺点是有中断问题,处理不直观。关于websocket简介,可以参考我曾经的这篇文章: https://zhuanlan.zhihu.com/p/22693475

本文将主要介绍在FMZ发明者量化平台,使用JavaScript语言,使用平台封装的Dial函数进行连接,具体说明和参数在文档,搜索Dial,为了实现各种功能,Dial函数进行了几次更新,本文将涵盖这一点,并介绍基于wss的事件驱动的策略,以及连接多交易所问题。

1.websocket连接:

一般直接连接即可,如获取币安全ticker推送:

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

对于返回数据是压缩格式,需要在连接是指定,compress指定压缩格式,mode代表发送返回数据那个需要压缩,如连接OKEX:

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

Dial函数支持重连,由底层Go语言完成,检测的连接断开会重连,对于请求数据内容已经在url中的,如刚才币安的例子,很方便,推荐使用。对于需要发送订消息的,可以自己维护重连机制。

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

订阅wss消息,一些交易所的请求在url中,也有一些需要自己发送订阅的频道,如coinbase:

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

2.websocket读取:

一般在死循环中不断读取即可,代码如下:

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数据推送速度很快,Go的底层会把所有的数据缓存在队列中,等程序调用read时,再依次返回。而机器人的下单等操作会带来延时,可能会造成数据的累积。对于成交推送,账户推送,深度插值推送等这类信息,我们需要历史数据,对于行情数据,我们大部分情况只关心最新的,不关心历史数据。

read()如果不加参数,会返回最旧的数据,没数据时阻塞到返回。如果想要最新数据,可以用client.read(-2)立即返回最新数据,但再没数据时回返回null,需要判断再引用。

根据如何对待缓存的旧数据,以及无数据时是否堵塞,read有不同的参数,具体如下图,看起来很复杂,但让程序更加灵活。 img

3.连接多个交易所websocket:

对于这种情况程序中显然不能用简单的read(),因为一个交易所会堵塞等待消息,此时另一个交易所即使有新消息也将接收不到。一般处理方式为:

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
    }
}

4.断线重连问题:

这部分处理较为麻烦,因为推送数据可能中断,或者推送延时极高,即使能接收到heartbeat也不代表数据还在推送,可以设置一个事件间隔,如果超过间隔没有收到更新就重新连接,并且最好隔一段时间和rest返回的结果对比,看数据是否准确。对于币安这种特殊情况,直接设置自动重连即可。

5.使用websocket的一般程序框架:

由于已经使用了推送数据,程序自然也要写成事件驱动,注意推送数据频繁,不用过多请求导致被封,一般可以写成:

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)
    }
}

6.总结:

各个交易所的websocket的连接方式,数据发送方式,可订阅的内容,数据格式往往不相同,所以平台并没有进行封装,需要用Dial函数自行连接。本文基本涵盖了一些基本的注意事项,如果还有问题,欢迎提问。

PS.一些交易所虽然没有提供websocket行情,但实际上登陆网站使用调式功能,会发现都是使用的websocket推送,研究一下就会发现订阅格式和返回格式。有些看起来像是加密过,用base64解码再解压就可以看到了。


Related

More