基本上所有数字货币交易所都支持websocket发送行情,部分交易所支持websocket更新账户信息。相比于rest API, websocket一般具有延时低,频率高,不受平台rest API频率限制等有带你,缺点是有中断问题,处理不直观。关于websocket简介,可以参考我曾经的这篇文章:比特币程序化交易入门(5):WebSocket API
Python使用websocket协议需要websocket的包,官方范例已经提供了,直接可用。Okcoin官方Python Websocket API代码。我刚开始接触websocket时,一头雾水,最大的问题是不知道怎么处理推送数据,也不知道数据如何用在自己的策略里。本文将回答这些问题。
1)有的推送数据是经过压缩的,有的没有。需要判断。
2)订阅第一条推送和后续不同,以订阅ticker为例,订阅成功后先推送{'success': True, 'channel': 'ok_sub_spotcny_btc_ticker'},表示订阅成功,后来才是ticker数据。这样订阅账户信息就比较尴尬,如果账户没有变化,实际上是得不到具体的账户信息的。
3)订阅多重消息,也是分别推送,具体是那个信息,需要根据channel中的信息的判断。
4)websocket协议也会中断。
5)个人认为websocket使用行情推送,和消息驱动的策略,我目前只用于行情推送,下单还是使用REST。
6)推送的数据可以用全局变量来传递。
下面为通过websocket订阅ticker行情,实现每3s输出一次最新成交价的代码。
import websocket
import zlib #压缩相关的库
from recursive_json_loads import * #参见前面文章,用于json解析
import threading
import hashlib
import time
api_key='your api_key '
secret_key ='your secret_key'
#解压函数
def inflate(data):
decompress = zlib.decompressobj(-zlib.MAX_WBITS)
inflated = decompress.decompress(data)
inflated += decompress.flush()
return inflated
#签名函数,订阅个人信息,买卖等都需要签名
def buildMySign(params,secretKey):
sign = ''
for key in sorted(params.keys()):
sign += key + '=' + str(params[key]) +'&'
return hashlib.md5((sign+'secret_key='+secretKey).encode("utf-8")).hexdigest().upper()
#返回签名的信息
def wsGetAccount(channel,api_key,secret_key):
params = {
'api_key':api_key,
}
sign = buildMySign(params,secret_key)
return "{'event':'addChannel','channel':'"+channel+"','parameters':{'api_key':'"+api_key+"','sign':'"+sign+"'}}"
#每当有消息推送时,就会触发,信息包含为message,注意在这里也可以使用ws.send()发送新的信息。
def on_message(ws, message):
try:
msg=recursive_json_loads(inflate(message).decode('utf-8'))[0]
except Exception as e:
msg=recursive_json_loads(message)[0]
finally:
#print(msg) #判断是否需要解压,然后用json解析出来
pass
global depth #推送的信息保存在全局变量中,这样在其他线程中就可以读取了
global account
global ticker
try:
if 'ok_sub_spotcny_userinfo' ==msg.channel:
account=msg.data
if 'ok_sub_spotcny_btc_depth_60' == msg.channel:
depth=msg.data
if 'ok_sub_spotcny_btc_ticker'== msg.channel:
ticker=msg.data #判断推送信息的类型
except Exception as e:
pass
#出现错误时执行
def on_error(ws, error):
print(error)
#关闭连接时执行
def on_close(ws):
print("### closed ###")
#开始连接时执行,需要订阅的消息和其它操作都要在这里完成
def on_open(ws):
ws.send(wsGetAccount('ok_sub_spotcny_userinfo',api_key,secret_key))
ws.send("{'event':'addChannel','channel':'ok_sub_spotcny_btc_depth_60'}")
ws.send("{'event':'addChannel','channel':'ok_sub_spotcny_btc_ticker'}")
#创建websocket连接
def ws_main():
websocket.enableTrace(True)
host = "wss://real.okcoin.cn:10440/websocket/okcoinapi"
ws = websocket.WebSocketApp(host,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.on_open = on_open
ws.run_forever() #开始运行
if __name__ == "__main__":
account=0
depth=0
ticker=0
threading.Thread(target=ws_main).start()
while True:
#这里是需要进行的任务,下单的策略可以安排在这里
time.sleep(3)
print(ticker.last)
更新OKEX的一个简单websocket链接范例(python2.7):
#pip install websocket_client
import websocket,json
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
ws = websocket.create_connection("wss://real.okex.com:10441/websocket", timeout=15, sslopt= {"cert_reqs": ssl.CERT_NONE})
ws.settimeout(15)
msg = [{'event':'addChannel','channel':'ok_sub_spot_btc_usdt_ticker'},{'event':'addChannel','channel':'ok_sub_spot_btc_usdt_depth'}]
ws.send(json.dumps(msg))
while True:
print ws.recv()
具体解析ws.recv()以及保持链接自行探索吧