From b8c0d436d639e0bdb9029a37455522caa933ddfd Mon Sep 17 00:00:00 2001 From: ipqhjjybj Date: Sun, 10 Dec 2017 20:52:43 +0800 Subject: [PATCH 1/4] add zaif, coincheck, okex , zb api interface. And add okex,coincheckk gateway --- vnpy/api/coincheck/README.txt | 24 + vnpy/api/coincheck/__init__.py | 3 + vnpy/api/coincheck/test.html | 34 + vnpy/api/coincheck/test.py | 65 ++ vnpy/api/coincheck/test2.py | 6 + vnpy/api/coincheck/test3.py | 31 + vnpy/api/coincheck/test4.py | 18 + vnpy/api/coincheck/vncoincheck.py | 461 +++++++++ vnpy/api/okex/__init__.py | 3 + vnpy/api/okex/readme.txt | 5 + vnpy/api/okex/test.py | 49 + vnpy/api/okex/vnokex.py | 461 +++++++++ vnpy/api/zaif/README.txt | 23 + vnpy/api/zaif/__init__.py | 3 + vnpy/api/zaif/test.php | 153 +++ vnpy/api/zaif/test.py | 55 ++ vnpy/api/zaif/vnzaif.py | 500 ++++++++++ vnpy/api/zb/__init__.py | 3 + vnpy/api/zb/reame.txt | 53 + vnpy/api/zb/test.py | 33 + vnpy/api/zb/vnzb.py | 258 +++++ .../coincheckGateway/Coincheck_connect.json | 7 + .../gateway/coincheckGateway/__init__.py | 11 + .../coincheckGateway/coincheckGateway.py | 741 ++++++++++++++ .../gateway/okexGateway/OKEX_connect.json | 6 + vnpy/trader/gateway/okexGateway/__init__.py | 11 + .../trader/gateway/okexGateway/okexGateway.py | 934 ++++++++++++++++++ vnpy/trader/language/chinese/constant.py | 5 + vnpy/trader/language/english/constant.py | 5 + 29 files changed, 3961 insertions(+) create mode 100644 vnpy/api/coincheck/README.txt create mode 100644 vnpy/api/coincheck/__init__.py create mode 100644 vnpy/api/coincheck/test.html create mode 100644 vnpy/api/coincheck/test.py create mode 100644 vnpy/api/coincheck/test2.py create mode 100644 vnpy/api/coincheck/test3.py create mode 100644 vnpy/api/coincheck/test4.py create mode 100644 vnpy/api/coincheck/vncoincheck.py create mode 100644 vnpy/api/okex/__init__.py create mode 100644 vnpy/api/okex/readme.txt create mode 100644 vnpy/api/okex/test.py create mode 100644 vnpy/api/okex/vnokex.py create mode 100644 vnpy/api/zaif/README.txt create mode 100644 vnpy/api/zaif/__init__.py create mode 100644 vnpy/api/zaif/test.php create mode 100644 vnpy/api/zaif/test.py create mode 100644 vnpy/api/zaif/vnzaif.py create mode 100644 vnpy/api/zb/__init__.py create mode 100644 vnpy/api/zb/reame.txt create mode 100644 vnpy/api/zb/test.py create mode 100644 vnpy/api/zb/vnzb.py create mode 100644 vnpy/trader/gateway/coincheckGateway/Coincheck_connect.json create mode 100644 vnpy/trader/gateway/coincheckGateway/__init__.py create mode 100644 vnpy/trader/gateway/coincheckGateway/coincheckGateway.py create mode 100644 vnpy/trader/gateway/okexGateway/OKEX_connect.json create mode 100644 vnpy/trader/gateway/okexGateway/__init__.py create mode 100644 vnpy/trader/gateway/okexGateway/okexGateway.py diff --git a/vnpy/api/coincheck/README.txt b/vnpy/api/coincheck/README.txt new file mode 100644 index 00000000..cb71fbbc --- /dev/null +++ b/vnpy/api/coincheck/README.txt @@ -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 \ No newline at end of file diff --git a/vnpy/api/coincheck/__init__.py b/vnpy/api/coincheck/__init__.py new file mode 100644 index 00000000..f08065df --- /dev/null +++ b/vnpy/api/coincheck/__init__.py @@ -0,0 +1,3 @@ +# encoding: UTF-8 + +from vncoincheck import TradeApi, DataApi \ No newline at end of file diff --git a/vnpy/api/coincheck/test.html b/vnpy/api/coincheck/test.html new file mode 100644 index 00000000..2ca5c51b --- /dev/null +++ b/vnpy/api/coincheck/test.html @@ -0,0 +1,34 @@ + + + + + + WebSocket 客户端 + + + + + + + + \ No newline at end of file diff --git a/vnpy/api/coincheck/test.py b/vnpy/api/coincheck/test.py new file mode 100644 index 00000000..dac1e7c9 --- /dev/null +++ b/vnpy/api/coincheck/test.py @@ -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() \ No newline at end of file diff --git a/vnpy/api/coincheck/test2.py b/vnpy/api/coincheck/test2.py new file mode 100644 index 00000000..db4d59a6 --- /dev/null +++ b/vnpy/api/coincheck/test2.py @@ -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) \ No newline at end of file diff --git a/vnpy/api/coincheck/test3.py b/vnpy/api/coincheck/test3.py new file mode 100644 index 00000000..b0a85827 --- /dev/null +++ b/vnpy/api/coincheck/test3.py @@ -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() \ No newline at end of file diff --git a/vnpy/api/coincheck/test4.py b/vnpy/api/coincheck/test4.py new file mode 100644 index 00000000..0995e9c9 --- /dev/null +++ b/vnpy/api/coincheck/test4.py @@ -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() \ No newline at end of file diff --git a/vnpy/api/coincheck/vncoincheck.py b/vnpy/api/coincheck/vncoincheck.py new file mode 100644 index 00000000..a00fc576 --- /dev/null +++ b/vnpy/api/coincheck/vncoincheck.py @@ -0,0 +1,461 @@ +# 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 diff --git a/vnpy/api/okex/__init__.py b/vnpy/api/okex/__init__.py new file mode 100644 index 00000000..3adf4698 --- /dev/null +++ b/vnpy/api/okex/__init__.py @@ -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 \ No newline at end of file diff --git a/vnpy/api/okex/readme.txt b/vnpy/api/okex/readme.txt new file mode 100644 index 00000000..3030ef7c --- /dev/null +++ b/vnpy/api/okex/readme.txt @@ -0,0 +1,5 @@ +okex 的平台地址 + +https://www.okex.com/intro_apiOverview.html + +api 文档 https://www.okex.com/ws_request.html diff --git a/vnpy/api/okex/test.py b/vnpy/api/okex/test.py new file mode 100644 index 00000000..ae8d499f --- /dev/null +++ b/vnpy/api/okex/test.py @@ -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() + +''' +合约账户信息、 持仓信息等,在登录后都会自动推送。。。官方文档这样写的,还没实际验证过 +''' \ No newline at end of file diff --git a/vnpy/api/okex/vnokex.py b/vnpy/api/okex/vnokex.py new file mode 100644 index 00000000..339ee3a7 --- /dev/null +++ b/vnpy/api/okex/vnokex.py @@ -0,0 +1,461 @@ +# 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应用对象 现货对象 + + #---------------------------------------------------------------------- + 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, {}) + \ No newline at end of file diff --git a/vnpy/api/zaif/README.txt b/vnpy/api/zaif/README.txt new file mode 100644 index 00000000..125b4260 --- /dev/null +++ b/vnpy/api/zaif/README.txt @@ -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 \ No newline at end of file diff --git a/vnpy/api/zaif/__init__.py b/vnpy/api/zaif/__init__.py new file mode 100644 index 00000000..210d0e4a --- /dev/null +++ b/vnpy/api/zaif/__init__.py @@ -0,0 +1,3 @@ +# encoding: UTF-8 + +from vnzaif import TradeApi, DataApi \ No newline at end of file diff --git a/vnpy/api/zaif/test.php b/vnpy/api/zaif/test.php new file mode 100644 index 00000000..2fb4b098 --- /dev/null +++ b/vnpy/api/zaif/test.php @@ -0,0 +1,153 @@ +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 ); + +?> \ No newline at end of file diff --git a/vnpy/api/zaif/test.py b/vnpy/api/zaif/test.py new file mode 100644 index 00000000..ed41d65b --- /dev/null +++ b/vnpy/api/zaif/test.py @@ -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() \ No newline at end of file diff --git a/vnpy/api/zaif/vnzaif.py b/vnpy/api/zaif/vnzaif.py new file mode 100644 index 00000000..5f46ea36 --- /dev/null +++ b/vnpy/api/zaif/vnzaif.py @@ -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 + diff --git a/vnpy/api/zb/__init__.py b/vnpy/api/zb/__init__.py new file mode 100644 index 00000000..175129db --- /dev/null +++ b/vnpy/api/zb/__init__.py @@ -0,0 +1,3 @@ +# encoding: UTF-8 + +from vnzb import ZB_Sub_Spot_Api \ No newline at end of file diff --git a/vnpy/api/zb/reame.txt b/vnpy/api/zb/reame.txt new file mode 100644 index 00000000..06f5cec0 --- /dev/null +++ b/vnpy/api/zb/reame.txt @@ -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获取用户信息 + diff --git a/vnpy/api/zb/test.py b/vnpy/api/zb/test.py new file mode 100644 index 00000000..cb1fabed --- /dev/null +++ b/vnpy/api/zb/test.py @@ -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) \ No newline at end of file diff --git a/vnpy/api/zb/vnzb.py b/vnpy/api/zb/vnzb.py new file mode 100644 index 00000000..81f92a79 --- /dev/null +++ b/vnpy/api/zb/vnzb.py @@ -0,0 +1,258 @@ +# 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_symbols = ["ltc_btc"] + +class ZB_Sub_Spot_Api(object): + """基于Websocket的API对象""" + def __init__(self): + """Constructor""" + self.apiKey = '' # 用户名 + self.secretKey = '' # 密码 + + self.ws_sub_spot = None # websocket应用对象 现货对象 + + #---------------------------------------------------------------------- + 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" + + self.sendTradingRequest(channel, params) + + #---------------------------------------------------------------------- + def spotCancelOrder(self, symbol_pair, orderid): + """现货撤单""" + symbol_pair = symbol_pair.replace('_','') + params = {} + params['id'] = 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) \ No newline at end of file diff --git a/vnpy/trader/gateway/coincheckGateway/Coincheck_connect.json b/vnpy/trader/gateway/coincheckGateway/Coincheck_connect.json new file mode 100644 index 00000000..dcf2b1f9 --- /dev/null +++ b/vnpy/trader/gateway/coincheckGateway/Coincheck_connect.json @@ -0,0 +1,7 @@ +{ + "accountID": "你的账户ID", + "accessKey": "你的key", + "secretKey": "你的secretKey", + "interval": 0.5, + "debug": false +} \ No newline at end of file diff --git a/vnpy/trader/gateway/coincheckGateway/__init__.py b/vnpy/trader/gateway/coincheckGateway/__init__.py new file mode 100644 index 00000000..5cb64a33 --- /dev/null +++ b/vnpy/trader/gateway/coincheckGateway/__init__.py @@ -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 + diff --git a/vnpy/trader/gateway/coincheckGateway/coincheckGateway.py b/vnpy/trader/gateway/coincheckGateway/coincheckGateway.py new file mode 100644 index 00000000..7bb692e8 --- /dev/null +++ b/vnpy/trader/gateway/coincheckGateway/coincheckGateway.py @@ -0,0 +1,741 @@ +# 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__) + + + #---------------------------------------------------------------------- + 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] + self.startQuery() + + #---------------------------------------------------------------------- + def query(self, event): + """注册到事件处理引擎上的查询函数""" + 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.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']: + posObj = VtPositionData() + posObj.gatewayName = self.gatewayName + posObj.symbol = symbol + "_jpy." + EXCHANGE_COINCHECK + posObj.exchange = EXCHANGE_COINCHECK + posObj.vtSymbol = posObj.symbol + posObj.direction = DIRECTION_LONG + 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.volume = 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.volume - 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.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.volume = 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) + + 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) + diff --git a/vnpy/trader/gateway/okexGateway/OKEX_connect.json b/vnpy/trader/gateway/okexGateway/OKEX_connect.json new file mode 100644 index 00000000..a0224e4c --- /dev/null +++ b/vnpy/trader/gateway/okexGateway/OKEX_connect.json @@ -0,0 +1,6 @@ +{ + "apiKey": "你的apiKey", + "secretKey": "你的secretKey", + "trace": false, + "leverage": 10 +} \ No newline at end of file diff --git a/vnpy/trader/gateway/okexGateway/__init__.py b/vnpy/trader/gateway/okexGateway/__init__.py new file mode 100644 index 00000000..d22d522a --- /dev/null +++ b/vnpy/trader/gateway/okexGateway/__init__.py @@ -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 + diff --git a/vnpy/trader/gateway/okexGateway/okexGateway.py b/vnpy/trader/gateway/okexGateway/okexGateway.py new file mode 100644 index 00000000..9408862f --- /dev/null +++ b/vnpy/trader/gateway/okexGateway/okexGateway.py @@ -0,0 +1,934 @@ +# 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): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + 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.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 spotAllOrders(self): + for 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() + + for symbol_pair in okex_all_symbol_pairs: + self.spotOrderInfo(symbol_pair , '-1') + + + 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 + pos.vtSymbol = symbol + 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 + pos.vtSymbol = symbol + 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 + pos.vtSymbol = symbol + 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): + """委托信息查询回调""" + 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'] + 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 diff --git a/vnpy/trader/language/chinese/constant.py b/vnpy/trader/language/chinese/constant.py index 1aadb9b4..df9abb92 100644 --- a/vnpy/trader/language/chinese/constant.py +++ b/vnpy/trader/language/chinese/constant.py @@ -85,6 +85,11 @@ EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所 EXCHANGE_HUOBI = 'HUOBI' # 火币比特币交易所 EXCHANGE_LHANG = 'LHANG' # 链行比特币交易所 +EXCHANGE_ZB = 'ZB' # ZB 中国比特币交易所 (比特币中国) +EXCHANGE_OKEX = 'OKEX' # OKEX 中国比特币交易所 (okcoin) +EXCHANGE_ZAIF = "ZAIF" # ZAIF 日本比特币交易所 +EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK 日本比特币交易所 + # 货币类型 CURRENCY_USD = 'USD' # 美元 CURRENCY_CNY = 'CNY' # 人民币 diff --git a/vnpy/trader/language/english/constant.py b/vnpy/trader/language/english/constant.py index bb0b8e03..d0f52675 100644 --- a/vnpy/trader/language/english/constant.py +++ b/vnpy/trader/language/english/constant.py @@ -81,6 +81,11 @@ EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所 EXCHANGE_HUOBI = 'HUOBI' # 火币比特币交易所 EXCHANGE_LHANG = 'LHANG' # 链行比特币交易所 +EXCHANGE_ZB = 'ZB' # ZB 中国比特币交易所 (比特币中国) +EXCHANGE_OKEX = 'OKEX' # OKEX 中国比特币交易所 (okcoin) +EXCHANGE_ZAIF = "ZAIF" # ZAIF 日本比特币交易所 +EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK 日本比特币交易所 + # 货币类型 CURRENCY_USD = 'USD' # 美元 CURRENCY_CNY = 'CNY' # 人民币 From ab6755c76d68f1d1d32dcc37f9e29abac8886e83 Mon Sep 17 00:00:00 2001 From: ipqhjjybj Date: Fri, 15 Dec 2017 21:10:38 +0800 Subject: [PATCH 2/4] s --- vnpy/api/coincheck/vncoincheck.py | 1 - vnpy/api/korbit/__init__.py | 3 + .../korbit/korbit-python-master/.travis.yml | 8 + vnpy/api/korbit/korbit-python-master/LICENSE | 21 + .../api/korbit/korbit-python-master/README.md | 51 ++ .../korbit-python-master/korbit/__init__.py | 12 + .../korbit/private_api.py | 173 ++++ .../korbit-python-master/korbit/public_api.py | 77 ++ .../korbit-python-master/korbit/test.py | 11 + vnpy/api/korbit/korbit-python-master/setup.py | 24 + .../korbit-python-master/tests/__init__.py | 0 .../korbit/korbit-python-master/tests/test.py | 12 + vnpy/api/korbit/readme.txt | 5 + vnpy/api/korbit/run.bat | 1 + vnpy/api/korbit/test.py | 34 + vnpy/api/korbit/vnkorbit.py | 377 ++++++++ vnpy/api/okex/vnokex.py | 2 + vnpy/api/zb/__init__.py | 2 +- vnpy/api/zb/vnzb.py | 18 +- .../coincheckGateway/coincheckGateway.py | 38 +- .../gateway/korbitGateway/KORBIT_connect.json | 9 + vnpy/trader/gateway/korbitGateway/__init__.py | 11 + .../gateway/korbitGateway/korbitGateway.py | 586 ++++++++++++ .../trader/gateway/okexGateway/okexGateway.py | 63 +- vnpy/trader/gateway/zbGateway/ZB_connect.json | 5 + vnpy/trader/gateway/zbGateway/__init__.py | 11 + vnpy/trader/gateway/zbGateway/zbGateway.py | 857 ++++++++++++++++++ 27 files changed, 2377 insertions(+), 35 deletions(-) create mode 100644 vnpy/api/korbit/__init__.py create mode 100644 vnpy/api/korbit/korbit-python-master/.travis.yml create mode 100644 vnpy/api/korbit/korbit-python-master/LICENSE create mode 100644 vnpy/api/korbit/korbit-python-master/README.md create mode 100644 vnpy/api/korbit/korbit-python-master/korbit/__init__.py create mode 100644 vnpy/api/korbit/korbit-python-master/korbit/private_api.py create mode 100644 vnpy/api/korbit/korbit-python-master/korbit/public_api.py create mode 100644 vnpy/api/korbit/korbit-python-master/korbit/test.py create mode 100644 vnpy/api/korbit/korbit-python-master/setup.py create mode 100644 vnpy/api/korbit/korbit-python-master/tests/__init__.py create mode 100644 vnpy/api/korbit/korbit-python-master/tests/test.py create mode 100644 vnpy/api/korbit/readme.txt create mode 100644 vnpy/api/korbit/run.bat create mode 100644 vnpy/api/korbit/test.py create mode 100644 vnpy/api/korbit/vnkorbit.py create mode 100644 vnpy/trader/gateway/korbitGateway/KORBIT_connect.json create mode 100644 vnpy/trader/gateway/korbitGateway/__init__.py create mode 100644 vnpy/trader/gateway/korbitGateway/korbitGateway.py create mode 100644 vnpy/trader/gateway/zbGateway/ZB_connect.json create mode 100644 vnpy/trader/gateway/zbGateway/__init__.py create mode 100644 vnpy/trader/gateway/zbGateway/zbGateway.py diff --git a/vnpy/api/coincheck/vncoincheck.py b/vnpy/api/coincheck/vncoincheck.py index a00fc576..018c92b9 100644 --- a/vnpy/api/coincheck/vncoincheck.py +++ b/vnpy/api/coincheck/vncoincheck.py @@ -189,7 +189,6 @@ class TradeApi(object): self.active = True self.reqThread.start() - #---------------------------------------------------------------------- def exit(self): """退出""" diff --git a/vnpy/api/korbit/__init__.py b/vnpy/api/korbit/__init__.py new file mode 100644 index 00000000..a35bb985 --- /dev/null +++ b/vnpy/api/korbit/__init__.py @@ -0,0 +1,3 @@ +# encoding: UTF-8 + +from vnkorbit import Korbit_TradeApi, Korbit_DataApi , KORBIT_ALL_SYMBOL_PAIR , KORBIT_ALL_SYMBOLS \ No newline at end of file diff --git a/vnpy/api/korbit/korbit-python-master/.travis.yml b/vnpy/api/korbit/korbit-python-master/.travis.yml new file mode 100644 index 00000000..70eb4e19 --- /dev/null +++ b/vnpy/api/korbit/korbit-python-master/.travis.yml @@ -0,0 +1,8 @@ +language: python +python: + - "2.7" + - "3.5" +install: "pip install requests" +script: "python -m unittest" +notifications: + slack: test1030team:nXUuJifoc4Rg8VFYmdW9cd1T diff --git a/vnpy/api/korbit/korbit-python-master/LICENSE b/vnpy/api/korbit/korbit-python-master/LICENSE new file mode 100644 index 00000000..c39d0234 --- /dev/null +++ b/vnpy/api/korbit/korbit-python-master/LICENSE @@ -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. diff --git a/vnpy/api/korbit/korbit-python-master/README.md b/vnpy/api/korbit/korbit-python-master/README.md new file mode 100644 index 00000000..27c2d862 --- /dev/null +++ b/vnpy/api/korbit/korbit-python-master/README.md @@ -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. diff --git a/vnpy/api/korbit/korbit-python-master/korbit/__init__.py b/vnpy/api/korbit/korbit-python-master/korbit/__init__.py new file mode 100644 index 00000000..20fa16f5 --- /dev/null +++ b/vnpy/api/korbit/korbit-python-master/korbit/__init__.py @@ -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 diff --git a/vnpy/api/korbit/korbit-python-master/korbit/private_api.py b/vnpy/api/korbit/korbit-python-master/korbit/private_api.py new file mode 100644 index 00000000..da724922 --- /dev/null +++ b/vnpy/api/korbit/korbit-python-master/korbit/private_api.py @@ -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) diff --git a/vnpy/api/korbit/korbit-python-master/korbit/public_api.py b/vnpy/api/korbit/korbit-python-master/korbit/public_api.py new file mode 100644 index 00000000..36fd3d08 --- /dev/null +++ b/vnpy/api/korbit/korbit-python-master/korbit/public_api.py @@ -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 diff --git a/vnpy/api/korbit/korbit-python-master/korbit/test.py b/vnpy/api/korbit/korbit-python-master/korbit/test.py new file mode 100644 index 00000000..718bf972 --- /dev/null +++ b/vnpy/api/korbit/korbit-python-master/korbit/test.py @@ -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") \ No newline at end of file diff --git a/vnpy/api/korbit/korbit-python-master/setup.py b/vnpy/api/korbit/korbit-python-master/setup.py new file mode 100644 index 00000000..2ec2d0dd --- /dev/null +++ b/vnpy/api/korbit/korbit-python-master/setup.py @@ -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', +) diff --git a/vnpy/api/korbit/korbit-python-master/tests/__init__.py b/vnpy/api/korbit/korbit-python-master/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/vnpy/api/korbit/korbit-python-master/tests/test.py b/vnpy/api/korbit/korbit-python-master/tests/test.py new file mode 100644 index 00000000..eced0dc5 --- /dev/null +++ b/vnpy/api/korbit/korbit-python-master/tests/test.py @@ -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()) diff --git a/vnpy/api/korbit/readme.txt b/vnpy/api/korbit/readme.txt new file mode 100644 index 00000000..8fbab19e --- /dev/null +++ b/vnpy/api/korbit/readme.txt @@ -0,0 +1,5 @@ +korbit 韩国交易所 +https://apidocs.korbit.co.kr/#exchange:-public + +参考代码文档: +https://github.com/HoonJin/korbit-python \ No newline at end of file diff --git a/vnpy/api/korbit/run.bat b/vnpy/api/korbit/run.bat new file mode 100644 index 00000000..48f3fd60 --- /dev/null +++ b/vnpy/api/korbit/run.bat @@ -0,0 +1 @@ +python test.py \ No newline at end of file diff --git a/vnpy/api/korbit/test.py b/vnpy/api/korbit/test.py new file mode 100644 index 00000000..af0aeb20 --- /dev/null +++ b/vnpy/api/korbit/test.py @@ -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() \ No newline at end of file diff --git a/vnpy/api/korbit/vnkorbit.py b/vnpy/api/korbit/vnkorbit.py new file mode 100644 index 00000000..0f134325 --- /dev/null +++ b/vnpy/api/korbit/vnkorbit.py @@ -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 + diff --git a/vnpy/api/okex/vnokex.py b/vnpy/api/okex/vnokex.py index 339ee3a7..4e008fad 100644 --- a/vnpy/api/okex/vnokex.py +++ b/vnpy/api/okex/vnokex.py @@ -36,6 +36,8 @@ class OKEX_Sub_Spot_Api(object): self.ws_sub_spot = None # websocket应用对象 现货对象 + self.thread = None # 线程初始化 + #---------------------------------------------------------------------- def reconnect(self): """重新连接""" diff --git a/vnpy/api/zb/__init__.py b/vnpy/api/zb/__init__.py index 175129db..835d9d9a 100644 --- a/vnpy/api/zb/__init__.py +++ b/vnpy/api/zb/__init__.py @@ -1,3 +1,3 @@ # encoding: UTF-8 -from vnzb import ZB_Sub_Spot_Api \ No newline at end of file +from vnzb import ZB_Sub_Spot_Api , zb_all_symbol_pairs , zb_all_symbols , zb_all_real_pair \ No newline at end of file diff --git a/vnpy/api/zb/vnzb.py b/vnpy/api/zb/vnzb.py index 81f92a79..0e700fa3 100644 --- a/vnpy/api/zb/vnzb.py +++ b/vnpy/api/zb/vnzb.py @@ -13,16 +13,23 @@ import urllib2, hashlib,struct,sha,time # OKEX网站 zb_usd_url = "wss://api.zb.com:9999/websocket" -zb_all_symbols = ["ltc_btc"] +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.apiKey = '' # 用户名 + self.secretKey = '' # 密码 - self.ws_sub_spot = None # websocket应用对象 现货对象 + self.ws_sub_spot = None # websocket应用对象 现货对象 + + self.thread = None # 线程变量 #---------------------------------------------------------------------- def reconnect(self): @@ -203,14 +210,17 @@ class ZB_Sub_Spot_Api(object): 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" diff --git a/vnpy/trader/gateway/coincheckGateway/coincheckGateway.py b/vnpy/trader/gateway/coincheckGateway/coincheckGateway.py index 7bb692e8..fe32c887 100644 --- a/vnpy/trader/gateway/coincheckGateway/coincheckGateway.py +++ b/vnpy/trader/gateway/coincheckGateway/coincheckGateway.py @@ -37,6 +37,8 @@ class CoincheckGateway(VtGateway): self.fileName = self.gatewayName + '_connect.json' self.filePath = getJsonPath(self.fileName, __file__) + self.total_count = 0 + self.delayTime = 3 #---------------------------------------------------------------------- def connect(self): @@ -76,6 +78,7 @@ class CoincheckGateway(VtGateway): #self.dataApi.connect(interval, debug) self.dataApi.connect() self.writeLog(u'行情接口初始化成功') + # 启动查询 self.initQuery() @@ -123,16 +126,18 @@ class CoincheckGateway(VtGateway): #---------------------------------------------------------------------- def initQuery(self): """初始化连续查询""" + if self.qryEnabled: self.qryFunctionList = [self.tradeApi.get_balance , self.tradeApi.list_orders] #self.qryFunctionList = [self.tradeApi.queryWorkingOrders, self.tradeApi.queryAccount] - self.startQuery() #---------------------------------------------------------------------- def query(self, event): """注册到事件处理引擎上的查询函数""" - for function in self.qryFunctionList: - function() + self.total_count += 1 + if self.total_count % self.delayTime == 0: + for function in self.qryFunctionList: + function() #---------------------------------------------------------------------- def startQuery(self): @@ -218,7 +223,6 @@ u'dash': u'0', u'cny_debt': u'0.0', u'xrp_lend_in_use': u'0.0', u'xem_reserved': account.accountID = self.accountID account.vtAccountID = '.'.join([ self.gatewayName , self.accountID]) account.balance = float(data['jpy']) - account.balance = float(data['jpy']) account.available = float(data['jpy']) account.margin = 1.0 @@ -227,15 +231,16 @@ u'dash': u'0', u'cny_debt': u'0.0', u'xrp_lend_in_use': u'0.0', u'xem_reserved': account.commission = 0.0 account.now_has_hands = float(data['jpy']) + self.gateway.onAccount(account) - for symbol in ['btc']: + for symbol in ['btc' , 'jpy']: posObj = VtPositionData() posObj.gatewayName = self.gatewayName - posObj.symbol = symbol + "_jpy." + EXCHANGE_COINCHECK + posObj.symbol = symbol + "." + EXCHANGE_COINCHECK posObj.exchange = EXCHANGE_COINCHECK posObj.vtSymbol = posObj.symbol - posObj.direction = DIRECTION_LONG + 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"]) @@ -282,7 +287,8 @@ u'dash': u'0', u'cny_debt': u'0.0', u'xrp_lend_in_use': u'0.0', u'xem_reserved': else: order.offset = OFFSET_CLOSE order.price = req.price - order.volume = req.volume + order.tradedVolume = 0 + order.totalVolume = req.volume order.orderTime = datetime.now().strftime('%H:%M:%S') order.status = STATUS_UNKNOWN @@ -380,7 +386,7 @@ pending_market_buy_amount': None, u'rate': u'100.0', u'pair': u'btc_jpy', u'stop order = self.workingOrderDict.get(localID, None) if order != None: bef_has_volume = self.tradedVolumeDict.get(localID , 0.0) - newTradeVolume = order.volume - bef_has_volume + newTradeVolume = order.totalVolume - bef_has_volume trade = VtTradeData() trade.gatewayName = self.gatewayName @@ -443,6 +449,7 @@ pending_market_buy_amount': None, u'rate': u'100.0', u'pair': u'btc_jpy', u'stop self.gateway.onTrade(trade) + order.tradedVolume = has_traded_volume order.status = STATUS_PARTTRADED self.gateway.onOrder(order) else: @@ -475,7 +482,7 @@ pending_market_buy_amount': None, u'rate': u'100.0', u'pair': u'btc_jpy', u'stop order.offset = OFFSET_OPEN order.price = float(d["rate"]) - order.volume = float(d["pending_amount"]) + order.totalVolume = float(d["pending_amount"]) order.orderTime = d["created_at"] order.status = STATUS_MISTAKE @@ -535,6 +542,17 @@ class CoincheckSocketDataApi(vncoincheck.DataApiSocket): 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: diff --git a/vnpy/trader/gateway/korbitGateway/KORBIT_connect.json b/vnpy/trader/gateway/korbitGateway/KORBIT_connect.json new file mode 100644 index 00000000..3c1be7e6 --- /dev/null +++ b/vnpy/trader/gateway/korbitGateway/KORBIT_connect.json @@ -0,0 +1,9 @@ +{ + "accountID": "随意取个账户名", + "accessKey": "accessID", + "secretKey": "secretKey", + "username": "你的网站用户名", + "password": "你的网站密码", + "interval": 3, + "debug": false +} \ No newline at end of file diff --git a/vnpy/trader/gateway/korbitGateway/__init__.py b/vnpy/trader/gateway/korbitGateway/__init__.py new file mode 100644 index 00000000..e4fc53b6 --- /dev/null +++ b/vnpy/trader/gateway/korbitGateway/__init__.py @@ -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 + diff --git a/vnpy/trader/gateway/korbitGateway/korbitGateway.py b/vnpy/trader/gateway/korbitGateway/korbitGateway.py new file mode 100644 index 00000000..32a4d352 --- /dev/null +++ b/vnpy/trader/gateway/korbitGateway/korbitGateway.py @@ -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 \ No newline at end of file diff --git a/vnpy/trader/gateway/okexGateway/okexGateway.py b/vnpy/trader/gateway/okexGateway/okexGateway.py index 9408862f..d7d974c6 100644 --- a/vnpy/trader/gateway/okexGateway/okexGateway.py +++ b/vnpy/trader/gateway/okexGateway/okexGateway.py @@ -38,6 +38,7 @@ statusMap[1] = STATUS_PARTTRADED statusMap[2] = STATUS_ALLTRADED statusMap[4] = STATUS_UNKNOWN + ######################################################################## class okexGateway(VtGateway): #---------------------------------------------------------------------- @@ -100,7 +101,7 @@ class okexGateway(VtGateway): #---------------------------------------------------------------------- def subscribe(self, subscribeReq): """订阅行情""" - pass + self.api_spot.subscribe(subscribeReq) #---------------------------------------------------------------------- def sendOrder(self, orderReq): @@ -205,6 +206,8 @@ class Api_Spot(OKEX_Sub_Spot_Api): self.cache_some_order = {} self.tradeID = 0 + + self.registerSymbolPairArray = set([]) self.initCallback() @@ -281,11 +284,31 @@ class Api_Spot(OKEX_Sub_Spot_Api): 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): - for symbol in okex_all_symbol_pairs: - self.spotOrderInfo(symbol , '-1') + 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) @@ -303,14 +326,11 @@ class Api_Spot(OKEX_Sub_Spot_Api): # 连接后查询账户和委托数据 self.spotUserInfo() - for symbol_pair in okex_all_symbol_pairs: - self.spotOrderInfo(symbol_pair , '-1') - - + self.subscribeSingleSymbol("etc_usdt") for symbol in okex_all_symbol_pairs: - self.subscribeSpotTicker(symbol) - self.subscribeSpotDepth5(symbol) - self.subscribeSpotDeals(symbol) + # self.subscribeSpotTicker(symbol) + # self.subscribeSpotDepth5(symbol) + # self.subscribeSpotDeals(symbol) #Ticker数据 self.channelSymbolMap["ok_sub_spot_%s_ticker" % symbol] = symbol @@ -509,8 +529,8 @@ class Api_Spot(OKEX_Sub_Spot_Api): for symbol in info["freezed"].keys(): pos = VtPositionData() pos.gatewayName = self.gatewayName - pos.symbol = symbol - pos.vtSymbol = symbol + 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]) @@ -557,8 +577,8 @@ etc': u'0', u'act': u'0', u'eth': u'0', u'ltc': u'0', u'bcs': u'0'}, u'free': {u pos = VtPositionData() pos.gatewayName = self.gatewayName - pos.symbol = symbol - pos.vtSymbol = symbol + pos.symbol = symbol + "." + EXCHANGE_OKEX + pos.vtSymbol = symbol + "." + EXCHANGE_OKEX pos.vtPositionName = symbol pos.direction = DIRECTION_NET @@ -593,8 +613,8 @@ etc': u'0', u'act': u'0', u'eth': u'0', u'ltc': u'0', u'bcs': u'0'}, u'free': {u pos = VtPositionData() pos.gatewayName = self.gatewayName - pos.symbol = symbol - pos.vtSymbol = symbol + pos.symbol = symbol + "." + EXCHANGE_OKEX + pos.vtSymbol = symbol + "." + EXCHANGE_OKEX pos.vtPositionName = symbol pos.direction = DIRECTION_NET @@ -763,8 +783,10 @@ nel': u'ok_sub_spot_etc_usdt_order'} #---------------------------------------------------------------------- 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) @@ -810,8 +832,11 @@ nel': u'ok_sub_spot_etc_usdt_order'} ] ''' def onSpotOrder(self, data): - rawData = data['data'] + if 'error_code' in rawData.keys(): + print data + return + orderId = str(rawData['order_id']) # 尽管websocket接口的委托号返回是异步的,但经过测试是 @@ -874,7 +899,7 @@ nel': u'ok_sub_spot_etc_usdt_order'} order = VtOrderData() order.gatewayName = self.gatewayName - order.symbol = ','.join([rawData['symbol'] , EXCHANGE_OKEX]) + order.symbol = '.'.join([rawData['symbol'] , EXCHANGE_OKEX]) order.vtSymbol = order.symbol order.orderID = localNo diff --git a/vnpy/trader/gateway/zbGateway/ZB_connect.json b/vnpy/trader/gateway/zbGateway/ZB_connect.json new file mode 100644 index 00000000..5628383e --- /dev/null +++ b/vnpy/trader/gateway/zbGateway/ZB_connect.json @@ -0,0 +1,5 @@ +{ + "trace": false, + "apiKey": "你的api key", + "secretKey": "你的 secreKey" +} \ No newline at end of file diff --git a/vnpy/trader/gateway/zbGateway/__init__.py b/vnpy/trader/gateway/zbGateway/__init__.py new file mode 100644 index 00000000..9b25efff --- /dev/null +++ b/vnpy/trader/gateway/zbGateway/__init__.py @@ -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 + diff --git a/vnpy/trader/gateway/zbGateway/zbGateway.py b/vnpy/trader/gateway/zbGateway/zbGateway.py new file mode 100644 index 00000000..db7f7f16 --- /dev/null +++ b/vnpy/trader/gateway/zbGateway/zbGateway.py @@ -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 + \ No newline at end of file From 6bc54e353d02c6d23819ef21505e0460095156fa Mon Sep 17 00:00:00 2001 From: ipqhjjybj Date: Fri, 15 Dec 2017 21:11:48 +0800 Subject: [PATCH 3/4] s --- vnpy/trader/language/chinese/constant.py | 1 + vnpy/trader/language/english/constant.py | 1 + 2 files changed, 2 insertions(+) diff --git a/vnpy/trader/language/chinese/constant.py b/vnpy/trader/language/chinese/constant.py index df9abb92..06f7e342 100644 --- a/vnpy/trader/language/chinese/constant.py +++ b/vnpy/trader/language/chinese/constant.py @@ -85,6 +85,7 @@ 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 日本比特币交易所 diff --git a/vnpy/trader/language/english/constant.py b/vnpy/trader/language/english/constant.py index d0f52675..e765ab8b 100644 --- a/vnpy/trader/language/english/constant.py +++ b/vnpy/trader/language/english/constant.py @@ -81,6 +81,7 @@ 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 日本比特币交易所 From 6a927787c00c5241f19d806c65311f6d1f1688ed Mon Sep 17 00:00:00 2001 From: ipqhjjybj Date: Fri, 15 Dec 2017 21:15:05 +0800 Subject: [PATCH 4/4] add zb.com, fix one okex error ,add other platform --- vnpy/trader/language/english/constant.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vnpy/trader/language/english/constant.py b/vnpy/trader/language/english/constant.py index e765ab8b..bd8be331 100644 --- a/vnpy/trader/language/english/constant.py +++ b/vnpy/trader/language/english/constant.py @@ -81,10 +81,10 @@ EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所 EXCHANGE_HUOBI = 'HUOBI' # 火币比特币交易所 EXCHANGE_LHANG = 'LHANG' # 链行比特币交易所 -EXCHANGE_KORBIT = 'KORBIT' # KORBIT 韩国交易所 +EXCHANGE_KORBIT = 'KORBIT' # KORBIT 韩国交易所 EXCHANGE_ZB = 'ZB' # ZB 中国比特币交易所 (比特币中国) EXCHANGE_OKEX = 'OKEX' # OKEX 中国比特币交易所 (okcoin) -EXCHANGE_ZAIF = "ZAIF" # ZAIF 日本比特币交易所 +EXCHANGE_ZAIF = "ZAIF" # ZAIF 日本比特币交易所 尚未实现 EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK 日本比特币交易所 # 货币类型