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