[Add]新增币安接口
This commit is contained in:
parent
5374829224
commit
ba647a1b63
1
vnpy/api/binance/__init__.py
Normal file
1
vnpy/api/binance/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .vnbinance import BinanceApi
|
43
vnpy/api/binance/test.py
Normal file
43
vnpy/api/binance/test.py
Normal file
@ -0,0 +1,43 @@
|
||||
from time import sleep
|
||||
|
||||
from vnbinance import BinanceApi
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
apiKey = ''
|
||||
secretKey = ''
|
||||
|
||||
api = BinanceApi()
|
||||
api.init(apiKey, secretKey)
|
||||
api.start()
|
||||
|
||||
#api.queryPing()
|
||||
#api.queryTime()
|
||||
#api.queryExchangeInfo()
|
||||
|
||||
api.queryDepth('BTCUSDT')
|
||||
#api.queryDepth('BTCUSDT')
|
||||
#api.queryTrades('BTCUSDT')
|
||||
#api.queryAggTrades('BTCUSDT')
|
||||
#api.queryKlines('BTCUSDT', '1m')
|
||||
#api.queryTicker24HR()
|
||||
#api.queryTickerPrice()
|
||||
#api.queryBookTicker()
|
||||
|
||||
api.queryAccount()
|
||||
#api.queryOrder('BTCUSDT', '1231231')
|
||||
#api.queryOpenOrders('BTCUSDT')
|
||||
#api.queryAllOrders('BTCUSDT')
|
||||
#api.queryMyTrades('BTCUSDT')
|
||||
|
||||
#api.startStream()
|
||||
#api.keepaliveStream('12312312')
|
||||
#api.closeStream('123213')
|
||||
|
||||
#api.newOrder('BTCUSDT', 'BUY', 'LIMIT', 3000, 1, 'GTC')
|
||||
#api.cancelOrder('BTCUSDT', '132213123')
|
||||
|
||||
#api.initDataStream(['btcusdt@ticker', 'btcusdt@depth5'])
|
||||
#api.initUserStream('NXvaiFwZz2LuKqINVerKOnWaQQG1JhdQNejiZKY9Kmgk4lZgTvm3nRAnXJK7')
|
||||
|
||||
raw_input()
|
624
vnpy/api/binance/vnbinance.py
Normal file
624
vnpy/api/binance/vnbinance.py
Normal file
@ -0,0 +1,624 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
import json
|
||||
import requests
|
||||
import hmac
|
||||
import hashlib
|
||||
import traceback
|
||||
|
||||
from queue import Queue, Empty
|
||||
from threading import Thread
|
||||
from multiprocessing.dummy import Pool
|
||||
from time import time, sleep
|
||||
from urllib import urlencode
|
||||
|
||||
from websocket import create_connection
|
||||
|
||||
|
||||
|
||||
REST_ENDPOINT = 'https://www.binance.com'
|
||||
DATASTREAM_ENDPOINT = 'wss://stream.binance.com:9443/stream?streams='
|
||||
USERSTREAM_ENDPOINT = 'wss://stream.binance.com:9443/ws/'
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
class BinanceApi(object):
|
||||
""""""
|
||||
|
||||
###################################################
|
||||
## Basic Function
|
||||
###################################################
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.apiKey = ''
|
||||
self.secretKey = ''
|
||||
|
||||
self.active = False
|
||||
self.reqid = 0
|
||||
self.queue = Queue()
|
||||
self.pool = None
|
||||
|
||||
self.headers = {}
|
||||
self.secret = ''
|
||||
self.recvWindow = 5000
|
||||
|
||||
self.dataStreamNameList = []
|
||||
self.dataStreamUrl = ''
|
||||
self.dataStreamActive = False
|
||||
self.dataStreamWs = None
|
||||
self.dataStreamThread = None
|
||||
|
||||
self.userStreamKey = ''
|
||||
self.userStreamUrl = ''
|
||||
self.userStreamActive = False
|
||||
self.userStreamWs = None
|
||||
self.userStreamThread = None
|
||||
|
||||
self.keepaliveCount = 0
|
||||
self.keepaliveThread = None
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def init(self, apiKey, secretKey, recvWindow=5000):
|
||||
""""""
|
||||
self.apiKey = apiKey
|
||||
self.secretKey = secretKey
|
||||
|
||||
self.headers['X-MBX-APIKEY'] = apiKey
|
||||
self.secret = bytes(secretKey.encode('utf-8'))
|
||||
self.recvWindow = recvWindow
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def start(self, n=10):
|
||||
""""""
|
||||
if self.active:
|
||||
return
|
||||
|
||||
self.active = True
|
||||
|
||||
self.pool = Pool(n)
|
||||
self.pool.map_async(self.run, range(n))
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
""""""
|
||||
self.active = False
|
||||
self.pool.close()
|
||||
self.pool.join()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def request(self, method, path, params=None, signed=False, stream=False):
|
||||
""""""
|
||||
if not signed:
|
||||
url = REST_ENDPOINT + path
|
||||
headers = {}
|
||||
else:
|
||||
if not stream:
|
||||
params['recvWindow'] = self.recvWindow
|
||||
params['timestamp'] = int(time()*1000)
|
||||
query = urlencode(sorted(params.items()))
|
||||
|
||||
signature = hmac.new(self.secret, query.encode('utf-8'),
|
||||
hashlib.sha256).hexdigest()
|
||||
query += "&signature={}".format(signature)
|
||||
|
||||
url = REST_ENDPOINT + path + '?' + query
|
||||
params = None # 参数添加到query中后,清空参数字典
|
||||
else:
|
||||
if params:
|
||||
query = urlencode(sorted(params.items()))
|
||||
url = REST_ENDPOINT + path + '?' + query
|
||||
params = None
|
||||
else:
|
||||
url = REST_ENDPOINT + path
|
||||
|
||||
headers = self.headers
|
||||
|
||||
try:
|
||||
resp = requests.request(method, url, params=params, headers=headers)
|
||||
|
||||
if resp.status_code == 200:
|
||||
return True, resp.json()
|
||||
else:
|
||||
error = {
|
||||
'method': method,
|
||||
'params': params,
|
||||
'code': resp.status_code,
|
||||
'msg': resp.json()['msg']
|
||||
}
|
||||
return False, error
|
||||
except Exception as e:
|
||||
error = {
|
||||
'method': method,
|
||||
'params': params,
|
||||
'code': e,
|
||||
'msg': traceback.format_exc()
|
||||
}
|
||||
return False, error
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def addReq(self, method, path, params, callback, signed=False, stream=False):
|
||||
"""添加请求"""
|
||||
self.reqid += 1
|
||||
req = (method, path, params, callback, signed, stream, self.reqid)
|
||||
self.queue.put(req)
|
||||
return self.reqid
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def processReq(self, req):
|
||||
""""""
|
||||
method, path, params, callback, signed, stream, reqid = req
|
||||
result, data = self.request(method, path, params, signed, stream)
|
||||
|
||||
if result:
|
||||
callback(data, reqid)
|
||||
else:
|
||||
self.onError(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def run(self, n):
|
||||
""""""
|
||||
while self.active:
|
||||
try:
|
||||
req = self.queue.get(timeout=1)
|
||||
self.processReq(req)
|
||||
except Empty:
|
||||
pass
|
||||
|
||||
###################################################
|
||||
## REST Function
|
||||
###################################################
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryPing(self):
|
||||
""""""
|
||||
path = '/api/v1/ping'
|
||||
return self.addReq('GET', path, {}, self.onQueryPing)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryTime(self):
|
||||
""""""
|
||||
path = '/api/v1/time'
|
||||
return self.addReq('GET', path, {}, self.onQueryTime)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryExchangeInfo(self):
|
||||
""""""
|
||||
path = '/api/v1/exchangeInfo'
|
||||
return self.addReq('GET', path, {}, self.onQueryExchangeInfo)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryDepth(self, symbol, limit=0):
|
||||
""""""
|
||||
path = '/api/v1/depth'
|
||||
params = {'symbol': symbol}
|
||||
if limit:
|
||||
params['limit'] = limit
|
||||
return self.addReq('GET', path, params, self.onQueryDepth)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryTrades(self, symbol, limit=0):
|
||||
""""""
|
||||
path = '/api/v1/trades'
|
||||
params = {'symbol': symbol}
|
||||
if limit:
|
||||
params['limit'] = limit
|
||||
return self.addReq('GET', path, params, self.onQueryTrades)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryAggTrades(self, symbol, fromId=0, startTime=0, endTime=0, limit=0):
|
||||
""""""
|
||||
path = '/api/v1/aggTrades'
|
||||
|
||||
params = {'symbol': symbol}
|
||||
if fromId:
|
||||
params['fromId'] = fromId
|
||||
if startTime:
|
||||
params['startTime'] = startTime
|
||||
if endTime:
|
||||
params['endTime'] = endTime
|
||||
if limit:
|
||||
params['limit'] = limit
|
||||
|
||||
return self.addReq('GET', path, params, self.onQueryAggTrades)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryKlines(self, symbol, interval, limit=0, startTime=0, endTime=0):
|
||||
""""""
|
||||
path = '/api/v1/klines'
|
||||
|
||||
params = {
|
||||
'symbol': symbol,
|
||||
'interval': interval
|
||||
}
|
||||
if limit:
|
||||
params['limit'] = limit
|
||||
if startTime:
|
||||
params['startTime'] = startTime
|
||||
if endTime:
|
||||
params['endTime'] = endTime
|
||||
|
||||
return self.addReq('GET', path, params, self.onQueryKlines)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryTicker24HR(self, symbol=''):
|
||||
""""""
|
||||
path = '/api/v1/ticker/24hr'
|
||||
params = {}
|
||||
if symbol:
|
||||
params['symbol'] = symbol
|
||||
return self.addReq('GET', path, params, self.onQueryTicker24HR)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryTickerPrice(self, symbol=''):
|
||||
""""""
|
||||
path = '/api/v3/ticker/price'
|
||||
params = {}
|
||||
if symbol:
|
||||
params['symbol'] = symbol
|
||||
return self.addReq('GET', path, params, self.onQueryTickerPrice)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryBookTicker(self, symbol=''):
|
||||
""""""
|
||||
path = '/api/v3/ticker/bookTicker'
|
||||
params = {}
|
||||
if symbol:
|
||||
params['symbol'] = symbol
|
||||
return self.addReq('GET', path, params, self.onQueryBookTicker)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def newOrder(self, symbol, side, type_, price, quantity, timeInForce,
|
||||
newClientOrderId='', stopPrice=0, icebergQty=0, newOrderRespType=''):
|
||||
""""""
|
||||
path = '/api/v3/order'
|
||||
|
||||
params = {
|
||||
'symbol': symbol,
|
||||
'side': side,
|
||||
'type': type_,
|
||||
'price': price,
|
||||
'quantity': quantity,
|
||||
'timeInForce': timeInForce
|
||||
}
|
||||
if newClientOrderId:
|
||||
params['newClientOrderId'] = newClientOrderId
|
||||
if timeInForce:
|
||||
params['timeInForce'] = timeInForce
|
||||
if stopPrice:
|
||||
params['stopPrice'] = stopPrice
|
||||
if icebergQty:
|
||||
params['icebergQty'] = icebergQty
|
||||
if newOrderRespType:
|
||||
params['newOrderRespType'] = newOrderRespType
|
||||
|
||||
return self.addReq('POST', path, params, self.onNewOrder, signed=True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryOrder(self, symbol, orderId=0, origClientOrderId=0):
|
||||
""""""
|
||||
path = '/api/v3/order'
|
||||
params = {'symbol': symbol}
|
||||
if orderId:
|
||||
params['orderId'] = orderId
|
||||
if origClientOrderId:
|
||||
params['origClientOrderId'] = origClientOrderId
|
||||
return self.addReq('GET', path, params, self.onQueryOrder, signed=True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, symbol, orderId=0, origClientOrderId='',
|
||||
newClientOrderId=''):
|
||||
""""""
|
||||
path = '/api/v3/order'
|
||||
params = {'symbol': symbol}
|
||||
if orderId:
|
||||
params['orderId'] = orderId
|
||||
if origClientOrderId:
|
||||
params['origClientOrderId'] = origClientOrderId
|
||||
if newClientOrderId:
|
||||
params['newClientOrderId'] = newClientOrderId
|
||||
return self.addReq('DELETE', path, params, self.onCancelOrder, signed=True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryOpenOrders(self, symbol=''):
|
||||
""""""
|
||||
path = '/api/v3/openOrders'
|
||||
params = {}
|
||||
if symbol:
|
||||
params['symbol'] = symbol
|
||||
return self.addReq('GET', path, params, self.onQueryOpenOrders, signed=True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryAllOrders(self, symbol, orderId=0, limit=0):
|
||||
""""""
|
||||
path = '/api/v3/allOrders'
|
||||
params = {'symbol': symbol}
|
||||
if orderId:
|
||||
params['orderId'] = orderId
|
||||
if limit:
|
||||
params['limit'] = limit
|
||||
return self.addReq('GET', path, params, self.onQueryAllOrders, signed=True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryAccount(self):
|
||||
""""""
|
||||
path = '/api/v3/account'
|
||||
params = {}
|
||||
return self.addReq('GET', path, params, self.onQueryAccount, signed=True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryMyTrades(self, symbol, limit=0, fromId=0):
|
||||
""""""
|
||||
path = '/api/v3/myTrades'
|
||||
params = {'symbol': symbol}
|
||||
if limit:
|
||||
params['limit'] = limit
|
||||
if fromId:
|
||||
params['fromId'] = fromId
|
||||
return self.addReq('GET', path, params, self.onQueryMyTrades, signed=True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def startStream(self):
|
||||
""""""
|
||||
path = '/api/v1/userDataStream'
|
||||
return self.addReq('POST', path, {}, self.onStartStream, signed=True, stream=True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def keepaliveStream(self, listenKey):
|
||||
""""""
|
||||
path = '/api/v1/userDataStream'
|
||||
params = {'listenKey': listenKey}
|
||||
return self.addReq('PUT', path, params, self.onKeepaliveStream, signed=True, stream=True)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def closeStream(self, listenKey):
|
||||
""""""
|
||||
path = '/api/v1/userDataStream'
|
||||
params = {'listenKey': listenKey}
|
||||
return self.addReq('DELETE', path, params, self.onCloseStream, signed=True, stream=True)
|
||||
|
||||
###################################################
|
||||
## REST Callback
|
||||
###################################################
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryPing(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryTime(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryExchangeInfo(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryDepth(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryTrades(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryAggTrades(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryKlines(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryTicker24HR(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryTickerPrice(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryBookTicker(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onNewOrder(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryOrder(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onCancelOrder(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryOpenOrders(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryAllOrders(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryAccount(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryMyTrades(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onStartStream(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onKeepaliveStream(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onCloseStream(self, data, reqid):
|
||||
""""""
|
||||
print(data, reqid)
|
||||
|
||||
###################################################
|
||||
## Websocket Function
|
||||
###################################################
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initDataStream(self, nameList=None):
|
||||
""""""
|
||||
if nameList:
|
||||
self.dataStreamNameList = nameList
|
||||
s = '/'.join(self.dataStreamNameList)
|
||||
self.dataStreamUrl = DATASTREAM_ENDPOINT + s
|
||||
|
||||
result = self.connectDataStream()
|
||||
if result:
|
||||
self.dataStreamActive = True
|
||||
self.dataStreamThread = Thread(target=self.runDataStream)
|
||||
self.dataStreamThread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def runDataStream(self):
|
||||
""""""
|
||||
while self.dataStreamActive:
|
||||
try:
|
||||
stream = self.dataStreamWs.recv()
|
||||
data = json.loads(stream)
|
||||
self.onMarketData(data)
|
||||
except:
|
||||
self.onDataStreamError('Data stream connection lost')
|
||||
result = self.connectDataStream()
|
||||
if not result:
|
||||
self.onDataStreamError(u'Waiting 3 seconds to reconnect')
|
||||
sleep(3)
|
||||
else:
|
||||
self.onDataStreamError(u'Data stream reconnected')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def closeDataStream(self):
|
||||
""""""
|
||||
self.dataStreamActive = False
|
||||
self.dataStreamThread.join()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connectDataStream(self):
|
||||
""""""
|
||||
try:
|
||||
self.dataStreamWs = create_connection(self.dataStreamUrl)
|
||||
return True
|
||||
except:
|
||||
msg = traceback.format_exc()
|
||||
self.onDataStreamError('Connecting data stream falied: %s' %msg)
|
||||
return False
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onDataStreamError(self, msg):
|
||||
""""""
|
||||
print msg
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onMarketData(self, data):
|
||||
""""""
|
||||
print data
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initUserStream(self, key):
|
||||
""""""
|
||||
self.userStreamKey = key
|
||||
self.userStreamUrl = USERSTREAM_ENDPOINT + key
|
||||
|
||||
result = self.connectUserStream()
|
||||
if result:
|
||||
self.userStreamActive = True
|
||||
self.userStreamThread = Thread(target=self.runUserStream)
|
||||
self.userStreamThread.start()
|
||||
|
||||
self.keepaliveThread = Thread(target=self.runKeepalive)
|
||||
self.keepaliveThread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def runUserStream(self):
|
||||
""""""
|
||||
while self.userStreamActive:
|
||||
try:
|
||||
stream = self.userStreamWs.recv()
|
||||
data = json.loads(stream)
|
||||
self.onUserData(data)
|
||||
except:
|
||||
self.onUserStreamError('User stream connection lost')
|
||||
result = self.connectUserStream()
|
||||
if not result:
|
||||
self.onUserStreamError(u'Waiting 3 seconds to reconnect')
|
||||
sleep(3)
|
||||
else:
|
||||
self.onUserStreamError(u'User stream reconnected')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def closeUserStream(self):
|
||||
""""""
|
||||
self.userStreamActive = False
|
||||
self.userStreamThread.join()
|
||||
self.keepaliveThread.join()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connectUserStream(self):
|
||||
""""""
|
||||
try:
|
||||
self.userStreamWs = create_connection(self.userStreamUrl)
|
||||
return True
|
||||
except:
|
||||
msg = traceback.format_exc()
|
||||
self.onUserStreamError('Connecting user stream falied: %s' %msg)
|
||||
return False
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onUserStreamError(self, msg):
|
||||
""""""
|
||||
print msg
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onUserData(self, data):
|
||||
""""""
|
||||
print data
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def runKeepalive(self):
|
||||
""""""
|
||||
while self.userStreamActive:
|
||||
self.keepaliveCount += 1
|
||||
|
||||
if self.keepaliveCount >= 1800:
|
||||
self.keepaliveCount = 0
|
||||
self.keepaliveStream(self.userStreamKey)
|
||||
|
||||
sleep(1)
|
||||
|
5
vnpy/trader/gateway/binanceGateway/BINANCE_connect.json
Normal file
5
vnpy/trader/gateway/binanceGateway/BINANCE_connect.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"apiKey": "",
|
||||
"secretKey": "",
|
||||
"symbols": ["BTCUSDT", "ETHUSDT", "ETHBTC"]
|
||||
}
|
10
vnpy/trader/gateway/binanceGateway/__init__.py
Normal file
10
vnpy/trader/gateway/binanceGateway/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnpy.trader import vtConstant
|
||||
from .binanceGateway import BinanceGateway
|
||||
|
||||
gatewayClass = BinanceGateway
|
||||
gatewayName = 'BINANCE'
|
||||
gatewayDisplayName = u'币安'
|
||||
gatewayType = vtConstant.GATEWAYTYPE_BTC
|
||||
gatewayQryEnabled = True
|
498
vnpy/trader/gateway/binanceGateway/binanceGateway.py
Normal file
498
vnpy/trader/gateway/binanceGateway/binanceGateway.py
Normal file
@ -0,0 +1,498 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
'''
|
||||
vnpy.api.binance的gateway接入
|
||||
'''
|
||||
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from copy import copy
|
||||
|
||||
from vnpy.api.binance import BinanceApi
|
||||
from vnpy.trader.vtGateway import *
|
||||
from vnpy.trader.vtFunction import getJsonPath, getTempPath
|
||||
|
||||
|
||||
# 委托状态类型映射
|
||||
statusMapReverse = {}
|
||||
statusMapReverse['NEW'] = STATUS_NOTTRADED
|
||||
statusMapReverse['PARTIALLY_FILLED'] = STATUS_PARTTRADED
|
||||
statusMapReverse['FILLED'] = STATUS_ALLTRADED
|
||||
statusMapReverse['CANCELED'] = STATUS_CANCELLED
|
||||
statusMapReverse['REJECTED'] = STATUS_REJECTED
|
||||
statusMapReverse['EXPIRED'] = STATUS_CANCELLED
|
||||
|
||||
# 方向映射
|
||||
directionMap = {}
|
||||
directionMap[DIRECTION_LONG] = 'BUY'
|
||||
directionMap[DIRECTION_SHORT] = 'SELL'
|
||||
directionMapReverse = {v:k for k,v in directionMap.items()}
|
||||
|
||||
# 价格类型映射
|
||||
priceTypeMap = {}
|
||||
priceTypeMap[PRICETYPE_LIMITPRICE] = 'LIMIT'
|
||||
priceTypeMap[PRICETYPE_MARKETPRICE] = 'MARKET'
|
||||
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def print_dict(d):
|
||||
""""""
|
||||
print '-' * 30
|
||||
l = d.keys()
|
||||
l.sort()
|
||||
for k in l:
|
||||
print '%s:%s' %(k, d[k])
|
||||
|
||||
|
||||
########################################################################
|
||||
class BinanceGateway(VtGateway):
|
||||
"""币安接口"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, eventEngine, gatewayName=''):
|
||||
"""Constructor"""
|
||||
super(BinanceGateway, self).__init__(eventEngine, gatewayName)
|
||||
|
||||
self.api = GatewayApi(self)
|
||||
|
||||
self.qryEnabled = False # 是否要启动循环查询
|
||||
|
||||
self.fileName = self.gatewayName + '_connect.json'
|
||||
self.filePath = getJsonPath(self.fileName, __file__)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self):
|
||||
"""连接"""
|
||||
try:
|
||||
f = file(self.filePath)
|
||||
except IOError:
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'读取连接配置出错,请检查'
|
||||
self.onLog(log)
|
||||
return
|
||||
|
||||
# 解析json文件
|
||||
setting = json.load(f)
|
||||
try:
|
||||
apiKey = str(setting['apiKey'])
|
||||
secretKey = str(setting['secretKey'])
|
||||
symbols = setting['symbols']
|
||||
except KeyError:
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'连接配置缺少字段,请检查'
|
||||
self.onLog(log)
|
||||
return
|
||||
|
||||
# 创建行情和交易接口对象
|
||||
self.api.connect(apiKey, secretKey, symbols)
|
||||
|
||||
# 初始化并启动查询
|
||||
#self.initQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
"""订阅行情"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, orderReq):
|
||||
"""发单"""
|
||||
return self.api.sendOrder(orderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, cancelOrderReq):
|
||||
"""撤单"""
|
||||
self.api.cancel(cancelOrderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭"""
|
||||
self.api.close()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def queryAccount(self):
|
||||
""""""
|
||||
self.api.queryAccount()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initQuery(self):
|
||||
"""初始化连续查询"""
|
||||
if self.qryEnabled:
|
||||
# 需要循环的查询函数列表
|
||||
self.qryFunctionList = [self.queryAccount]
|
||||
|
||||
self.qryCount = 0 # 查询触发倒计时
|
||||
self.qryTrigger = 1 # 查询触发点
|
||||
self.qryNextFunction = 0 # 上次运行的查询函数索引
|
||||
|
||||
self.startQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def query(self, event):
|
||||
"""注册到事件处理引擎上的查询函数"""
|
||||
self.qryCount += 1
|
||||
|
||||
if self.qryCount > self.qryTrigger:
|
||||
# 清空倒计时
|
||||
self.qryCount = 0
|
||||
|
||||
# 执行查询函数
|
||||
function = self.qryFunctionList[self.qryNextFunction]
|
||||
function()
|
||||
|
||||
# 计算下次查询函数的索引,如果超过了列表长度,则重新设为0
|
||||
self.qryNextFunction += 1
|
||||
if self.qryNextFunction == len(self.qryFunctionList):
|
||||
self.qryNextFunction = 0
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def startQuery(self):
|
||||
"""启动连续查询"""
|
||||
self.eventEngine.register(EVENT_TIMER, self.query)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setQryEnabled(self, qryEnabled):
|
||||
"""设置是否要启动循环查询"""
|
||||
self.qryEnabled = qryEnabled
|
||||
|
||||
|
||||
########################################################################
|
||||
class GatewayApi(BinanceApi):
|
||||
"""API实现"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, gateway):
|
||||
"""Constructor"""
|
||||
super(GatewayApi, self).__init__()
|
||||
|
||||
self.gateway = gateway # gateway对象
|
||||
self.gatewayName = gateway.gatewayName # gateway对象名称
|
||||
|
||||
self.date = datetime.now().strftime('%y%m%d%H%M%S')
|
||||
self.orderId = 0
|
||||
|
||||
self.tickDict = {}
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self, apiKey, secretKey, symbols):
|
||||
"""连接服务器"""
|
||||
self.init(apiKey, secretKey)
|
||||
self.start()
|
||||
self.writeLog(u'交易API启动成功')
|
||||
|
||||
l = []
|
||||
for symbol in symbols:
|
||||
symbol = symbol.lower()
|
||||
l.append(symbol+'@ticker')
|
||||
l.append(symbol+'@depth5')
|
||||
self.initDataStream(l)
|
||||
self.writeLog(u'行情推送订阅成功')
|
||||
|
||||
self.startStream()
|
||||
|
||||
# 初始化查询
|
||||
self.queryExchangeInfo()
|
||||
self.queryAccount()
|
||||
|
||||
for symbol in symbols:
|
||||
self.queryOpenOrders(symbol.upper())
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def writeLog(self, content):
|
||||
"""发出日志"""
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = content
|
||||
self.gateway.onLog(log)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, data, reqid):
|
||||
""""""
|
||||
err = VtErrorData()
|
||||
err.gatewayName = self.gatewayName
|
||||
err.errorID = data['code']
|
||||
err.errorMsg = data['msg']
|
||||
self.gateway.onError(err)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryExchangeInfo(self, data, reqid):
|
||||
""""""
|
||||
for d in data['symbols']:
|
||||
if str(d['symbol']) == 'ETHUSDT':
|
||||
print d
|
||||
|
||||
contract = VtContractData()
|
||||
contract.gatewayName = self.gatewayName
|
||||
|
||||
contract.symbol = d['symbol']
|
||||
contract.exchange = EXCHANGE_BINANCE
|
||||
contract.vtSymbol = '.'.join([contract.symbol, contract.exchange])
|
||||
contract.name = contract.vtSymbol
|
||||
contract.productClass = PRODUCT_SPOT
|
||||
contract.size = 1
|
||||
|
||||
for f in d['filters']:
|
||||
if f['filterType'] == 'PRICE_FILTER':
|
||||
contract.priceTick = float(f['tickSize'])
|
||||
|
||||
self.gateway.onContract(contract)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onNewOrder(self, data, reqid):
|
||||
""""""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onCancelOrder(self, data, reqid):
|
||||
""""""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryOpenOrders(self, data, reqid):
|
||||
""""""
|
||||
for d in data:
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
order.symbol = d['symbol']
|
||||
order.exchange = EXCHANGE_BINANCE
|
||||
order.vtSymbol = '.'.join([order.symbol, order.exchange])
|
||||
|
||||
order.orderID = d['clientOrderId']
|
||||
order.vtOrderID = '.'.join([order.gatewayName, order.orderID])
|
||||
|
||||
order.direction = directionMapReverse[d['side']]
|
||||
order.price = float(d['price'])
|
||||
order.totalVolume = float(d['origQty'])
|
||||
order.tradedVolume = float(d['executedQty'])
|
||||
date, order.orderTime = self.generateDateTime(d['time'])
|
||||
order.status = statusMapReverse[d['status']]
|
||||
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryAllOrders(self, data, reqid):
|
||||
""""""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryAccount(self, data, reqid):
|
||||
""""""
|
||||
for d in data['balances']:
|
||||
free = float(d['free'])
|
||||
locked = float(d['locked'])
|
||||
|
||||
if free or locked:
|
||||
pos = VtPositionData()
|
||||
pos.gatewayName = self.gatewayName
|
||||
pos.symbol = d['asset']
|
||||
pos.exchange = EXCHANGE_BINANCE
|
||||
pos.vtSymbol = '.'.join([pos.vtSymbol, pos.direction])
|
||||
pos.direction = DIRECTION_LONG
|
||||
pos.vtPositionName = '.'.join([pos.symbol, pos.direction])
|
||||
pos.frozen = locked
|
||||
pos.position = free + locked
|
||||
self.gateway.onPosition(pos)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onQueryMyTrades(self, data, reqid):
|
||||
""""""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onStartStream(self, data, reqid):
|
||||
""""""
|
||||
key = data['listenKey']
|
||||
self.initUserStream(key)
|
||||
self.writeLog(u'交易推送订阅成功')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onKeepaliveStream(self, data, reqid):
|
||||
""""""
|
||||
self.writeLog(u'交易推送刷新成功')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onCloseStream(self, data, reqid):
|
||||
""""""
|
||||
self.writeLog(u'交易推送关闭')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onUserData(self, data):
|
||||
""""""
|
||||
if data['e'] == 'outboundAccountInfo':
|
||||
self.onPushAccount(data)
|
||||
elif data['e'] == 'executionReport':
|
||||
self.onPushOrder(data)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onPushAccount(self, data):
|
||||
""""""
|
||||
for d in data['B']:
|
||||
free = float(d['f'])
|
||||
locked = float(d['l'])
|
||||
|
||||
if free or locked:
|
||||
pos = VtPositionData()
|
||||
pos.gatewayName = self.gatewayName
|
||||
pos.symbol = d['a']
|
||||
pos.exchange = EXCHANGE_BINANCE
|
||||
pos.vtSymbol = '.'.join([pos.vtSymbol, pos.direction])
|
||||
pos.direction = DIRECTION_LONG
|
||||
pos.vtPositionName = '.'.join([pos.symbol, pos.direction])
|
||||
pos.frozen = locked
|
||||
pos.position = free + locked
|
||||
self.gateway.onPosition(pos)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onPushOrder(self, d):
|
||||
""""""
|
||||
# 委托更新
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
order.symbol = d['s']
|
||||
order.exchange = EXCHANGE_BINANCE
|
||||
order.vtSymbol = '.'.join([order.symbol, order.exchange])
|
||||
|
||||
if d['C'] != 'null':
|
||||
order.orderID = d['C'] # 撤单原始委托号
|
||||
else:
|
||||
order.orderID = d['c']
|
||||
order.vtOrderID = '.'.join([order.gatewayName, order.orderID])
|
||||
|
||||
order.direction = directionMapReverse[d['S']]
|
||||
order.price = float(d['p'])
|
||||
order.totalVolume = float(d['q'])
|
||||
order.tradedVolume = float(d['z'])
|
||||
date, order.orderTime = self.generateDateTime(d['T'])
|
||||
order.status = statusMapReverse[d['X']]
|
||||
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
# 成交更新
|
||||
if float(d['l']):
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
|
||||
trade.symbol = order.symbol
|
||||
trade.exchange = order.exchange
|
||||
trade.vtSymbol = order.vtSymbol
|
||||
trade.orderID = order.orderID
|
||||
trade.vtOrderID = order.vtOrderID
|
||||
trade.tradeID = str(d['t'])
|
||||
trade.vtTradeID = '.'.join([trade.gatewayName, trade.tradeID])
|
||||
trade.direction = order.direction
|
||||
trade.price = float(d['L'])
|
||||
trade.volume = float(d['l'])
|
||||
date, trade.tradeTime = self.generateDateTime(d['E'])
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onMarketData(self, data):
|
||||
""""""
|
||||
name = data['stream']
|
||||
symbol, channel = name.split('@')
|
||||
symbol = symbol.upper()
|
||||
|
||||
if symbol in self.tickDict:
|
||||
tick = self.tickDict[symbol]
|
||||
else:
|
||||
tick = VtTickData()
|
||||
tick.gatewayName = self.gatewayName
|
||||
tick.symbol = symbol
|
||||
tick.exchange = EXCHANGE_BINANCE
|
||||
tick.vtSymbol = '.'.join([tick.symbol, tick.exchange])
|
||||
|
||||
self.tickDict[symbol] = tick
|
||||
|
||||
data = data['data']
|
||||
if channel == 'ticker':
|
||||
tick.volume = float(data['v'])
|
||||
tick.openPrice = float(data['o'])
|
||||
tick.highPrice = float(data['h'])
|
||||
tick.lowPrice = float(data['l'])
|
||||
tick.lastPrice = float(data['c'])
|
||||
tick.date, tick.time = self.generateDateTime(data['E'])
|
||||
else:
|
||||
tick.askPrice1, tick.askVolume1, buf = data['asks'][0]
|
||||
tick.askPrice2, tick.askVolume2, buf = data['asks'][1]
|
||||
tick.askPrice3, tick.askVolume3, buf = data['asks'][2]
|
||||
tick.askPrice4, tick.askVolume4, buf = data['asks'][3]
|
||||
tick.askPrice5, tick.askVolume5, buf = data['asks'][4]
|
||||
|
||||
tick.bidPrice1, tick.bidVolume1, buf = data['bids'][0]
|
||||
tick.bidPrice2, tick.bidVolume2, buf = data['bids'][1]
|
||||
tick.bidPrice3, tick.bidVolume3, buf = data['bids'][2]
|
||||
tick.bidPrice4, tick.bidVolume4, buf = data['bids'][3]
|
||||
tick.bidPrice5, tick.bidVolume5, buf = data['bids'][4]
|
||||
|
||||
tick.askPrice1 = float(tick.askPrice1)
|
||||
tick.askPrice2 = float(tick.askPrice2)
|
||||
tick.askPrice3 = float(tick.askPrice3)
|
||||
tick.askPrice4 = float(tick.askPrice4)
|
||||
tick.askPrice5 = float(tick.askPrice5)
|
||||
|
||||
tick.bidPrice1 = float(tick.bidPrice1)
|
||||
tick.bidPrice2 = float(tick.bidPrice2)
|
||||
tick.bidPrice3 = float(tick.bidPrice3)
|
||||
tick.bidPrice4 = float(tick.bidPrice4)
|
||||
tick.bidPrice5 = float(tick.bidPrice5)
|
||||
|
||||
tick.askVolume1 = float(tick.askVolume1)
|
||||
tick.askVolume2 = float(tick.askVolume2)
|
||||
tick.askVolume3 = float(tick.askVolume3)
|
||||
tick.askVolume4 = float(tick.askVolume4)
|
||||
tick.askVolume5 = float(tick.askVolume5)
|
||||
|
||||
tick.bidVolume1 = float(tick.bidVolume1)
|
||||
tick.bidVolume2 = float(tick.bidVolume2)
|
||||
tick.bidVolume3 = float(tick.bidVolume3)
|
||||
tick.bidVolume4 = float(tick.bidVolume4)
|
||||
tick.bidVolume5 = float(tick.bidVolume5)
|
||||
|
||||
self.gateway.onTick(copy(tick))
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onDataStreamError(self, msg):
|
||||
""""""
|
||||
self.writeLog(msg)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onUserStreamError(self, msg):
|
||||
""""""
|
||||
self.writeLog(msg)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def generateDateTime(self, s):
|
||||
"""生成时间"""
|
||||
dt = datetime.fromtimestamp(float(s)/1e3)
|
||||
time = dt.strftime("%H:%M:%S.%f")
|
||||
date = dt.strftime("%Y%m%d")
|
||||
return date, time
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, orderReq):
|
||||
""""""
|
||||
orderReq.volume = 0.02
|
||||
|
||||
self.orderId += 1
|
||||
orderId = self.date + str(self.orderId).rjust(6, '0')
|
||||
vtOrderID = '.'.join([self.gatewayName, orderId])
|
||||
side = directionMap.get(orderReq.direction, '')
|
||||
type_ = priceTypeMap.get(orderReq.priceType, PRICETYPE_LIMITPRICE)
|
||||
|
||||
self.newOrder(orderReq.symbol, side, type_, orderReq.price,
|
||||
orderReq.volume, 'GTC', newClientOrderId=orderId)
|
||||
|
||||
return vtOrderID
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancel(self, cancelOrderReq):
|
||||
""""""
|
||||
self.cancelOrder(cancelOrderReq.symbol, origClientOrderId=cancelOrderReq.orderID)
|
Loading…
Reference in New Issue
Block a user