Merge pull request #634 from ipqhjjybj/dev
增加比特币交易所 ZB,OKEX,ZAIF,COINCHECK等接口实现,及OKEX,COINCHECK gateway实现
This commit is contained in:
commit
ec88ac1ce3
24
vnpy/api/coincheck/README.txt
Normal file
24
vnpy/api/coincheck/README.txt
Normal file
@ -0,0 +1,24 @@
|
||||
# vn.coincheck
|
||||
|
||||
### 简介
|
||||
|
||||
coincheck 的比特币交易接口,基于Rest API开发,实现了官方提供API的全部功能。
|
||||
|
||||
|
||||
|
||||
|
||||
### 特点
|
||||
|
||||
1. 面向对象的API设计,接近CTP API的结构,对于国内用户而言更容易上手
|
||||
|
||||
2. 参考CTP API的设计,主动函数调用的结果通过异步(回调函数)的方式推送到程序中,适用于开发稳定可靠的实盘交易程序
|
||||
|
||||
|
||||
|
||||
链接:
|
||||
|
||||
https://coincheck.com/cn/documents/exchange/api
|
||||
https://coincheck.com/cn/documents/exchange/api#public
|
||||
https://coincheck.com/cn/documents/exchange/api#private
|
||||
|
||||
https://github.com/kmn/coincheck
|
3
vnpy/api/coincheck/__init__.py
Normal file
3
vnpy/api/coincheck/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vncoincheck import TradeApi, DataApi
|
34
vnpy/api/coincheck/test.html
Normal file
34
vnpy/api/coincheck/test.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>WebSocket 客户端</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
var socket;
|
||||
|
||||
socket = new WebSocket("wss://ws-api.coincheck.com/");
|
||||
|
||||
socket.onopen = function() {
|
||||
alert("Socket open");
|
||||
socket.send(JSON.stringify({type: "subscribe", channel: "btc_jpy-orderbook"}));
|
||||
//socket.send("这是来自客户端的消息" + location.href + new Date());
|
||||
};
|
||||
|
||||
socket.onmessage = function(e){
|
||||
var data = e.data;
|
||||
//处理数据
|
||||
alert(data);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
65
vnpy/api/coincheck/test.py
Normal file
65
vnpy/api/coincheck/test.py
Normal file
@ -0,0 +1,65 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from vncoincheck import *
|
||||
|
||||
def testTrade():
|
||||
"""测试交易"""
|
||||
accessKey = '你的accessKey'
|
||||
secretKey = '你的secretKey'
|
||||
|
||||
# 创建API对象并初始化
|
||||
api = TradeApi()
|
||||
api.DEBUG = True
|
||||
api.init(accessKey, secretKey)
|
||||
|
||||
# 查询账户,测试通过
|
||||
#api.get_info()
|
||||
|
||||
# api.get_info()
|
||||
#api.get_balance()
|
||||
# api.get_balance()
|
||||
|
||||
#api.buy_btc_jpy(rate = 200 , amount = 0.005)
|
||||
|
||||
#api.cancel_orders("439397799")
|
||||
|
||||
for i in range(10):
|
||||
api.buy_btc_jpy(rate = 200 , amount = 0.005)
|
||||
#api.sell_btc_jpy(rate = 200000 , amount = 0.005)
|
||||
|
||||
#orders = api.list_orders()
|
||||
|
||||
#sleep(3)
|
||||
# for d in orders:
|
||||
# api.cancel_orders( d["id"])
|
||||
|
||||
#sleep(0.3)
|
||||
|
||||
# 查询委托,测试通过
|
||||
#api.active_orders( currency_pair = SYMBOL_BTCJPY )
|
||||
|
||||
# 阻塞
|
||||
input()
|
||||
|
||||
|
||||
def testData():
|
||||
"""测试行情接口"""
|
||||
api = DataApi()
|
||||
|
||||
api.init(0.5 , 1)
|
||||
|
||||
# 订阅成交推送,测试通过
|
||||
# api.subscribeTick(SYMBOL_BTCJPY)
|
||||
# 订阅成交记录
|
||||
# api.subscribeTrades(SYMBOL_BTCJPY)
|
||||
# # 订阅十档行情
|
||||
api.subscribeOrderbooks(SYMBOL_BTCJPY)
|
||||
|
||||
|
||||
input()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
testTrade()
|
||||
#testTrade()
|
||||
#testData()
|
6
vnpy/api/coincheck/test2.py
Normal file
6
vnpy/api/coincheck/test2.py
Normal file
@ -0,0 +1,6 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from coincheck import order,market,account
|
||||
|
||||
ok = order.Order(access_key="你的accessKey", secret_key="你的secretKey")
|
||||
print ok.buy_btc_jpy(amount=0.01,rate=200)
|
31
vnpy/api/coincheck/test3.py
Normal file
31
vnpy/api/coincheck/test3.py
Normal file
@ -0,0 +1,31 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from vncoincheck import *
|
||||
|
||||
import socket
|
||||
import json
|
||||
import websocket
|
||||
from websocket import create_connection
|
||||
|
||||
ws = None
|
||||
def open():
|
||||
global ws
|
||||
print "open"
|
||||
ws.send( json.dumps({"type": "subscribe", "channel": "btc_jpy-trades"}))
|
||||
|
||||
def testWebsocket():
|
||||
global ws
|
||||
|
||||
while 1:
|
||||
ws = create_connection("wss://ws-api.coincheck.com/", on_open=open)
|
||||
if ws.connected:
|
||||
|
||||
print ws.recv()
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 阻塞
|
||||
input()
|
||||
|
||||
if __name__ == '__main__':
|
||||
testWebsocket()
|
18
vnpy/api/coincheck/test4.py
Normal file
18
vnpy/api/coincheck/test4.py
Normal file
@ -0,0 +1,18 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from vncoincheck import *
|
||||
|
||||
def test():
|
||||
|
||||
api = DataApiSocket()
|
||||
api.connect("wss://ws-api.coincheck.com")
|
||||
|
||||
sleep(2)
|
||||
api.sendOrderbookRequest()
|
||||
|
||||
api.sendTradesRequest()
|
||||
|
||||
raw_input()
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
460
vnpy/api/coincheck/vncoincheck.py
Normal file
460
vnpy/api/coincheck/vncoincheck.py
Normal file
@ -0,0 +1,460 @@
|
||||
# encoding: utf-8
|
||||
|
||||
import urllib
|
||||
import hashlib
|
||||
|
||||
import json
|
||||
import requests
|
||||
import hmac
|
||||
import time
|
||||
from datetime import datetime
|
||||
from time import time, sleep , mktime
|
||||
from Queue import Queue, Empty
|
||||
from threading import Thread
|
||||
import urllib
|
||||
import websocket
|
||||
|
||||
import inspect
|
||||
import requests
|
||||
import cerberus
|
||||
|
||||
CURRENCY_JPY = "jpy"
|
||||
SYMBOL_BTCJPY = 'btc_jpy'
|
||||
|
||||
FUNCTIONCODE_GET_INFO_COINCHECK = 'get_info'
|
||||
FUNCTIONCODE_GET_BALANCE_COINCHECK = 'get_balance'
|
||||
FUNCTIONCODE_LIST_ORDER_COINCHECK = 'list_order'
|
||||
FUNCTIONCODE_BUY_ORDER_COINCHECK = 'buy'
|
||||
FUNCTIONCODE_SELL_ORDER_COINCHECK = 'sell'
|
||||
FUNCTIONCODE_CANCEL_ORDER_COINCHECK = 'cancel_order'
|
||||
FUNCTIONCODE_HISTORY_ORDER_COINCHECK = 'history'
|
||||
|
||||
class TradeApi(object):
|
||||
API_account = "https://coincheck.com/api/accounts"
|
||||
API_balance = "https://coincheck.com/api/accounts/balance"
|
||||
API_trade = "https://coincheck.com/api/exchange/orders"
|
||||
API_list_order = "https://coincheck.com/api/exchange/orders/opens"
|
||||
API_history_order = "https://coincheck.com/api/exchange/orders/transactions"
|
||||
API_cancel_order = "https://coincheck.com/api/exchange/orders/%s"
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.accessKey = ''
|
||||
self.secretKey = ''
|
||||
|
||||
self.active = False # API工作状态
|
||||
self.reqID = 0 # 请求编号
|
||||
#self.reqQueue = Queue() # 请求队列
|
||||
self.reqQueue = [] # 请求的队列
|
||||
self.reqThread = Thread(target=self.processQueue) # 请求处理线程
|
||||
self.nonce = int(mktime(datetime.now().timetuple())) * 1000000000
|
||||
|
||||
|
||||
def make_header(self , url , body = ""):
|
||||
''' create request header function
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
'''
|
||||
self.nonce += 1
|
||||
nonce = str(self.nonce)
|
||||
message = nonce + url + body
|
||||
signature = hmac.new(self.secretKey.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()
|
||||
headers = {
|
||||
'ACCESS-KEY' : self.accessKey,
|
||||
'ACCESS-NONCE' : nonce,
|
||||
'ACCESS-SIGNATURE': signature
|
||||
}
|
||||
return headers
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def processRequest(self, req):
|
||||
"""处理请求"""
|
||||
# 读取方法和参数
|
||||
url = req['url']
|
||||
method = req['method']
|
||||
|
||||
r = None
|
||||
headers = self.make_header(url)
|
||||
if method in [FUNCTIONCODE_GET_INFO_COINCHECK , FUNCTIONCODE_GET_BALANCE_COINCHECK , FUNCTIONCODE_LIST_ORDER_COINCHECK , FUNCTIONCODE_HISTORY_ORDER_COINCHECK]:
|
||||
r = requests.get(url , headers=headers)
|
||||
elif method in [FUNCTIONCODE_CANCEL_ORDER_COINCHECK]:
|
||||
r = requests.delete(url , headers=headers)
|
||||
elif method in [FUNCTIONCODE_BUY_ORDER_COINCHECK , FUNCTIONCODE_SELL_ORDER_COINCHECK]:
|
||||
kwargs = req["kwargs"]
|
||||
payload = { 'rate': kwargs['rate'],
|
||||
'amount': kwargs['amount'],
|
||||
'order_type': method,
|
||||
'pair': SYMBOL_BTCJPY}
|
||||
body = 'rate={rate}&amount={amount}&order_type={order_type}&pair={pair}'.format(**payload)
|
||||
headers = self.make_header(url , body)
|
||||
r = requests.post(url,headers=headers,data=body)
|
||||
|
||||
# print r
|
||||
# from coincheck import order,market,account
|
||||
# ok = order.Order(access_key=self.accessKey, secret_key=self.secretKey)
|
||||
# print "???"
|
||||
# return ok.buy_btc_jpy(amount=0.01,rate=200)
|
||||
#return self.create2( rate = 200 , amount = 0.01 , order_type = 'buy' , pair = 'btc_jpy')
|
||||
|
||||
#self.create2( rate = 200 , amount = 0.01 , order_type = 'buy' , pair = 'btc_jpy')
|
||||
#r = self.create( rate = 200 , amount = 0.01 , order_type = 'buy' , pair = 'btc_jpy')
|
||||
|
||||
# print "!!!!!!!!!!!!!!" ,method
|
||||
# if method in [FUNCTIONCODE_BUY_ORDER_COINCHECK , FUNCTIONCODE_SELL_ORDER_COINCHECK]:
|
||||
# print "what?" , method , r.status_code
|
||||
# elif method in [FUNCTIONCODE_LIST_ORDER_COINCHECK]:
|
||||
# print "list?" , method , r.status_code
|
||||
|
||||
if r != None and r.status_code == 200:
|
||||
data = r.json()
|
||||
if data['success'] == 0:
|
||||
print "error in coincheck %s" % method
|
||||
return data
|
||||
else:
|
||||
return data
|
||||
else:
|
||||
try:
|
||||
data = json.loads(r.text)
|
||||
return data
|
||||
except Exception,ex:
|
||||
return None
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def processQueue(self):
|
||||
"""处理请求队列中的请求"""
|
||||
while self.active:
|
||||
try:
|
||||
#print "processQueue"
|
||||
#req = self.reqQueue.get(block=True, timeout=0.001) # 获取请求的阻塞为一秒
|
||||
# out_arr = [s[0] for s in self.reqQueue]
|
||||
# print out_arr
|
||||
|
||||
(Type , req) = self.reqQueue[0]
|
||||
|
||||
callback = req['callback']
|
||||
reqID = req['reqID']
|
||||
|
||||
data = self.processRequest(req)
|
||||
# 请求成功
|
||||
if data != None :
|
||||
if self.DEBUG:
|
||||
print callback.__name__
|
||||
callback(data, req, reqID)
|
||||
|
||||
self.reqQueue.pop(0)
|
||||
sleep(0.1)
|
||||
except Exception,ex:
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendRequest(self, url , method, callback, kwargs = None,optional=None):
|
||||
"""发送请求"""
|
||||
# 请求编号加1
|
||||
self.reqID += 1
|
||||
|
||||
# 生成请求字典并放入队列中
|
||||
req = {}
|
||||
req['url'] = url
|
||||
req['method'] = method
|
||||
req['callback'] = callback
|
||||
req['optional'] = optional
|
||||
req['kwargs'] = kwargs
|
||||
req['reqID'] = self.reqID
|
||||
|
||||
#if method in [FUNCTIONCODE_LIST_ORDER_COINCHECK , FUNCTIONCODE_GET_BALANCE_COINCHECK , FUNCTIONCODE_BUY_ORDER_COINCHECK , FUNCTIONCODE_SELL_ORDER_COINCHECK]:
|
||||
if method in [FUNCTIONCODE_LIST_ORDER_COINCHECK , FUNCTIONCODE_GET_BALANCE_COINCHECK ]:
|
||||
flag = False
|
||||
for use_method ,r in self.reqQueue:
|
||||
if use_method == method:
|
||||
flag = True
|
||||
break
|
||||
if False == flag:
|
||||
self.reqQueue.append( (method , req))
|
||||
else:
|
||||
self.reqQueue.append( (method , req))
|
||||
#self.reqQueue.put(req)
|
||||
|
||||
# 返回请求编号
|
||||
return self.reqID
|
||||
|
||||
####################################################
|
||||
## 主动函数
|
||||
####################################################
|
||||
#----------------------------------------------------------------------
|
||||
def init(self, accessKey, secretKey):
|
||||
"""初始化"""
|
||||
self.accessKey = accessKey
|
||||
self.secretKey = secretKey
|
||||
|
||||
self.active = True
|
||||
self.reqThread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def exit(self):
|
||||
"""退出"""
|
||||
self.active = False
|
||||
|
||||
if self.reqThread.isAlive():
|
||||
self.reqThread.join()
|
||||
|
||||
|
||||
def get_info(self):
|
||||
return self.sendRequest( self.API_account , FUNCTIONCODE_GET_INFO_COINCHECK , self.onGet_info , None)
|
||||
|
||||
def get_balance(self):
|
||||
return self.sendRequest( self.API_balance , FUNCTIONCODE_GET_BALANCE_COINCHECK , self.onGet_balance , None)
|
||||
|
||||
def buy_btc_jpy(self , **kwargs):
|
||||
print "buy_btc_jpy"
|
||||
return self.sendRequest( self.API_trade , FUNCTIONCODE_BUY_ORDER_COINCHECK , self.onBuy_btc , kwargs = kwargs, optional = None)
|
||||
|
||||
def sell_btc_jpy(self, **kwargs):
|
||||
print "sell_btc_jpy"
|
||||
return self.sendRequest( self.API_trade , FUNCTIONCODE_SELL_ORDER_COINCHECK , self.onSell_btc , kwargs = kwargs, optional = None)
|
||||
|
||||
def list_orders(self):
|
||||
return self.sendRequest( self.API_list_order , FUNCTIONCODE_LIST_ORDER_COINCHECK , self.onList_order , None)
|
||||
|
||||
def cancel_orders(self , order_id):
|
||||
return self.sendRequest( self.API_cancel_order % (str(order_id)) , FUNCTIONCODE_CANCEL_ORDER_COINCHECK , self.onCancel_orders , None)
|
||||
|
||||
def history_orders(self):
|
||||
return self.sendRequest( self.API_history_order , FUNCTIONCODE_HISTORY_ORDER_COINCHECK , self.onHistory_orders , None)
|
||||
|
||||
####################################################
|
||||
## 回调函数
|
||||
####################################################
|
||||
def onGet_info(self, data, req, reqID):
|
||||
print data
|
||||
def onGet_balance(self, data, req, reqID):
|
||||
print data
|
||||
def onBuy_btc(self, data, req, reqID):
|
||||
print data
|
||||
def onSell_btc(self, data, req, reqID):
|
||||
print data
|
||||
def onList_order(self, data, req, reqID):
|
||||
print data
|
||||
def onCancel_orders(self, data, req, reqID):
|
||||
print data
|
||||
def onHistory_orders(self, data, req, reqID):
|
||||
print data
|
||||
|
||||
class DataApiSocket(object):
|
||||
"""基于websocket的API对象"""
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self ):
|
||||
"""Constructor"""
|
||||
self.host = '' # 服务器地址
|
||||
|
||||
self.currency = '' # 货币类型(usd或者cny)
|
||||
|
||||
self.ws = None # websocket应用对象
|
||||
self.thread = None # 工作线程
|
||||
|
||||
#######################
|
||||
## 通用函数
|
||||
#######################
|
||||
#----------------------------------------------------------------------
|
||||
def onMessage(self, ws, evt):
|
||||
"""信息推送"""
|
||||
print 'onMessage'
|
||||
print evt
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, ws, evt):
|
||||
"""错误推送"""
|
||||
print 'onError'
|
||||
print evt
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onClose(self, ws):
|
||||
"""接口断开"""
|
||||
print 'onClose'
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOpen(self, ws):
|
||||
"""接口打开"""
|
||||
print "onOpen"
|
||||
self.sendOrderbookRequest()
|
||||
self.sendTradesRequest()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self, host, trace=False):
|
||||
"""连接服务器"""
|
||||
self.host = host
|
||||
|
||||
self.currency = CURRENCY_JPY
|
||||
|
||||
websocket.enableTrace(trace)
|
||||
|
||||
self.ws = websocket.WebSocketApp(host,
|
||||
on_message=self.onMessage,
|
||||
on_error=self.onError,
|
||||
on_close=self.onClose,
|
||||
on_open=self.onOpen)
|
||||
|
||||
self.thread = Thread(target=self.ws.run_forever)
|
||||
self.thread.start()
|
||||
|
||||
sleep(8)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def reconnect(self):
|
||||
"""重新连接"""
|
||||
# 首先关闭之前的连接
|
||||
self.close()
|
||||
|
||||
# 再执行重连任务
|
||||
self.ws = websocket.WebSocketApp(self.host,
|
||||
on_message=self.onMessage,
|
||||
on_error=self.onError,
|
||||
on_close=self.onClose,
|
||||
on_open=self.onOpen)
|
||||
|
||||
self.thread = Thread(target=self.ws.run_forever)
|
||||
self.thread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def exit(self):
|
||||
self.close()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭接口"""
|
||||
if self.thread and self.thread.isAlive():
|
||||
self.ws.close()
|
||||
self.thread.join()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrderbookRequest(self):
|
||||
"""发送行情请求 , 订阅 orderbook"""
|
||||
# 生成请求
|
||||
d = {}
|
||||
d['type'] = 'subscribe'
|
||||
d['channel'] = "btc_jpy-orderbook"
|
||||
|
||||
# 使用json打包并发送
|
||||
j = json.dumps(d)
|
||||
|
||||
# 若触发异常则重连
|
||||
try:
|
||||
self.ws.send(j)
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendTradesRequest(self):
|
||||
"""订阅最近的交易记录 """
|
||||
|
||||
# 生成请求
|
||||
d = {}
|
||||
d['type'] = 'subscribe'
|
||||
d['channel'] = "btc_jpy-trades"
|
||||
|
||||
# 使用json打包并发送
|
||||
j = json.dumps(d)
|
||||
|
||||
# 若触发异常则重连
|
||||
try:
|
||||
self.ws.send(j)
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class DataApi(object):
|
||||
|
||||
'''get latest information of coincheck market'''
|
||||
TICK_SYMBOL_URL = {
|
||||
SYMBOL_BTCJPY: 'https://coincheck.com/api/ticker'
|
||||
}
|
||||
'''get latest deal history of coincheck market'''
|
||||
TRADES_SYMBOL_URL = {
|
||||
SYMBOL_BTCJPY: 'https://coincheck.com/api/trades'
|
||||
}
|
||||
'''get latest asks/bids information of coincheck market'''
|
||||
ORDERBOOKERS_SYMBOL_URL = {
|
||||
SYMBOL_BTCJPY: 'https://coincheck.com/api/order_books'
|
||||
}
|
||||
|
||||
DEBUG = False
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.active = False
|
||||
|
||||
self.taskInterval = 0 # 每轮请求延时
|
||||
self.taskList = [] # 订阅的任务列表
|
||||
self.taskThread = Thread(target=self.run) # 处理任务的线程
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def init(self, interval, debug):
|
||||
"""初始化"""
|
||||
self.taskInterval = interval
|
||||
self.DEBUG = debug
|
||||
|
||||
self.active = True
|
||||
self.taskThread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def exit(self):
|
||||
"""退出"""
|
||||
self.active = False
|
||||
|
||||
if self.taskThread.isAlive():
|
||||
self.taskThread.join()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def run(self):
|
||||
"""连续运行"""
|
||||
while self.active:
|
||||
for url, callback in self.taskList:
|
||||
try:
|
||||
r = requests.get(url)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
if self.DEBUG:
|
||||
print callback.__name__
|
||||
callback(data)
|
||||
except Exception, e:
|
||||
print e
|
||||
sleep(self.taskInterval)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeTick(self, symbol):
|
||||
"""订阅实时成交数据"""
|
||||
url = self.TICK_SYMBOL_URL[symbol]
|
||||
task = (url, self.onTick)
|
||||
self.taskList.append(task)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeTrades(self, symbol):
|
||||
"""订阅实时成交数据"""
|
||||
url = self.TRADES_SYMBOL_URL[symbol]
|
||||
task = (url, self.onTrades)
|
||||
self.taskList.append(task)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeOrderbooks(self, symbol):
|
||||
"""订阅实时成交数据"""
|
||||
url = self.ORDERBOOKERS_SYMBOL_URL[symbol]
|
||||
task = (url, self.onOrderbooks)
|
||||
self.taskList.append(task)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTick(self, data):
|
||||
"""实时成交推送"""
|
||||
print data
|
||||
#----------------------------------------------------------------------
|
||||
def onTrades(self, data):
|
||||
"""实时成交推送"""
|
||||
print data
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOrderbooks(self, data):
|
||||
"""实时成交推送"""
|
||||
print data
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
3
vnpy/api/korbit/__init__.py
Normal file
3
vnpy/api/korbit/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnkorbit import Korbit_TradeApi, Korbit_DataApi , KORBIT_ALL_SYMBOL_PAIR , KORBIT_ALL_SYMBOLS
|
8
vnpy/api/korbit/korbit-python-master/.travis.yml
Normal file
8
vnpy/api/korbit/korbit-python-master/.travis.yml
Normal file
@ -0,0 +1,8 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.5"
|
||||
install: "pip install requests"
|
||||
script: "python -m unittest"
|
||||
notifications:
|
||||
slack: test1030team:nXUuJifoc4Rg8VFYmdW9cd1T
|
21
vnpy/api/korbit/korbit-python-master/LICENSE
Normal file
21
vnpy/api/korbit/korbit-python-master/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 HoonJin(Daniel) Ji
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
51
vnpy/api/korbit/korbit-python-master/README.md
Normal file
51
vnpy/api/korbit/korbit-python-master/README.md
Normal file
@ -0,0 +1,51 @@
|
||||
# korbit API wrapper for Python
|
||||
|
||||
You can get detail information of API in [API Reference](https://apidocs.korbit.co.kr/)
|
||||
|
||||
|
||||
## Installation
|
||||
Install from Git Repository
|
||||
```sh
|
||||
pip install git+https://github.com/HoonJin/korbit-python.git
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
You can use public API very easily.
|
||||
```python
|
||||
import korbit
|
||||
korbit.ticker()
|
||||
```
|
||||
|
||||
## If you want to use Exchange
|
||||
You have to get API key in [this page](https://www.korbit.co.kr/settings/api)
|
||||
```python
|
||||
import korbit
|
||||
api = korbit.PrivateAPI('your key', 'your secret')
|
||||
api.create_token_directly('your email', 'your pa$$w0rd')
|
||||
|
||||
api.market_ask_order(0.01)
|
||||
```
|
||||
|
||||
|
||||
## License
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2017 HoonJin(Daniel) Ji bwjhj1030@gmail.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
12
vnpy/api/korbit/korbit-python-master/korbit/__init__.py
Normal file
12
vnpy/api/korbit/korbit-python-master/korbit/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .private_api import PrivateAPI, PublicAPI
|
||||
|
||||
__public = PublicAPI()
|
||||
ticker = __public.ticker
|
||||
detailed_ticker = __public.detailed_ticker
|
||||
orderbook = __public.orderbook
|
||||
asks_orderbook = __public.asks_orderbook
|
||||
bids_orderbook = __public.bids_orderbook
|
||||
list_of_filled_orders = __public.list_of_filled_orders
|
||||
constants = __public.constants
|
173
vnpy/api/korbit/korbit-python-master/korbit/private_api.py
Normal file
173
vnpy/api/korbit/korbit-python-master/korbit/private_api.py
Normal file
@ -0,0 +1,173 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
from public_api import PublicAPI
|
||||
|
||||
|
||||
class PrivateAPI(PublicAPI):
|
||||
def __init__(self, client_id, secret, production=True, version="v1", timeout=20):
|
||||
try:
|
||||
super(self.__class__, self).__init__(production, version, timeout)
|
||||
except TypeError:
|
||||
PublicAPI.__init__(self, production, version, timeout)
|
||||
self.__client_id = client_id
|
||||
self.__secret = secret
|
||||
self.__token = {}
|
||||
|
||||
# https://apidocs.korbit.co.kr/#authentication
|
||||
def create_token_directly(self, username, password):
|
||||
payload = {
|
||||
'client_id': self.__client_id,
|
||||
'client_secret': self.__secret,
|
||||
'username': username,
|
||||
'password': password,
|
||||
'grant_type': "password"
|
||||
}
|
||||
self.__token = self.request_post("oauth2/access_token", data=payload)
|
||||
return self.__token
|
||||
|
||||
def set_token(self, token):
|
||||
self.__token = token
|
||||
|
||||
def refresh_token(self):
|
||||
payload = {
|
||||
'client_id': self.__client_id,
|
||||
'client_secret': self.__secret,
|
||||
'refresh_token': self.__token['refresh_token'],
|
||||
'grant_type': "refresh_token"
|
||||
}
|
||||
self.__token = self.request_post("oauth2/access_token", data=payload)
|
||||
return self.__token
|
||||
|
||||
def get_user_info(self):
|
||||
return self.request_get("user/info", headers=self.headers)
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
return {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': "{} {}".format(self.__token['token_type'], self.__token['access_token'])
|
||||
}
|
||||
|
||||
# https://apidocs.korbit.co.kr/#exchange
|
||||
def bid_order(self, bid_type, coin_amount=None, price=None, fiat_amount=None, currency_pair="btc_krw"):
|
||||
payload = {
|
||||
'type': bid_type,
|
||||
'currency_pair': currency_pair,
|
||||
'price': price,
|
||||
'coin_amount': coin_amount,
|
||||
'fiat_amount': fiat_amount,
|
||||
'nonce': self.nonce
|
||||
}
|
||||
#return self.request_post("user/orders/buy", headers=self.headers, data=payload)
|
||||
return self.request_post("user/orders/buy", headers=self.headers, data=payload)
|
||||
|
||||
def market_bid_order(self, fiat_amount, currency_pair="btc_krw"):
|
||||
return self.bid_order('market', fiat_amount=fiat_amount, currency_pair=currency_pair)
|
||||
|
||||
def limit_bid_order(self, coin_amount, price, currency_pair="btc_krw"):
|
||||
return self.bid_order('limit', coin_amount=coin_amount, price=price, currency_pair=currency_pair)
|
||||
|
||||
def ask_order(self, ask_type, coin_amount, price=None, currency_pair="btc_krw"):
|
||||
payload = {
|
||||
'type': ask_type,
|
||||
'currency_pair': currency_pair,
|
||||
'price': price,
|
||||
'coin_amount': coin_amount,
|
||||
'nonce': self.nonce
|
||||
}
|
||||
return self.request_post("user/orders/sell", headers=self.headers, data=payload)
|
||||
|
||||
def market_ask_order(self, coin_amount, currency_pair="btc_krw"):
|
||||
return self.ask_order('market', coin_amount=coin_amount, currency_pair=currency_pair)
|
||||
|
||||
def limit_ask_order(self, coin_amount, price, currency_pair="btc_krw"):
|
||||
return self.ask_order('limit', coin_amount, price, currency_pair)
|
||||
|
||||
def cancel_order(self, ids, currency_pair="btc_krw"):
|
||||
payload = {
|
||||
'id': ids,
|
||||
'currency_pair': currency_pair,
|
||||
'nonce': self.nonce
|
||||
}
|
||||
return self.request_post("user/orders/cancel", headers=self.headers, data=payload)
|
||||
|
||||
def list_open_orders(self, offset=0, limit=10, currency_pair="btc_krw"):
|
||||
params = {
|
||||
'currency_pair': currency_pair,
|
||||
'offset': offset,
|
||||
'limit': limit
|
||||
}
|
||||
return self.request_get("user/orders/open", headers=self.headers, params=params)
|
||||
|
||||
def view_exchange_orders(self, offset=0, limit=10, currency_pair="btc_krw"):
|
||||
params = {
|
||||
'currency_pair': currency_pair,
|
||||
'offset': offset,
|
||||
'limit': limit
|
||||
}
|
||||
print "user/orders" , self.headers , params
|
||||
return self.request_get("user/orders", headers=self.headers, params=params)
|
||||
|
||||
def view_transfers(self, offset=0, limit=10, currency="btc"):
|
||||
params = {
|
||||
'currency': currency,
|
||||
'offset': offset,
|
||||
'limit': limit
|
||||
}
|
||||
return self.request_get("user/transfers", headers=self.headers, params=params)
|
||||
|
||||
def trading_volume_and_fees(self, currency_pair="all"):
|
||||
params = {
|
||||
'currency_pair': currency_pair
|
||||
}
|
||||
return self.request_get("user/volume", headers=self.headers, params=params)
|
||||
|
||||
# https://apidocs.korbit.co.kr/#wallet
|
||||
def user_balances(self):
|
||||
return self.request_get("user/balances", headers=self.headers)
|
||||
|
||||
def user_accounts(self):
|
||||
return self.request_get("user/accounts", headers=self.headers)
|
||||
|
||||
def retrieve_wallet_status(self, currency_pair="btc_krw"):
|
||||
params = {
|
||||
'currency_pair': currency_pair
|
||||
}
|
||||
return self.request_get("user/wallet", headers=self.headers, params=params)
|
||||
|
||||
def assign_btc_address(self, currency="btc"):
|
||||
payload = {
|
||||
'currency': currency,
|
||||
'nonce': self.nonce
|
||||
}
|
||||
return self.request_post("user/coins/address/assign", headers=self.headers, data=payload)
|
||||
|
||||
def request_btc_withdrawal(self, address, amount, currency="btc"):
|
||||
payload = {
|
||||
'address': address,
|
||||
'amount': amount,
|
||||
'currency': currency,
|
||||
'nonce': self.nonce
|
||||
}
|
||||
return self.request_post("user/coins/out", headers=self.headers, data=payload)
|
||||
|
||||
def status_of_btc_deposit_and_transfer(self, transfer_id="", currency="btc"):
|
||||
params = {
|
||||
'currency': currency
|
||||
}
|
||||
if transfer_id != "":
|
||||
params['id'] = transfer_id
|
||||
|
||||
return self.request_get("user/coins/status", headers=self.headers, params=params)
|
||||
|
||||
def cancel_btc_transfer_request(self, transfer_id, currency="btc"):
|
||||
payload = {
|
||||
'id': transfer_id,
|
||||
'currency': currency,
|
||||
'nonce': self.nonce
|
||||
}
|
||||
return self.request_post("user/coins/out/cancel", headers=self.headers, data=payload)
|
||||
|
||||
@property
|
||||
def nonce(self):
|
||||
return int(time.time() * 1000)
|
77
vnpy/api/korbit/korbit-python-master/korbit/public_api.py
Normal file
77
vnpy/api/korbit/korbit-python-master/korbit/public_api.py
Normal file
@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import requests
|
||||
import json
|
||||
import logging
|
||||
try:
|
||||
from urllib.parse import urljoin
|
||||
except ImportError:
|
||||
from urlparse import urljoin
|
||||
|
||||
|
||||
class PublicAPI:
|
||||
def __init__(self, production=True, version="v1", timeout=20):
|
||||
self.__host = production and "https://api.korbit.co.kr/%s/" % version \
|
||||
or "https://api.korbit-test.com/%s/" % version
|
||||
self.__timeout = timeout
|
||||
|
||||
# https://apidocs.korbit.co.kr/#public
|
||||
def ticker(self, currency_pair="btc_krw"):
|
||||
params = {
|
||||
'currency_pair': currency_pair
|
||||
}
|
||||
return self.request_get("ticker", params=params)
|
||||
|
||||
def detailed_ticker(self, currency_pair="btc_krw"):
|
||||
params = {
|
||||
'currency_pair': currency_pair
|
||||
}
|
||||
return self.request_get("ticker/detailed", params=params)
|
||||
|
||||
def orderbook(self, currency_pair="btc_krw", category="all", group=True):
|
||||
params = {
|
||||
'group': group,
|
||||
'category': category,
|
||||
'currency_pair': currency_pair
|
||||
}
|
||||
return self.request_get("orderbook", params=params)
|
||||
|
||||
def bids_orderbook(self, currency_pair="btc_krw", group=True):
|
||||
return self.orderbook(currency_pair=currency_pair, category="bid", group=group)
|
||||
|
||||
def asks_orderbook(self, currency_pair="btc_krw", group=True):
|
||||
return self.orderbook(currency_pair=currency_pair, category="ask", group=group)
|
||||
|
||||
def list_of_filled_orders(self, currency_pair="btc_krw", interval="hour"):
|
||||
params = {
|
||||
'time': interval,
|
||||
'currency_pair': currency_pair
|
||||
}
|
||||
return self.request_get("transactions", params=params)
|
||||
|
||||
def constants(self):
|
||||
return self.request_get("constants")
|
||||
|
||||
def request_get(self, path, headers=None, params=None):
|
||||
print urljoin(self.host, path)
|
||||
response = requests.get(urljoin(self.host, path), headers=headers, params=params, timeout=self.__timeout)
|
||||
print response
|
||||
try:
|
||||
return response.json()
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
logging.error("exception: {}, response_text: {}".format(e, response.text))
|
||||
return response.text
|
||||
|
||||
def request_post(self, path, headers=None, data=None):
|
||||
print urljoin(self.host, path) , headers , data
|
||||
response = requests.post(urljoin(self.host, path), headers=headers, data=data, timeout=self.__timeout)
|
||||
print response
|
||||
try:
|
||||
return response.json()
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
logging.error("exception: {}, response_text: {}".format(e, response.text))
|
||||
return response.text
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
return self.__host
|
11
vnpy/api/korbit/korbit-python-master/korbit/test.py
Normal file
11
vnpy/api/korbit/korbit-python-master/korbit/test.py
Normal file
@ -0,0 +1,11 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from private_api import *
|
||||
|
||||
api = PrivateAPI('FssV9Jw0aZVCOYbu9YlqVNc2Rhgmh1QpruNgSVhvVmFP1iw1aPfc3APbfz0ZM', 'lSMc1TT2AZYX5pyEm01SFqx7PgMjNB18eXWpi8DQKHf0rcYhLiaiRVzJwaVZR')
|
||||
|
||||
print api.create_token_directly('xiaoshuang8921@naver.com', 'Wxiaoshuang8921')
|
||||
|
||||
# print api.view_exchange_orders( 0 , 10 , "btc_krw")
|
||||
#api.limit_bid_order(coin_amount = 0.01 , price = 1000 , currency_pair="btc_krw")
|
||||
api.limit_ask_order(coin_amount = 0.01 , price = 10000000 , currency_pair = "bch_krw")
|
24
vnpy/api/korbit/korbit-python-master/setup.py
Normal file
24
vnpy/api/korbit/korbit-python-master/setup.py
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
setup(
|
||||
name='korbit-python',
|
||||
packages=['korbit'],
|
||||
version='0.5.0',
|
||||
description='korbit API wrapper for Python',
|
||||
url='http://github.com/Hoonjin/korbit-python/',
|
||||
author='Daniel Ji',
|
||||
author_email='bwjhj1030@gmail.com',
|
||||
install_requires=['requests'],
|
||||
classifiers=[
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules"
|
||||
],
|
||||
license='MIT',
|
||||
)
|
12
vnpy/api/korbit/korbit-python-master/tests/test.py
Normal file
12
vnpy/api/korbit/korbit-python-master/tests/test.py
Normal file
@ -0,0 +1,12 @@
|
||||
import unittest
|
||||
import korbit
|
||||
|
||||
|
||||
class TestKorbit(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_ticker(self):
|
||||
ticker = korbit.ticker()
|
||||
self.assertTrue('last' in ticker.keys())
|
||||
self.assertTrue('timestamp' in ticker.keys())
|
5
vnpy/api/korbit/readme.txt
Normal file
5
vnpy/api/korbit/readme.txt
Normal file
@ -0,0 +1,5 @@
|
||||
korbit 韩国交易所
|
||||
https://apidocs.korbit.co.kr/#exchange:-public
|
||||
|
||||
参考代码文档:
|
||||
https://github.com/HoonJin/korbit-python
|
1
vnpy/api/korbit/run.bat
Normal file
1
vnpy/api/korbit/run.bat
Normal file
@ -0,0 +1 @@
|
||||
python test.py
|
34
vnpy/api/korbit/test.py
Normal file
34
vnpy/api/korbit/test.py
Normal file
@ -0,0 +1,34 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnkorbit import Korbit_TradeApi , Korbit_DataApi
|
||||
|
||||
apiKey = 'FssV9Jw0aZVCOYbu9YlqVNc2Rhgmh1QpruNgSVhvVmFP1iw1aPfc3APbfz0ZM'
|
||||
secretKey = 'lSMc1TT2AZYX5pyEm01SFqx7PgMjNB18eXWpi8DQKHf0rcYhLiaiRVzJwaVZR'
|
||||
|
||||
|
||||
def testData():
|
||||
d = Korbit_DataApi()
|
||||
|
||||
d.init(0.5 , 1)
|
||||
d.subscribeTick("btc_krw")
|
||||
|
||||
# d.subscribeTrades("btc_krw")
|
||||
|
||||
# d.subscribeOrderbooks("btc_krw")
|
||||
|
||||
|
||||
def testTrade():
|
||||
global apiKey , secretKey
|
||||
d = Korbit_TradeApi()
|
||||
d.init(apiKey , secretKey , "xiaoshuang8921@naver.com" , "Wxiaoshuang8921")
|
||||
|
||||
# print d.headers
|
||||
d.list_market_orders(currency_pair = 'bch_krw' , offset = 0 , limit = 10)
|
||||
#print d.buy_currency( coin_amount = 0.01 , price = 10000 , currency_pair = 'bch_krw')
|
||||
|
||||
#print d.sell_currency( coin_amount = 0.01 , price = 10000000 , currency_pair = 'bch_krw')
|
||||
# 4441418
|
||||
#print d.cancel_orders( order_id = 4441418 , currency_pair = 'bch_krw')
|
||||
|
||||
# testData()
|
||||
testTrade()
|
377
vnpy/api/korbit/vnkorbit.py
Normal file
377
vnpy/api/korbit/vnkorbit.py
Normal file
@ -0,0 +1,377 @@
|
||||
# encoding: utf-8
|
||||
|
||||
import urllib
|
||||
import hashlib
|
||||
|
||||
import json
|
||||
import requests
|
||||
import hmac
|
||||
import time
|
||||
from datetime import datetime
|
||||
from time import time, sleep , mktime
|
||||
from Queue import Queue, Empty
|
||||
from threading import Thread
|
||||
import urllib
|
||||
import websocket
|
||||
|
||||
import inspect
|
||||
import requests
|
||||
import cerberus
|
||||
|
||||
|
||||
korbit_host = "https://api.korbit.co.kr/v1/"
|
||||
|
||||
|
||||
KORBITFUNCTIONCODE_LIMIT_BID_ORDER = 'limit_bid_order'
|
||||
KORBITFUNCTIONCODE_LIMIT_ASK_ORDER = 'limit_ask_order'
|
||||
KORBITFUNCTIONCODE_CANCEL_ORDER = 'cancel'
|
||||
KORBITFUNCTIONCODE_LIST_OPEN_ORDERS = 'list_open_orders' # 不实现这个了,好像用不到,获得所有的 开仓单
|
||||
KORBITFUNCTIONCODE_LIST_EXCHANGE_ORDERS = 'list_exchange_orders' # 实现这个,获得所有的在交易的单子
|
||||
KORBITFUNCTIONCODE_LIST_TRANSFERS = 'list_transfers' # 不实现这个,获得所有的 提现充值订单
|
||||
KORBITFUNCTIONCODE_FEE = 'fee' # 不实现这个,获得交易所手续费
|
||||
KORBITFUNCTIONCODE_BALANCES = "balances" # 实现这个,获取用户账户持仓信息
|
||||
KORBITFUNCTIONCODE_ACCOUNTS = "accounts" # 不实现这个,获取用户的提币地址
|
||||
|
||||
KORBIT_ALL_SYMBOL_PAIR = ["btc_krw","bch_krw" , "eth_krw"]
|
||||
KORBIT_ALL_SYMBOLS = ["krw","btc","eth","etc","xrp","bch"]
|
||||
|
||||
class Korbit_TradeApi(object):
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.accessKey = ''
|
||||
self.secretKey = ''
|
||||
self.__token = {}
|
||||
|
||||
self.active = False # API 工作状态
|
||||
self.reqID = 0 # 请求编号
|
||||
|
||||
# self.reqQueue = Queue() # 请求队列
|
||||
self.reqQueue = [] # 请求的队列
|
||||
|
||||
self.reqThread = Thread(target=self.processQueue) # 请求处理线程
|
||||
|
||||
self.DEBUG = True
|
||||
|
||||
'''
|
||||
直接发送 request ,获得身份
|
||||
'''
|
||||
def request_post(self, path, headers=None, data=None):
|
||||
url = korbit_host + path
|
||||
response = requests.post(url, headers=headers, data=data, timeout=20)
|
||||
try:
|
||||
return response.json()
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print "exception: {}, response_text: {}".format(e, response.text)
|
||||
return response.text
|
||||
|
||||
'''
|
||||
直接发送 get request ,获得其他数据
|
||||
'''
|
||||
def request_get(self, url, headers=None, params=None):
|
||||
response = requests.get( url, headers=headers, params=params, timeout=self.__timeout)
|
||||
try:
|
||||
return response.json()
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
logging.error("exception: {}, response_text: {}".format(e, response.text))
|
||||
return response.text
|
||||
'''
|
||||
直接发送 request ,获得 token
|
||||
'''
|
||||
# https://apidocs.korbit.co.kr/#authentication
|
||||
def create_token_directly(self, username, password):
|
||||
payload = {
|
||||
'client_id': self.accessKey,
|
||||
'client_secret': self.secretKey,
|
||||
'username': username,
|
||||
'password': password,
|
||||
'grant_type': "password"
|
||||
}
|
||||
self.__token = self.request_post("oauth2/access_token", data=payload)
|
||||
return self.__token
|
||||
|
||||
def refresh_token(self):
|
||||
payload = {
|
||||
'client_id': self.__client_id,
|
||||
'client_secret': self.__secret,
|
||||
'refresh_token': self.__token['refresh_token'],
|
||||
'grant_type': "refresh_token"
|
||||
}
|
||||
self.__token = self.request_post("oauth2/access_token", data=payload)
|
||||
return self.__token
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def exit(self):
|
||||
"""退出"""
|
||||
self.active = False
|
||||
|
||||
if self.reqThread.isAlive():
|
||||
self.reqThread.join()
|
||||
|
||||
@property
|
||||
def nonce(self):
|
||||
return int(time() * 1000)
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
return {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': "{} {}".format(self.__token['token_type'], self.__token['access_token'])
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def processRequest(self , req):
|
||||
"""处理请求"""
|
||||
# 读取方法和参数
|
||||
url = req['url']
|
||||
method = req['method']
|
||||
|
||||
r = None
|
||||
headers = self.headers
|
||||
kwargs = req["kwargs"]
|
||||
if method in [KORBITFUNCTIONCODE_LIMIT_BID_ORDER , KORBITFUNCTIONCODE_LIMIT_ASK_ORDER, KORBITFUNCTIONCODE_CANCEL_ORDER]:
|
||||
if method in [KORBITFUNCTIONCODE_CANCEL_ORDER]:
|
||||
payload = {
|
||||
'id': kwargs["ids"],
|
||||
'currency_pair': kwargs["currency_pair"],
|
||||
'nonce': self.nonce
|
||||
}
|
||||
r = requests.post(url , headers=headers , data = payload)
|
||||
else:
|
||||
payload = {
|
||||
'type': 'limit',
|
||||
'currency_pair': kwargs["currency_pair"],
|
||||
'price': kwargs["price"],
|
||||
'coin_amount': kwargs["coin_amount"],
|
||||
'fiat_amount': None,
|
||||
'nonce': self.nonce
|
||||
}
|
||||
|
||||
r = requests.post(url , headers=headers , data = payload)
|
||||
elif method in [KORBITFUNCTIONCODE_LIST_EXCHANGE_ORDERS , KORBITFUNCTIONCODE_BALANCES]:
|
||||
|
||||
if method in [KORBITFUNCTIONCODE_LIST_EXCHANGE_ORDERS]:
|
||||
payload = {
|
||||
'currency_pair': kwargs["currency_pair"],
|
||||
'offset': kwargs["offset"],
|
||||
'limit': kwargs["limit"]
|
||||
}
|
||||
r = requests.get(url , headers = headers , params = payload)
|
||||
|
||||
elif method in [KORBITFUNCTIONCODE_BALANCES]:
|
||||
|
||||
r = requests.get(url , headers = headers )
|
||||
|
||||
# print url , method, headers , kwargs , r
|
||||
if r != None and r.status_code == 200:
|
||||
data = r.json()
|
||||
return data
|
||||
else:
|
||||
try:
|
||||
data = json.loads(r.text)
|
||||
print "Error in r , " , data
|
||||
return data
|
||||
except Exception,ex:
|
||||
print ex
|
||||
return None
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def processQueue(self):
|
||||
"""处理请求队列中的请求"""
|
||||
while self.active:
|
||||
try:
|
||||
# req = self.reqQueue.get(block=True, timeout=0.1) # 获取请求的阻塞为一秒
|
||||
if len(self.reqQueue) > 0:
|
||||
(Type , req) = self.reqQueue[0]
|
||||
self.reqQueue.pop(0)
|
||||
|
||||
callback = req['callback']
|
||||
reqID = req['reqID']
|
||||
|
||||
data = self.processRequest(req)
|
||||
|
||||
# 请求成功
|
||||
if data != None :
|
||||
if self.DEBUG:
|
||||
print callback.__name__
|
||||
callback(data, req, reqID)
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
except Exception,ex:
|
||||
print ex
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendRequest(self, url , method, callback, kwargs = None,optional=None):
|
||||
"""发送请求"""
|
||||
# 请求编号加1
|
||||
self.reqID += 1
|
||||
|
||||
# 生成请求字典并放入队列中
|
||||
req = {}
|
||||
req['url'] = url
|
||||
req['method'] = method
|
||||
req['callback'] = callback
|
||||
req['optional'] = optional
|
||||
req['kwargs'] = kwargs
|
||||
req['reqID'] = self.reqID
|
||||
|
||||
if method in [KORBITFUNCTIONCODE_LIST_EXCHANGE_ORDERS ]:
|
||||
flag = False
|
||||
for use_method ,r in self.reqQueue:
|
||||
if use_method == method:
|
||||
flag = True
|
||||
break
|
||||
if False == flag:
|
||||
self.reqQueue.append( (method , req))
|
||||
else:
|
||||
self.reqQueue.append( (method , req))
|
||||
#self.reqQueue.put(req)
|
||||
|
||||
# 返回请求编号
|
||||
return self.reqID
|
||||
#----------------------------------------------------------------------
|
||||
def exit(self):
|
||||
"""退出"""
|
||||
self.active = False
|
||||
|
||||
if self.reqThread.isAlive():
|
||||
self.reqThread.join()
|
||||
|
||||
####################################################
|
||||
## 主动函数
|
||||
####################################################
|
||||
#----------------------------------------------------------------------
|
||||
def init(self, accessKey, secretKey , username , password):
|
||||
"""初始化"""
|
||||
self.accessKey = accessKey
|
||||
self.secretKey = secretKey
|
||||
|
||||
self.create_token_directly( username , password)
|
||||
|
||||
self.active = True
|
||||
self.reqThread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def buy_currency(self , coin_amount, price, currency_pair="btc_krw"):
|
||||
kwargs = {"currency_pair":currency_pair , "coin_amount":coin_amount , "price":int(price)}
|
||||
return self.sendRequest( korbit_host + "user/orders/buy" , KORBITFUNCTIONCODE_LIMIT_BID_ORDER , self.on_buy_currency , kwargs = kwargs, optional = None)
|
||||
#----------------------------------------------------------------------
|
||||
def sell_currency(self , coin_amount , price , currency_pair="btc_krw"):
|
||||
kwargs = {"currency_pair":currency_pair , "coin_amount":coin_amount , "price":int(price)}
|
||||
return self.sendRequest( korbit_host + "user/orders/sell" , KORBITFUNCTIONCODE_LIMIT_ASK_ORDER , self.on_sell_currency , kwargs = kwargs, optional = None)
|
||||
#----------------------------------------------------------------------
|
||||
def list_market_orders(self , currency_pair = "btc_krw" , offset = 0 , limit = 50):
|
||||
kwargs = {"currency_pair":currency_pair , "offset":offset , "limit":limit}
|
||||
return self.sendRequest( korbit_host + "user/orders",KORBITFUNCTIONCODE_LIST_EXCHANGE_ORDERS , self.on_list_exchange_orders , kwargs = kwargs , optional = None)
|
||||
#----------------------------------------------------------------------
|
||||
def cancel_orders(self , order_id , currency_pair = "btc_krw"):
|
||||
kwargs = {"currency_pair":currency_pair , "ids": order_id}
|
||||
return self.sendRequest( korbit_host + "user/orders/cancel" , KORBITFUNCTIONCODE_CANCEL_ORDER , self.onCancelOrders , kwargs = kwargs , optional = None)
|
||||
#----------------------------------------------------------------------
|
||||
def user_balances(self):
|
||||
return self.sendRequest( korbit_host + "user/balances" , KORBITFUNCTIONCODE_BALANCES , self.onBalances , kwargs = {} , optional = None)
|
||||
|
||||
####################################################
|
||||
## 回调函数
|
||||
####################################################
|
||||
def on_buy_currency(self, data , req, reqID):
|
||||
print data
|
||||
#----------------------------------------------------------------------
|
||||
def on_sell_currency(self, data , req, reqID):
|
||||
print data
|
||||
#----------------------------------------------------------------------
|
||||
def on_list_exchange_orders(self, data , req, reqID):
|
||||
print data
|
||||
#----------------------------------------------------------------------
|
||||
def onCancelOrders(self, data , req, reqID):
|
||||
print data
|
||||
#----------------------------------------------------------------------
|
||||
def onBalances(self, data , req, reqID):
|
||||
print data
|
||||
|
||||
class Korbit_DataApi(object):
|
||||
|
||||
simple_ticker_url = korbit_host + "ticker"
|
||||
detail_ticker_url = korbit_host + "ticker/detailed"
|
||||
orderbook_url = korbit_host + "orderbook"
|
||||
transactions_url = korbit_host + "transactions"
|
||||
constants_url = korbit_host + "constants"
|
||||
|
||||
def __init__(self ):
|
||||
self.active = False
|
||||
|
||||
self.taskInterval = 0 # 每轮请求延时
|
||||
self.taskList = [] # 订阅的任务列表
|
||||
self.taskThread = Thread(target=self.run) # 处理任务的线程
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def init(self, interval, debug):
|
||||
"""初始化"""
|
||||
self.taskInterval = interval
|
||||
self.DEBUG = debug
|
||||
|
||||
self.active = True
|
||||
self.taskThread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def exit(self):
|
||||
"""退出"""
|
||||
self.active = False
|
||||
|
||||
if self.taskThread.isAlive():
|
||||
self.taskThread.join()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def run(self):
|
||||
"""连续运行"""
|
||||
while self.active:
|
||||
for url, callback , symbol in self.taskList:
|
||||
try:
|
||||
r = requests.get(url)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
data["symbol"] = symbol
|
||||
if self.DEBUG:
|
||||
print callback.__name__
|
||||
callback(data)
|
||||
except Exception, e:
|
||||
print "Korbit_DataApi" , e
|
||||
sleep(self.taskInterval)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeTick(self, symbol):
|
||||
"""订阅实时成交数据"""
|
||||
url = self.detail_ticker_url + "?currency_pair=" + symbol
|
||||
task = (url, self.onTick , symbol)
|
||||
self.taskList.append( task)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeTrades(self, symbol ):
|
||||
"""订阅实时成交数据"""
|
||||
# time in ('minute','hour','day')
|
||||
url = self.transactions_url + "?currency_pair=" + symbol + "&time=" + "minute"
|
||||
task = (url, self.onTrades , symbol)
|
||||
self.taskList.append(task)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeOrderbooks(self, symbol):
|
||||
"""订阅实时成交数据"""
|
||||
url = self.orderbook_url + "?currency_pair=" + symbol
|
||||
task = (url, self.onOrderbooks , symbol)
|
||||
self.taskList.append(task)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTick(self, data):
|
||||
"""实时成交推送"""
|
||||
print data
|
||||
#----------------------------------------------------------------------
|
||||
def onTrades(self, data):
|
||||
"""实时成交推送"""
|
||||
print data
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOrderbooks(self, data):
|
||||
"""实时成交推送"""
|
||||
print data
|
||||
|
3
vnpy/api/okex/__init__.py
Normal file
3
vnpy/api/okex/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnokex import OKEX_Sub_Spot_Api , OKEX_Contract_Api , okex_all_symbol_pairs , okex_all_contract_symbol , okex_all_symbol_type
|
5
vnpy/api/okex/readme.txt
Normal file
5
vnpy/api/okex/readme.txt
Normal file
@ -0,0 +1,5 @@
|
||||
okex 的平台地址
|
||||
|
||||
https://www.okex.com/intro_apiOverview.html
|
||||
|
||||
api 文档 https://www.okex.com/ws_request.html
|
49
vnpy/api/okex/test.py
Normal file
49
vnpy/api/okex/test.py
Normal file
@ -0,0 +1,49 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnokex import *
|
||||
|
||||
# 在OkCoin网站申请这两个Key,分别对应用户名和密码
|
||||
apiKey = '你的accessKey'
|
||||
secretKey = '你的secretKey'
|
||||
|
||||
# 创建API对象
|
||||
api = OKEX_Sub_Spot_Api()
|
||||
|
||||
api.connect_Subpot(apiKey, secretKey, True)
|
||||
|
||||
sleep(3)
|
||||
|
||||
#api.login()
|
||||
#api.subscribeSpotTicker("bch_btc")
|
||||
#api.subscribeSpotDepth("bch_btc")
|
||||
#api.subscribeSpotDepth5("bch_btc")
|
||||
#api.subscribeSpotDeals("bch_btc")
|
||||
#api.subscribeSpotKlines("bch_btc","30min")
|
||||
|
||||
api.spotTrade("etc_usdt","sell", "50" , "0.01")
|
||||
#api.spotCancelOrder("etc_btc","44274138")
|
||||
#api.spotUserInfo()
|
||||
#api.spotOrderInfo("etc_btc", 44284731)
|
||||
|
||||
# api = OKEX_Contract_Api()
|
||||
# api.connect_Contract(apiKey, secretKey, True)
|
||||
|
||||
# sleep(3)
|
||||
#api.subsribeFutureTicker("btc","this_week")
|
||||
#api.subscribeFutureKline("btc","this_week","30min")
|
||||
#api.subscribeFutureDepth("btc","this_week")
|
||||
#api.subscribeFutureDepth20("btc","this_week")
|
||||
#api.subscribeFutureTrades("btc","this_week")
|
||||
#api.subscribeFutureIndex("btc")
|
||||
#api.subscribeFutureForecast_price("btc")
|
||||
|
||||
#api.login()
|
||||
#api.futureTrade( "etc_usd", "this_week" ,"1" , 20 , 1 , _match_price = '0' , _lever_rate = '10') # 14245727693
|
||||
#api.futureCancelOrder("etc_usd","14245727693" , "this_week")
|
||||
#api.futureUserInfo()
|
||||
#api.futureOrderInfo("etc_usd" , "14245727693" , "this_week" , '1', '1' , '10')
|
||||
# api.subscribeFutureTrades()
|
||||
|
||||
'''
|
||||
合约账户信息、 持仓信息等,在登录后都会自动推送。。。官方文档这样写的,还没实际验证过
|
||||
'''
|
463
vnpy/api/okex/vnokex.py
Normal file
463
vnpy/api/okex/vnokex.py
Normal file
@ -0,0 +1,463 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
import hashlib
|
||||
import zlib
|
||||
import json
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
|
||||
import websocket
|
||||
|
||||
# OKEX网站
|
||||
|
||||
OKEX_USD_SUB_SPOT = 'wss://real.okex.com:10441/websocket' # OKEX 现货地址
|
||||
OKEX_USD_CONTRACT = 'wss://real.okex.com:10440/websocket/okexapi' # OKEX 期货地址
|
||||
|
||||
okex_all_symbol_type = ["usdt","btc","ltc","eth","etc","bch"]
|
||||
okex_all_symbol_pairs = ["ltc_btc","eth_btc","etc_btc","bch_btc","btc_usdt","eth_usdt",\
|
||||
"ltc_usdt","etc_usdt","bch_usdt","etc_eth","bt1_btc","bt2_btc","btg_btc","qtum_btc",\
|
||||
"hsr_btc","neo_btc","gas_btc","qtum_usdt","hsr_usdt","neo_usdt","gas_usdt"]
|
||||
|
||||
# for test
|
||||
# okex_all_symbol_pairs = ['etc_usdt']
|
||||
|
||||
okex_all_k_line_periods = ["1min","3min","5min","15min","30min","1hour","2hour","4hour","6hour","12hour","day","3day","week"]
|
||||
|
||||
okex_all_contract_symbol = ["btc","ltc","eth","bch"]
|
||||
okex_all_contract_type = ["this_week","next_week","quarter"]
|
||||
|
||||
class OKEX_Sub_Spot_Api(object):
|
||||
"""基于Websocket的API对象"""
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.apiKey = '' # 用户名
|
||||
self.secretKey = '' # 密码
|
||||
|
||||
self.ws_sub_spot = None # websocket应用对象 现货对象
|
||||
|
||||
self.thread = None # 线程初始化
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def reconnect(self):
|
||||
"""重新连接"""
|
||||
# 首先关闭之前的连接
|
||||
self.close()
|
||||
|
||||
# 再执行重连任务
|
||||
self.ws_sub_spot = websocket.WebSocketApp(self.host,
|
||||
on_message=self.onMessage,
|
||||
on_error=self.onError,
|
||||
on_close=self.onClose,
|
||||
on_open=self.onOpen)
|
||||
|
||||
self.thread = Thread(target=self.ws_sub_spot.run_forever)
|
||||
self.thread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect_Subpot(self, apiKey , secretKey , trace = False):
|
||||
self.host = OKEX_USD_SUB_SPOT
|
||||
self.apiKey = apiKey
|
||||
self.secretKey = secretKey
|
||||
|
||||
websocket.enableTrace(trace)
|
||||
|
||||
self.ws_sub_spot = websocket.WebSocketApp(self.host,
|
||||
on_message=self.onMessage,
|
||||
on_error=self.onError,
|
||||
on_close=self.onClose,
|
||||
on_open=self.onOpen)
|
||||
|
||||
self.thread = Thread(target=self.ws_sub_spot.run_forever)
|
||||
self.thread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def readData(self, evt):
|
||||
"""解压缩推送收到的数据"""
|
||||
# # 创建解压器
|
||||
# decompress = zlib.decompressobj(-zlib.MAX_WBITS)
|
||||
|
||||
# # 将原始数据解压成字符串
|
||||
# inflated = decompress.decompress(evt) + decompress.flush()
|
||||
|
||||
# 通过json解析字符串
|
||||
#data = json.loads(inflated)
|
||||
data = json.loads(evt)
|
||||
|
||||
return data
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭接口"""
|
||||
if self.thread and self.thread.isAlive():
|
||||
self.ws_sub_spot.close()
|
||||
self.thread.join()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onMessage(self, ws, evt):
|
||||
"""信息推送"""
|
||||
print evt
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, ws, evt):
|
||||
"""错误推送"""
|
||||
print 'onError'
|
||||
print evt
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onClose(self, ws):
|
||||
"""接口断开"""
|
||||
print 'onClose'
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOpen(self, ws):
|
||||
"""接口打开"""
|
||||
print 'onOpen'
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSpotTicker(self, symbol_pair):
|
||||
# 现货的 ticker
|
||||
req = "{'event':'addChannel','channel':'ok_sub_spot_%s_ticker'}" % symbol_pair
|
||||
self.ws_sub_spot.send(req)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSpotDepth(self, symbol_pair):
|
||||
# 现货的 市场深度
|
||||
req = "{'event':'addChannel','channel':'ok_sub_spot_%s_depth'}" % symbol_pair
|
||||
self.ws_sub_spot.send(req)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSpotDepth5(self, symbol_pair):
|
||||
# 现货的 市场深度 5
|
||||
req = "{'event':'addChannel','channel':'ok_sub_spot_%s_depth_5'}" % symbol_pair
|
||||
self.ws_sub_spot.send(req)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSpotDeals(self, symbol_pair):
|
||||
req = "{'event':'addChannel','channel':'ok_sub_spot_%s_deals'}" % symbol_pair
|
||||
self.ws_sub_spot.send(req)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSpotKlines(self, symbol_pair , time_period):
|
||||
req = "{'event':'addChannel','channel':'ok_sub_spot_%s_kline_%s'}" % ( symbol_pair , time_period)
|
||||
self.ws_sub_spot.send(req)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def generateSign(self, params):
|
||||
"""生成签名"""
|
||||
l = []
|
||||
for key in sorted(params.keys()):
|
||||
l.append('%s=%s' %(key, params[key]))
|
||||
l.append('secret_key=%s' %self.secretKey)
|
||||
sign = '&'.join(l)
|
||||
return hashlib.md5(sign.encode('utf-8')).hexdigest().upper()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendTradingRequest(self, channel, params):
|
||||
"""发送交易请求"""
|
||||
# 在参数字典中加上api_key和签名字段
|
||||
params['api_key'] = self.apiKey
|
||||
params['sign'] = self.generateSign(params)
|
||||
|
||||
# 生成请求
|
||||
d = {}
|
||||
d['event'] = 'addChannel'
|
||||
d['channel'] = channel
|
||||
d['parameters'] = params
|
||||
|
||||
# 使用json打包并发送
|
||||
j = json.dumps(d)
|
||||
|
||||
# 若触发异常则重连
|
||||
try:
|
||||
self.ws_sub_spot.send(j)
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotTrade(self, symbol_pair, type_, price, amount):
|
||||
"""现货委托"""
|
||||
params = {}
|
||||
params['symbol'] = str(symbol_pair)
|
||||
params['type'] = str(type_)
|
||||
params['price'] = str(price)
|
||||
params['amount'] = str(amount)
|
||||
|
||||
channel = 'ok_spot_order'
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotCancelOrder(self, symbol_pair, orderid):
|
||||
"""现货撤单"""
|
||||
params = {}
|
||||
params['symbol'] = str(symbol_pair)
|
||||
params['order_id'] = str(orderid)
|
||||
|
||||
channel = 'ok_spot_cancel_order'
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotUserInfo(self):
|
||||
"""查询现货账户"""
|
||||
channel = 'ok_spot_userinfo'
|
||||
self.sendTradingRequest(channel, {})
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotOrderInfo(self, symbol_pair, orderid):
|
||||
"""查询现货委托信息"""
|
||||
params = {}
|
||||
params['symbol'] = str(symbol_pair)
|
||||
params['order_id'] = str(orderid)
|
||||
|
||||
channel = 'ok_spot_orderinfo'
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# 这个 API 应该已经别废弃了
|
||||
# def subscribeSpotTrades(self , symbol_pair):
|
||||
# """订阅现货成交信息"""
|
||||
# channel = 'ok_sub_spot_%s_trades' % ( symbol_pair)
|
||||
|
||||
# self.sendTradingRequest(channel, {})
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# 这个 API 应该已经别废弃了
|
||||
# def subscribeSpotUserInfo(self):
|
||||
# """订阅现货账户信息"""
|
||||
# channel = 'ok_sub_spot_userinfo'
|
||||
|
||||
# self.sendTradingRequest(channel, {})
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def login(self):
|
||||
params = {}
|
||||
params['api_key'] = self.apiKey
|
||||
params['sign'] = self.generateSign(params)
|
||||
|
||||
# 生成请求
|
||||
d = {}
|
||||
d['event'] = 'login'
|
||||
d['parameters'] = params
|
||||
|
||||
# 使用json打包并发送
|
||||
j = json.dumps(d)
|
||||
|
||||
# 若触发异常则重连
|
||||
try:
|
||||
self.ws_sub_spot.send(j)
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
pass
|
||||
|
||||
|
||||
'''
|
||||
OKEX 合约接口
|
||||
|
||||
[{
|
||||
"channel": "btc_forecast_price",
|
||||
"timestamp":"1490341322021",
|
||||
"data": "998.8"
|
||||
}]
|
||||
data(string): 预估交割价格
|
||||
timestamp(string): 时间戳
|
||||
操作说明
|
||||
无需订阅,交割前一小时自动返回
|
||||
|
||||
这段数据,交割前会自动返回
|
||||
'''
|
||||
class OKEX_Contract_Api(object):
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.apiKey = '' # 用户名
|
||||
self.secretKey = '' # 密码
|
||||
|
||||
self.ws_contract = None # websocket应用对象 期货对象
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
def connect_Contract(self, apiKey , secretKey , trace = False):
|
||||
self.host = OKEX_USD_CONTRACT
|
||||
self.apiKey = apiKey
|
||||
self.secretKey = secretKey
|
||||
|
||||
websocket.enableTrace(trace)
|
||||
|
||||
self.ws_contract = websocket.WebSocketApp(self.host,
|
||||
on_message=self.onMessage,
|
||||
on_error=self.onError,
|
||||
on_close=self.onClose,
|
||||
on_open=self.onOpen)
|
||||
|
||||
self.thread = Thread(target=self.ws_contract.run_forever)
|
||||
self.thread.start()
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
#----------------------------------------------------------------------
|
||||
def onMessage(self, ws, evt):
|
||||
"""信息推送"""
|
||||
print evt
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, ws, evt):
|
||||
"""错误推送"""
|
||||
print 'onError'
|
||||
print evt
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onClose(self, ws):
|
||||
"""接口断开"""
|
||||
print 'onClose'
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOpen(self, ws):
|
||||
"""接口打开"""
|
||||
print 'onOpen'
|
||||
|
||||
def subsribeFutureTicker(self, symbol , contract_type):
|
||||
req = "{'event':'addChannel','channel':'ok_sub_futureusd_%s_ticker_%s'}" % (symbol , contract_type)
|
||||
self.ws_contract.send(req)
|
||||
|
||||
def subscribeFutureKline(self, symbol , contract_type , time_period):
|
||||
req = "{'event':'addChannel','channel':'ok_sub_futureusd_%s_kline_%s_%s'}" % (symbol , contract_type , time_period)
|
||||
self.ws_contract.send(req)
|
||||
|
||||
def subscribeFutureDepth(self, symbol , contract_type):
|
||||
req = "{'event':'addChannel','channel':'ok_sub_future_%s_depth_%s_usd'}" % (symbol , contract_type )
|
||||
self.ws_contract.send(req)
|
||||
|
||||
def subscribeFutureDepth20(self, symbol , contract_type):
|
||||
req = "{'event':'addChannel','channel':'ok_sub_futureusd_%s_depth_%s_20'}" % (symbol , contract_type)
|
||||
self.ws_contract.send(req)
|
||||
|
||||
def subscribeFutureTrades(self, symbol , contract_type):
|
||||
req = "{'event':'addChannel','channel':'ok_sub_futureusd_%s_trade_%s'}" % (symbol , contract_type)
|
||||
self.ws_contract.send(req)
|
||||
|
||||
def subscribeFutureIndex(self, symbol ):
|
||||
req = "{'event':'addChannel','channel':'ok_sub_futureusd_%s_index'}" % (symbol)
|
||||
self.ws_contract.send(req)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def generateSign(self, params):
|
||||
"""生成签名"""
|
||||
l = []
|
||||
for key in sorted(params.keys()):
|
||||
l.append('%s=%s' %(key, params[key]))
|
||||
l.append('secret_key=%s' %self.secretKey)
|
||||
sign = '&'.join(l)
|
||||
return hashlib.md5(sign.encode('utf-8')).hexdigest().upper()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendTradingRequest(self, channel, params):
|
||||
"""发送交易请求"""
|
||||
# 在参数字典中加上api_key和签名字段
|
||||
params['api_key'] = self.apiKey
|
||||
params['sign'] = self.generateSign(params)
|
||||
|
||||
# 生成请求
|
||||
d = {}
|
||||
d['event'] = 'addChannel'
|
||||
d['channel'] = channel
|
||||
d['parameters'] = params
|
||||
|
||||
# 使用json打包并发送
|
||||
j = json.dumps(d)
|
||||
|
||||
# 若触发异常则重连
|
||||
try:
|
||||
self.ws_contract.send(j)
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def login(self):
|
||||
params = {}
|
||||
params['api_key'] = self.apiKey
|
||||
params['sign'] = self.generateSign(params)
|
||||
|
||||
# 生成请求
|
||||
d = {}
|
||||
d['event'] = 'login'
|
||||
d['parameters'] = params
|
||||
|
||||
# 使用json打包并发送
|
||||
j = json.dumps(d)
|
||||
|
||||
# 若触发异常则重连
|
||||
try:
|
||||
self.ws_contract.send(j)
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def futureTrade(self, symbol_pair, contract_type ,type_, price, amount , _match_price = '0' , _lever_rate = '10'):
|
||||
"""期货委托"""
|
||||
params = {}
|
||||
params['symbol'] = str(symbol_pair)
|
||||
params['contract_type'] = str(contract_type)
|
||||
params['price'] = str(price)
|
||||
params['amount'] = str(amount)
|
||||
params['type'] = type_ # 1:开多 2:开空 3:平多 4:平空
|
||||
params['match_price'] = _match_price # 是否为对手价: 0:不是 1:是 当取值为1时,price无效
|
||||
params['lever_rate'] = _lever_rate
|
||||
|
||||
channel = 'ok_futureusd_trade'
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def futureCancelOrder(self, symbol_pair, orderid , contract_type):
|
||||
"""期货撤单"""
|
||||
params = {}
|
||||
params['symbol'] = str(symbol_pair)
|
||||
params['order_id'] = str(orderid)
|
||||
params['contract_type'] = str(contract_type)
|
||||
|
||||
channel = 'ok_futureusd_cancel_order'
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def futureUserInfo(self):
|
||||
"""查询期货账户"""
|
||||
channel = 'ok_futureusd_userinfo'
|
||||
self.sendTradingRequest(channel, {})
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def futureOrderInfo(self , symbol_pair , order_id , contract_type , status , current_page , page_length = 10):
|
||||
"""查询期货委托"""
|
||||
params = {}
|
||||
params['symbol'] = str(symbol_pair)
|
||||
params['order_id'] = str(order_id)
|
||||
params['contract_type'] = str(contract_type)
|
||||
params['status'] = str(status)
|
||||
params['current_page'] = str(current_page)
|
||||
params['page_length'] = str(page_length)
|
||||
|
||||
channel = 'ok_futureusd_orderinfo'
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeFutureTrades( self):
|
||||
channel = 'ok_sub_futureusd_trades'
|
||||
self.sendTradingRequest(channel, {})
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeFutureUserInfo(self):
|
||||
"""订阅期货账户信息"""
|
||||
channel = 'ok_sub_futureusd_userinfo'
|
||||
|
||||
self.sendTradingRequest(channel, {})
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeFuturePositions(self):
|
||||
"""订阅期货持仓信息"""
|
||||
channel = 'ok_sub_futureusd_positions'
|
||||
|
||||
self.sendTradingRequest(channel, {})
|
||||
|
23
vnpy/api/zaif/README.txt
Normal file
23
vnpy/api/zaif/README.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# vn.zaif
|
||||
|
||||
### 简介
|
||||
|
||||
Zaif 的比特币交易接口,基于Rest API开发,实现了官方提供API的全部功能。
|
||||
|
||||
### 特点
|
||||
|
||||
1. 面向对象的API设计,接近CTP API的结构,对于国内用户而言更容易上手
|
||||
|
||||
2. 参考CTP API的设计,主动函数调用的结果通过异步(回调函数)的方式推送到程序中,适用于开发稳定可靠的实盘交易程序
|
||||
|
||||
### API版本
|
||||
日期:2015-12-02
|
||||
|
||||
链接:
|
||||
https://corp.zaif.jp/api-docs/
|
||||
https://corp.zaif.jp/api-docs/trade-api/
|
||||
|
||||
|
||||
https://github.com/E-Tsubo/zaifapi/blob/master/zaifapi/impl.py
|
||||
|
||||
https://github.com/tk1024/Zaif4PHP/blob/master/Zaif.php
|
3
vnpy/api/zaif/__init__.py
Normal file
3
vnpy/api/zaif/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnzaif import TradeApi, DataApi
|
153
vnpy/api/zaif/test.php
Normal file
153
vnpy/api/zaif/test.php
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
use WebSocket\Client;
|
||||
class Zaif {
|
||||
const PUBLIC_BASE_URL = "https://api.zaif.jp/api/1";
|
||||
const TRADE_BASE_URL = "https://api.zaif.jp/tapi";
|
||||
const STREAMING_BASE_URL = "ws://api.zaif.jp:8888/stream";
|
||||
private $key;
|
||||
private $secret;
|
||||
private $nonce;
|
||||
public function __construct($key, $secret) {
|
||||
$this->key = $key;
|
||||
$this->secret = $secret;
|
||||
$this->nonce = time();
|
||||
$this->nonce = 1507466145;
|
||||
}
|
||||
|
||||
public static function pub($endpoint, $prm) {
|
||||
switch ($endpoint) {
|
||||
case 'last_price':
|
||||
case 'ticker':
|
||||
case 'trades':
|
||||
case 'depth':
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Argument has not been set.');
|
||||
break;
|
||||
}
|
||||
switch ($prm) {
|
||||
case 'btc_jpy':
|
||||
case 'mona_jpy':
|
||||
case 'mona_btc':
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Argument has not been set.');
|
||||
break;
|
||||
}
|
||||
$url = self::PUBLIC_BASE_URL.'/'.$endpoint.'/'.$prm;
|
||||
$data = self::get($url);
|
||||
$data = json_decode( $data );
|
||||
return $data;
|
||||
}
|
||||
public function trade($method, $prms=null) {
|
||||
switch ($method) {
|
||||
case 'get_info':
|
||||
case 'get_info2':
|
||||
case 'get_personal_info':
|
||||
case 'trade_history':
|
||||
case 'active_orders':
|
||||
case 'trade' :
|
||||
case 'cancel_order' :
|
||||
case 'withdraw' :
|
||||
case 'deposit_history' :
|
||||
case 'withdraw_history' :
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Argument has not been set.');
|
||||
break;
|
||||
}
|
||||
$postdata = array( "nonce" => $this->nonce++, "method" => $method );
|
||||
if( !empty( $prms ) ) {
|
||||
$postdata = array_merge( $postdata, $prms );
|
||||
}
|
||||
$postdata_query = http_build_query( $postdata );
|
||||
echo $postdata_query;
|
||||
echo "\n";
|
||||
$sign = hash_hmac( 'sha512', $postdata_query, $this->secret);
|
||||
echo $sign;
|
||||
echo "\n";
|
||||
$header = array( "Sign: {$sign}", "Key: {$this->key}", );
|
||||
print_r($postdata_query);
|
||||
echo "\n";
|
||||
print_r($header);
|
||||
$data = self::post( self::TRADE_BASE_URL, $header, $postdata_query );
|
||||
$data = json_decode( $data );
|
||||
return $data;
|
||||
}
|
||||
public static function streaming($prms, $callback) {
|
||||
$file_path = dirname(__FILE__).'/vendor/autoload.php';
|
||||
if (file_exists($file_path) && is_readable($file_path)) {
|
||||
require_once $file_path ;
|
||||
} else {
|
||||
throw new Exception('You can not use Streaming API.You should check including libray.');
|
||||
}
|
||||
switch ($prms['currency_pair']) {
|
||||
case 'btc_jpy':
|
||||
case 'mona_jpy':
|
||||
case 'mona_btc':
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Argument has not been set.');
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
$ws = self::STREAMING_BASE_URL.'?'.http_build_query($prms);
|
||||
$client = new Client($ws);
|
||||
while(true) {
|
||||
try {
|
||||
$json = $client->receive();
|
||||
$data = json_decode($json);
|
||||
$callback($data);
|
||||
} catch (WebSocket\ConnectionException $e) {
|
||||
$clinet = new Client($ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static function get($url) {
|
||||
$ch = curl_init();
|
||||
$options = array(
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
);
|
||||
curl_setopt_array($ch, $options);
|
||||
$data = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $data;
|
||||
}
|
||||
private static function post($url, $header, $postdata) {
|
||||
$ch = curl_init();
|
||||
$options = array(
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $postdata,
|
||||
CURLOPT_HTTPHEADER => $header,
|
||||
);
|
||||
curl_setopt_array($ch, $options);
|
||||
$data = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$zaif = new Zaif("f8893900-b764-4cdd-9693-16c23b8a118e","049d3499-1788-4145-b033-dab66ea2feda");
|
||||
|
||||
//$info = $zaif -> trade( "get_info");
|
||||
$info2 = $zaif -> trade( "get_info2");
|
||||
// 1モナ100円で15モナ売り板に出す
|
||||
// $ trade_ask = $ zaif - > trade( “ trade ”,
|
||||
// array( ' currency_pair ' => ' mona_jpy ', ' action ' => ' ask ', ' price ' = > 100, ' amount ' => 15 ));
|
||||
|
||||
// MONA_JPYの现在有效な注文一覧を表示する
|
||||
//$ active_orders = $ zaif - > trade( “ active_orders ”, array( ' currency_pair ' => ' mona_jpy '));
|
||||
|
||||
//出力
|
||||
// var_dump( $info);
|
||||
var_dump( $info2 );
|
||||
|
||||
?>
|
55
vnpy/api/zaif/test.py
Normal file
55
vnpy/api/zaif/test.py
Normal file
@ -0,0 +1,55 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from vnzaif import *
|
||||
|
||||
def testTrade():
|
||||
"""测试交易"""
|
||||
accessKey = '你的accessKey'
|
||||
secretKey = '你的secretKey'
|
||||
|
||||
# 创建API对象并初始化
|
||||
api = TradeApi()
|
||||
api.DEBUG = True
|
||||
api.init(accessKey, secretKey)
|
||||
|
||||
# 查询账户,测试通过
|
||||
#api.get_info()
|
||||
|
||||
api.get_info2()
|
||||
|
||||
|
||||
# 查询委托,测试通过
|
||||
#api.active_orders( currency_pair = SYMBOL_BTCJPY )
|
||||
|
||||
# 阻塞
|
||||
input()
|
||||
|
||||
|
||||
def testData():
|
||||
"""测试行情接口"""
|
||||
api = DataApi()
|
||||
|
||||
api.init(0.5 , 1)
|
||||
|
||||
# 订阅成交推送,测试通过
|
||||
api.subscribeTick(SYMBOL_BTCJPY)
|
||||
|
||||
# 订阅最新价推送,测试通过
|
||||
#api.subscribeLast(SYMBOL_BTCJPY)
|
||||
|
||||
# 订阅深度推送,测试通过
|
||||
api.subscribeDepth(SYMBOL_BTCJPY, 1)
|
||||
|
||||
api.subscribeTrades(SYMBOL_BTCJPY)
|
||||
|
||||
# 查询K线数据,测试通过
|
||||
#data = api.getKline(SYMBOL_BTCJPY, PERIOD_1MIN, 100)
|
||||
#print data
|
||||
|
||||
input()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
#testTrade()
|
||||
#testTrade()
|
||||
testData()
|
500
vnpy/api/zaif/vnzaif.py
Normal file
500
vnpy/api/zaif/vnzaif.py
Normal file
@ -0,0 +1,500 @@
|
||||
# encoding: utf-8
|
||||
|
||||
import urllib
|
||||
import hashlib
|
||||
|
||||
import json
|
||||
import requests
|
||||
import hmac
|
||||
import time
|
||||
from datetime import datetime
|
||||
from time import time, sleep , mktime
|
||||
from Queue import Queue, Empty
|
||||
from threading import Thread
|
||||
import urllib
|
||||
|
||||
import hashlib
|
||||
import inspect
|
||||
import requests
|
||||
import cerberus
|
||||
|
||||
SYMBOL_BTCJPY = 'btc_jpy'
|
||||
SYMBOL_ETHJPY = 'eth_jpy'
|
||||
|
||||
FUNCTIONCODE_GETINFO_ZAIF = 'get_info'
|
||||
FUNCTIONCODE_GETINFO2_ZAIF = 'get_info2'
|
||||
FUNCTIONCODE_GETPERSONALINFO_ZAIF = 'get_personal_info'
|
||||
FUNCTIONCODE_TRADEHISTORY_ZAIF = 'trade_history'
|
||||
FUNCTIONCODE_ACTIVEORDERS_ZAIF = 'active_orders'
|
||||
FUNCTIONCODE_TRADE_ZAIF = 'trade'
|
||||
FUNCTIONCODE_CANCEL_ORDER_ZAIF = 'cancel_order'
|
||||
FUNCTIONCODE_WITHDRAL_ZAIF = 'withdraw'
|
||||
FUNCTIONCODE_DEPOSIT_HISTORY_ZAIF = 'deposit_history'
|
||||
FUNCTIONCODE_WITHDRAW_HISTORY_ZAIF = 'withdraw_history'
|
||||
|
||||
SCHEMA = {
|
||||
'from_num': {
|
||||
'type': 'integer'
|
||||
},
|
||||
'count': {
|
||||
'type': 'integer'
|
||||
},
|
||||
'from_id': {
|
||||
'type': 'integer'
|
||||
},
|
||||
'end_id': {
|
||||
'type': ['string', 'integer']
|
||||
},
|
||||
'order': {
|
||||
'type': 'string',
|
||||
'allowed': ['ASC', 'DESC']
|
||||
},
|
||||
'since': {
|
||||
'type': 'integer'
|
||||
},
|
||||
'end': {
|
||||
'type': ['string', 'integer']
|
||||
},
|
||||
'currency_pair': {
|
||||
'type': 'string',
|
||||
'allowed': ['btc_jpy', 'xem_jpy', 'mona_jpy', 'mona_btc']
|
||||
},
|
||||
'currency': {
|
||||
'required': True,
|
||||
'type': 'string',
|
||||
'allowed': ['jpy', 'btc', 'mona']
|
||||
},
|
||||
'address': {
|
||||
'required': True,
|
||||
'type': 'string'
|
||||
},
|
||||
'message': {
|
||||
'type': 'string'
|
||||
},
|
||||
'amount': {
|
||||
'required': True,
|
||||
'type': 'number'
|
||||
},
|
||||
'opt_fee': {
|
||||
'type': 'number'
|
||||
},
|
||||
'order_id': {
|
||||
'required': True,
|
||||
'type': 'integer'
|
||||
},
|
||||
'action': {
|
||||
'required': True,
|
||||
'type': 'string',
|
||||
'allowed': ['bid', 'ask']
|
||||
},
|
||||
'price': {
|
||||
'required': True,
|
||||
'type': 'number'
|
||||
},
|
||||
'limit': {
|
||||
'type': 'number'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
########################################################################
|
||||
class TradeApi(object):
|
||||
"""交易接口"""
|
||||
__API_URL = 'https://api.zaif.jp/tapi'
|
||||
DEBUG = True
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.accessKey = ''
|
||||
self.secretKey = ''
|
||||
|
||||
self.active = False # API工作状态
|
||||
self.reqID = 0 # 请求编号
|
||||
self.reqQueue = Queue() # 请求队列
|
||||
self.reqThread = Thread(target=self.processQueue) # 请求处理线程
|
||||
self.nonce = int(mktime(datetime.now().timetuple()))
|
||||
|
||||
def params_pre_processing(self, schema_keys, params):
|
||||
schema = self.__get_schema(schema_keys)
|
||||
self.__validate(schema, params)
|
||||
return self.__edit_params(params)
|
||||
|
||||
@classmethod
|
||||
def __get_schema(cls, keys):
|
||||
schema = {}
|
||||
for key in keys:
|
||||
schema[key] = SCHEMA[key]
|
||||
return schema
|
||||
|
||||
@classmethod
|
||||
def __edit_params(cls, params):
|
||||
if 'from_num' in params:
|
||||
params['from'] = params['from_num']
|
||||
del (params['from_num'])
|
||||
return params
|
||||
|
||||
@classmethod
|
||||
def __validate(cls, schema, param):
|
||||
v = cerberus.Validator(schema)
|
||||
if v.validate(param):
|
||||
return
|
||||
raise Exception(json.dumps(v.errors))
|
||||
|
||||
def __get_parameter(self , func_name, params):
|
||||
params['method'] = func_name
|
||||
params['nonce'] = self.nonce
|
||||
self.nonce += 1
|
||||
return urllib.urlencode(params)
|
||||
|
||||
|
||||
def __get_header(self, params):
|
||||
signature = hmac.new(bytearray(self.secretKey.encode('utf-8')), digestmod=hashlib.sha512)
|
||||
signature.update(params.encode('utf-8'))
|
||||
|
||||
return {
|
||||
'key': str(self.accessKey),
|
||||
'sign': str(signature.hexdigest())
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
def processRequest(self, req):
|
||||
"""处理请求"""
|
||||
# 读取方法和参数
|
||||
schema_keys = req['schema_keys']
|
||||
method = req['method']
|
||||
kwargs = req['kwargs']
|
||||
|
||||
optional = req['optional']
|
||||
params = self.params_pre_processing(schema_keys, kwargs)
|
||||
params = self.__get_parameter(method, params)
|
||||
header = self.__get_header(params)
|
||||
|
||||
r = requests.post(self.__API_URL , data=params , headers=header)
|
||||
if r != None:
|
||||
try:
|
||||
data = r.json()
|
||||
if data['success'] == 0:
|
||||
print "error in vnzaif %s" % method
|
||||
return data
|
||||
else:
|
||||
return data
|
||||
except Exception,ex:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
'''
|
||||
def processRequest(self, req):
|
||||
schema_keys = req['schema_keys']
|
||||
kwargs = req['kwargs']
|
||||
method = req['method']
|
||||
optional = req['optional']
|
||||
params = self.params_pre_processing(schema_keys, kwargs)
|
||||
params = self.__get_parameter(method, params)
|
||||
header = self.__get_header(params)
|
||||
response = requests.post(self.__API_URL, data=params, headers=header)
|
||||
if response.status_code != 200:
|
||||
raise Exception('return status code is {}'.format(response.status_code))
|
||||
res = json.loads(response.text)
|
||||
if res['success'] == 0:
|
||||
raise Exception(res['error'])
|
||||
return res['return']
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def processQueue(self):
|
||||
"""处理请求队列中的请求"""
|
||||
while self.active:
|
||||
try:
|
||||
req = self.reqQueue.get(block=True, timeout=1) # 获取请求的阻塞为一秒
|
||||
callback = req['callback']
|
||||
reqID = req['reqID']
|
||||
|
||||
data = self.processRequest(req)
|
||||
|
||||
# 请求失败
|
||||
if 'code' in data and 'message' in data:
|
||||
error = u'错误信息:%s' %data['message']
|
||||
self.onError(error, req, reqID)
|
||||
# 请求成功
|
||||
else:
|
||||
if self.DEBUG:
|
||||
print callback.__name__
|
||||
callback(data, req, reqID)
|
||||
|
||||
except Empty:
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendRequest(self, method, schema_keys , kwargs , callback, optional=None):
|
||||
"""发送请求"""
|
||||
# 请求编号加1
|
||||
self.reqID += 1
|
||||
|
||||
# 生成请求字典并放入队列中
|
||||
req = {}
|
||||
req['method'] = method
|
||||
req['schema_keys'] = schema_keys
|
||||
req['kwargs'] = kwargs
|
||||
#req['params'] = params
|
||||
req['callback'] = callback
|
||||
req['optional'] = optional
|
||||
req['reqID'] = self.reqID
|
||||
self.reqQueue.put(req)
|
||||
|
||||
# 返回请求编号
|
||||
return self.reqID
|
||||
|
||||
####################################################
|
||||
## 主动函数
|
||||
####################################################
|
||||
#----------------------------------------------------------------------
|
||||
def init(self, accessKey, secretKey):
|
||||
"""初始化"""
|
||||
self.accessKey = accessKey
|
||||
self.secretKey = secretKey
|
||||
|
||||
self.active = True
|
||||
self.reqThread.start()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def exit(self):
|
||||
"""退出"""
|
||||
self.active = False
|
||||
|
||||
if self.reqThread.isAlive():
|
||||
self.reqThread.join()
|
||||
|
||||
#---------------------------------------------------
|
||||
'''
|
||||
def get_info(self):
|
||||
print FUNCTIONCODE_GETINFO_ZAIF
|
||||
method = FUNCTIONCODE_GETINFO_ZAIF
|
||||
key = {}
|
||||
params = {}
|
||||
callback = self.onGet_info
|
||||
optional = {}
|
||||
print params
|
||||
return self.sendRequest(method, key, params,callback, optional)
|
||||
'''
|
||||
|
||||
def get_info2(self):
|
||||
method = FUNCTIONCODE_GETINFO2_ZAIF
|
||||
key = {}
|
||||
params = {}
|
||||
callback = self.onGet_info2
|
||||
optional = {}
|
||||
return self.sendRequest(method, key, params, callback, optional)
|
||||
|
||||
def trade_history(self , **kwargs):
|
||||
method = FUNCTIONCODE_TRADEHISTORY_ZAIF
|
||||
schema_keys = ['from_num', 'count', 'from_id', 'end_id', 'order', 'since', 'end', 'currency_pair']
|
||||
callback = self.onTradeHistory
|
||||
optional = {}
|
||||
return self.sendRequest(method, schema_keys, kwargs, callback, optional)
|
||||
|
||||
def active_orders(self , **kwargs):
|
||||
method = FUNCTIONCODE_ACTIVEORDERS_ZAIF
|
||||
schema_keys = ['currency_pair']
|
||||
callback = self.onActiveOrders
|
||||
optional = {}
|
||||
return self.sendRequest(method, schema_keys, kwargs, callback, optional)
|
||||
|
||||
def withdraw_history(self , **kwargs):
|
||||
method = FUNCTIONCODE_WITHDRAW_HISTORY_ZAIF
|
||||
schema_keys = ['currency', 'from_num', 'count', 'from_id', 'end_id', 'order', 'since', 'end']
|
||||
callback = self.onWithdrawHistory
|
||||
optional = {}
|
||||
return self.sendRequest(method, schema_keys, kwargs, callback, optional)
|
||||
|
||||
def deposit_history(self , **kwargs):
|
||||
method = FUNCTIONCODE_DEPOSIT_HISTORY_ZAIF
|
||||
schema_keys = ['currency', 'from_num', 'count', 'from_id', 'end_id', 'order', 'since', 'end']
|
||||
callback = self.onDepositHistory
|
||||
optional = {}
|
||||
return self.sendRequest(method, schema_keys, kwargs, callback, optional)
|
||||
|
||||
def withdraw(self , **kwargs):
|
||||
method = FUNCTIONCODE_WITHDRAL_ZAIF
|
||||
schema_keys = ['currency', 'address', 'message', 'amount', 'opt_fee']
|
||||
callback = self.onWithdraw
|
||||
optional = {}
|
||||
return self.sendRequest(method, schema_keys, kwargs, callback, optional)
|
||||
|
||||
def cancel_order(self, **kwargs):
|
||||
method = FUNCTIONCODE_CANCEL_ORDER_ZAIF
|
||||
schema_keys = ['order_id']
|
||||
callback = self.onCancelOrder
|
||||
optional = {}
|
||||
return self.sendRequest(method, schema_keys, kwargs, callback, optional)
|
||||
|
||||
def trade(self, **kwargs):
|
||||
method = FUNCTIONCODE_TRADE_ZAIF
|
||||
schema_keys = ['currency_pair', 'action', 'price', 'amount', 'limit']
|
||||
callback = self.onTrade
|
||||
optional = {}
|
||||
return self.sendRequest(method, schema_keys, kwargs, callback, optional)
|
||||
|
||||
####################################################
|
||||
## 回调函数
|
||||
####################################################
|
||||
'''
|
||||
def onGet_info(self, data, req, reqID):
|
||||
"""用户信息"""
|
||||
print data
|
||||
'''
|
||||
def onTrade(self, data, req, reqID):
|
||||
print data
|
||||
|
||||
def onCancelOrder(self, data, req, reqID):
|
||||
print data
|
||||
|
||||
def onWithdraw(self, data, req, reqID):
|
||||
print data
|
||||
|
||||
def onDepositHistory(self, data, req, reqID):
|
||||
print data
|
||||
|
||||
def onWithdrawHistory(self, data, req, reqID):
|
||||
print data
|
||||
|
||||
def onTradeHistory(self, data, req, reqID):
|
||||
print data
|
||||
|
||||
def onActiveOrders(self, data, req, reqID):
|
||||
print data
|
||||
|
||||
def onGet_info2(self, data, req, reqID):
|
||||
"""用户信息"""
|
||||
print data
|
||||
|
||||
|
||||
|
||||
|
||||
class DataApi(object):
|
||||
"""
|
||||
行情接口
|
||||
"""
|
||||
TICK_SYMBOL_URL = {
|
||||
SYMBOL_BTCJPY: 'https://api.zaif.jp/api/1/ticker/btc_jpy',
|
||||
SYMBOL_ETHJPY: 'https://api.zaif.jp/api/1/ticker/eth_jpy'
|
||||
}
|
||||
|
||||
TRADES_SYMBOL_URL = {
|
||||
SYMBOL_BTCJPY: 'https://api.zaif.jp/api/1/trades/btc_jpy',
|
||||
SYMBOL_ETHJPY: 'https://api.zaif.jp/api/1/trades/eth_jpy',
|
||||
}
|
||||
|
||||
DEPTH_SYMBOL_URL = {
|
||||
SYMBOL_BTCJPY: 'https://api.zaif.jp/api/1/depth/btc_jpy',
|
||||
SYMBOL_ETHJPY: 'https://api.zaif.jp/api/1/depth/eth_jpy',
|
||||
}
|
||||
|
||||
LAST_SYMBOL_URL = {
|
||||
SYMBOL_BTCJPY: 'https://api.zaif.jp/api/1/last_price/btc_jpy',
|
||||
SYMBOL_ETHJPY: 'https://api.zaif.jp/api/1/last_price/eth_jpy',
|
||||
}
|
||||
|
||||
|
||||
DEBUG = True
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.active = False
|
||||
|
||||
self.taskInterval = 0 # 每轮请求延时
|
||||
self.taskList = [] # 订阅的任务列表
|
||||
self.taskThread = Thread(target=self.run) # 处理任务的线程
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def init(self, interval, debug):
|
||||
"""初始化"""
|
||||
self.taskInterval = interval
|
||||
self.DEBUG = debug
|
||||
|
||||
self.active = True
|
||||
self.taskThread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def exit(self):
|
||||
"""退出"""
|
||||
self.active = False
|
||||
|
||||
if self.taskThread.isAlive():
|
||||
self.taskThread.join()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def run(self):
|
||||
"""连续运行"""
|
||||
while self.active:
|
||||
for url, callback in self.taskList:
|
||||
try:
|
||||
r = requests.get(url)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
if self.DEBUG:
|
||||
print callback.__name__
|
||||
|
||||
data = {"return_symbol": (url.split('/'))[-1].split('_')[0] , "data":data}
|
||||
#data["return_symbol"] =
|
||||
callback(data)
|
||||
except Exception, e:
|
||||
print e
|
||||
|
||||
sleep(self.taskInterval)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeTick(self, symbol):
|
||||
"""订阅实时成交数据"""
|
||||
url = self.TICK_SYMBOL_URL[symbol]
|
||||
task = (url, self.onTick)
|
||||
self.taskList.append(task)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeLast(self, symbol):
|
||||
"""订阅实时成交数据"""
|
||||
url = self.LAST_SYMBOL_URL[symbol]
|
||||
task = (url, self.onLast)
|
||||
self.taskList.append(task)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeTrades(self, symbol):
|
||||
"""订阅实时报价数据"""
|
||||
url = self.TRADES_SYMBOL_URL[symbol]
|
||||
task = (url, self.onTrades)
|
||||
self.taskList.append(task)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeDepth(self, symbol, level=0):
|
||||
"""订阅深度数据"""
|
||||
url = self.DEPTH_SYMBOL_URL[symbol]
|
||||
|
||||
if level:
|
||||
url = url.replace('json', str(level))
|
||||
|
||||
task = (url, self.onDepth)
|
||||
self.taskList.append(task)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTick(self, data):
|
||||
"""实时成交推送"""
|
||||
print data
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onLast(self, data):
|
||||
"""实时深度推送"""
|
||||
print data
|
||||
#----------------------------------------------------------------------
|
||||
def onTrades(self, data):
|
||||
"""实时深度推送"""
|
||||
print data
|
||||
#----------------------------------------------------------------------
|
||||
def onDepth(self, data):
|
||||
"""实时深度推送"""
|
||||
print data
|
||||
|
3
vnpy/api/zb/__init__.py
Normal file
3
vnpy/api/zb/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnzb import ZB_Sub_Spot_Api , zb_all_symbol_pairs , zb_all_symbols , zb_all_real_pair
|
53
vnpy/api/zb/reame.txt
Normal file
53
vnpy/api/zb/reame.txt
Normal file
@ -0,0 +1,53 @@
|
||||
API 文档地址
|
||||
https://www.zb.com/i/developer/websocketApi#config
|
||||
|
||||
代码 描述
|
||||
1000 调用成功
|
||||
1001 一般错误提示
|
||||
1002 内部错误
|
||||
1003 验证不通过
|
||||
1004 资金安全密码锁定
|
||||
1005 资金安全密码错误,请确认后重新输入。
|
||||
1006 实名认证等待审核或审核不通过
|
||||
1007 频道为空
|
||||
1008 事件为空
|
||||
1009 此接口维护中
|
||||
2001 人民币账户余额不足
|
||||
2002 比特币账户余额不足
|
||||
2003 莱特币账户余额不足
|
||||
2005 以太币账户余额不足
|
||||
2006 ETC币账户余额不足
|
||||
2007 BTS币账户余额不足
|
||||
2008 EOS币账户余额不足
|
||||
2009 账户余额不足
|
||||
3001 挂单没有找到
|
||||
3002 无效的金额
|
||||
3003 无效的数量
|
||||
3004 用户不存在
|
||||
3005 无效的参数
|
||||
3006 无效的IP或与绑定的IP不一致
|
||||
3007 请求时间已失效
|
||||
3008 交易记录没有找到
|
||||
4001 API接口被锁定或未启用
|
||||
4002 请求过于频繁
|
||||
|
||||
|
||||
WebSocket服务地址
|
||||
ZB WebSocket服务连接地址:wss://api.zb.com:9999/websocket
|
||||
|
||||
接口 描述
|
||||
ticker行情
|
||||
depth市场深度
|
||||
trades历史成交
|
||||
交易API
|
||||
|
||||
用于ZB快速进行交易
|
||||
|
||||
接口 描述
|
||||
order委托下单
|
||||
cancelOrder取消委托
|
||||
getOrder获取委托买单或卖单
|
||||
getorders获取多个委托买单或卖单,每次请求返回10条记录
|
||||
getordersignoretradetype取消tradeType字段过滤,可同时获取买单和卖单,每次请求返回pageSize<100条记录
|
||||
getaccountinfo获取用户信息
|
||||
|
33
vnpy/api/zb/test.py
Normal file
33
vnpy/api/zb/test.py
Normal file
@ -0,0 +1,33 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnzb import *
|
||||
|
||||
# 在OkCoin网站申请这两个Key,分别对应用户名和密码
|
||||
apiKey = '你的apiKey'
|
||||
secretKey = '你的secreKey'
|
||||
|
||||
# 创建API对象
|
||||
api = ZB_Sub_Spot_Api()
|
||||
|
||||
api.connect_Subpot(apiKey, secretKey, True)
|
||||
|
||||
sleep(3)
|
||||
|
||||
# api.login()
|
||||
# api.subscribeSpotTicker("ltc_btc")
|
||||
# api.subscribeSpotDepth("ltc_btc")
|
||||
# api.subscribeSpotTrades("ltc_btc")
|
||||
#api.subscribeSpotKlines("bch_btc","30min")
|
||||
|
||||
#api.spotTrade("btc_qc", 1 , "50" , "0.01")
|
||||
'''
|
||||
{"message":"鎿嶄綔鎴愬姛","no":"0","data":"{entrustId:2017121051685}","code":1000,"channel":"btcqc_order","success":true}
|
||||
{"message":"鎿嶄綔鎴愬姛","no":"0","data":"{entrustId:2017121051713}","code":1000,"channel":"btcqc_order","success":true}
|
||||
'''
|
||||
#api.spotUserInfo()
|
||||
|
||||
#api.spotCancelOrder("btc_qc", "2017121051685")
|
||||
#api.spotOrderInfo("btc_qc","2017121051713")
|
||||
|
||||
#api.spotGetOrders("btc_qc" , "1" , "1")
|
||||
#api.spotGetOrderSignOrderTradeType("btc_qc", 1 , 10 , 1)
|
268
vnpy/api/zb/vnzb.py
Normal file
268
vnpy/api/zb/vnzb.py
Normal file
@ -0,0 +1,268 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
import hashlib
|
||||
import zlib
|
||||
import json
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
|
||||
import websocket
|
||||
import urllib2, hashlib,struct,sha,time
|
||||
|
||||
|
||||
|
||||
# OKEX网站
|
||||
zb_usd_url = "wss://api.zb.com:9999/websocket"
|
||||
zb_all_symbol_pairs = ["ltc_btc","btc_qc","bcc_qc","ltc_qc","eth_qc","etc_qc","bts_qc","eos_qc","qtum_qc","btc_qc","hsr_qc","xpr_qc","bcd_qc","dash_qc"]
|
||||
zb_all_symbols = ["btc","ltc","qc","bch","eth","hsr","ubtc","sbtc"]
|
||||
|
||||
zb_all_real_pair = {}
|
||||
for symbol_pair in zb_all_symbol_pairs:
|
||||
zb_all_real_pair[ symbol_pair.replace('_',"")] = symbol_pair
|
||||
|
||||
class ZB_Sub_Spot_Api(object):
|
||||
"""基于Websocket的API对象"""
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
self.apiKey = '' # 用户名
|
||||
self.secretKey = '' # 密码
|
||||
|
||||
self.ws_sub_spot = None # websocket应用对象 现货对象
|
||||
|
||||
self.thread = None # 线程变量
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def reconnect(self):
|
||||
"""重新连接"""
|
||||
# 首先关闭之前的连接
|
||||
self.close()
|
||||
|
||||
# 再执行重连任务
|
||||
self.ws_sub_spot = websocket.WebSocketApp(self.host,
|
||||
on_message=self.onMessage,
|
||||
on_error=self.onError,
|
||||
on_close=self.onClose,
|
||||
on_open=self.onOpen)
|
||||
|
||||
self.thread = Thread(target=self.ws_sub_spot.run_forever)
|
||||
self.thread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect_Subpot(self, apiKey , secretKey , trace = False):
|
||||
self.host = zb_usd_url
|
||||
self.apiKey = apiKey
|
||||
self.secretKey = secretKey
|
||||
|
||||
websocket.enableTrace(trace)
|
||||
|
||||
self.ws_sub_spot = websocket.WebSocketApp(self.host,
|
||||
on_message=self.onMessage,
|
||||
on_error=self.onError,
|
||||
on_close=self.onClose,
|
||||
on_open=self.onOpen)
|
||||
|
||||
self.thread = Thread(target=self.ws_sub_spot.run_forever)
|
||||
self.thread.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def readData(self, evt):
|
||||
"""解压缩推送收到的数据"""
|
||||
# # 创建解压器
|
||||
# decompress = zlib.decompressobj(-zlib.MAX_WBITS)
|
||||
|
||||
# # 将原始数据解压成字符串
|
||||
# inflated = decompress.decompress(evt) + decompress.flush()
|
||||
|
||||
# 通过json解析字符串
|
||||
data = json.loads(evt)
|
||||
|
||||
return data
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭接口"""
|
||||
if self.thread and self.thread.isAlive():
|
||||
self.ws_sub_spot.close()
|
||||
self.thread.join()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onMessage(self, ws, evt):
|
||||
"""信息推送"""
|
||||
print evt
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, ws, evt):
|
||||
"""错误推送"""
|
||||
print 'onError'
|
||||
print evt
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onClose(self, ws):
|
||||
"""接口断开"""
|
||||
print 'onClose'
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOpen(self, ws):
|
||||
"""接口打开"""
|
||||
print 'onOpen'
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSpotTicker(self, symbol_pair):
|
||||
# 现货的 ticker
|
||||
symbol_pair = symbol_pair.replace('_','')
|
||||
req = "{'event':'addChannel','channel':'%s_ticker'}" % symbol_pair
|
||||
self.ws_sub_spot.send(req)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSpotDepth(self, symbol_pair):
|
||||
# 现货的 市场深度
|
||||
symbol_pair = symbol_pair.replace('_','')
|
||||
req = "{'event':'addChannel','channel':'%s_depth'}" % symbol_pair
|
||||
self.ws_sub_spot.send(req)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSpotTrades(self, symbol_pair):
|
||||
symbol_pair = symbol_pair.replace('_','')
|
||||
req = "{'event':'addChannel','channel':'%s_trades'}" % symbol_pair
|
||||
self.ws_sub_spot.send(req)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __fill(self, value, lenght, fillByte):
|
||||
if len(value) >= lenght:
|
||||
return value
|
||||
else:
|
||||
fillSize = lenght - len(value)
|
||||
return value + chr(fillByte) * fillSize
|
||||
#----------------------------------------------------------------------
|
||||
def __doXOr(self, s, value):
|
||||
slist = list(s)
|
||||
for index in xrange(len(slist)):
|
||||
slist[index] = chr(ord(slist[index]) ^ value)
|
||||
return "".join(slist)
|
||||
#----------------------------------------------------------------------
|
||||
def __hmacSign(self, aValue, aKey):
|
||||
keyb = struct.pack("%ds" % len(aKey), aKey)
|
||||
value = struct.pack("%ds" % len(aValue), aValue)
|
||||
k_ipad = self.__doXOr(keyb, 0x36)
|
||||
k_opad = self.__doXOr(keyb, 0x5c)
|
||||
k_ipad = self.__fill(k_ipad, 64, 54)
|
||||
k_opad = self.__fill(k_opad, 64, 92)
|
||||
m = hashlib.md5()
|
||||
m.update(k_ipad)
|
||||
m.update(value)
|
||||
dg = m.digest()
|
||||
|
||||
m = hashlib.md5()
|
||||
m.update(k_opad)
|
||||
subStr = dg[0:16]
|
||||
m.update(subStr)
|
||||
dg = m.hexdigest()
|
||||
return dg
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __digest(self, aValue):
|
||||
value = struct.pack("%ds" % len(aValue), aValue)
|
||||
h = sha.new()
|
||||
h.update(value)
|
||||
dg = h.hexdigest()
|
||||
return dg
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def generateSign(self, params):
|
||||
"""生成签名"""
|
||||
l = []
|
||||
for key in sorted(params.keys()):
|
||||
l.append('"%s":"%s"' %(key, params[key]))
|
||||
sign = ','.join(l)
|
||||
sign = '{' + sign + '}'
|
||||
|
||||
SHA_secret = self.__digest(self.secretKey)
|
||||
return self.__hmacSign( sign, SHA_secret)
|
||||
# return hashlib.md5(sign.encode('utf-8')).hexdigest().upper()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendTradingRequest(self, channel, params):
|
||||
"""发送交易请求"""
|
||||
# 在参数字典中加上api_key和签名字段
|
||||
params['accesskey'] = self.apiKey
|
||||
params['channel'] = channel
|
||||
params['event'] = "addChannel"
|
||||
|
||||
params['sign'] = self.generateSign(params)
|
||||
|
||||
# 使用json打包并发送
|
||||
j = json.dumps(params)
|
||||
|
||||
# 若触发异常则重连
|
||||
try:
|
||||
self.ws_sub_spot.send(j)
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotTrade(self, symbol_pair, type_, price, amount):
|
||||
"""现货委托"""
|
||||
symbol_pair = symbol_pair.replace('_','')
|
||||
params = {}
|
||||
params['tradeType'] = str(type_)
|
||||
params['price'] = str(price)
|
||||
params['amount'] = str(amount)
|
||||
|
||||
channel = symbol_pair.lower() + "_order"
|
||||
|
||||
print channel , str(type_) , str(price) , str(amount)
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotCancelOrder(self, symbol_pair, orderid):
|
||||
"""现货撤单"""
|
||||
bef_symbol_pair = symbol_pair
|
||||
symbol_pair = symbol_pair.replace('_','')
|
||||
params = {}
|
||||
params['id'] = str(orderid)
|
||||
params['no'] = str(bef_symbol_pair) + "." + str(orderid)
|
||||
|
||||
channel = symbol_pair.lower() + "_cancelorder"
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotUserInfo(self):
|
||||
"""查询现货账户"""
|
||||
channel = 'getaccountinfo'
|
||||
self.sendTradingRequest(channel, {})
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotOrderInfo(self, symbol_pair, orderid):
|
||||
"""查询现货委托信息"""
|
||||
symbol_pair = symbol_pair.replace('_','')
|
||||
params = {}
|
||||
params['id'] = str(orderid)
|
||||
|
||||
channel = symbol_pair.lower() + "_getorder"
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotGetOrders(self, symbol_pair , pageIndex , type_):
|
||||
"""查询现货所有委托信息"""
|
||||
symbol_pair = symbol_pair.replace('_','')
|
||||
params = {}
|
||||
params['pageIndex'] = str(pageIndex)
|
||||
params['tradeType'] = str(type_)
|
||||
|
||||
channel = symbol_pair.lower() + "_getorders"
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotGetOrderSignOrderTradeType(self , symbol_pair , pageIndex , pageSize , type_):
|
||||
symbol_pair = symbol_pair.replace('_','')
|
||||
params = {}
|
||||
params['pageIndex'] = str(pageIndex)
|
||||
params['pageSize'] = str(pageSize)
|
||||
params['tradeType'] = str(type_)
|
||||
|
||||
channel = symbol_pair.lower() + "_getordersignoretradetype"
|
||||
|
||||
self.sendTradingRequest(channel, params)
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"accountID": "你的账户ID",
|
||||
"accessKey": "你的key",
|
||||
"secretKey": "你的secretKey",
|
||||
"interval": 0.5,
|
||||
"debug": false
|
||||
}
|
11
vnpy/trader/gateway/coincheckGateway/__init__.py
Normal file
11
vnpy/trader/gateway/coincheckGateway/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnpy.trader import vtConstant
|
||||
from coincheckGateway import CoincheckGateway, CoincheckTradeApi , CoincheckSocketDataApi
|
||||
|
||||
gatewayClass = CoincheckGateway
|
||||
gatewayName = 'COINCHECK'
|
||||
gatewayDisplayName = u'COINCHECK'
|
||||
gatewayType = vtConstant.GATEWAYTYPE_BTC
|
||||
gatewayQryEnabled = True
|
||||
|
759
vnpy/trader/gateway/coincheckGateway/coincheckGateway.py
Normal file
759
vnpy/trader/gateway/coincheckGateway/coincheckGateway.py
Normal file
@ -0,0 +1,759 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
'''
|
||||
vn.coincheck的gateway接入
|
||||
'''
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
from copy import copy
|
||||
from threading import Condition
|
||||
from Queue import Queue
|
||||
from threading import Thread
|
||||
|
||||
import json
|
||||
from vnpy.api.coincheck import vncoincheck
|
||||
from vnpy.trader.vtGateway import *
|
||||
from vnpy.trader.vtFunction import getJsonPath
|
||||
|
||||
from datetime import datetime , timedelta
|
||||
|
||||
SYMBOL_BTCJPY = 'btc_jpy'
|
||||
COINCHECK_HOSTS = "wss://ws-api.coincheck.com"
|
||||
|
||||
class CoincheckGateway(VtGateway):
|
||||
"""coincheck 接口"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, eventEngine, gatewayName='COINCHECK'):
|
||||
"""Constructor"""
|
||||
super(CoincheckGateway, self).__init__(eventEngine, gatewayName)
|
||||
|
||||
|
||||
self.tradeApi = CoincheckTradeApi(self)
|
||||
#self.dataApi = CoincheckDataApi(self)
|
||||
self.dataApi = CoincheckSocketDataApi(self)
|
||||
|
||||
self.fileName = self.gatewayName + '_connect.json'
|
||||
self.filePath = getJsonPath(self.fileName, __file__)
|
||||
|
||||
self.total_count = 0
|
||||
self.delayTime = 3
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self):
|
||||
"""连接"""
|
||||
# 载入json文件
|
||||
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:
|
||||
accessKey = str(setting['accessKey'])
|
||||
secretKey = str(setting['secretKey'])
|
||||
interval = setting['interval']
|
||||
debug = setting['debug']
|
||||
useAccountID = str(setting['accountID'])
|
||||
except KeyError:
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'连接配置缺少字段,请检查'
|
||||
self.onLog(log)
|
||||
return
|
||||
|
||||
# 设置账户ID
|
||||
self.tradeApi.setAccountID( useAccountID)
|
||||
|
||||
# 初始化接口
|
||||
self.tradeApi.init(accessKey, secretKey)
|
||||
self.writeLog(u'交易接口初始化成功')
|
||||
|
||||
#self.dataApi.connect(interval, debug)
|
||||
self.dataApi.connect()
|
||||
self.writeLog(u'行情接口初始化成功')
|
||||
|
||||
|
||||
# 启动查询
|
||||
self.initQuery()
|
||||
self.startQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def writeLog(self, content):
|
||||
"""发出日志"""
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = content
|
||||
self.onLog(log)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
"""订阅行情,自动订阅全部行情,无需实现"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, orderReq):
|
||||
"""发单"""
|
||||
return self.tradeApi.sendOrder(orderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, cancelOrderReq):
|
||||
"""撤单"""
|
||||
return self.tradeApi.cancel(cancelOrderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryAccount(self):
|
||||
"""查询账户资金"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryPosition(self):
|
||||
"""查询持仓"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭"""
|
||||
self.tradeApi.exit()
|
||||
self.dataApi.exit()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initQuery(self):
|
||||
"""初始化连续查询"""
|
||||
|
||||
if self.qryEnabled:
|
||||
self.qryFunctionList = [self.tradeApi.get_balance , self.tradeApi.list_orders]
|
||||
#self.qryFunctionList = [self.tradeApi.queryWorkingOrders, self.tradeApi.queryAccount]
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def query(self, event):
|
||||
"""注册到事件处理引擎上的查询函数"""
|
||||
self.total_count += 1
|
||||
if self.total_count % self.delayTime == 0:
|
||||
for function in self.qryFunctionList:
|
||||
function()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def startQuery(self):
|
||||
"""启动连续查询"""
|
||||
self.eventEngine.register(EVENT_TIMER, self.query)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onListOrder(self, data):
|
||||
print data
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setQryEnabled(self, qryEnabled):
|
||||
"""设置是否要启动循环查询"""
|
||||
self.qryEnabled = qryEnabled
|
||||
|
||||
class CoincheckTradeApi(vncoincheck.TradeApi):
|
||||
|
||||
def __init__(self, gateway):
|
||||
super(CoincheckTradeApi , self).__init__()
|
||||
|
||||
self.gateway = gateway
|
||||
self.gatewayName = gateway.gatewayName
|
||||
self.accountID = "COINCHECK"
|
||||
self.DEBUG = False
|
||||
|
||||
self.localID = 0 # 本地委托号
|
||||
self.localSystemDict = {} # key:localID, value:systemID
|
||||
self.systemLocalDict = {} # key:systemID, value:localID
|
||||
self.workingOrderDict = {} # key:localID, value:order
|
||||
self.reqLocalDict = {} # key:reqID, value:localID
|
||||
self.cancelDict = {} # key:localID, value:cancelOrderReq
|
||||
|
||||
self.tradedVolumeDict = {} # key:localID, value:volume ,已经交易成功的数量
|
||||
|
||||
self.tradeID = 0 # 本地成交号
|
||||
|
||||
#--------------
|
||||
def setAccountID(self, useAccountID):
|
||||
self.accountID = useAccountID
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
def onError(self, method ,data):
|
||||
print method , data
|
||||
#
|
||||
'''
|
||||
"return" :
|
||||
{u'lending_leverage': u'5.0', u'success': True, u'maker_fee': u'0.0', u'email': u'liyi.riki.thomas@g
|
||||
mail.com', u'bitcoin_address': u'1Q73J2e46TrBv9cRCtfgcszqEcwNDsei53', u'taker_fee': u'0.0', u'identi
|
||||
ty_status': u'identity_verified', u'id': 1007549}
|
||||
'''
|
||||
def onGet_info(self, data, req, reqID):
|
||||
"""用户信息"""
|
||||
print data
|
||||
'''
|
||||
{u'zec': u'0', u'rep_debt': u'0.0', u'xem': u'0', u'lsk': u'0', u'rep_lend_in_use': u'0.0', u'ltc_de
|
||||
bt': u'0.0', u'xmr_reserved': u'0.0', u'cny': u'0', u'btc_reserved': u'0.0', u'dao_reserved': u'0.0'
|
||||
, u'ltc_lent': u'0.0', u'dao_lend_in_use': u'0.0', u'xrp_reserved': u'0.0', u'zec_debt': u'0.0', u'b
|
||||
ch_lent': u'0.0', u'dao_debt': u'0.0', u'xmr': u'0', u'rep_reserved': u'0.0', u'dao': u'0', u'xem_le
|
||||
nd_in_use': u'0.0', u'fct_lent': u'0.0', u'jpy_reserved': u'0.0', u'success': True, u'fct_reserved':
|
||||
u'0.0', u'xem_lent': u'0.0', u'rep_lent': u'0.0', u'eth_lend_in_use': u'0.0', u'btc': u'0', u'usd_l
|
||||
end_in_use': u'0.0', u'zec_lent': u'0.0', u'rep': u'0', u'xmr_debt': u'0.0', u'bch_lend_in_use': u'0
|
||||
.0', u'xrp_debt': u'0.0', u'etc_lend_in_use': u'0.0', u'dash_reserved': u'0.0', u'dash_lent': u'0.0'
|
||||
, u'dash_debt': u'0.0', u'jpy_lend_in_use': u'0.0', u'lsk_lend_in_use': u'0.0', u'eth_lent': u'0.0',
|
||||
u'ltc': u'0', u'etc': u'0', u'ltc_lend_in_use': u'0.0', u'eth': u'0', u'usd_debt': u'0.0', u'ltc_re
|
||||
served': u'0.0', u'cny_reserved': u'0.0', u'xem_debt': u'0.0', u'eth_reserved': u'0.0', u'zec_reserv
|
||||
ed': u'0.0', u'usd': u'0', u'cny_lend_in_use': u'0.0', u'lsk_debt': u'0.0', u'xmr_lend_in_use': u'0.
|
||||
0', u'dash_lend_in_use': u'0.0', u'xrp_lent': u'0.0', u'bch_reserved': u'0.0', u'xmr_lent': u'0.0',
|
||||
u'bch_debt': u'0.0', u'bch': u'0', u'jpy': u'0', u'fct_debt': u'0.0', u'btc_debt': u'0.0', u'usd_len
|
||||
t': u'0.0', u'btc_lent': u'0.0', u'lsk_reserved': u'0.0', u'etc_debt': u'0.0', u'jpy_lent': u'0.0',
|
||||
u'dash': u'0', u'cny_debt': u'0.0', u'xrp_lend_in_use': u'0.0', u'xem_reserved': u'0.0', u'dao_lent'
|
||||
: u'0.0', u'lsk_lent': u'0.0', u'etc_lent': u'0.0', u'jpy_debt': u'0.0', u'xrp': u'0', u'fct': u'0',
|
||||
u'etc_reserved': u'0.0', u'usd_reserved': u'0.0', u'fct_lend_in_use': u'0.0', u'btc_lend_in_use': u
|
||||
'0.0', u'zec_lend_in_use': u'0.0', u'eth_debt': u'0.0', u'cny_lent': u'0.0'}
|
||||
'''
|
||||
def onGet_balance(self, data, req, reqID):
|
||||
if data["success"] == 0:
|
||||
print "Error in onGet_balance"
|
||||
print data
|
||||
else:
|
||||
account = VtAccountData()
|
||||
account.gatewayName = self.gatewayName
|
||||
account.accountID = self.accountID
|
||||
account.vtAccountID = '.'.join([ self.gatewayName , self.accountID])
|
||||
account.balance = float(data['jpy'])
|
||||
account.available = float(data['jpy'])
|
||||
|
||||
account.margin = 1.0
|
||||
account.closeProfit = 0.0
|
||||
account.positionProfit = 0.0
|
||||
account.commission = 0.0
|
||||
account.now_has_hands = float(data['jpy'])
|
||||
|
||||
|
||||
self.gateway.onAccount(account)
|
||||
|
||||
for symbol in ['btc' , 'jpy']:
|
||||
posObj = VtPositionData()
|
||||
posObj.gatewayName = self.gatewayName
|
||||
posObj.symbol = symbol + "." + EXCHANGE_COINCHECK
|
||||
posObj.exchange = EXCHANGE_COINCHECK
|
||||
posObj.vtSymbol = posObj.symbol
|
||||
posObj.direction = DIRECTION_NET
|
||||
posObj.vtPositionName = '.'.join( [posObj.vtSymbol, posObj.direction])
|
||||
posObj.ydPosition = float(data[symbol])
|
||||
posObj.position = float(data[symbol]) + float(data[symbol + "_reserved"])
|
||||
posObj.frozen = float(data[symbol + "_reserved"])
|
||||
posObj.positionProfit = 0
|
||||
self.gateway.onPosition(posObj)
|
||||
|
||||
'''
|
||||
发送系统委托
|
||||
'''
|
||||
def sendOrder(self, req):
|
||||
"""发送委托"""
|
||||
# 检查是否填入了价格,禁止市价委托
|
||||
if req.priceType != PRICETYPE_LIMITPRICE:
|
||||
err = VtErrorData()
|
||||
err.gatewayName = self.gatewayName
|
||||
err.errorMsg = u'Coincheck接口仅支持限价单'
|
||||
err.errorTime = datetime.now().strftime('%H:%M:%S')
|
||||
self.gateway.onError(err)
|
||||
return None
|
||||
symbol = req.vtSymbol
|
||||
if req.direction == DIRECTION_LONG:
|
||||
reqID = self.buy_btc_jpy( rate = req.price , amount = req.volume )
|
||||
else:
|
||||
reqID = self.sell_btc_jpy( rate = req.price , amount = req.volume )
|
||||
|
||||
self.localID += 1
|
||||
localID = str(self.localID)
|
||||
self.reqLocalDict[reqID] = localID
|
||||
|
||||
# 推送委托信息
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
order.symbol = req.symbol
|
||||
order.exchange = EXCHANGE_COINCHECK
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localID
|
||||
order.vtOrderID = '.'.join([order.orderID, order.gatewayName])
|
||||
|
||||
order.direction = req.direction
|
||||
if req.direction == DIRECTION_LONG:
|
||||
order.offset = OFFSET_OPEN
|
||||
else:
|
||||
order.offset = OFFSET_CLOSE
|
||||
order.price = req.price
|
||||
order.tradedVolume = 0
|
||||
order.totalVolume = req.volume
|
||||
order.orderTime = datetime.now().strftime('%H:%M:%S')
|
||||
order.status = STATUS_UNKNOWN
|
||||
|
||||
self.workingOrderDict[localID] = order
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
# 返回委托号
|
||||
return order.vtOrderID
|
||||
|
||||
|
||||
'''
|
||||
{u'market_buy_amount': None, u'order_type': u'buy', u'success': True, u'created_at': u'2017-10-16T13
|
||||
:53:01.678Z', u'rate': u'100.0', u'amount': u'0.005', u'pair': u'btc_jpy', u'stop_loss_rate': None,
|
||||
u'id': 324141928}
|
||||
'''
|
||||
def onBuy_btc(self, data, req, reqID):
|
||||
# print "onBuy_btc"
|
||||
# print data
|
||||
if data["success"] == 0:
|
||||
print "Error in onBuy_btc"
|
||||
print data
|
||||
else:
|
||||
localID = self.reqLocalDict[reqID]
|
||||
systemID = data['id']
|
||||
self.localSystemDict[localID] = systemID
|
||||
self.systemLocalDict[systemID] = localID
|
||||
|
||||
# 撤单
|
||||
if localID in self.cancelDict:
|
||||
req = self.cancelDict[localID]
|
||||
self.cancel(req)
|
||||
del self.cancelDict[localID]
|
||||
|
||||
# 推送委托信息
|
||||
order = self.workingOrderDict[localID]
|
||||
if data['success'] != 0:
|
||||
order.status = STATUS_NOTTRADED
|
||||
|
||||
self.tradedVolumeDict[localID] = 0.0
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
def onSell_btc(self, data, req, reqID):
|
||||
# print "onSell_btc"
|
||||
# print data
|
||||
"""卖出回调"""
|
||||
if data["success"] == 0:
|
||||
print "Error in onSell_btc"
|
||||
else:
|
||||
localID = self.reqLocalDict[reqID]
|
||||
systemID = data['id']
|
||||
self.localSystemDict[localID] = systemID
|
||||
self.systemLocalDict[systemID] = localID
|
||||
|
||||
# 撤单
|
||||
if localID in self.cancelDict:
|
||||
req = self.cancelDict[localID]
|
||||
self.cancel(req)
|
||||
del self.cancelDict[localID]
|
||||
|
||||
# 推送委托信息
|
||||
order = self.workingOrderDict[localID]
|
||||
if data['success'] != 0:
|
||||
order.status = STATUS_NOTTRADED
|
||||
self.tradedVolumeDict[localID] = 0.0
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
'''
|
||||
{u'orders': [{u'order_type': u'buy', u'created_at': u'2017-10-16T13:51:41.000Z', u'pending_market_bu
|
||||
y_amount': None, u'rate': u'200.0', u'pair': u'btc_jpy', u'stop_loss_rate': None, u'id': 324139122,
|
||||
u'pending_amount': u'0.005'}, {u'order_type': u'buy', u'created_at': u'2017-10-16T13:53:01.000Z', u'
|
||||
pending_market_buy_amount': None, u'rate': u'100.0', u'pair': u'btc_jpy', u'stop_loss_rate': None, u
|
||||
'id': 324141928, u'pending_amount': u'0.005'}], u'success': True}
|
||||
只显示 未结算的 订单。 如果订单被结算了,说明已经成交了
|
||||
'''
|
||||
def onList_order(self, data, req, reqID):
|
||||
# print "onList_order"
|
||||
# self.gateway.onListOrder( data)
|
||||
if data["success"] == 0:
|
||||
pass
|
||||
else:
|
||||
orders = data["orders"]
|
||||
now_datetime = datetime.now()
|
||||
ten_seconds_before = now_datetime + timedelta(seconds=-10)
|
||||
ten_seconds_str = (ten_seconds_before.strftime("%Y-%m-%dT%H:%M:%S.%f"))[:-3] + "Z"
|
||||
|
||||
stile_live_order_system_id = [ x["id"] for x in orders]
|
||||
#print "stile_live_order_system_id", stile_live_order_system_id
|
||||
local_system_dict_keys = self.systemLocalDict.keys()
|
||||
# 对系统中有的订单,进行
|
||||
for bef_system_id in local_system_dict_keys:
|
||||
if bef_system_id not in stile_live_order_system_id:
|
||||
# 说明这个单子成交完毕了!
|
||||
# 或者就是取消了
|
||||
localID = self.systemLocalDict[bef_system_id]
|
||||
order = self.workingOrderDict.get(localID, None)
|
||||
if order != None:
|
||||
bef_has_volume = self.tradedVolumeDict.get(localID , 0.0)
|
||||
newTradeVolume = order.totalVolume - bef_has_volume
|
||||
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
trade.symbol = order.symbol
|
||||
trade.vtSymbol = order.vtSymbol
|
||||
|
||||
self.tradeID += 1
|
||||
trade.tradeID = str(self.tradeID)
|
||||
trade.vtTradeID = '.'.join([trade.tradeID, trade.gatewayName])
|
||||
trade.orderID = order.orderID
|
||||
trade.vtOrderID = order.vtOrderID
|
||||
|
||||
trade.volume = newTradeVolume
|
||||
trade.price = order.price
|
||||
trade.direction = order.direction
|
||||
trade.offset = order.offset
|
||||
trade.exchange = order.exchange
|
||||
trade.tradeTime = datetime.now().strftime('%H:%M:%S')
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
|
||||
order.status = STATUS_ALLTRADED
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
del self.tradedVolumeDict[localID]
|
||||
del self.systemLocalDict[bef_system_id]
|
||||
del self.workingOrderDict[localID]
|
||||
|
||||
for d in orders:
|
||||
coinID = d["id"]
|
||||
if coinID in local_system_dict_keys:
|
||||
localID = self.systemLocalDict[coinID]
|
||||
order = self.workingOrderDict.get(localID, None)
|
||||
|
||||
if order != None:
|
||||
bef_has_volume = self.tradedVolumeDict.get(localID , 0.0)
|
||||
has_traded_volume = d["pending_market_buy_amount"]
|
||||
if has_traded_volume == None:
|
||||
has_traded_volume = 0.0
|
||||
newTradeVolume = float(has_traded_volume) - float(bef_has_volume)
|
||||
|
||||
if newTradeVolume > 0.00000001:
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
trade.symbol = order.symbol
|
||||
trade.vtSymbol = order.vtSymbol
|
||||
|
||||
self.tradeID += 1
|
||||
trade.tradeID = str(self.tradeID)
|
||||
trade.vtTradeID = '.'.join([trade.tradeID, trade.gatewayName])
|
||||
trade.orderID = order.orderID
|
||||
trade.vtOrderID = order.vtOrderID
|
||||
|
||||
trade.volume = newTradeVolume
|
||||
trade.price = order.price
|
||||
trade.direction = order.direction
|
||||
trade.offset = order.offset
|
||||
trade.exchange = order.exchange
|
||||
trade.tradeTime = datetime.now().strftime('%H:%M:%S')
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
|
||||
order.tradedVolume = has_traded_volume
|
||||
order.status = STATUS_PARTTRADED
|
||||
self.gateway.onOrder(order)
|
||||
else:
|
||||
|
||||
# 说明这是一个 不知道 哪里来的订单
|
||||
# 推送委托信息
|
||||
# 订单有两种可能
|
||||
# 1、人工发的单
|
||||
# 2、前面取消失败的单 # 总有些订单是取消失败的 , 如果出现了,那么就取消掉这些交易
|
||||
# 所以对于订单进行判断,如果订单时间超过10秒, 那么取消掉
|
||||
|
||||
if order.orderTime < ten_seconds_str :
|
||||
# 判断为需要取消的单子
|
||||
self.cancel_orders( coinID )
|
||||
else:
|
||||
self.localID += 1
|
||||
localID = str(self.localID)
|
||||
|
||||
symbol_pair = d['pair'] # btc_jpy
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
order.symbol = symbol_pair + "." + self.gatewayName
|
||||
order.exchange = EXCHANGE_COINCHECK
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localID
|
||||
order.vtOrderID = '.'.join(["mistake", order.gatewayName])
|
||||
|
||||
order.direction = DIRECTION_LONG
|
||||
order.offset = OFFSET_OPEN
|
||||
|
||||
order.price = float(d["rate"])
|
||||
order.totalVolume = float(d["pending_amount"])
|
||||
order.orderTime = d["created_at"]
|
||||
order.status = STATUS_MISTAKE
|
||||
|
||||
self.workingOrderDict[localID] = order
|
||||
self.systemLocalDict[coinID] = localID
|
||||
self.localSystemDict[localID] = coinID
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
|
||||
'''
|
||||
{
|
||||
"success": true,
|
||||
"id": 12345
|
||||
}
|
||||
'''
|
||||
def onCancel_orders(self, data, req, reqID):
|
||||
# self.gateway.onCancelOrder( data)
|
||||
if data['success'] != 0:
|
||||
systemID = data["id"]
|
||||
localID = self.systemLocalDict[systemID]
|
||||
|
||||
order = self.workingOrderDict[localID]
|
||||
order.status = STATUS_CANCELLED
|
||||
|
||||
del self.workingOrderDict[localID]
|
||||
del self.systemLocalDict[systemID]
|
||||
del self.localSystemDict[localID]
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
def onHistory_orders(self, data, req, reqID):
|
||||
print data
|
||||
|
||||
def cancel(self, req):
|
||||
localID = req.orderID
|
||||
if localID in self.localSystemDict:
|
||||
systemID = self.localSystemDict[localID]
|
||||
self.cancel_orders( systemID )
|
||||
else:
|
||||
self.cancelDict[localID] = req
|
||||
|
||||
|
||||
class CoincheckSocketDataApi(vncoincheck.DataApiSocket):
|
||||
"""基于websocket的TICK数据获得对象"""
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, gateway):
|
||||
super(CoincheckSocketDataApi, self).__init__()
|
||||
|
||||
self.market = 'jpy'
|
||||
self.gateway = gateway
|
||||
self.gatewayName = gateway.gatewayName
|
||||
|
||||
self.tickDict = {} # key:symbol, value:tick
|
||||
|
||||
self.period_flag = False
|
||||
|
||||
|
||||
def connect(self ):
|
||||
super(CoincheckSocketDataApi, self).connect( COINCHECK_HOSTS)
|
||||
|
||||
contract = VtContractData()
|
||||
contract.gatewayName = self.gatewayName
|
||||
contract.symbol = SYMBOL_BTCJPY
|
||||
contract.exchange = EXCHANGE_COINCHECK
|
||||
contract.vtSymbol = '.'.join([contract.symbol, contract.exchange])
|
||||
contract.name = u'日元coincheck现货BTC'
|
||||
contract.size = 0.0001
|
||||
contract.priceTick = 0.0001
|
||||
contract.productClass = PRODUCT_SPOT
|
||||
self.gateway.onContract(contract)
|
||||
|
||||
def onOrderbooks(self, data):
|
||||
symbol = SYMBOL_BTCJPY
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.gatewayName = self.gatewayName
|
||||
tick.exchange = EXCHANGE_COINCHECK
|
||||
tick.symbol = '.'.join([symbol, tick.exchange])
|
||||
tick.vtSymbol = '.'.join([symbol, tick.exchange])
|
||||
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
data = json.loads(data)
|
||||
load_symbol , dic = data
|
||||
if load_symbol == symbol:
|
||||
bids = dic["bids"]
|
||||
asks = dic["asks"]
|
||||
|
||||
bids = [ (float(x[0]) , float(x[1])) for x in bids ]
|
||||
asks = [ (float(x[0]) , float(x[1])) for x in asks ]
|
||||
|
||||
tick.bidPrice1, tick.bidVolume1 = [0 , 0]
|
||||
tick.bidPrice2, tick.bidVolume2 = [0 , 0]
|
||||
tick.bidPrice3, tick.bidVolume3 = [0 , 0]
|
||||
tick.bidPrice4, tick.bidVolume4 = [0 , 0]
|
||||
tick.bidPrice5, tick.bidVolume5 = [0 , 0]
|
||||
|
||||
tick.askPrice1, tick.askVolume1 = [0 , 0]
|
||||
tick.askPrice2, tick.askVolume2 = [0 , 0]
|
||||
tick.askPrice3, tick.askVolume3 = [0 , 0]
|
||||
tick.askPrice4, tick.askVolume4 = [0 , 0]
|
||||
tick.askPrice5, tick.askVolume5 = [0 , 0]
|
||||
try:
|
||||
tick.bids = bids
|
||||
tick.asks = asks
|
||||
tick.bidPrice1, tick.bidVolume1 = bids[0]
|
||||
tick.bidPrice2, tick.bidVolume2 = bids[1]
|
||||
tick.bidPrice3, tick.bidVolume3 = bids[2]
|
||||
tick.bidPrice4, tick.bidVolume4 = bids[3]
|
||||
tick.bidPrice5, tick.bidVolume5 = bids[4]
|
||||
except Exception,ex:
|
||||
pass
|
||||
|
||||
try:
|
||||
tick.askPrice1, tick.askVolume1 = asks[0]
|
||||
tick.askPrice2, tick.askVolume2 = asks[1]
|
||||
tick.askPrice3, tick.askVolume3 = asks[2]
|
||||
tick.askPrice4, tick.askVolume4 = asks[3]
|
||||
tick.askPrice5, tick.askVolume5 = asks[4]
|
||||
except Exception,ex:
|
||||
pass
|
||||
|
||||
now = datetime.now()
|
||||
tick.time = now.strftime('%H:%M:%S')
|
||||
tick.date = now.strftime('%Y%m%d')
|
||||
tick.datetime = now
|
||||
|
||||
self.gateway.onTick(tick)
|
||||
self.period_flag = False
|
||||
|
||||
def onTrade(self , data):
|
||||
|
||||
orderId, symbol , price , volume , direction = data
|
||||
|
||||
price = float(price)
|
||||
volume = float(volume)
|
||||
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.gatewayName = self.gatewayName
|
||||
tick.exchange = EXCHANGE_COINCHECK
|
||||
tick.symbol = '.'.join([symbol, tick.exchange])
|
||||
tick.vtSymbol = '.'.join([symbol, tick.exchange])
|
||||
tick.volume = 0
|
||||
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
if self.period_flag == False:
|
||||
self.period_flag = True
|
||||
|
||||
tick.highPrice = price
|
||||
tick.lowPrice = price
|
||||
tick.lastPrice = price
|
||||
|
||||
else:
|
||||
tick.highPrice = max(tick.highPrice , price)
|
||||
tick.lowPrice = min(tick.lowPrice , price)
|
||||
tick.lastPrice = price
|
||||
|
||||
tick.volume += volume
|
||||
|
||||
|
||||
def onMessage(self, ws , evt):
|
||||
if evt:
|
||||
data = json.loads(evt)
|
||||
cclen = len(data)
|
||||
if cclen == 2:
|
||||
self.onOrderbooks( evt)
|
||||
elif cclen == 5:
|
||||
self.onTrade(data)
|
||||
|
||||
|
||||
class CoincheckDataApi(vncoincheck.DataApi):
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, gateway):
|
||||
"""Constructor"""
|
||||
super(CoincheckDataApi, self).__init__()
|
||||
|
||||
self.market = 'jpy'
|
||||
self.gateway = gateway
|
||||
self.gatewayName = gateway.gatewayName
|
||||
|
||||
self.tickDict = {} # key:symbol, value:tick
|
||||
|
||||
def connect(self, interval , market , debug = False):
|
||||
self.init(interval , debug)
|
||||
# 订阅行情并推送合约信息
|
||||
if self.market == 'jpy':
|
||||
self.subscribeTick(SYMBOL_BTCJPY)
|
||||
self.subscribeOrderbooks(SYMBOL_BTCJPY)
|
||||
|
||||
contract = VtContractData()
|
||||
contract.gatewayName = self.gatewayName
|
||||
contract.symbol = SYMBOL_BTCJPY
|
||||
contract.exchange = EXCHANGE_COINCHECK
|
||||
contract.vtSymbol = '.'.join([contract.symbol, contract.exchange])
|
||||
contract.name = u'日元coincheck现货BTC'
|
||||
contract.size = 0.0001
|
||||
contract.priceTick = 0.0001
|
||||
contract.productClass = PRODUCT_SPOT
|
||||
self.gateway.onContract(contract)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTick(self, data):
|
||||
"""实时成交推送"""
|
||||
symbol = SYMBOL_BTCJPY
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.gatewayName = self.gatewayName
|
||||
|
||||
tick.exchange = EXCHANGE_COINCHECK
|
||||
tick.symbol = '.'.join([symbol, tick.exchange])
|
||||
tick.vtSymbol = '.'.join([symbol, tick.exchange])
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
tick.highPrice = float(data['high'])
|
||||
tick.lowPrice = float(data['low'])
|
||||
tick.lastPrice = float(data['last'])
|
||||
tick.volume = float(data['volume'])
|
||||
|
||||
now = datetime.now()
|
||||
tick.time = now.strftime('%H:%M:%S')
|
||||
tick.date = now.strftime('%Y%m%d')
|
||||
tick.datetime = now
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTrades(self, data):
|
||||
"""实时成交推送"""
|
||||
print data
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOrderbooks(self, data):
|
||||
"""实时成交推送"""
|
||||
symbol = SYMBOL_BTCJPY
|
||||
bids = data["bids"]
|
||||
asks = data["asks"]
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.gatewayName = self.gatewayName
|
||||
|
||||
tick.symbol = symbol
|
||||
tick.exchange = EXCHANGE_COINCHECK
|
||||
tick.vtSymbol = '.'.join([tick.symbol, tick.exchange])
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
bids = [ (float(x[0]) , float(x[1])) for x in bids ]
|
||||
asks = [ (float(x[0]) , float(x[1])) for x in asks ]
|
||||
tick.bidPrice1, tick.bidVolume1 = bids[0]
|
||||
tick.bidPrice2, tick.bidVolume2 = bids[1]
|
||||
tick.bidPrice3, tick.bidVolume3 = bids[2]
|
||||
tick.bidPrice4, tick.bidVolume4 = bids[3]
|
||||
tick.bidPrice5, tick.bidVolume5 = bids[4]
|
||||
|
||||
tick.askPrice1, tick.askVolume1 = asks[0]
|
||||
tick.askPrice2, tick.askVolume2 = asks[1]
|
||||
tick.askPrice3, tick.askVolume3 = asks[2]
|
||||
tick.askPrice4, tick.askVolume4 = asks[3]
|
||||
tick.askPrice5, tick.askVolume5 = asks[4]
|
||||
|
||||
|
||||
now = datetime.now()
|
||||
tick.time = now.strftime('%H:%M:%S')
|
||||
tick.date = now.strftime('%Y%m%d')
|
||||
tick.datetime = now
|
||||
|
||||
self.gateway.onTick(tick)
|
||||
|
9
vnpy/trader/gateway/korbitGateway/KORBIT_connect.json
Normal file
9
vnpy/trader/gateway/korbitGateway/KORBIT_connect.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"accountID": "随意取个账户名",
|
||||
"accessKey": "accessID",
|
||||
"secretKey": "secretKey",
|
||||
"username": "你的网站用户名",
|
||||
"password": "你的网站密码",
|
||||
"interval": 3,
|
||||
"debug": false
|
||||
}
|
11
vnpy/trader/gateway/korbitGateway/__init__.py
Normal file
11
vnpy/trader/gateway/korbitGateway/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnpy.trader import vtConstant
|
||||
from korbitGateway import korbitGateway
|
||||
|
||||
gatewayClass = korbitGateway
|
||||
gatewayName = 'KORBIT'
|
||||
gatewayDisplayName = u'KORBIT'
|
||||
gatewayType = vtConstant.GATEWAYTYPE_BTC
|
||||
gatewayQryEnabled = True
|
||||
|
586
vnpy/trader/gateway/korbitGateway/korbitGateway.py
Normal file
586
vnpy/trader/gateway/korbitGateway/korbitGateway.py
Normal file
@ -0,0 +1,586 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
'''
|
||||
vn.coincheck的gateway接入
|
||||
'''
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
from copy import copy
|
||||
from threading import Condition
|
||||
from Queue import Queue
|
||||
from threading import Thread
|
||||
|
||||
import json
|
||||
from vnpy.api.korbit import vnkorbit , KORBIT_ALL_SYMBOL_PAIR , KORBIT_ALL_SYMBOLS
|
||||
from vnpy.trader.vtGateway import *
|
||||
from vnpy.trader.vtFunction import getJsonPath
|
||||
|
||||
from datetime import datetime , timedelta
|
||||
|
||||
korbit_order_status_map ={}
|
||||
korbit_order_status_map["partially_filled"] = STATUS_PARTTRADED
|
||||
korbit_order_status_map["unfilled"] = STATUS_NOTTRADED
|
||||
korbit_order_status_map["filled"] = STATUS_ALLTRADED
|
||||
|
||||
class korbitGateway(VtGateway):
|
||||
"""korbit接口"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, eventEngine, gatewayName = "KORBIT"):
|
||||
"""Constructor"""
|
||||
super(korbitGateway , self).__init__(eventEngine, gatewayName)
|
||||
|
||||
self.tradeApi = KorbitTradeApi(self)
|
||||
self.dataApi = KorbitDataApi(self)
|
||||
|
||||
self.fileName = self.gatewayName + "_connect.json"
|
||||
self.filePath = getJsonPath(self.fileName, __file__)
|
||||
self.accountID = "NONE"
|
||||
|
||||
self.total_count = 0
|
||||
self.delayTime = 3
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self):
|
||||
"""连接"""
|
||||
# 载入json文件
|
||||
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:
|
||||
self.accountID = str(setting["accountID"])
|
||||
self.accessKey = str(setting['accessKey'])
|
||||
self.secretKey = str(setting['secretKey'])
|
||||
self.username = str(setting["username"])
|
||||
self.password = str(setting["password"])
|
||||
self.interval = setting['interval']
|
||||
self.debug = setting['debug']
|
||||
|
||||
except KeyError:
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'连接配置缺少字段,请检查'
|
||||
self.onLog(log)
|
||||
return
|
||||
|
||||
# 设置账户ID
|
||||
self.tradeApi.setUserInfo( self.accountID , self.username , self.password)
|
||||
|
||||
# 初始化接口
|
||||
self.tradeApi.init(self.accessKey, self.secretKey , self.username , self.password)
|
||||
self.writeLog(u'交易接口初始化成功')
|
||||
|
||||
self.dataApi.connect(self.interval , self.debug)
|
||||
self.writeLog(u'行情接口初始化成功')
|
||||
|
||||
# 启动查询
|
||||
self.initQuery()
|
||||
self.startQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def writeLog(self, content):
|
||||
"""发出日志"""
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = content
|
||||
self.onLog(log)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
"""订阅行情,自动订阅全部行情,无需实现"""
|
||||
self.dataApi.subscribe(subscribeReq)
|
||||
self.tradeApi.subscribe(subscribeReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, orderReq):
|
||||
"""发单"""
|
||||
return self.tradeApi.sendOrder(orderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, cancelOrderReq):
|
||||
"""撤单"""
|
||||
return self.tradeApi.cancel(cancelOrderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryAccount(self):
|
||||
"""查询账户资金"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryPosition(self):
|
||||
"""查询持仓"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭"""
|
||||
self.tradeApi.exit()
|
||||
self.dataApi.exit()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initQuery(self):
|
||||
"""初始化连续查询"""
|
||||
if self.qryEnabled:
|
||||
self.qryFunctionList = [ self.tradeApi.list_market_all_register , self.tradeApi.user_balances]
|
||||
self.startQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def query(self, event):
|
||||
"""注册到事件处理引擎上的查询函数"""
|
||||
self.total_count += 1
|
||||
if self.total_count % self.delayTime == 0:
|
||||
for function in self.qryFunctionList:
|
||||
function()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def startQuery(self):
|
||||
"""启动连续查询"""
|
||||
self.eventEngine.register(EVENT_TIMER, self.query)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setQryEnabled(self, qryEnabled):
|
||||
"""设置是否要启动循环查询"""
|
||||
self.qryEnabled = qryEnabled
|
||||
|
||||
class KorbitTradeApi(vnkorbit.Korbit_TradeApi):
|
||||
def __init__(self, gateway):
|
||||
super(KorbitTradeApi , self).__init__()
|
||||
|
||||
self.gateway = gateway
|
||||
self.gatewayName = gateway.gatewayName
|
||||
self.accountID = "KORBIT"
|
||||
self.DEBUG = False
|
||||
|
||||
self.localID = 0 # 本地委托号
|
||||
self.localSystemDict = {} # key:localID, value:systemID
|
||||
self.systemLocalDict = {} # key:systemID, value:localID
|
||||
self.workingOrderDict = {} # key:localID, value:order
|
||||
self.reqLocalDict = {} # key:reqID, value:localID
|
||||
self.cancelDict = {} # key:localID, value:cancelOrderReq
|
||||
|
||||
self.tradedVolumeDict = {} # key:localID, value:volume ,已经交易成功的数量
|
||||
|
||||
self.tradeID = 0 # 本地成交号
|
||||
|
||||
self.registerSymbolPair = set([])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
use_symbol = (subscribeReq.symbol.split('.'))[0]
|
||||
self.registerSymbolPair.add( use_symbol )
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def list_market_all_register(self):
|
||||
for symbol_pair in self.registerSymbolPair:
|
||||
self.list_market_orders(currency_pair = symbol_pair , offset = 0 , limit = 50)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def setUserInfo(self, _accountID , _username , _password):
|
||||
self.accountID = _accountID
|
||||
self.username = _username
|
||||
self.password = _password
|
||||
|
||||
'''
|
||||
发送系统委托
|
||||
'''
|
||||
def sendOrder(self, req):
|
||||
"""发送委托"""
|
||||
# 检查是否填入了价格,禁止市价委托
|
||||
if req.priceType != PRICETYPE_LIMITPRICE:
|
||||
err = VtErrorData()
|
||||
err.gatewayName = self.gatewayName
|
||||
err.errorMsg = u'Korbit接口仅支持限价单'
|
||||
err.errorTime = datetime.now().strftime('%H:%M:%S')
|
||||
self.gateway.onError(err)
|
||||
return None
|
||||
|
||||
symbol = req.vtSymbol
|
||||
|
||||
currency = (symbol.split('.'))[0]
|
||||
if req.direction == DIRECTION_LONG:
|
||||
reqID = self.buy_currency( coin_amount = req.volume , price = req.price , currency_pair = currency)
|
||||
else:
|
||||
reqID = self.sell_currency( coin_amount = req.volume , price = req.price , currency_pair = currency)
|
||||
|
||||
self.localID += 1
|
||||
localID = str(self.localID)
|
||||
self.reqLocalDict[reqID] = localID
|
||||
|
||||
|
||||
# 推送委托信息
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
order.symbol = req.symbol
|
||||
order.exchange = EXCHANGE_KORBIT
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localID
|
||||
order.vtOrderID = '.'.join([order.orderID, order.gatewayName])
|
||||
|
||||
order.direction = req.direction
|
||||
if req.direction == DIRECTION_LONG:
|
||||
order.offset = OFFSET_OPEN
|
||||
else:
|
||||
order.offset = OFFSET_CLOSE
|
||||
order.price = req.price
|
||||
order.tradedVolume = 0.0
|
||||
order.totalVolume = req.volume
|
||||
order.orderTime = datetime.now().strftime('%H:%M:%S')
|
||||
order.status = STATUS_UNKNOWN
|
||||
|
||||
self.workingOrderDict[localID] = order
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
# 返回委托号
|
||||
return order.vtOrderID
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def cancel(self, req):
|
||||
localID = req.orderID
|
||||
if localID in self.localSystemDict:
|
||||
systemID = self.localSystemDict[localID]
|
||||
symbol_pair = (req.symbol.split('.'))[0]
|
||||
self.cancel_orders( systemID , currency_pair = symbol_pair)
|
||||
else:
|
||||
self.cancelDict[localID] = req
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def onError(self, method ,data):
|
||||
print method , data
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def on_buy_currency(self, data , req, reqID):
|
||||
if data["status"] != "success":
|
||||
print "Error in on_buy_currency"
|
||||
print data
|
||||
else:
|
||||
localID = self.reqLocalDict[reqID]
|
||||
systemID = str(data['orderId'])
|
||||
self.localSystemDict[localID] = systemID
|
||||
self.systemLocalDict[systemID] = localID
|
||||
|
||||
# 撤单
|
||||
if localID in self.cancelDict:
|
||||
req = self.cancelDict[localID]
|
||||
self.cancel(req)
|
||||
del self.cancelDict[localID]
|
||||
|
||||
# 推送委托信息
|
||||
order = self.workingOrderDict[localID]
|
||||
|
||||
order.status = STATUS_NOTTRADED
|
||||
|
||||
self.tradedVolumeDict[localID] = 0.0
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def on_sell_currency(self, data , req, reqID):
|
||||
"""卖出回调"""
|
||||
if data["status"] != "success":
|
||||
print "Error in on_sell_currency"
|
||||
else:
|
||||
localID = self.reqLocalDict[reqID]
|
||||
systemID = str(data['orderId'])
|
||||
self.localSystemDict[localID] = systemID
|
||||
self.systemLocalDict[systemID] = localID
|
||||
|
||||
# 撤单
|
||||
if localID in self.cancelDict:
|
||||
req = self.cancelDict[localID]
|
||||
self.cancel(req)
|
||||
del self.cancelDict[localID]
|
||||
|
||||
# 推送委托信息
|
||||
order = self.workingOrderDict[localID]
|
||||
order.status = STATUS_NOTTRADED
|
||||
|
||||
self.tradedVolumeDict[localID] = 0.0
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
print "what"
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def on_list_exchange_orders(self, data , req, reqID):
|
||||
if len(data) > 0:
|
||||
local_system_dict_keys = self.systemLocalDict.keys()
|
||||
for d_order in data:
|
||||
systemID = str(d_order["id"])
|
||||
if systemID in local_system_dict_keys:
|
||||
localID = self.systemLocalDict[systemID]
|
||||
order = self.workingOrderDict.get(localID, None)
|
||||
if order != None:
|
||||
bef_has_volume = self.tradedVolumeDict.get(localID , 0.0)
|
||||
|
||||
total_need_volume = float(d_order["order_amount"])
|
||||
nowTradeVolume = float(d_order["filled_amount"])
|
||||
status = d_order["status"]
|
||||
|
||||
order.tradedVolume = nowTradeVolume
|
||||
order.totalVolume = total_need_volume
|
||||
|
||||
newTradeVolume = nowTradeVolume - bef_has_volume
|
||||
|
||||
if newTradeVolume > 0.000001:
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
trade.symbol = order.symbol
|
||||
trade.vtSymbol = order.vtSymbol
|
||||
|
||||
self.tradeID += 1
|
||||
trade.tradeID = str(self.tradeID)
|
||||
trade.vtTradeID = '.'.join([trade.tradeID, trade.gatewayName])
|
||||
trade.orderID = order.orderID
|
||||
trade.vtOrderID = order.vtOrderID
|
||||
|
||||
trade.volume = newTradeVolume
|
||||
trade.price = order.price
|
||||
trade.direction = order.direction
|
||||
trade.offset = order.offset
|
||||
trade.exchange = order.exchange
|
||||
trade.tradeTime = datetime.now().strftime('%H:%M:%S')
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
|
||||
if korbit_order_status_map[status] == STATUS_ALLTRADED:
|
||||
order.status = STATUS_ALLTRADED
|
||||
del self.tradedVolumeDict[localID]
|
||||
del self.systemLocalDict[systemID]
|
||||
del self.workingOrderDict[localID]
|
||||
else:
|
||||
order.status = STATUS_PARTTRADED
|
||||
self.tradedVolumeDict[localID] = nowTradeVolume
|
||||
self.gateway.onOrder(order)
|
||||
else:
|
||||
total_need_volume = float(d_order["order_amount"])
|
||||
nowTradeVolume = float(d_order["filled_amount"])
|
||||
status = d_order["status"]
|
||||
side = d_order["side"]
|
||||
|
||||
if korbit_order_status_map[status] != STATUS_ALLTRADED:
|
||||
# 说明这是一个尚未被系统记录的订单
|
||||
self.localID += 1
|
||||
localID = str(self.localID)
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
order.symbol = d_order["currency_pair"] + "." + self.gatewayName
|
||||
order.exchange = EXCHANGE_KORBIT
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localID
|
||||
order.vtOrderID = '.'.join([order.orderID, order.gatewayName])
|
||||
|
||||
if side == "bid":
|
||||
order.direction = DIRECTION_LONG
|
||||
order.offset = OFFSET_OPEN
|
||||
else:
|
||||
order.direction = DIRECTION_SHORT
|
||||
order.offset = OFFSET_CLOSE
|
||||
|
||||
order.price = float(d_order["price"])
|
||||
order.tradedVolume = float(d_order["filled_amount"])
|
||||
order.totalVolume = float(d_order["order_amount"])
|
||||
""" """
|
||||
dt , date , utime = self.generateDateTime(d_order["created_at"])
|
||||
order.orderTime = date + " " + utime
|
||||
order.status = korbit_order_status_map[status]
|
||||
|
||||
self.workingOrderDict[localID] = order
|
||||
self.systemLocalDict[systemID] = localID
|
||||
self.localSystemDict[localID] = systemID
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def onCancelOrders(self, data , req, reqID):
|
||||
for d_order in data:
|
||||
systemID = d_order["orderId"]
|
||||
status = d_order["status"]
|
||||
|
||||
if status == "success":
|
||||
localID = self.systemLocalDict[systemID]
|
||||
order = self.workingOrderDict[localID]
|
||||
order.status = STATUS_CANCELLED
|
||||
|
||||
del self.workingOrderDict[localID]
|
||||
del self.systemLocalDict[systemID]
|
||||
del self.localSystemDict[localID]
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def onBalances(self, data , req, reqID):
|
||||
all_keys_returns = data.keys()
|
||||
|
||||
total_balance = 0.0
|
||||
for symbol in all_keys_returns:
|
||||
if symbol in KORBIT_ALL_SYMBOLS:
|
||||
val = data[symbol]
|
||||
ava = float(val["available"])
|
||||
trade_vol = float(val["trade_in_use"])
|
||||
|
||||
posObj = VtPositionData()
|
||||
posObj.gatewayName = self.gatewayName
|
||||
posObj.symbol = symbol + "." + EXCHANGE_KORBIT
|
||||
posObj.exchange = EXCHANGE_KORBIT
|
||||
posObj.vtSymbol = posObj.symbol
|
||||
posObj.direction = DIRECTION_LONG
|
||||
posObj.vtPositionName = '.'.join( [posObj.vtSymbol, posObj.direction])
|
||||
posObj.ydPosition = ava + trade_vol
|
||||
posObj.position = ava + trade_vol
|
||||
posObj.frozen = trade_vol
|
||||
posObj.positionProfit = 0
|
||||
self.gateway.onPosition(posObj)
|
||||
|
||||
|
||||
account = VtAccountData()
|
||||
account.gatewayName = self.gatewayName
|
||||
account.accountID = self.accountID
|
||||
account.vtAccountID = '.'.join([ self.gatewayName , self.accountID])
|
||||
account.balance = total_balance
|
||||
account.available = total_balance
|
||||
|
||||
account.margin = 1.0
|
||||
account.closeProfit = 0.0
|
||||
account.positionProfit = 0.0
|
||||
account.commission = 0.0
|
||||
|
||||
self.gateway.onAccount(account)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
def generateDateTime(self , s):
|
||||
dt = datetime.fromtimestamp(float(s)/1e3)
|
||||
time = dt.strftime("%H:%M:%S.%f")
|
||||
date = dt.strftime("%Y%m%d")
|
||||
return dt , date, time
|
||||
|
||||
class KorbitDataApi(vnkorbit.Korbit_DataApi):
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, gateway):
|
||||
"""Constructor"""
|
||||
super(KorbitDataApi, self).__init__()
|
||||
|
||||
self.gateway = gateway
|
||||
self.gatewayName = gateway.gatewayName
|
||||
|
||||
self.tickDict = {} # key:symbol, value:tick
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSingle(self, symbol_pair):
|
||||
self.subscribeTick(symbol_pair)
|
||||
self.subscribeOrderbooks(symbol_pair)
|
||||
# self.subscribeTrades(symbol_pair)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
self.subscribeSingle( subscribeReq.symbol)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self, interval , market , debug = False):
|
||||
self.init(interval , debug)
|
||||
|
||||
self.subscribeSingle("bch_krw")
|
||||
|
||||
# 订阅行情并推送合约信息
|
||||
for symbol_pair in KORBIT_ALL_SYMBOL_PAIR:
|
||||
#self.subscribeSingle( symbol_pair)
|
||||
|
||||
contract = VtContractData()
|
||||
contract.gatewayName = self.gatewayName
|
||||
contract.symbol = symbol_pair
|
||||
contract.exchange = EXCHANGE_KORBIT
|
||||
contract.vtSymbol = '.'.join([contract.symbol, contract.exchange])
|
||||
contract.name = symbol_pair
|
||||
contract.size = 0.0001
|
||||
contract.priceTick = 0.0001
|
||||
contract.productClass = PRODUCT_SPOT
|
||||
self.gateway.onContract(contract)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTick(self, data):
|
||||
"""实时成交推送"""
|
||||
symbol = data["symbol"]
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.gatewayName = self.gatewayName
|
||||
|
||||
tick.exchange = EXCHANGE_KORBIT
|
||||
tick.symbol = '.'.join([symbol, tick.exchange])
|
||||
tick.vtSymbol = '.'.join([symbol, tick.exchange])
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
tick.highPrice = float(data['high'])
|
||||
tick.lowPrice = float(data['low'])
|
||||
tick.lastPrice = float(data['last'])
|
||||
tick.volume = float(data['volume'])
|
||||
|
||||
tick.datetime , tick.date , tick.time = self.generateDateTime(data["timestamp"])
|
||||
|
||||
# now = datetime.now()
|
||||
# tick.time = now.strftime('%H:%M:%S')
|
||||
# tick.date = now.strftime('%Y%m%d')
|
||||
# tick.datetime = now
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTrades(self, data):
|
||||
"""实时成交推送"""
|
||||
#print data
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOrderbooks(self, data):
|
||||
"""实时成交推送"""
|
||||
symbol = data["symbol"]
|
||||
|
||||
bids = data["bids"]
|
||||
asks = data["asks"]
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.gatewayName = self.gatewayName
|
||||
|
||||
tick.symbol = symbol
|
||||
tick.exchange = EXCHANGE_KORBIT
|
||||
tick.vtSymbol = '.'.join([tick.symbol, tick.exchange])
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
bids = [ (float(x[0]) , float(x[1])) for x in bids ]
|
||||
asks = [ (float(x[0]) , float(x[1])) for x in asks ]
|
||||
tick.bidPrice1, tick.bidVolume1 = bids[0]
|
||||
tick.bidPrice2, tick.bidVolume2 = bids[1]
|
||||
tick.bidPrice3, tick.bidVolume3 = bids[2]
|
||||
tick.bidPrice4, tick.bidVolume4 = bids[3]
|
||||
tick.bidPrice5, tick.bidVolume5 = bids[4]
|
||||
|
||||
tick.askPrice1, tick.askVolume1 = asks[0]
|
||||
tick.askPrice2, tick.askVolume2 = asks[1]
|
||||
tick.askPrice3, tick.askVolume3 = asks[2]
|
||||
tick.askPrice4, tick.askVolume4 = asks[3]
|
||||
tick.askPrice5, tick.askVolume5 = asks[4]
|
||||
|
||||
|
||||
tick.datetime , tick.date , tick.time = self.generateDateTime(data["timestamp"])
|
||||
|
||||
# now = datetime.now()
|
||||
# tick.time = now.strftime('%H:%M:%S')
|
||||
# tick.date = now.strftime('%Y%m%d')
|
||||
# tick.datetime = now
|
||||
|
||||
self.gateway.onTick(tick)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def generateDateTime(self, s):
|
||||
"""生成时间"""
|
||||
dt = datetime.fromtimestamp(float(s)/1e3)
|
||||
time = dt.strftime("%H:%M:%S.%f")
|
||||
date = dt.strftime("%Y%m%d")
|
||||
return dt , date, time
|
6
vnpy/trader/gateway/okexGateway/OKEX_connect.json
Normal file
6
vnpy/trader/gateway/okexGateway/OKEX_connect.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"apiKey": "你的apiKey",
|
||||
"secretKey": "你的secretKey",
|
||||
"trace": false,
|
||||
"leverage": 10
|
||||
}
|
11
vnpy/trader/gateway/okexGateway/__init__.py
Normal file
11
vnpy/trader/gateway/okexGateway/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnpy.trader import vtConstant
|
||||
from okexGateway import okexGateway
|
||||
|
||||
gatewayClass = okexGateway
|
||||
gatewayName = 'OKEX'
|
||||
gatewayDisplayName = u'OKEX'
|
||||
gatewayType = vtConstant.GATEWAYTYPE_BTC
|
||||
gatewayQryEnabled = True
|
||||
|
959
vnpy/trader/gateway/okexGateway/okexGateway.py
Normal file
959
vnpy/trader/gateway/okexGateway/okexGateway.py
Normal file
@ -0,0 +1,959 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
'''
|
||||
vn.okex的gateway接入
|
||||
|
||||
注意:
|
||||
1. 前仅支持USD 现货交易,以及usd的期货交易
|
||||
'''
|
||||
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
from time import sleep
|
||||
from copy import copy
|
||||
from threading import Condition
|
||||
from Queue import Queue
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
|
||||
from vnpy.api.okex import OKEX_Sub_Spot_Api , OKEX_Contract_Api , okex_all_symbol_pairs , okex_all_contract_symbol , okex_all_symbol_type
|
||||
from vnpy.trader.vtGateway import *
|
||||
from vnpy.trader.vtFunction import getJsonPath
|
||||
|
||||
# 价格类型映射
|
||||
# 买卖类型: 限价单(buy/sell) 市价单(buy_market/sell_market)
|
||||
priceTypeMap = {}
|
||||
priceTypeMap['buy'] = (DIRECTION_LONG, PRICETYPE_LIMITPRICE)
|
||||
priceTypeMap['buy_market'] = (DIRECTION_LONG, PRICETYPE_MARKETPRICE)
|
||||
priceTypeMap['sell'] = (DIRECTION_SHORT, PRICETYPE_LIMITPRICE)
|
||||
priceTypeMap['sell_market'] = (DIRECTION_SHORT, PRICETYPE_MARKETPRICE)
|
||||
priceTypeMapReverse = {v: k for k, v in priceTypeMap.items()}
|
||||
|
||||
# 委托状态印射
|
||||
statusMap = {}
|
||||
statusMap[-1] = STATUS_CANCELLED
|
||||
statusMap[0] = STATUS_NOTTRADED
|
||||
statusMap[1] = STATUS_PARTTRADED
|
||||
statusMap[2] = STATUS_ALLTRADED
|
||||
statusMap[4] = STATUS_UNKNOWN
|
||||
|
||||
|
||||
########################################################################
|
||||
class okexGateway(VtGateway):
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, eventEngine, gatewayName='OKEX'):
|
||||
"""Constructor"""
|
||||
super(okexGateway, self).__init__(eventEngine, gatewayName)
|
||||
|
||||
self.api_spot = Api_Spot(self)
|
||||
# self.api_contract = Api_contract(self)
|
||||
|
||||
self.leverage = 0
|
||||
self.connected = False
|
||||
|
||||
self.fileName = self.gatewayName + '_connect.json'
|
||||
self.filePath = getJsonPath(self.fileName, __file__)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self):
|
||||
"""连接"""
|
||||
# 载入json文件
|
||||
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'])
|
||||
trace = setting['trace']
|
||||
leverage = setting['leverage']
|
||||
except KeyError:
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'连接配置缺少字段,请检查'
|
||||
self.onLog(log)
|
||||
return
|
||||
|
||||
# 初始化接口
|
||||
self.leverage = leverage
|
||||
|
||||
|
||||
self.api_spot.active = True
|
||||
self.api_spot.connect_Subpot( apiKey, secretKey, trace)
|
||||
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'接口初始化成功'
|
||||
self.onLog(log)
|
||||
|
||||
# 启动查询
|
||||
# self.initQuery()
|
||||
# self.startQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
"""订阅行情"""
|
||||
self.api_spot.subscribe(subscribeReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, orderReq):
|
||||
"""发单"""
|
||||
return self.api_spot.spotSendOrder(orderReq)
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, cancelOrderReq):
|
||||
"""撤单"""
|
||||
self.api_spot.spotCancel(cancelOrderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryAccount(self):
|
||||
"""查询账户资金"""
|
||||
self.api_spot.spotUserInfo()
|
||||
#----------------------------------------------------------------------
|
||||
def qryOrderInfo(self):
|
||||
self.api_spot.spotAllOrders()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryPosition(self):
|
||||
"""查询持仓"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭"""
|
||||
self.api_spot.active = False
|
||||
self.api_spot.close()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initQuery(self):
|
||||
"""初始化连续查询"""
|
||||
if self.qryEnabled:
|
||||
# 需要循环的查询函数列表
|
||||
#self.qryFunctionList = [self.qryAccount , self.qryOrderInfo]
|
||||
self.qryFunctionList = [ self.qryOrderInfo]
|
||||
#self.qryFunctionList = []
|
||||
|
||||
self.qryCount = 0 # 查询触发倒计时
|
||||
self.qryTrigger = 2 # 查询触发点
|
||||
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 Api_Spot(OKEX_Sub_Spot_Api):
|
||||
"""okex的API实现"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, gateway):
|
||||
"""Constructor"""
|
||||
super(Api_Spot, self).__init__()
|
||||
|
||||
self.gateway = gateway # gateway对象
|
||||
self.gatewayName = gateway.gatewayName # gateway对象名称
|
||||
|
||||
|
||||
self.active = False # 若为True则会在断线后自动重连
|
||||
|
||||
self.cbDict = {}
|
||||
self.tickDict = {}
|
||||
self.orderDict = {}
|
||||
|
||||
self.channelSymbolMap = {}
|
||||
|
||||
self.localNo = 0 # 本地委托号
|
||||
self.localNoQueue = Queue() # 未收到系统委托号的本地委托号队列
|
||||
self.localNoDict = {} # key为本地委托号,value为系统委托号
|
||||
self.orderIdDict = {} # key为系统委托号,value为本地委托号
|
||||
self.cancelDict = {} # key为本地委托号,value为撤单请求
|
||||
|
||||
self.recordOrderId_BefVolume = {} # 记录的之前处理的量
|
||||
|
||||
self.cache_some_order = {}
|
||||
self.tradeID = 0
|
||||
|
||||
self.registerSymbolPairArray = set([])
|
||||
|
||||
self.initCallback()
|
||||
|
||||
'''
|
||||
登录后,每次订单执行撤销后又这样的 推送。。不知道干啥的。先过滤掉了
|
||||
{u'binary': 1, u'product': u'spot', u'type': u'order', u'base': u'etc'
|
||||
, u'quote': u'usdt', u'data': {u'status': -1, u'orderType': 0, u'price': u'25.4050', u'modifyTime':
|
||||
1512288275000L, u'userId': 6548935, u'createTime': 1512288275000L, u'source': 0, u'quoteSize': u'0.0
|
||||
0000000', u'executedValue': u'0.00000000', u'id': 62877909, u'filledSize': u'0.00000000', u'side': 1
|
||||
, u'size': u'0.01000000'}}
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onMessage(self, ws, evt):
|
||||
"""信息推送"""
|
||||
# print evt
|
||||
|
||||
data = self.readData(evt)[0]
|
||||
try:
|
||||
channel = data['channel']
|
||||
except Exception,ex:
|
||||
channel = None
|
||||
if channel == None:
|
||||
return
|
||||
# try:
|
||||
if channel == "addChannel" and 'data' in data:
|
||||
channel = data['data']["channel"]
|
||||
if channel != "addChannel" and 'future' not in channel and channel != 'login':
|
||||
|
||||
# print channel
|
||||
callback = self.cbDict[channel]
|
||||
callback(data)
|
||||
|
||||
# if 'depth' not in channel and 'ticker' not in channel and 'deals' not in channel and 'userinfo' not in channel and 'future' not in channel:
|
||||
# print data
|
||||
|
||||
# except Exception,ex:
|
||||
# print "Error in callback cbDict ", channel
|
||||
|
||||
#print self.cbDict
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, ws, evt):
|
||||
"""错误推送"""
|
||||
error = VtErrorData()
|
||||
error.gatewayName = self.gatewayName
|
||||
error.errorMsg = str(evt)
|
||||
self.gateway.onError(error)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, data):
|
||||
error = VtErrorData()
|
||||
error.gatewayName = self.gatewayName
|
||||
error.errorMsg = str(data["data"]["error_code"])
|
||||
self.gateway.onError(error)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onClose(self, ws):
|
||||
"""接口断开"""
|
||||
# 如果尚未连上,则忽略该次断开提示
|
||||
if not self.gateway.connected:
|
||||
return
|
||||
|
||||
self.gateway.connected = False
|
||||
self.writeLog(u'服务器连接断开')
|
||||
|
||||
# 重新连接
|
||||
if self.active:
|
||||
def reconnect():
|
||||
while not self.gateway.connected:
|
||||
self.writeLog(u'等待10秒后重新连接')
|
||||
sleep(10)
|
||||
if not self.gateway.connected:
|
||||
self.reconnect()
|
||||
|
||||
t = Thread(target=reconnect)
|
||||
t.start()
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
symbol_pair_gateway = subscribeReq.symbol
|
||||
arr = symbol_pair_gateway.split('.')
|
||||
symbol_pair = arr[0]
|
||||
|
||||
if symbol_pair not in self.registerSymbolPairArray:
|
||||
self.registerSymbolPairArray.add(symbol_pair)
|
||||
self.subscribeSingleSymbol( symbol_pair)
|
||||
|
||||
self.spotOrderInfo(symbol_pair , '-1')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeSingleSymbol(self, symbol):
|
||||
if symbol in okex_all_symbol_pairs:
|
||||
self.subscribeSpotTicker(symbol)
|
||||
self.subscribeSpotDepth5(symbol)
|
||||
#self.subscribeSpotDeals(symbol)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotAllOrders(self):
|
||||
print spotAllOrders
|
||||
for symbol in registerSymbolPairArray:
|
||||
if symbol in okex_all_symbol_pairs:
|
||||
self.spotOrderInfo(symbol , '-1')
|
||||
|
||||
for orderId in self.orderIdDict.keys():
|
||||
order = self.orderDict.get(orderId , None)
|
||||
if order != None:
|
||||
symbol_pair = (order.symbol.split('.'))[0]
|
||||
self.spotOrderInfo(symbol_pair , orderId)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOpen(self, ws):
|
||||
"""连接成功"""
|
||||
self.gateway.connected = True
|
||||
self.writeLog(u'服务器连接成功')
|
||||
|
||||
self.login()
|
||||
# 连接后查询账户和委托数据
|
||||
self.spotUserInfo()
|
||||
|
||||
self.subscribeSingleSymbol("etc_usdt")
|
||||
for symbol in okex_all_symbol_pairs:
|
||||
# self.subscribeSpotTicker(symbol)
|
||||
# self.subscribeSpotDepth5(symbol)
|
||||
# self.subscribeSpotDeals(symbol)
|
||||
|
||||
#Ticker数据
|
||||
self.channelSymbolMap["ok_sub_spot_%s_ticker" % symbol] = symbol
|
||||
#盘口的深度
|
||||
self.channelSymbolMap["ok_sub_spot_%s_depth_5" % symbol] = symbol
|
||||
#所有人的交易数据
|
||||
self.channelSymbolMap["ok_sub_spot_%s_deals" % symbol] = symbol
|
||||
|
||||
contract = VtContractData()
|
||||
contract.gatewayName = self.gatewayName
|
||||
contract.symbol = symbol
|
||||
contract.exchange = EXCHANGE_OKEX
|
||||
contract.vtSymbol = '.'.join([contract.symbol, contract.exchange])
|
||||
contract.name = u'OKEX现货%s' % symbol
|
||||
contract.size = 0.00001
|
||||
contract.priceTick = 0.00001
|
||||
contract.productClass = PRODUCT_SPOT
|
||||
self.gateway.onContract(contract)
|
||||
|
||||
'''
|
||||
[{
|
||||
"channel":"ok_sub_spot_bch_btc_deals",
|
||||
"data":[["1001","2463.86","0.052","16:34:07","ask"]]
|
||||
}]
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotSubDeals(self, data):
|
||||
if 'data' not in data:
|
||||
return
|
||||
rawData = data["data"]
|
||||
|
||||
# print rawData
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def writeLog(self, content):
|
||||
"""快速记录日志"""
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = content
|
||||
self.gateway.onLog(log)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initCallback(self):
|
||||
"""初始化回调函数"""
|
||||
# USD_SPOT
|
||||
for symbol_pair in okex_all_symbol_pairs:
|
||||
self.cbDict["ok_sub_spot_%s_ticker" % symbol_pair] = self.onTicker
|
||||
self.cbDict["ok_sub_spot_%s_depth_5" % symbol_pair] = self.onDepth
|
||||
self.cbDict["ok_sub_spot_%s_deals" % symbol_pair] = self.onSpotSubDeals
|
||||
|
||||
self.cbDict["ok_sub_spot_%s_order" % symbol_pair] = self.onSpotSubOrder
|
||||
self.cbDict["ok_sub_spot_%s_balance" % symbol_pair] = self.onSpotBalance
|
||||
|
||||
self.cbDict['ok_spot_userinfo'] = self.onSpotUserInfo
|
||||
self.cbDict['ok_spot_orderinfo'] = self.onSpotOrderInfo
|
||||
|
||||
# 下面这两个好像废弃了
|
||||
#self.cbDict['ok_sub_spot_userinfo'] = self.onSpotSubUserInfo
|
||||
#self.cbDict['ok_sub_spot_trades'] = self.onSpotSubTrades
|
||||
|
||||
self.cbDict['ok_spot_order'] = self.onSpotOrder
|
||||
self.cbDict['ok_spot_cancel_order'] = self.onSpotCancelOrder
|
||||
|
||||
'''
|
||||
[
|
||||
{
|
||||
"binary": 0,
|
||||
"channel": "ok_sub_spot_bch_btc_ticker",
|
||||
"data": {
|
||||
"high": "10000",
|
||||
"vol": "185.03743858",
|
||||
"last": "111",
|
||||
"low": "0.00000001",
|
||||
"buy": "115",
|
||||
"change": "101",
|
||||
"sell": "115",
|
||||
"dayLow": "0.00000001",
|
||||
"dayHigh": "10000",
|
||||
"timestamp": 1500444626000
|
||||
}
|
||||
}
|
||||
]
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onTicker(self, data):
|
||||
""""""
|
||||
if 'data' not in data:
|
||||
return
|
||||
|
||||
channel = data['channel']
|
||||
if channel == 'addChannel':
|
||||
return
|
||||
try:
|
||||
symbol = self.channelSymbolMap[channel]
|
||||
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.exchange = EXCHANGE_OKEX
|
||||
tick.symbol = '.'.join([symbol, tick.exchange])
|
||||
tick.vtSymbol = '.'.join([symbol, tick.exchange])
|
||||
|
||||
tick.gatewayName = self.gatewayName
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
rawData = data['data']
|
||||
tick.highPrice = float(rawData['high'])
|
||||
tick.lowPrice = float(rawData['low'])
|
||||
tick.lastPrice = float(rawData['last'])
|
||||
tick.volume = float(rawData['vol'].replace(',', ''))
|
||||
# tick.date, tick.time = self.generateDateTime(rawData['timestamp'])
|
||||
|
||||
# print "ticker", tick.date , tick.time
|
||||
# newtick = copy(tick)
|
||||
# self.gateway.onTick(newtick)
|
||||
except Exception,ex:
|
||||
print "Error in onTicker " , channel
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onDepth(self, data):
|
||||
""""""
|
||||
if 'data' not in data:
|
||||
return
|
||||
try:
|
||||
channel = data['channel']
|
||||
symbol = self.channelSymbolMap[channel]
|
||||
except Exception,ex:
|
||||
symbol = None
|
||||
|
||||
if symbol == None:
|
||||
return
|
||||
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.symbol = symbol
|
||||
tick.vtSymbol = symbol
|
||||
tick.gatewayName = self.gatewayName
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
if 'data' not in data:
|
||||
return
|
||||
rawData = data['data']
|
||||
|
||||
|
||||
tick.bidPrice1, tick.bidVolume1 = rawData['bids'][0]
|
||||
tick.bidPrice2, tick.bidVolume2 = rawData['bids'][1]
|
||||
tick.bidPrice3, tick.bidVolume3 = rawData['bids'][2]
|
||||
tick.bidPrice4, tick.bidVolume4 = rawData['bids'][3]
|
||||
tick.bidPrice5, tick.bidVolume5 = rawData['bids'][4]
|
||||
|
||||
tick.askPrice1, tick.askVolume1 = rawData['asks'][-1]
|
||||
tick.askPrice2, tick.askVolume2 = rawData['asks'][-2]
|
||||
tick.askPrice3, tick.askVolume3 = rawData['asks'][-3]
|
||||
tick.askPrice4, tick.askVolume4 = rawData['asks'][-4]
|
||||
tick.askPrice5, tick.askVolume5 = rawData['asks'][-5]
|
||||
|
||||
tick.date, tick.time = self.generateDateTime(rawData['timestamp'])
|
||||
# print "Depth", tick.date , tick.time
|
||||
|
||||
newtick = copy(tick)
|
||||
self.gateway.onTick(newtick)
|
||||
|
||||
'''
|
||||
[
|
||||
{
|
||||
"base": "bch",
|
||||
"binary": 0,
|
||||
"channel": "ok_sub_spot_bch_btc_balance",
|
||||
"data": {
|
||||
"info": {
|
||||
"free": {
|
||||
"btc": 5814.850605790395
|
||||
},
|
||||
"freezed": {
|
||||
"btc": 7341
|
||||
}
|
||||
}
|
||||
},
|
||||
"product": "spot",
|
||||
"quote": "btc",
|
||||
"type": "order"
|
||||
}
|
||||
]
|
||||
'''
|
||||
def onSpotBalance(self, data):
|
||||
"""交易发生金额变动之后会触发这个函数"""
|
||||
# print data
|
||||
|
||||
rawData = data['data']
|
||||
info = rawData['info']
|
||||
|
||||
for symbol in info["freezed"].keys():
|
||||
pos = VtPositionData()
|
||||
pos.gatewayName = self.gatewayName
|
||||
pos.symbol = symbol + "." + EXCHANGE_OKEX
|
||||
pos.vtSymbol = symbol + "." + EXCHANGE_OKEX
|
||||
pos.direction = DIRECTION_NET
|
||||
pos.frozen = float(info['freezed'][symbol])
|
||||
pos.position = pos.frozen + float(info['free'][symbol])
|
||||
|
||||
self.gateway.onPosition(pos)
|
||||
|
||||
'''
|
||||
[{"binary":0,"channel":"ok_spot_userinfo","data":{"result":true,"info":{"funds":{"borrow":{"dgd":"0"
|
||||
,"bcd":"0","bcc":"0","bch":"0","hsr":"0","xuc":"0","omg":"0","eos":"0","qtum":"0","btc":"0","act":"0
|
||||
","bcs":"0","btg":"0","etc":"0","eth":"0","usdt":"0","gas":"0","zec":"0","neo":"0","ltc":"0","bt1":"
|
||||
0","bt2":"0","iota":"0","pay":"0","storj":"0","gnt":"0","snt":"0","dash":"0"},"free":{"dgd":"0","bcd
|
||||
":"0","bcc":"0","bch":"0","hsr":"0","xuc":"3","omg":"0","eos":"0","qtum":"0","btc":"0.00266884258369
|
||||
","act":"0","bcs":"0","btg":"0","etc":"7.9909635","eth":"0","usdt":"0","gas":"0","zec":"0","neo":"0"
|
||||
,"ltc":"0","bt1":"0","bt2":"0","iota":"0","pay":"0","storj":"0","gnt":"0","snt":"0","dash":"0"},"fre
|
||||
ezed":{"dgd":"0","bcd":"0","bcc":"0","bch":"0","hsr":"0","xuc":"0","omg":"0","eos":"0","qtum":"0","b
|
||||
tc":"0","act":"0","bcs":"0","btg":"0","etc":"0","eth":"0","usdt":"0","gas":"0","zec":"0","neo":"0","
|
||||
ltc":"0","bt1":"0","bt2":"0","iota":"0","pay":"0","storj":"0","gnt":"0","snt":"0","dash":"0"}}}}}]
|
||||
{u'binary': 0, u'data': {u'info': {u'funds': {u'freezed': {u'zec': u'0', u'usdt': u'0', u'btg': u'0'
|
||||
, u'btc': u'0', u'bt1': u'0', u'neo': u'0', u'pay': u'0', u'storj': u'0', u'iota': u'0', u'omg': u'0
|
||||
', u'dgd': u'0', u'bt2': u'0', u'xuc': u'0', u'gas': u'0', u'hsr': u'0', u'snt': u'0', u'dash': u'0'
|
||||
, u'bch': u'0', u'gnt': u'0', u'bcd': u'0', u'qtum': u'0', u'bcc': u'0', u'eos': u'0', u'etc': u'0',
|
||||
u'act': u'0', u'eth': u'0', u'ltc': u'0', u'bcs': u'0'}, u'borrow': {u'zec': u'0', u'usdt': u'0', u
|
||||
'btg': u'0', u'btc': u'0', u'bt1': u'0', u'neo': u'0', u'pay': u'0', u'storj': u'0', u'iota': u'0',
|
||||
u'omg': u'0', u'dgd': u'0', u'bt2': u'0', u'xuc': u'0', u'gas': u'0', u'hsr': u'0', u'snt': u'0', u'
|
||||
dash': u'0', u'bch': u'0', u'gnt': u'0', u'bcd': u'0', u'qtum': u'0', u'bcc': u'0', u'eos': u'0', u'
|
||||
etc': u'0', u'act': u'0', u'eth': u'0', u'ltc': u'0', u'bcs': u'0'}, u'free': {u'zec': u'0', u'usdt'
|
||||
: u'0', u'btg': u'0', u'btc': u'0.00266884258369', u'bt1': u'0', u'neo': u'0', u'pay': u'0', u'storj
|
||||
': u'0', u'iota': u'0', u'omg': u'0', u'dgd': u'0', u'bt2': u'0', u'xuc': u'3', u'gas': u'0', u'hsr'
|
||||
: u'0', u'snt': u'0', u'dash': u'0', u'bch': u'0', u'gnt': u'0', u'bcd': u'0', u'qtum': u'0', u'bcc'
|
||||
: u'0', u'eos': u'0', u'etc': u'7.9909635', u'act': u'0', u'eth': u'0', u'ltc': u'0', u'bcs': u'0'}}
|
||||
}, u'result': True}, u'channel': u'ok_spot_userinfo'}
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotUserInfo(self, data):
|
||||
"""现货账户资金推送"""
|
||||
rawData = data['data']
|
||||
info = rawData['info']
|
||||
funds = rawData['info']['funds']
|
||||
|
||||
# 持仓信息
|
||||
#for symbol in ['btc', 'ltc','eth', self.currency]:
|
||||
for symbol in okex_all_symbol_type:
|
||||
if symbol in funds['free']:
|
||||
pos = VtPositionData()
|
||||
pos.gatewayName = self.gatewayName
|
||||
|
||||
pos.symbol = symbol + "." + EXCHANGE_OKEX
|
||||
pos.vtSymbol = symbol + "." + EXCHANGE_OKEX
|
||||
pos.vtPositionName = symbol
|
||||
pos.direction = DIRECTION_NET
|
||||
|
||||
pos.frozen = float(funds['freezed'][symbol])
|
||||
pos.position = pos.frozen + float(funds['free'][symbol])
|
||||
|
||||
self.gateway.onPosition(pos)
|
||||
|
||||
# 账户资金
|
||||
account = VtAccountData()
|
||||
account.gatewayName = self.gatewayName
|
||||
account.accountID = self.gatewayName
|
||||
account.vtAccountID = account.accountID
|
||||
account.balance = 0.0
|
||||
#account.balance = float(funds['asset']['net'])
|
||||
self.gateway.onAccount(account)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# 这个 API 现在文档没找到。。 好像废弃了
|
||||
def onSpotSubUserInfo(self, data):
|
||||
"""现货账户资金推送"""
|
||||
if 'data' not in data:
|
||||
return
|
||||
|
||||
rawData = data['data']
|
||||
info = rawData['info']
|
||||
|
||||
# 持仓信息
|
||||
#for symbol in ['btc', 'ltc','eth', self.currency]:
|
||||
for symbol in okex_all_symbol_type:
|
||||
if symbol in info['free']:
|
||||
pos = VtPositionData()
|
||||
pos.gatewayName = self.gatewayName
|
||||
|
||||
pos.symbol = symbol + "." + EXCHANGE_OKEX
|
||||
pos.vtSymbol = symbol + "." + EXCHANGE_OKEX
|
||||
pos.vtPositionName = symbol
|
||||
pos.direction = DIRECTION_NET
|
||||
|
||||
pos.frozen = float(info['freezed'][symbol])
|
||||
pos.position = pos.frozen + float(info['free'][symbol])
|
||||
|
||||
self.gateway.onPosition(pos)
|
||||
|
||||
'''
|
||||
交易数据
|
||||
[
|
||||
{
|
||||
"base": "bch",
|
||||
"binary": 0,
|
||||
"channel": "ok_sub_spot_bch_btc_order",
|
||||
"data": {
|
||||
"symbol": "bch_btc",
|
||||
"tradeAmount": "1.00000000",
|
||||
"createdDate": "1504530228987",
|
||||
"orderId": 6191,
|
||||
"completedTradeAmount": "0.00000000",
|
||||
"averagePrice": "0",
|
||||
"tradePrice": "0.00000000",
|
||||
"tradeType": "buy",
|
||||
"status": 0,
|
||||
"tradeUnitPrice": "113.00000000"
|
||||
},
|
||||
"product": "spot",
|
||||
"quote": "btc",
|
||||
"type": "balance"
|
||||
}
|
||||
]
|
||||
|
||||
{u'binary': 0, u'data': {u'orderId': 62870564, u'status': 0, u'tradeType': u'sell', u'tradeUnitPrice
|
||||
': u'25.3500', u'symbol': u'etc_usdt', u'tradePrice': u'0.0000', u'createdDate': u'1512287172393', u
|
||||
'averagePrice': u'0', u'tradeAmount': u'0.01000000', u'completedTradeAmount': u'0.00000000'}, u'chan
|
||||
nel': u'ok_sub_spot_etc_usdt_order'}
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotSubOrder(self, data):
|
||||
"""交易数据"""
|
||||
if 'data' not in data:
|
||||
return
|
||||
|
||||
rawData = data["data"]
|
||||
|
||||
# 本地和系统委托号
|
||||
orderId = str(rawData['orderId'])
|
||||
|
||||
# 这时候出现None , 情况是 已经发出了单子,但是系统这里还没建立 索引
|
||||
# 先这样返回试一下
|
||||
# 因为 发完单,订单变化是先推送的。。导致不清楚他的localID
|
||||
# 现在的处理方式是, 先缓存这里的信息,等到出现了 localID,再来处理这一段
|
||||
localNo = self.orderIdDict.get(orderId , None)
|
||||
if localNo == None:
|
||||
arr = self.cache_some_order.get(orderId , None)
|
||||
if arr == None:
|
||||
arr = []
|
||||
arr.append( data)
|
||||
self.cache_some_order[orderId] = arr
|
||||
else:
|
||||
arr.append( data)
|
||||
return
|
||||
|
||||
# 委托信息
|
||||
if orderId not in self.orderDict:
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
order.symbol = '.'.join([rawData['symbol'] , EXCHANGE_OKEX])
|
||||
#order.symbol = spotSymbolMap[rawData['symbol']]
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localNo
|
||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||
|
||||
order.price = float(rawData['tradeUnitPrice'])
|
||||
order.totalVolume = float(rawData['tradeAmount'])
|
||||
order.direction, priceType = priceTypeMap[rawData['tradeType']]
|
||||
|
||||
self.orderDict[orderId] = order
|
||||
else:
|
||||
order = self.orderDict[orderId]
|
||||
|
||||
order.tradedVolume = float(rawData['completedTradeAmount'])
|
||||
order.status = statusMap[rawData['status']]
|
||||
|
||||
self.gateway.onOrder(copy(order))
|
||||
|
||||
|
||||
bef_volume = self.recordOrderId_BefVolume.get( orderId , 0.0 )
|
||||
now_volume = float(rawData['completedTradeAmount']) - bef_volume
|
||||
|
||||
if now_volume > 0.000001:
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
|
||||
trade.symbol = order.symbol
|
||||
trade.vtSymbol = order.symbol
|
||||
|
||||
self.tradeID += 1
|
||||
trade.tradeID = str(self.tradeID)
|
||||
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
|
||||
|
||||
trade.orderID = localNo
|
||||
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
|
||||
|
||||
trade.price = float(rawData['tradeUnitPrice'])
|
||||
trade.volume = float(now_volume)
|
||||
|
||||
trade.direction, priceType = priceTypeMap[rawData['tradeType']]
|
||||
|
||||
trade.tradeTime = datetime.now().strftime('%H:%M:%S')
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
|
||||
"""
|
||||
原来的OK coin方式,不过数据一直没有 所以换一种方式
|
||||
# 成交信息
|
||||
if 'sigTradeAmount' in rawData and float(rawData['sigTradeAmount'])>0:
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
|
||||
trade.symbol = spotSymbolMap[rawData['symbol']]
|
||||
trade.vtSymbol = order.symbol
|
||||
|
||||
trade.tradeID = str(rawData['id'])
|
||||
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
|
||||
|
||||
trade.orderID = localNo
|
||||
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
|
||||
|
||||
trade.price = float(rawData['sigTradePrice'])
|
||||
trade.volume = float(rawData['sigTradeAmount'])
|
||||
|
||||
trade.direction, priceType = priceTypeMap[rawData['tradeType']]
|
||||
|
||||
trade.tradeTime = datetime.now().strftime('%H:%M:%S')
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
"""
|
||||
'''
|
||||
[
|
||||
{
|
||||
"binary": 0,
|
||||
"channel": "ok_spot_orderinfo",
|
||||
"data": {
|
||||
"result": true,
|
||||
"orders": [
|
||||
{
|
||||
"symbol": "bch_btc",
|
||||
"amount": "0.10000000",
|
||||
"price": "1.00000000",
|
||||
"avg_price": 0,
|
||||
"create_date": 1504529828000,
|
||||
"type": "buy",
|
||||
"deal_amount": 0,
|
||||
"order_id": 6189,
|
||||
"status": -1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotOrderInfo(self, data):
|
||||
"""委托信息查询回调"""
|
||||
if "error_code" in data.keys():
|
||||
print data
|
||||
return
|
||||
rawData = data['data']
|
||||
for d in rawData['orders']:
|
||||
self.localNo += 1
|
||||
localNo = str(self.localNo)
|
||||
orderId = str(d['order_id'])
|
||||
|
||||
self.localNoDict[localNo] = orderId
|
||||
self.orderIdDict[orderId] = localNo
|
||||
|
||||
if orderId not in self.orderDict:
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
#order.symbol = spotSymbolMap[d['symbol']]
|
||||
order.symbol = '.'.join([d["symbol"] , EXCHANGE_OKEX])
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localNo
|
||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||
|
||||
order.price = d['price']
|
||||
order.totalVolume = d['amount']
|
||||
order.direction, priceType = priceTypeMap[d['type']]
|
||||
|
||||
self.orderDict[orderId] = order
|
||||
else:
|
||||
order = self.orderDict[orderId]
|
||||
|
||||
order.tradedVolume = d['deal_amount']
|
||||
order.status = statusMap[d['status']]
|
||||
|
||||
self.gateway.onOrder(copy(order))
|
||||
|
||||
'''
|
||||
[
|
||||
{
|
||||
"binary": 0,
|
||||
"channel": "ok_spot_order",
|
||||
"data": {
|
||||
"result": true,
|
||||
"order_id": 6189
|
||||
}
|
||||
}
|
||||
]
|
||||
'''
|
||||
def onSpotOrder(self, data):
|
||||
rawData = data['data']
|
||||
if 'error_code' in rawData.keys():
|
||||
print data
|
||||
return
|
||||
|
||||
orderId = str(rawData['order_id'])
|
||||
|
||||
# 尽管websocket接口的委托号返回是异步的,但经过测试是
|
||||
# 符合先发现回的规律,因此这里通过queue获取之前发送的
|
||||
# 本地委托号,并把它和推送的系统委托号进行映射
|
||||
|
||||
# localNo = self.orderIdDict.get(orderId , None)
|
||||
# if localNo == None:
|
||||
|
||||
localNo = self.localNoQueue.get_nowait()
|
||||
|
||||
self.localNoDict[localNo] = orderId
|
||||
self.orderIdDict[orderId] = localNo
|
||||
|
||||
# print orderId , self.cache_some_order
|
||||
if orderId in self.cache_some_order.keys():
|
||||
arr = self.cache_some_order[orderId]
|
||||
for d in arr:
|
||||
self.onSpotSubOrder(d)
|
||||
|
||||
# 处理完就删除掉这里
|
||||
del self.cache_some_order[orderId]
|
||||
|
||||
# 检查是否有系统委托号返回前就发出的撤单请求,若有则进
|
||||
# 行撤单操作
|
||||
if localNo in self.cancelDict:
|
||||
req = self.cancelDict[localNo]
|
||||
self.spotCancel(req)
|
||||
del self.cancelDict[localNo]
|
||||
|
||||
|
||||
'''
|
||||
[
|
||||
{
|
||||
"binary": 0,
|
||||
"channel": "ok_spot_cancel_order",
|
||||
"data": {
|
||||
"result": true,
|
||||
"order_id": "125433027"
|
||||
}
|
||||
}
|
||||
]
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotCancelOrder(self, data):
|
||||
"""撤单回报"""
|
||||
if 'data' not in data:
|
||||
return
|
||||
|
||||
if 'error' in data["data"].keys():
|
||||
self.onError(data)
|
||||
return
|
||||
|
||||
rawData = data['data']
|
||||
orderId = str(rawData['order_id'])
|
||||
|
||||
localNo = self.orderIdDict[orderId]
|
||||
|
||||
if orderId not in self.orderDict:
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
order.symbol = '.'.join([rawData['symbol'] , EXCHANGE_OKEX])
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localNo
|
||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||
|
||||
self.orderDict[orderId] = order
|
||||
else:
|
||||
order = self.orderDict[orderId]
|
||||
|
||||
order.status = STATUS_CANCELLED
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
del self.orderDict[orderId]
|
||||
del self.orderIdDict[orderId]
|
||||
del self.localNoDict[localNo]
|
||||
|
||||
|
||||
if orderId in self.cache_some_order.keys():
|
||||
del self.cache_some_order[orderId]
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotSendOrder(self, req):
|
||||
"""发单"""
|
||||
#symbol = spotSymbolMapReverse[req.symbol][:4]
|
||||
symbol = (req.symbol.split('.'))[0]
|
||||
type_ = priceTypeMapReverse[(req.direction, req.priceType)]
|
||||
|
||||
self.spotTrade(symbol, type_, str(req.price), str(req.volume))
|
||||
|
||||
# 本地委托号加1,并将对应字符串保存到队列中,返回基于本地委托号的vtOrderID
|
||||
self.localNo += 1
|
||||
self.localNoQueue.put(str(self.localNo))
|
||||
vtOrderID = '.'.join([self.gatewayName, str(self.localNo)])
|
||||
return vtOrderID
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotCancel(self, req):
|
||||
"""撤单"""
|
||||
#symbol = spotSymbolMapReverse[req.symbol][:4]
|
||||
symbol = (req.symbol.split('.'))[0]
|
||||
localNo = req.orderID
|
||||
|
||||
if localNo in self.localNoDict:
|
||||
orderID = self.localNoDict[localNo]
|
||||
self.spotCancelOrder(symbol, orderID)
|
||||
else:
|
||||
# 如果在系统委托号返回前客户就发送了撤单请求,则保存
|
||||
# 在cancelDict字典中,等待返回后执行撤单任务
|
||||
self.cancelDict[localNo] = req
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
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
|
5
vnpy/trader/gateway/zbGateway/ZB_connect.json
Normal file
5
vnpy/trader/gateway/zbGateway/ZB_connect.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"trace": false,
|
||||
"apiKey": "你的api key",
|
||||
"secretKey": "你的 secreKey"
|
||||
}
|
11
vnpy/trader/gateway/zbGateway/__init__.py
Normal file
11
vnpy/trader/gateway/zbGateway/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from vnpy.trader import vtConstant
|
||||
from zbGateway import zbGateway
|
||||
|
||||
gatewayClass = zbGateway
|
||||
gatewayName = 'ZB'
|
||||
gatewayDisplayName = u'ZB'
|
||||
gatewayType = vtConstant.GATEWAYTYPE_BTC
|
||||
gatewayQryEnabled = True
|
||||
|
857
vnpy/trader/gateway/zbGateway/zbGateway.py
Normal file
857
vnpy/trader/gateway/zbGateway/zbGateway.py
Normal file
@ -0,0 +1,857 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
'''
|
||||
vn.zb的gateway接入
|
||||
'''
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
from time import sleep
|
||||
from copy import copy
|
||||
from threading import Condition
|
||||
from Queue import Queue
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
|
||||
from vnpy.trader.vtGateway import *
|
||||
from vnpy.api.zb import ZB_Sub_Spot_Api , zb_all_symbol_pairs , zb_all_symbols , zb_all_real_pair
|
||||
|
||||
from vnpy.trader.vtFunction import getJsonPath
|
||||
|
||||
|
||||
# 价格类型映射
|
||||
# 买卖类型: 限价单(buy/sell) 市价单(buy_market/sell_market)
|
||||
zb_priceTypeMap = {}
|
||||
zb_priceTypeMap["1"] = (DIRECTION_LONG, PRICETYPE_LIMITPRICE)
|
||||
zb_priceTypeMap['buy_market'] = (DIRECTION_LONG, PRICETYPE_MARKETPRICE)
|
||||
zb_priceTypeMap["0"] = (DIRECTION_SHORT, PRICETYPE_LIMITPRICE)
|
||||
zb_priceTypeMap['sell_market'] = (DIRECTION_SHORT, PRICETYPE_MARKETPRICE)
|
||||
zb_priceTypeMapReverse = {v: k for k, v in zb_priceTypeMap.items()}
|
||||
|
||||
# 委托状态印射
|
||||
zb_statusMap = {}
|
||||
|
||||
zb_statusMap[0] = STATUS_NOTTRADED
|
||||
zb_statusMap[1] = STATUS_CANCELLED
|
||||
zb_statusMap[2] = STATUS_ALLTRADED
|
||||
zb_statusMap[3] = STATUS_PARTTRADED
|
||||
|
||||
class zbGateway(VtGateway):
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, eventEngine, gatewayName='ZB'):
|
||||
"""Constructor"""
|
||||
super(zbGateway, self).__init__(eventEngine, gatewayName)
|
||||
|
||||
self.api_spot = ZB_API_Spot(self)
|
||||
|
||||
self.connected = False
|
||||
|
||||
self.fileName = self.gatewayName + '_connect.json'
|
||||
self.filePath = getJsonPath(self.fileName, __file__)
|
||||
|
||||
|
||||
self.qryEnabled = True
|
||||
|
||||
self.countTimer = 0
|
||||
self.localTimeDelay = 3
|
||||
|
||||
# 启动查询
|
||||
self.initQuery()
|
||||
self.startQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self):
|
||||
"""连接"""
|
||||
# 载入json文件
|
||||
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'])
|
||||
trace = setting["trace"]
|
||||
except KeyError:
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'连接配置缺少字段,请检查'
|
||||
self.onLog(log)
|
||||
return
|
||||
|
||||
# 初始化接口
|
||||
|
||||
self.api_spot.active = True
|
||||
self.api_spot.connect_Subpot( apiKey, secretKey, trace)
|
||||
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'接口初始化成功'
|
||||
self.onLog(log)
|
||||
|
||||
# 启动查询
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
"""订阅行情"""
|
||||
self.api_spot.subscribe(subscribeReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, orderReq):
|
||||
"""发单"""
|
||||
return self.api_spot.spotSendOrder(orderReq)
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, cancelOrderReq):
|
||||
"""撤单"""
|
||||
self.api_spot.spotCancel(cancelOrderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryAccount(self):
|
||||
"""查询账户资金"""
|
||||
self.api_spot.spotUserInfo()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryOrderInfo(self):
|
||||
self.api_spot.spotAllOrders()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryPosition(self):
|
||||
"""查询持仓"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭"""
|
||||
self.api_spot.active = False
|
||||
self.api_spot.close()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initQuery(self):
|
||||
"""初始化连续查询"""
|
||||
if self.qryEnabled:
|
||||
# 需要循环的查询函数列表
|
||||
#self.qryFunctionList = [self.qryAccount , self.qryOrderInfo]
|
||||
self.qryFunctionList = [ self.qryOrderInfo]
|
||||
#self.qryFunctionList = []
|
||||
|
||||
self.qryCount = 0 # 查询触发倒计时
|
||||
self.qryTrigger = 2 # 查询触发点
|
||||
self.qryNextFunction = 0 # 上次运行的查询函数索引
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def query(self, event):
|
||||
"""注册到事件处理引擎上的查询函数"""
|
||||
self.qryCount += 1
|
||||
|
||||
self.countTimer += 1
|
||||
|
||||
if self.countTimer % self.localTimeDelay == 0:
|
||||
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 ZB_API_Spot(ZB_Sub_Spot_Api):
|
||||
""" zb 的 API实现 """
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, gateway):
|
||||
"""Constructor"""
|
||||
super(ZB_API_Spot, self).__init__()
|
||||
|
||||
self.gateway = gateway # gateway对象
|
||||
self.gatewayName = gateway.gatewayName # gateway对象名称
|
||||
self.active = False # 若为True则会在断线后自动重连
|
||||
|
||||
self.cbDict = {}
|
||||
self.tickDict = {}
|
||||
self.orderDict = {}
|
||||
|
||||
self.channelSymbolMap = {}
|
||||
|
||||
self.localNo = 0 # 本地委托号
|
||||
self.localNoQueue = Queue() # 未收到系统委托号的本地委托号队列
|
||||
self.localNoDict = {} # key为本地委托号,value为系统委托号
|
||||
self.orderIdDict = {} # key为系统委托号,value为本地委托号
|
||||
self.cancelDict = {} # key为本地委托号,value为撤单请求
|
||||
|
||||
self.recordOrderId_BefVolume = {} # 记录的之前处理的量
|
||||
|
||||
self.tradeID = 0
|
||||
|
||||
self.local_status_dict = {}
|
||||
self.registerSymbolPairArray = set([])
|
||||
|
||||
self.initCallback()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self ,subscribeReq):
|
||||
"""订阅行情"""
|
||||
symbol_pair_gateway = subscribeReq.symbol
|
||||
arr = symbol_pair_gateway.split('.')
|
||||
symbol_pair = arr[0]
|
||||
|
||||
if symbol_pair not in self.registerSymbolPairArray:
|
||||
self.registerSymbolPairArray.add(symbol_pair)
|
||||
self.subscirbeSinglePair( symbol_pair)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onMessage(self, ws, evt):
|
||||
"""信息推送"""
|
||||
# print evt
|
||||
data = self.readData(evt)
|
||||
try:
|
||||
channel = data['channel']
|
||||
except Exception,ex:
|
||||
channel = None
|
||||
if channel == None:
|
||||
return
|
||||
|
||||
callback = self.cbDict[channel]
|
||||
callback(data)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, ws, evt):
|
||||
"""错误推送"""
|
||||
error = VtErrorData()
|
||||
error.gatewayName = self.gatewayName
|
||||
error.errorMsg = str(evt)
|
||||
self.gateway.onError(error)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, data):
|
||||
error = VtErrorData()
|
||||
error.gatewayName = self.gatewayName
|
||||
error.errorMsg = str(data["data"]["error_code"])
|
||||
self.gateway.onError(error)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onClose(self, ws):
|
||||
"""接口断开"""
|
||||
# 如果尚未连上,则忽略该次断开提示
|
||||
if not self.gateway.connected:
|
||||
return
|
||||
|
||||
self.gateway.connected = False
|
||||
self.writeLog(u'服务器连接断开')
|
||||
|
||||
# 重新连接
|
||||
if self.active:
|
||||
def reconnect():
|
||||
while not self.gateway.connected:
|
||||
self.writeLog(u'等待10秒后重新连接')
|
||||
sleep(10)
|
||||
if not self.gateway.connected:
|
||||
self.reconnect()
|
||||
|
||||
t = Thread(target=reconnect)
|
||||
t.start()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscirbeSinglePair(self ,symbol_pair):
|
||||
if symbol_pair in zb_all_symbol_pairs:
|
||||
self.subscribeSpotTicker(symbol_pair)
|
||||
self.subscribeSpotDepth(symbol_pair)
|
||||
#self.self.subscribeSpotTrades(symbol_pair)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onOpen(self, ws):
|
||||
"""连接成功"""
|
||||
self.gateway.connected = True
|
||||
self.writeLog(u'服务器连接成功')
|
||||
|
||||
self.spotUserInfo()
|
||||
|
||||
self.subscirbeSinglePair("btc_qc")
|
||||
|
||||
for symbol in zb_all_symbol_pairs:
|
||||
|
||||
#self.subscirbeSinglePair(symbol)
|
||||
|
||||
use_symbol = symbol.replace('_','')
|
||||
|
||||
#Ticker数据
|
||||
self.channelSymbolMap["%s_ticker" % use_symbol] = symbol
|
||||
#盘口的深度
|
||||
self.channelSymbolMap["%s_depth" % use_symbol] = symbol
|
||||
#所有人的交易数据
|
||||
self.channelSymbolMap["%s_trades" % use_symbol] = symbol
|
||||
|
||||
contract = VtContractData()
|
||||
contract.gatewayName = self.gatewayName
|
||||
contract.symbol = symbol
|
||||
contract.exchange = EXCHANGE_ZB
|
||||
contract.vtSymbol = '.'.join([contract.symbol, contract.exchange])
|
||||
contract.name = u'ZB现货%s' % symbol
|
||||
contract.size = 0.00001
|
||||
contract.priceTick = 0.00001
|
||||
contract.productClass = PRODUCT_SPOT
|
||||
self.gateway.onContract(contract)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initCallback(self):
|
||||
for symbol_pair in zb_all_symbol_pairs:
|
||||
use_symbol = symbol_pair.replace('_','')
|
||||
|
||||
self.cbDict["%s_ticker" % use_symbol] = self.onTicker
|
||||
self.cbDict["%s_depth" % use_symbol] = self.onDepth
|
||||
self.cbDict["%s_trades" % use_symbol] = self.onTrades
|
||||
|
||||
self.cbDict["%s_order" % use_symbol] = self.onSpotOrder
|
||||
self.cbDict["%s_cancelorder" % use_symbol] = self.onSpotCancelOrder
|
||||
self.cbDict["%s_getorder" % use_symbol] = self.onSpotGetOrder
|
||||
self.cbDict["%s_getorders" % use_symbol] = self.onSpotGetOrders
|
||||
####self.cbDict["%s_getordersignoretradetype" % use_symbol] = self.onSpotGetOrdersignoretradetype
|
||||
self.cbDict["%s_getordersignoretradetype" % use_symbol] = self.onSpotGetOrders
|
||||
|
||||
# self.cbDict["%s_ticker" % symbol_pair] = self.onTicker
|
||||
# self.cbDict["%s_depth" % symbol_pair] = self.onDepth
|
||||
# self.cbDict["%s_trades" % symbol_pair] = self.onTrades
|
||||
|
||||
# self.cbDict["%s_order" % symbol_pair] = self.onSpotOrder
|
||||
# self.cbDict["%s_cancelorder" % symbol_pair] = self.onSpotCancelOrder
|
||||
# self.cbDict["%s_getorder" % symbol_pair] = self.onSpotGetOrder
|
||||
# self.cbDict["%s_getorders" % symbol_pair] = self.onSpotGetOrders
|
||||
# ####self.cbDict["%s_getordersignoretradetype" % use_symbol] = self.onSpotGetOrdersignoretradetype
|
||||
# self.cbDict["%s_getordersignoretradetype" % use_symbol] = self.onSpotGetOrders
|
||||
|
||||
self.cbDict["getaccountinfo"] = self.onSpotUserInfo
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def writeLog(self, content):
|
||||
"""快速记录日志"""
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = content
|
||||
self.gateway.onLog(log)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onTicker(self, data):
|
||||
""""""
|
||||
if 'ticker' not in data:
|
||||
return
|
||||
channel = data['channel']
|
||||
if channel == 'addChannel':
|
||||
return
|
||||
try:
|
||||
symbol = self.channelSymbolMap[channel]
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.exchange = EXCHANGE_ZB
|
||||
tick.symbol = '.'.join([symbol, tick.exchange])
|
||||
tick.vtSymbol = '.'.join([symbol, tick.exchange])
|
||||
|
||||
tick.gatewayName = self.gatewayName
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
rawData = data['ticker']
|
||||
tick.highPrice = float(rawData['high'])
|
||||
tick.lowPrice = float(rawData['low'])
|
||||
tick.lastPrice = float(rawData['last'])
|
||||
tick.volume = float(rawData['vol'])
|
||||
|
||||
tick.date, tick.time = self.generateDateTime(data['date'])
|
||||
|
||||
# print "ticker", tick.date , tick.time
|
||||
# newtick = copy(tick)
|
||||
# self.gateway.onTick(newtick)
|
||||
except Exception,ex:
|
||||
print "Error in onTicker " , channel
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onDepth(self, data):
|
||||
""""""
|
||||
try:
|
||||
channel = data['channel']
|
||||
symbol = self.channelSymbolMap[channel]
|
||||
except Exception,ex:
|
||||
symbol = None
|
||||
|
||||
if symbol == None:
|
||||
return
|
||||
|
||||
if symbol not in self.tickDict:
|
||||
tick = VtTickData()
|
||||
tick.symbol = symbol
|
||||
tick.vtSymbol = symbol
|
||||
tick.gatewayName = self.gatewayName
|
||||
self.tickDict[symbol] = tick
|
||||
else:
|
||||
tick = self.tickDict[symbol]
|
||||
|
||||
if 'asks' not in data:
|
||||
return
|
||||
|
||||
asks = data["asks"]
|
||||
bids = data["bids"]
|
||||
|
||||
|
||||
tick.bidPrice1, tick.bidVolume1 = bids[0]
|
||||
tick.bidPrice2, tick.bidVolume2 = bids[1]
|
||||
tick.bidPrice3, tick.bidVolume3 = bids[2]
|
||||
tick.bidPrice4, tick.bidVolume4 = bids[3]
|
||||
tick.bidPrice5, tick.bidVolume5 = bids[4]
|
||||
|
||||
tick.askPrice1, tick.askVolume1 = asks[-1]
|
||||
tick.askPrice2, tick.askVolume2 = asks[-2]
|
||||
tick.askPrice3, tick.askVolume3 = asks[-3]
|
||||
tick.askPrice4, tick.askVolume4 = asks[-4]
|
||||
tick.askPrice5, tick.askVolume5 = asks[-5]
|
||||
|
||||
tick.date, tick.time = self.generateDateTimeAccordingLocalTime()
|
||||
# print "Depth", tick.date , tick.time
|
||||
|
||||
newtick = copy(tick)
|
||||
self.gateway.onTick(newtick)
|
||||
|
||||
'''
|
||||
//# Request
|
||||
{
|
||||
'event':'addChannel',
|
||||
'channel':'ltcbtc_trades',
|
||||
}
|
||||
//# Response
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"date":"1443428902",
|
||||
"price":"1565.91",
|
||||
"amount":"0.553",
|
||||
"tid":"37594617",
|
||||
"type":"sell",
|
||||
"trade_type":"ask"
|
||||
}...
|
||||
],
|
||||
"no": 1031995,
|
||||
"channel": "ltcbtc_trades"
|
||||
}
|
||||
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onTrades(self, data):
|
||||
pass
|
||||
# try:
|
||||
# channel = data['channel']
|
||||
# symbol = self.channelSymbolMap[channel]
|
||||
# except Exception,ex:
|
||||
# symbol = None
|
||||
|
||||
# if symbol == None:
|
||||
# return
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotAllOrders(self):
|
||||
for symbol_pair in self.registerSymbolPairArray:
|
||||
self.spotGetOrderSignOrderTradeType(symbol_pair , 1 , 50 , 1)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotOrder(self, data):
|
||||
code = data["code"]
|
||||
if str(code) != "1000":
|
||||
errorData = {"data":{"error_code":str(code)}}
|
||||
self.onError(errorData)
|
||||
return
|
||||
|
||||
rawData = json.loads(data['data'].replace("entrustId",'"entrustId"'))
|
||||
orderId = str(rawData["entrustId"])
|
||||
# 尽管websocket接口的委托号返回是异步的,但经过测试是
|
||||
# 符合先发现回的规律,因此这里通过queue获取之前发送的
|
||||
# 本地委托号,并把它和推送的系统委托号进行映射
|
||||
localNo = self.localNoQueue.get_nowait()
|
||||
|
||||
self.localNoDict[localNo] = orderId
|
||||
self.orderIdDict[orderId] = localNo
|
||||
|
||||
t_symbol = (data["channel"].split('_'))[0]
|
||||
symbol = zb_all_real_pair[t_symbol]
|
||||
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
order.symbol = '.'.join([symbol , EXCHANGE_ZB])
|
||||
order.vtSymbol = order.symbol
|
||||
order.orderID = localNo
|
||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||
self.orderDict[orderId] = order
|
||||
order.status = STATUS_UNKNOWN
|
||||
|
||||
|
||||
local_status = self.local_status_dict.get( str(localNo) , None)
|
||||
|
||||
if local_status != None:
|
||||
order.direction, priceType = zb_priceTypeMap[str(local_status)]
|
||||
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
# 检查是否有系统委托号返回前就发出的撤单请求,若有则进
|
||||
# 行撤单操作
|
||||
if localNo in self.cancelDict:
|
||||
req = self.cancelDict[localNo]
|
||||
self.spotCancel(req)
|
||||
del self.cancelDict[localNo]
|
||||
|
||||
'''
|
||||
{
|
||||
"success": true,
|
||||
"code": 1000,
|
||||
"channel": "ltcbtc_cancelorder",
|
||||
"message": "操作成功。",
|
||||
"no": "1472814987517496849777"
|
||||
}
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotCancelOrder(self, data):
|
||||
code = data["code"]
|
||||
if str(code) != "1000":
|
||||
errorData = {"data":{"error_code":str(code)}}
|
||||
self.onError(errorData)
|
||||
return
|
||||
|
||||
symbol , orderId = data["no"].split('.')
|
||||
orderId = str(orderId)
|
||||
localNo = self.orderIdDict[orderId]
|
||||
|
||||
if orderId not in self.orderDict:
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
order.symbol = '.'.join([symbol , EXCHANGE_ZB])
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localNo
|
||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||
|
||||
self.orderDict[orderId] = order
|
||||
else:
|
||||
order = self.orderDict[orderId]
|
||||
|
||||
order.status = STATUS_CANCELLED
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
del self.orderDict[orderId]
|
||||
del self.orderIdDict[orderId]
|
||||
del self.localNoDict[localNo]
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotSendOrder(self, req):
|
||||
"""发单"""
|
||||
symbol = (req.symbol.split('.'))[0]
|
||||
type_ = zb_priceTypeMapReverse[(req.direction, req.priceType)]
|
||||
|
||||
self.spotTrade(symbol, type_, str(req.price), str(req.volume))
|
||||
|
||||
# 本地委托号加1,并将对应字符串保存到队列中,返回基于本地委托号的vtOrderID
|
||||
self.localNo += 1
|
||||
self.localNoQueue.put(str(self.localNo))
|
||||
|
||||
self.local_status_dict[str(self.localNo)] = str(type_)
|
||||
|
||||
vtOrderID = '.'.join([self.gatewayName, str(self.localNo)])
|
||||
return vtOrderID
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def spotCancel(self, req):
|
||||
"""撤单"""
|
||||
symbol = (req.symbol.split('.'))[0]
|
||||
localNo = req.orderID
|
||||
|
||||
if localNo in self.localNoDict:
|
||||
orderID = self.localNoDict[localNo]
|
||||
self.spotCancelOrder(symbol, orderID)
|
||||
else:
|
||||
# 如果在系统委托号返回前客户就发送了撤单请求,则保存
|
||||
# 在cancelDict字典中,等待返回后执行撤单任务
|
||||
self.cancelDict[localNo] = req
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotGetOrder(self , data):
|
||||
"""生成时间"""
|
||||
code = data["code"]
|
||||
if str(code) != "1000":
|
||||
errorData = {"data":{"error_code":str(code)}}
|
||||
self.onError(errorData)
|
||||
return
|
||||
rawData = data['data'].replace('{','{"').replace('}','"}').replace(":",'":"').replace(',','","').replace('}","{','}{').\
|
||||
replace(':"[',':[').replace('}{','},{').replace(']",','],').replace('}"}','}}').replace(':"{"',':{"')
|
||||
orderId = str(rawData["id"])
|
||||
|
||||
# 这时候出现None , 情况是 已经发出了单子,但是系统这里还没建立 索引
|
||||
# 先这样返回试一下
|
||||
# 因为 发完单,订单变化是先推送的。。导致不清楚他的localID
|
||||
# 现在的处理方式是, 先缓存这里的信息,等到出现了 localID,再来处理这一段
|
||||
localNo = self.orderIdDict.get(orderId , None)
|
||||
if localNo == None:
|
||||
print "Error , localNo is none !" + str(localNo)
|
||||
return
|
||||
|
||||
# 委托信息
|
||||
if orderId not in self.orderDict:
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
symbol = zb_all_real_pair[rawData["currency"].replace('_',"")]
|
||||
|
||||
order.symbol = '.'.join([symbol, EXCHANGE_ZB])
|
||||
#order.symbol = spotSymbolMap[rawData['symbol']]
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localNo
|
||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||
|
||||
order.price = float(rawData['price'])
|
||||
order.totalVolume = float(rawData['total_amount'])
|
||||
order.direction, priceType = zb_priceTypeMap[str(rawData['type'])]
|
||||
|
||||
self.orderDict[orderId] = order
|
||||
else:
|
||||
order = self.orderDict[orderId]
|
||||
|
||||
order.tradedVolume = float(rawData['trade_amount'])
|
||||
order.status = zb_statusMap[int(rawData['status'])]
|
||||
|
||||
self.gateway.onOrder(copy(order))
|
||||
|
||||
bef_volume = self.recordOrderId_BefVolume.get( orderId , 0.0 )
|
||||
now_volume = float(rawData['trade_amount']) - bef_volume
|
||||
|
||||
if now_volume > 0.00001:
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
|
||||
trade.symbol = order.symbol
|
||||
trade.vtSymbol = order.symbol
|
||||
|
||||
self.tradeID += 1
|
||||
trade.tradeID = str(self.tradeID)
|
||||
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
|
||||
|
||||
trade.orderID = localNo
|
||||
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
|
||||
|
||||
trade.price = float(rawData['price'])
|
||||
trade.volume = float(now_volume)
|
||||
|
||||
trade.direction, priceType = zb_priceTypeMap[str(rawData['type'])]
|
||||
|
||||
trade.tradeTime = datetime.now().strftime('%H:%M:%S')
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
|
||||
if order.status in [STATUS_CANCELLED , STATUS_ALLTRADED]:
|
||||
del self.orderIdDict[orderId]
|
||||
del self.orderDict[orderId]
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotGetOrders(self, data):
|
||||
code = data["code"]
|
||||
if str(code) != "1000":
|
||||
errorData = {"data":{"error_code":str(code)}}
|
||||
self.onError(errorData)
|
||||
return
|
||||
|
||||
rawData = data['data'].replace('{','{"').replace('}','"}').replace(":",'":"').replace(',','","').replace('}","{','}{').\
|
||||
replace(':"[',':[').replace('}{','},{').replace(']",','],').replace('}"}','}}').replace(':"{"',':{"')
|
||||
rawData = json.loads(rawData)
|
||||
|
||||
orderDictKeys = self.orderDict.keys()
|
||||
|
||||
system_has_orderID_list = self.orderIdDict.keys()
|
||||
|
||||
for d in rawData:
|
||||
orderId = str(d["id"])
|
||||
if orderId in system_has_orderID_list:
|
||||
localNo = self.orderIdDict[orderId]
|
||||
order = self.orderDict[orderId]
|
||||
|
||||
order.price = float(d["price"])
|
||||
order.totalVolume = float(d["total_amount"])
|
||||
order.tradedVolume = float(d['trade_amount'])
|
||||
order.status = zb_statusMap[int(d['status'])]
|
||||
|
||||
self.gateway.onOrder(copy(order))
|
||||
|
||||
bef_volume = self.recordOrderId_BefVolume.get( orderId , 0.0 )
|
||||
now_volume = float(d['trade_amount']) - bef_volume
|
||||
|
||||
if now_volume > 0.00001:
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
|
||||
trade.symbol = order.symbol
|
||||
trade.vtSymbol = order.symbol
|
||||
|
||||
self.tradeID += 1
|
||||
trade.tradeID = str(self.tradeID)
|
||||
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
|
||||
|
||||
trade.orderID = localNo
|
||||
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
|
||||
|
||||
trade.price = float(d['price'])
|
||||
trade.volume = float(now_volume)
|
||||
|
||||
trade.direction, priceType = zb_priceTypeMap[str(d['type'])]
|
||||
|
||||
trade.tradeTime = datetime.now().strftime('%H:%M:%S')
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
|
||||
if order.status in [STATUS_CANCELLED , STATUS_ALLTRADED]:
|
||||
del self.orderIdDict[orderId]
|
||||
del self.orderDict[orderId]
|
||||
|
||||
else:
|
||||
if zb_statusMap[int(d['status'])] not in [STATUS_CANCELLED , STATUS_ALLTRADED]:
|
||||
self.localNo += 1
|
||||
localNo = str(self.localNo)
|
||||
orderId = str(d['id'])
|
||||
self.localNoDict[localNo] = orderId
|
||||
self.orderIdDict[orderId] = localNo
|
||||
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
symbol = zb_all_real_pair[d["currency"].replace('_',"")]
|
||||
|
||||
order.symbol = '.'.join([symbol , EXCHANGE_ZB])
|
||||
order.vtSymbol = order.symbol
|
||||
|
||||
order.orderID = localNo
|
||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||
|
||||
order.price = float(d['price'])
|
||||
order.totalVolume = float(d['total_amount'])
|
||||
order.direction, priceType = zb_priceTypeMap[str(d['type'])]
|
||||
|
||||
self.orderDict[orderId] = order
|
||||
|
||||
order.tradedVolume = float(d['trade_amount'])
|
||||
order.status = zb_statusMap[int(d['status'])]
|
||||
|
||||
self.gateway.onOrder(copy(order))
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# def onSpotGetOrdersignoretradetype(self, data):
|
||||
"""取消tradeType字段过滤,可同时获取买单和卖单,每次请求返回pageSize<100条记录"""
|
||||
"""因为跟 getOrders 功能重复,统一处理了"""
|
||||
|
||||
'''
|
||||
{
|
||||
"message": "操作成功",
|
||||
"no": "15207605119",
|
||||
"data": {
|
||||
"coins": [
|
||||
{
|
||||
"freez": "1.35828369",
|
||||
"enName": "BTC",
|
||||
"unitDecimal": 8,
|
||||
"cnName": "BTC",
|
||||
"unitTag": "฿",
|
||||
"available": "0.72771906",
|
||||
"key": "btc"
|
||||
},
|
||||
{
|
||||
"freez": "0.011",
|
||||
"enName": "LTC",
|
||||
"unitDecimal": 8,
|
||||
"cnName": "LTC",
|
||||
"unitTag": "Ł",
|
||||
"available": "3.51859814",
|
||||
"key": "ltc"
|
||||
},
|
||||
...
|
||||
],
|
||||
"base": {
|
||||
"username": "15207605119",
|
||||
"trade_password_enabled": true,
|
||||
"auth_google_enabled": true,
|
||||
"auth_mobile_enabled": true
|
||||
}
|
||||
},
|
||||
"code": 1000,
|
||||
"channel": "getaccountinfo",
|
||||
"success": true
|
||||
}
|
||||
'''
|
||||
#----------------------------------------------------------------------
|
||||
def onSpotUserInfo(self, data):
|
||||
code = data["code"]
|
||||
if str(code) != "1000":
|
||||
errorData = {"data":{"error_code":str(code)}}
|
||||
self.onError(errorData)
|
||||
return
|
||||
try:
|
||||
|
||||
rawData = data['data'].replace('{','{"').replace('}','"}').replace(":",'":"').replace(',','","').replace('}","{','}{').\
|
||||
replace(':"[',':[').replace('}{','},{').replace(']",','],').replace('}"}','}}').replace(':"{"',':{"')
|
||||
|
||||
rawData = json.loads(rawData)
|
||||
coins = rawData["coins"]
|
||||
|
||||
except Exception,ex:
|
||||
print ex
|
||||
|
||||
for coin in coins:
|
||||
symbol = coin["cnName"].lower()
|
||||
if symbol in zb_all_symbols:
|
||||
pos = VtPositionData()
|
||||
pos.gatewayName = self.gatewayName
|
||||
|
||||
pos.symbol = symbol + "." + EXCHANGE_ZB
|
||||
pos.vtSymbol = symbol + "." + EXCHANGE_ZB
|
||||
pos.vtPositionName = symbol
|
||||
pos.direction = DIRECTION_NET
|
||||
|
||||
pos.frozen = float(coin["freez"])
|
||||
pos.position = pos.frozen + float(coin["available"])
|
||||
|
||||
self.gateway.onPosition(pos)
|
||||
|
||||
# 账户资金
|
||||
account = VtAccountData()
|
||||
account.gatewayName = self.gatewayName
|
||||
account.accountID = self.gatewayName
|
||||
account.vtAccountID = account.accountID
|
||||
account.balance = 0.0
|
||||
#account.balance = float(funds['asset']['net'])
|
||||
self.gateway.onAccount(account)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
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 generateDateTimeAccordingLocalTime(self):
|
||||
dt = datetime.now()
|
||||
time = dt.strftime("%H:%M:%S.%f")
|
||||
date = dt.strftime("%Y%m%d")
|
||||
return date, time
|
||||
|
@ -85,6 +85,12 @@ EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所
|
||||
EXCHANGE_HUOBI = 'HUOBI' # 火币比特币交易所
|
||||
EXCHANGE_LHANG = 'LHANG' # 链行比特币交易所
|
||||
|
||||
EXCHANGE_KORBIT = 'KORBIT' # KORBIT 韩国交易所
|
||||
EXCHANGE_ZB = 'ZB' # ZB 中国比特币交易所 (比特币中国)
|
||||
EXCHANGE_OKEX = 'OKEX' # OKEX 中国比特币交易所 (okcoin)
|
||||
EXCHANGE_ZAIF = "ZAIF" # ZAIF 日本比特币交易所
|
||||
EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK 日本比特币交易所
|
||||
|
||||
# 货币类型
|
||||
CURRENCY_USD = 'USD' # 美元
|
||||
CURRENCY_CNY = 'CNY' # 人民币
|
||||
|
@ -81,6 +81,12 @@ EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所
|
||||
EXCHANGE_HUOBI = 'HUOBI' # 火币比特币交易所
|
||||
EXCHANGE_LHANG = 'LHANG' # 链行比特币交易所
|
||||
|
||||
EXCHANGE_KORBIT = 'KORBIT' # KORBIT 韩国交易所
|
||||
EXCHANGE_ZB = 'ZB' # ZB 中国比特币交易所 (比特币中国)
|
||||
EXCHANGE_OKEX = 'OKEX' # OKEX 中国比特币交易所 (okcoin)
|
||||
EXCHANGE_ZAIF = "ZAIF" # ZAIF 日本比特币交易所 尚未实现
|
||||
EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK 日本比特币交易所
|
||||
|
||||
# 货币类型
|
||||
CURRENCY_USD = 'USD' # 美元
|
||||
CURRENCY_CNY = 'CNY' # 人民币
|
||||
|
Loading…
Reference in New Issue
Block a user