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' # 人民币