From 926aad586aff659a20160dd0e6437688a8f159be Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 27 May 2018 22:18:56 +0800 Subject: [PATCH 001/135] =?UTF-8?q?[Mod]=E9=83=A8=E5=88=863.0=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=85=BC=E5=AE=B9=E6=80=A7=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/ctaStrategy/language/__init__.py | 2 +- vnpy/trader/app/riskManager/__init__.py | 4 ++-- vnpy/trader/app/riskManager/language/__init__.py | 2 +- vnpy/trader/gateway/futuGateway/__init__.py | 2 +- vnpy/trader/gateway/futuGateway/futuGateway.py | 12 ++++++++---- vnpy/trader/vtEngine.py | 11 ----------- 6 files changed, 13 insertions(+), 20 deletions(-) diff --git a/vnpy/trader/app/ctaStrategy/language/__init__.py b/vnpy/trader/app/ctaStrategy/language/__init__.py index a058945f..f5c4ca18 100644 --- a/vnpy/trader/app/ctaStrategy/language/__init__.py +++ b/vnpy/trader/app/ctaStrategy/language/__init__.py @@ -5,7 +5,7 @@ import os import traceback # 默认设置 -from chinese import text +from .chinese import text # 是否要使用英文 from vnpy.trader.vtGlobal import globalSetting diff --git a/vnpy/trader/app/riskManager/__init__.py b/vnpy/trader/app/riskManager/__init__.py index c58c8c61..04e66bc5 100644 --- a/vnpy/trader/app/riskManager/__init__.py +++ b/vnpy/trader/app/riskManager/__init__.py @@ -1,7 +1,7 @@ # encoding: UTF-8 -from rmEngine import RmEngine -from uiRmWidget import RmEngineManager +from .rmEngine import RmEngine +from .uiRmWidget import RmEngineManager appName = 'RiskManager' appDisplayName = u'风险管理' diff --git a/vnpy/trader/app/riskManager/language/__init__.py b/vnpy/trader/app/riskManager/language/__init__.py index a058945f..f5c4ca18 100644 --- a/vnpy/trader/app/riskManager/language/__init__.py +++ b/vnpy/trader/app/riskManager/language/__init__.py @@ -5,7 +5,7 @@ import os import traceback # 默认设置 -from chinese import text +from .chinese import text # 是否要使用英文 from vnpy.trader.vtGlobal import globalSetting diff --git a/vnpy/trader/gateway/futuGateway/__init__.py b/vnpy/trader/gateway/futuGateway/__init__.py index 0b477e7f..827c2b6c 100644 --- a/vnpy/trader/gateway/futuGateway/__init__.py +++ b/vnpy/trader/gateway/futuGateway/__init__.py @@ -1,7 +1,7 @@ # encoding: UTF-8 from vnpy.trader import vtConstant -from futuGateway import FutuGateway +from .futuGateway import FutuGateway gatewayClass = FutuGateway gatewayName = 'FUTU' diff --git a/vnpy/trader/gateway/futuGateway/futuGateway.py b/vnpy/trader/gateway/futuGateway/futuGateway.py index 63f5ba3c..d557abcb 100644 --- a/vnpy/trader/gateway/futuGateway/futuGateway.py +++ b/vnpy/trader/gateway/futuGateway/futuGateway.py @@ -12,10 +12,14 @@ from datetime import datetime from copy import copy import futuquant as ft -from futuquant.open_context import (RET_ERROR, RET_OK, PriceRegularMode, - StockQuoteHandlerBase, OrderBookHandlerBase, - USTradeOrderHandlerBase, USTradeDealHandlerBase, - HKTradeOrderHandlerBase, HKTradeDealHandlerBase) +from futuquant import (RET_ERROR, RET_OK, PriceRegularMode, + StockQuoteHandlerBase, OrderBookHandlerBase, + TradeOrderHandlerBase, TradeDealHandlerBase) + +#from futuquant.open_context import (RET_ERROR, RET_OK, PriceRegularMode, + #StockQuoteHandlerBase, OrderBookHandlerBase, + #USTradeOrderHandlerBase, USTradeDealHandlerBase, + #HKTradeOrderHandlerBase, HKTradeDealHandlerBase) from vnpy.trader.vtGateway import * from vnpy.trader.vtConstant import GATEWAYTYPE_INTERNATIONAL diff --git a/vnpy/trader/vtEngine.py b/vnpy/trader/vtEngine.py index cfd1c5f1..75584a1f 100644 --- a/vnpy/trader/vtEngine.py +++ b/vnpy/trader/vtEngine.py @@ -872,8 +872,6 @@ class PositionDetail(object): self.shortPnl = pos.positionProfit self.shortPrice = pos.price - #self.output() - #---------------------------------------------------------------------- def updateOrderReq(self, req, vtOrderID): """发单更新""" @@ -986,15 +984,6 @@ class PositionDetail(object): self.longPosFrozen = self.longYdFrozen + self.longTdFrozen self.shortPosFrozen = self.shortYdFrozen + self.shortTdFrozen - #---------------------------------------------------------------------- - def output(self): - """""" - print self.vtSymbol, '-'*30 - print 'long, total:%s, td:%s, yd:%s' %(self.longPos, self.longTd, self.longYd) - print 'long frozen, total:%s, td:%s, yd:%s' %(self.longPosFrozen, self.longTdFrozen, self.longYdFrozen) - print 'short, total:%s, td:%s, yd:%s' %(self.shortPos, self.shortTd, self.shortYd) - print 'short frozen, total:%s, td:%s, yd:%s' %(self.shortPosFrozen, self.shortTdFrozen, self.shortYdFrozen) - #---------------------------------------------------------------------- def convertOrderReq(self, req): """转换委托请求""" From 277d2b1287da8aced04d2c6aeb7226b40af72fb5 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 27 May 2018 23:58:19 +0800 Subject: [PATCH 002/135] =?UTF-8?q?[Mod]=E8=BF=90=E8=A1=8C=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=8D=87=E7=BA=A7=E5=88=B0Anaconda=205.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/VnTrader/run.py | 3 +-- vnpy/trader/uiQt.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/VnTrader/run.py b/examples/VnTrader/run.py index f09170eb..24a54f97 100644 --- a/examples/VnTrader/run.py +++ b/examples/VnTrader/run.py @@ -27,7 +27,7 @@ if system == 'Linux': from vnpy.trader.gateway import xtpGateway elif system == 'Windows': from vnpy.trader.gateway import (femasGateway, xspeedGateway, - futuGateway, secGateway) + secGateway) # 加载上层应用 from vnpy.trader.app import (riskManager, ctaStrategy, spreadTrading) @@ -54,7 +54,6 @@ def main(): me.addGateway(femasGateway) me.addGateway(xspeedGateway) me.addGateway(secGateway) - me.addGateway(futuGateway) if system == 'Linux': me.addGateway(xtpGateway) diff --git a/vnpy/trader/uiQt.py b/vnpy/trader/uiQt.py index c31e2bce..6001457e 100644 --- a/vnpy/trader/uiQt.py +++ b/vnpy/trader/uiQt.py @@ -30,7 +30,7 @@ def createQApp(): if globalSetting['darkStyle']: try: import qdarkstyle - qApp.setStyleSheet(qdarkstyle.load_stylesheet(pyside=False)) + qApp.setStyleSheet(qdarkstyle.load_stylesheet_from_environment()) except ImportError: pass From 481797cea768942e834e4405aa6fe225cb23847e Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 27 May 2018 23:58:52 +0800 Subject: [PATCH 003/135] =?UTF-8?q?[Mod]=E4=BF=AE=E6=94=B9ctaHistoryData.p?= =?UTF-8?q?y=E4=B8=AD=E7=9A=84=E6=A8=A1=E5=9D=97=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/ctaStrategy/ctaHistoryData.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/vnpy/trader/app/ctaStrategy/ctaHistoryData.py b/vnpy/trader/app/ctaStrategy/ctaHistoryData.py index 070a2cd6..aa49d82e 100644 --- a/vnpy/trader/app/ctaStrategy/ctaHistoryData.py +++ b/vnpy/trader/app/ctaStrategy/ctaHistoryData.py @@ -11,6 +11,7 @@ import csv from datetime import datetime, timedelta from time import time +from struct import unpack import pymongo @@ -73,8 +74,6 @@ def downloadEquityDailyBarts(self, symbol): #---------------------------------------------------------------------- def loadMcCsv(fileName, dbName, symbol): """将Multicharts导出的csv格式的历史数据插入到Mongo数据库中""" - import csv - start = time() print u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) @@ -107,8 +106,6 @@ def loadMcCsv(fileName, dbName, symbol): #---------------------------------------------------------------------- def loadTbCsv(fileName, dbName, symbol): """将TradeBlazer导出的csv格式的历史分钟数据插入到Mongo数据库中""" - import csv - start = time() print u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) @@ -142,8 +139,6 @@ def loadTbCsv(fileName, dbName, symbol): #---------------------------------------------------------------------- def loadTbPlusCsv(fileName, dbName, symbol): """将TB极速版导出的csv格式的历史分钟数据插入到Mongo数据库中""" - import csv - start = time() print u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) @@ -189,8 +184,6 @@ def loadTbPlusCsv(fileName, dbName, symbol): """ def loadTdxCsv(fileName, dbName, symbol): """将通达信导出的csv格式的历史分钟数据插入到Mongo数据库中""" - import csv - start = time() date_correct = "" print u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) @@ -236,8 +229,6 @@ def loadTdxCsv(fileName, dbName, symbol): """ def loadTdxLc1(fileName, dbName, symbol): """将通达信导出的lc1格式的历史分钟数据插入到Mongo数据库中""" - from struct import * - start = time() print u'开始读取通达信Lc1文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) From 9546b1100defb2ff41209deaf6930c7a703e546b Mon Sep 17 00:00:00 2001 From: cclauss Date: Tue, 29 May 2018 12:32:14 +0200 Subject: [PATCH 004/135] Modernize vnpy/api/xspeed --- vnpy/api/xspeed/__init__.py | 7 ++-- .../api/xspeed/pyscript/generate_data_type.py | 13 ++++---- .../xspeed/pyscript/generate_md_functions.py | 7 ++-- vnpy/api/xspeed/pyscript/generate_struct.py | 5 +-- .../xspeed/pyscript/generate_td_functions.py | 13 ++++---- .../xspeed/pyscript/old/generate_data_type.py | 13 ++++---- .../xspeed/pyscript/old/generate_struct.py | 3 +- vnpy/api/xspeed/test/xspeedmdtest.py | 9 ++--- vnpy/api/xspeed/test/xspeedtdtest.py | 33 ++++++++++--------- 9 files changed, 56 insertions(+), 47 deletions(-) diff --git a/vnpy/api/xspeed/__init__.py b/vnpy/api/xspeed/__init__.py index 697e5c75..0c4659fc 100644 --- a/vnpy/api/xspeed/__init__.py +++ b/vnpy/api/xspeed/__init__.py @@ -1,5 +1,6 @@ # encoding: UTF-8 -from vnxspeedmd import MdApi -from vnxspeedtd import TdApi -from xspeed_data_type import defineDict \ No newline at end of file +from __future__ import absolute_import +from .vnxspeedmd import MdApi +from .vnxspeedtd import TdApi +from .xspeed_data_type import defineDict \ No newline at end of file diff --git a/vnpy/api/xspeed/pyscript/generate_data_type.py b/vnpy/api/xspeed/pyscript/generate_data_type.py index df43f654..4595939d 100644 --- a/vnpy/api/xspeed/pyscript/generate_data_type.py +++ b/vnpy/api/xspeed/pyscript/generate_data_type.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' # C++和python类型的映射字典 @@ -48,7 +49,7 @@ def process_typedef(line): else: keyword = content[-1] keyword = keyword.replace(';\n', '') - print content, keyword + print(content, keyword) if '[' in keyword: i = keyword.index('[') @@ -97,15 +98,15 @@ def main(): py_line = process_line(line) if py_line: fpy.write(py_line.decode('gbk').encode('utf-8')) - print n + print(n) fcpp.close() fpy.close() - print u'data_type.py生成过程完成' - except Exception, e: - print u'data_type.py生成过程出错' - print e + print(u'data_type.py生成过程完成') + except Exception as e: + print(u'data_type.py生成过程出错') + print(e) if __name__ == '__main__': diff --git a/vnpy/api/xspeed/pyscript/generate_md_functions.py b/vnpy/api/xspeed/pyscript/generate_md_functions.py index 9b380665..fb28de80 100644 --- a/vnpy/api/xspeed/pyscript/generate_md_functions.py +++ b/vnpy/api/xspeed/pyscript/generate_md_functions.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' from string import join @@ -224,7 +225,7 @@ def processFunction(line): fcArgsTypeList.append(content[1]) # 参数类型列表 fcArgsValueList.append(content[3]) # 参数数据列表 - print fcArgsTypeList + print(fcArgsTypeList) if len(fcArgsTypeList)>0 and fcArgsTypeList[0] in structDict: createFunction(fcName, fcArgsTypeList, fcArgsValueList) @@ -286,10 +287,10 @@ define_count = 1 for line in fcpp: if " virtual void On" in line: - print 'callback' + print('callback') processCallBack(line) elif " virtual int" in line: - print 'function' + print('function') processFunction(line) fcpp.close() diff --git a/vnpy/api/xspeed/pyscript/generate_struct.py b/vnpy/api/xspeed/pyscript/generate_struct.py index ad0ff1c5..5f5a35fd 100644 --- a/vnpy/api/xspeed/pyscript/generate_struct.py +++ b/vnpy/api/xspeed/pyscript/generate_struct.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' from xspeed_data_type import * @@ -47,10 +48,10 @@ def main(): n = line.index('//') line = line[:n] - print no, ':', line + print(no, ':', line) content = line.split('\t') - print content + print(content) typedef = content[1] type_ = typedefDict[typedef] diff --git a/vnpy/api/xspeed/pyscript/generate_td_functions.py b/vnpy/api/xspeed/pyscript/generate_td_functions.py index 52530211..a5dbe885 100644 --- a/vnpy/api/xspeed/pyscript/generate_td_functions.py +++ b/vnpy/api/xspeed/pyscript/generate_td_functions.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' from string import join @@ -33,7 +34,7 @@ def processCallBack(line): cbArgsTypeList.append(content[0]) # 参数类型列表 cbArgsValueList.append(content[1]) # 参数数据列表 else: - print content + print(content) cbArgsTypeList.append(content[1]) # 参数类型列表 cbArgsValueList.append(content[2]+content[3]) # 参数数据列表 @@ -230,8 +231,8 @@ def processFunction(line): fcArgsTypeList.append(content[1]) # 参数类型列表 fcArgsValueList.append(content[3]) # 参数数据列表 - print line - print fcArgsTypeList + print(line) + print(fcArgsTypeList) if len(fcArgsTypeList)>0 and fcArgsTypeList[0] in structDict: createFunction(fcName, fcArgsTypeList, fcArgsValueList) @@ -263,7 +264,7 @@ def createFunction(fcName, fcArgsTypeList, fcArgsValueList): elif value == 'short': line = '\tgetShort(req, "' + key + '", &myreq.' + key + ');\n' elif value == 'float': - print line + print(line) line = '\tgetDouble(req, "' + key + '", &myreq.' + key + ');\n' ffunction.write(line) @@ -294,10 +295,10 @@ define_count = 1 for line in fcpp: if " virtual void On" in line: - print 'callback' + print('callback') processCallBack(line) elif " virtual int" in line: - print 'function' + print('function') processFunction(line) fcpp.close() diff --git a/vnpy/api/xspeed/pyscript/old/generate_data_type.py b/vnpy/api/xspeed/pyscript/old/generate_data_type.py index 477d5288..e833bad2 100644 --- a/vnpy/api/xspeed/pyscript/old/generate_data_type.py +++ b/vnpy/api/xspeed/pyscript/old/generate_data_type.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' # C++和python类型的映射字典 @@ -48,7 +49,7 @@ def process_typedef(line): else: keyword = content[-1] keyword = keyword.replace(';\n', '') - print content, keyword + print(content, keyword) if '[' in keyword: i = keyword.index('[') @@ -91,15 +92,15 @@ def main(): py_line = process_line(line) if py_line: fpy.write(py_line.decode('gbk').encode('utf-8')) - print n + print(n) fcpp.close() fpy.close() - print u'data_type.py生成过程完成' - except Exception, e: - print u'data_type.py生成过程出错' - print e + print(u'data_type.py生成过程完成') + except Exception as e: + print(u'data_type.py生成过程出错') + print(e) if __name__ == '__main__': diff --git a/vnpy/api/xspeed/pyscript/old/generate_struct.py b/vnpy/api/xspeed/pyscript/old/generate_struct.py index df86652b..60f9662b 100644 --- a/vnpy/api/xspeed/pyscript/old/generate_struct.py +++ b/vnpy/api/xspeed/pyscript/old/generate_struct.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' from ksgold_data_type import * @@ -47,7 +48,7 @@ def main(): n = line.index('//') line = line[:n] - print no, ':', line + print(no, ':', line) content = line.split('\t') diff --git a/vnpy/api/xspeed/test/xspeedmdtest.py b/vnpy/api/xspeed/test/xspeedmdtest.py index 44484002..08a6eb51 100644 --- a/vnpy/api/xspeed/test/xspeedmdtest.py +++ b/vnpy/api/xspeed/test/xspeedmdtest.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function import sys from time import sleep import datetime @@ -13,7 +14,7 @@ from vnxspeedmd import * def print_dict(d): """按照键值打印一个字典""" for key,value in d.items(): - print key + ':' + str(value) + print(key + ':' + str(value)) def parseDateTime(date,time,milli): @@ -32,8 +33,8 @@ def parseDateTime(date,time,milli): def simple_log(func): """简单装饰器用于输出函数名""" def wrapper(*args, **kw): - print "" - print str(func.__name__) + print("") + print(str(func.__name__)) return func(*args, **kw) return wrapper @@ -57,7 +58,7 @@ class TestMdApi(MdApi): @simple_log def onFrontDisconnected(self, n): """服务器断开""" - print n + print(n) #---------------------------------------------------------------------- @simple_log diff --git a/vnpy/api/xspeed/test/xspeedtdtest.py b/vnpy/api/xspeed/test/xspeedtdtest.py index 6dcedd32..9b69a2c7 100644 --- a/vnpy/api/xspeed/test/xspeedtdtest.py +++ b/vnpy/api/xspeed/test/xspeedtdtest.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function import sys from time import sleep @@ -12,15 +13,15 @@ from vnxspeedtd import * def print_dict(d): """按照键值打印一个字典""" for key,value in d.items(): - print key + ':' + str(value) + print(key + ':' + str(value)) #---------------------------------------------------------------------- def simple_log(func): """简单装饰器用于输出函数名""" def wrapper(*args, **kw): - print "" - print str(func.__name__) + print("") + print(str(func.__name__)) return func(*args, **kw) return wrapper @@ -43,7 +44,7 @@ class TestTdApi(TdApi): @simple_log def onFrontDisconnected(self, n): """服务器断开""" - print n + print(n) #---------------------------------------------------------------------- @simple_log @@ -82,7 +83,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -90,7 +91,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -105,7 +106,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -113,7 +114,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -145,7 +146,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -153,7 +154,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -161,7 +162,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -188,7 +189,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -203,7 +204,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -217,7 +218,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -225,7 +226,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- @simple_log @@ -233,7 +234,7 @@ class TestTdApi(TdApi): """查询持仓""" print_dict(data) print_dict(error) - print last + print(last) #---------------------------------------------------------------------- From fc4699bc3915d61c839810b4ce1412e23b55fe36 Mon Sep 17 00:00:00 2001 From: cclauss Date: Tue, 29 May 2018 12:47:36 +0200 Subject: [PATCH 005/135] Modernize vnpy/api/xtp --- vnpy/api/xtp/__init__.py | 7 +- vnpy/api/xtp/pyscript/generate_data_type.py | 3 +- .../api/xtp/pyscript/generate_md_functions.py | 3 +- vnpy/api/xtp/pyscript/generate_struct_oms.py | 5 +- .../api/xtp/pyscript/generate_struct_quote.py | 3 +- .../api/xtp/pyscript/generate_td_functions.py | 7 +- vnpy/api/xtp/test/quotetest.py | 17 ++-- vnpy/api/xtp/test/tradertest.py | 77 ++++++++++--------- 8 files changed, 66 insertions(+), 56 deletions(-) diff --git a/vnpy/api/xtp/__init__.py b/vnpy/api/xtp/__init__.py index 4978cf9f..792f0dcc 100644 --- a/vnpy/api/xtp/__init__.py +++ b/vnpy/api/xtp/__init__.py @@ -1,5 +1,6 @@ # encoding: UTF-8 -from vnxtpquote import QuoteApi -from vnxtptrader import TraderApi -from xtp_data_type import * \ No newline at end of file +from __future__ import absolute_import +from .vnxtpquote import QuoteApi +from .vnxtptrader import TraderApi +from .xtp_data_type import * \ No newline at end of file diff --git a/vnpy/api/xtp/pyscript/generate_data_type.py b/vnpy/api/xtp/pyscript/generate_data_type.py index e04ae76e..ba72d668 100644 --- a/vnpy/api/xtp/pyscript/generate_data_type.py +++ b/vnpy/api/xtp/pyscript/generate_data_type.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = u'用Python的交易员' # C++和python类型的映射字典 @@ -128,7 +129,7 @@ def main(): fcpp.close() fpy.close() - print u'data_type.py生成过程完成' + print(u'data_type.py生成过程完成') if __name__ == '__main__': diff --git a/vnpy/api/xtp/pyscript/generate_md_functions.py b/vnpy/api/xtp/pyscript/generate_md_functions.py index 94aa92e2..54e089be 100644 --- a/vnpy/api/xtp/pyscript/generate_md_functions.py +++ b/vnpy/api/xtp/pyscript/generate_md_functions.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' from string import join @@ -331,4 +332,4 @@ fheaderon.close() fheaderfunction.close() fwrap.close() -print 'md functions done' \ No newline at end of file +print('md functions done') \ No newline at end of file diff --git a/vnpy/api/xtp/pyscript/generate_struct_oms.py b/vnpy/api/xtp/pyscript/generate_struct_oms.py index b230050c..dbab14ef 100644 --- a/vnpy/api/xtp/pyscript/generate_struct_oms.py +++ b/vnpy/api/xtp/pyscript/generate_struct_oms.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' from xtp_data_type import * @@ -27,7 +28,7 @@ def main(): fpy.write('\n') for row, line in enumerate(fcpp): - print row + print(row) # 结构体申明注释 if '///' in line and '\t' not in line: py_line = '#' + line[3:] @@ -38,7 +39,7 @@ def main(): # 结构体申明 elif 'struct ' in line: - print line + print(line) content = line.split(' ') name = content[1].replace('\n','') name = name.replace('\r', '') diff --git a/vnpy/api/xtp/pyscript/generate_struct_quote.py b/vnpy/api/xtp/pyscript/generate_struct_quote.py index 0f4e7833..b8ade4d7 100644 --- a/vnpy/api/xtp/pyscript/generate_struct_quote.py +++ b/vnpy/api/xtp/pyscript/generate_struct_quote.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' from xtp_data_type import * @@ -38,7 +39,7 @@ def main(): lcpp = replaceTabs(fcpp) for n, line in enumerate(lcpp): - print n + print(n) # 结构体申明注释 if '///' in line and '\t' not in line: py_line = '#' + line[3:] diff --git a/vnpy/api/xtp/pyscript/generate_td_functions.py b/vnpy/api/xtp/pyscript/generate_td_functions.py index ede47e4a..7d4081f3 100644 --- a/vnpy/api/xtp/pyscript/generate_td_functions.py +++ b/vnpy/api/xtp/pyscript/generate_td_functions.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function __author__ = 'CHENXY' from string import join @@ -194,7 +195,7 @@ def createProcess(cbName, cbArgsTypeList, cbArgsValueList): fprocess.write(" PyLock lock;\n") onArgsList = [] - print cbName, cbArgsTypeList + print(cbName, cbArgsTypeList) for i, type_ in enumerate(cbArgsTypeList): if 'XTPRI' in type_: @@ -258,7 +259,7 @@ def processFunction(line): fcArgs = fcArgs.replace(')', '') fcArgsList = fcArgs.split(', ') # 将每个参数转化为列表 - print fcArgsList + print(fcArgsList) fcArgsTypeList = [] fcArgsValueList = [] @@ -352,4 +353,4 @@ fheaderon.close() fheaderfunction.close() fwrap.close() -print 'td functions done' \ No newline at end of file +print('td functions done') \ No newline at end of file diff --git a/vnpy/api/xtp/test/quotetest.py b/vnpy/api/xtp/test/quotetest.py index e353940b..deec29e8 100644 --- a/vnpy/api/xtp/test/quotetest.py +++ b/vnpy/api/xtp/test/quotetest.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function import os from time import sleep @@ -8,11 +9,11 @@ from vnxtpquote import * #---------------------------------------------------------------------- def printDict(d): """""" - print '-' * 50 + print('-' * 50) l = d.keys() l.sort() for k in l: - print k, d[k] + print(k, d[k]) @@ -28,32 +29,32 @@ class TestApi(QuoteApi): #---------------------------------------------------------------------- def onDisconnected(self, reason): """""" - print 'disconnect', reason + print('disconnect', reason) #---------------------------------------------------------------------- def onError(self, data): """""" - print 'error' + print('error') printDict(data) #---------------------------------------------------------------------- def onSubMarketData(self, data, error, last): """""" - print 'sub market data' + print('sub market data') printDict(data) printDict(error) #---------------------------------------------------------------------- def onUnSubMarketData(self, data, error, last): """""" - print 'unsub market data' + print('unsub market data') printDict(data) printDict(error) #---------------------------------------------------------------------- def onMarketData(self, data): """""" - print 'new market data' + print('new market data') printDict(data) #---------------------------------------------------------------------- @@ -75,7 +76,7 @@ if __name__ == '__main__': # 登录 n = api.login(ip, port, user, password, 1) - print 'login result', n + print('login result', n) # 订阅行情 api.subscribeMarketData('000001', 2) diff --git a/vnpy/api/xtp/test/tradertest.py b/vnpy/api/xtp/test/tradertest.py index 86f012d3..843e3b8f 100644 --- a/vnpy/api/xtp/test/tradertest.py +++ b/vnpy/api/xtp/test/tradertest.py @@ -1,20 +1,23 @@ # encoding: UTF-8 +from __future__ import print_function import os from time import sleep +from six.moves import input + from vnxtptrader import * + #---------------------------------------------------------------------- def printDict(d): """""" - print '-' * 50 + print('-' * 50) l = d.keys() l.sort() for k in l: - print k, d[k] - - + print(k, d[k]) + ######################################################################## class TestApi(TraderApi): @@ -28,79 +31,79 @@ class TestApi(TraderApi): #---------------------------------------------------------------------- def onDisconnected(self, reason): """""" - print '-' * 30 - print 'onDisconnected' - print reason + print('-' * 30) + print('onDisconnected') + print(reason) #---------------------------------------------------------------------- def onError(self, data): """""" - print '-' * 30 - print 'onError' + print('-' * 30) + print('onError') printDict(data) #---------------------------------------------------------------------- def onOrderEvent(self, data, error): """""" - print '-' * 30 - print 'onOrderEvent' + print('-' * 30) + print('onOrderEvent') printDict(data) printDict(error) #---------------------------------------------------------------------- def onTradeEvent(self, data): """""" - print '-' * 30 - print 'onTradeEvent' + print('-' * 30) + print('onTradeEvent') printDict(data) #---------------------------------------------------------------------- def onCancelOrderError(self, data, error): """""" - print '-' * 30 - print 'onCancelOrderError' + print('-' * 30) + print('onCancelOrderError') printDict(data) printDict(error) #---------------------------------------------------------------------- def onQueryOrder(self, data, error, reqid, last): """""" - print '-' * 30 - print 'onQueryOrder' + print('-' * 30) + print('onQueryOrder') printDict(data) printDict(error) - print reqid - print last + print(reqid) + print(last) #---------------------------------------------------------------------- def onQueryTrade(self, data, error, reqid, last): """""" - print '-' * 30 - print 'onQueryTrade' + print('-' * 30) + print('onQueryTrade') printDict(data) printDict(error) - print reqid - print last + print(reqid) + print(last) #---------------------------------------------------------------------- def onQueryPosition(self, data, error, reqid, last): """""" - print '-' * 30 - print 'onQueryPosition' + print('-' * 30) + print('onQueryPosition') printDict(data) printDict(error) - print reqid - print last + print(reqid) + print(last) #---------------------------------------------------------------------- def onQueryAsset(self, data, error, reqid, last): """""" - print '-' * 30 - print 'onQueryAsset' + print('-' * 30) + print('onQueryAsset') printDict(data) printDict(error) - print reqid - print last + print(reqid) + print(last) @@ -121,12 +124,12 @@ if __name__ == '__main__': # 登录 session = api.login(ip, port, user, password, 1) - print 'login result', session + print('login result', session) # 调用同步函数查询一些信息 - print 'trading day is:', api.getTradingDay() - print 'api version is:', api.getApiVersion() - print 'last error is:', api.getApiLastError() + print('trading day is:', api.getTradingDay()) + print('api version is:', api.getApiVersion()) + print('last error is:', api.getApiLastError()) # 查询资产 sleep(2) @@ -166,7 +169,7 @@ if __name__ == '__main__': # 登出 sleep(5) - print 'logout:', api.logout(session) + print('logout:', api.logout(session)) # 阻塞 - raw_input() + input() From 6f9167303710a6a501da0153b95566836701a7c0 Mon Sep 17 00:00:00 2001 From: cclauss Date: Tue, 29 May 2018 13:21:14 +0200 Subject: [PATCH 006/135] Modernize vnpy/api/shzd --- vnpy/api/shzd/__init__.py | 3 ++- vnpy/api/shzd/test/test.py | 17 +++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/vnpy/api/shzd/__init__.py b/vnpy/api/shzd/__init__.py index 7a5e03d5..f76a6d69 100644 --- a/vnpy/api/shzd/__init__.py +++ b/vnpy/api/shzd/__init__.py @@ -1,3 +1,4 @@ # encoding: UTF-8 -from vnshzd import ShzdApi \ No newline at end of file +from __future__ import absolute_import +from .vnshzd import ShzdApi \ No newline at end of file diff --git a/vnpy/api/shzd/test/test.py b/vnpy/api/shzd/test/test.py index 13bb2c5f..a958d450 100644 --- a/vnpy/api/shzd/test/test.py +++ b/vnpy/api/shzd/test/test.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function from time import sleep from vnshzd import * @@ -10,7 +11,7 @@ def printDict(d): l = d.keys() l.sort() for key in l: - print '%s:%s' %(key, d[key]) + print('%s:%s' %(key, d[key])) ######################################################################## @@ -26,19 +27,19 @@ class TestApi(ShzdApi): #---------------------------------------------------------------------- def onReceiveErrorInfo(self, errcode, errmsg): """""" - print '-' * 50 - print 'errorcode %s, error msg %s' %(errcode, errmsg) + print('-' * 50) + print('errorcode %s, error msg %s' %(errcode, errmsg)) #---------------------------------------------------------------------- def onReceiveMarketInfo(self, data): """""" - print '-' * 50 + print('-' * 50) printDict(data) #---------------------------------------------------------------------- def onReceiveTradeInfo(self, data): """""" - print '-' * 50 + print('-' * 50) printDict(data) if __name__ == '__main__': @@ -49,8 +50,8 @@ if __name__ == '__main__': api.initShZdServer() # 注册前置机地址 - print api.registerFront('222.73.119.230', 7003) - print api.registerMarket('222.73.119.230', 9003) + print(api.registerFront('222.73.119.230', 7003)) + print(api.registerMarket('222.73.119.230', 9003)) # 登录 sleep(1) @@ -68,7 +69,7 @@ if __name__ == '__main__': data['201'] = '+' #data['307'] = "CME,6J1609" data['307'] = 'ICE,WBS1611' - print data + print(data) api.shzdSendInfoToMarket(data) # # 查询合约 From 0594e6dc46fee9849aa272684ac5422f9de261e1 Mon Sep 17 00:00:00 2001 From: cclauss Date: Tue, 29 May 2018 13:43:06 +0200 Subject: [PATCH 007/135] Modernize vnpy/api/sec --- vnpy/api/sec/__init__.py | 7 +- vnpy/api/sec/pyscript/generate_data_type.py | 3 +- .../api/sec/pyscript/generate_md_functions.py | 5 +- vnpy/api/sec/pyscript/generate_struct.py | 3 +- .../api/sec/pyscript/generate_td_functions.py | 5 +- vnpy/api/sec/test/md_test.py | 86 ++-- vnpy/api/sec/test/td_test.py | 376 +++++++++--------- 7 files changed, 247 insertions(+), 238 deletions(-) diff --git a/vnpy/api/sec/__init__.py b/vnpy/api/sec/__init__.py index 58843bbd..98f521e9 100644 --- a/vnpy/api/sec/__init__.py +++ b/vnpy/api/sec/__init__.py @@ -1,5 +1,6 @@ # encoding: UTF-8 -from vnsecmd import MdApi -from vnsectd import TdApi -import sec_data_type as DATA_TYPE \ No newline at end of file +from __future__ import absolute_import +from .vnsecmd import MdApi +from .vnsectd import TdApi +from . import sec_data_type as DATA_TYPE \ No newline at end of file diff --git a/vnpy/api/sec/pyscript/generate_data_type.py b/vnpy/api/sec/pyscript/generate_data_type.py index 9be99c04..1d4ffc4c 100644 --- a/vnpy/api/sec/pyscript/generate_data_type.py +++ b/vnpy/api/sec/pyscript/generate_data_type.py @@ -2,6 +2,7 @@ # C++和Python类型映射 +from __future__ import print_function type_map = { 'int': 'int', 'long': 'long', @@ -97,7 +98,7 @@ def main(cpp_filename, py_filename): cpp_f.close() py_f.close() - print u'data_type处理完成' + print(u'data_type处理完成') if __name__ == '__main__': diff --git a/vnpy/api/sec/pyscript/generate_md_functions.py b/vnpy/api/sec/pyscript/generate_md_functions.py index b11f90d7..87d778ad 100644 --- a/vnpy/api/sec/pyscript/generate_md_functions.py +++ b/vnpy/api/sec/pyscript/generate_md_functions.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function from generate_data_type import pre_process import sec_struct @@ -69,7 +70,7 @@ def process_function(cpp_line): args_type_list.append(l[0]) args_name_list.append(l[1]) - print args_type_list + print(args_type_list) if args_type_list and args_type_list[0] in STRUCT_DICT: create_function(fc_name, args_type_list, args_name_list) @@ -298,4 +299,4 @@ header_process_f.close() header_on_f.close() header_function_f.close() -print API_NAME + u'处理完成' \ No newline at end of file +print(API_NAME + u'处理完成') \ No newline at end of file diff --git a/vnpy/api/sec/pyscript/generate_struct.py b/vnpy/api/sec/pyscript/generate_struct.py index 9048ccb8..8bee3e6a 100644 --- a/vnpy/api/sec/pyscript/generate_struct.py +++ b/vnpy/api/sec/pyscript/generate_struct.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function from generate_data_type import pre_process import sec_data_type @@ -62,7 +63,7 @@ def main(cpp_filename, py_filename): cpp_f.close() py_f.close() - print u'struct处理完成' + print(u'struct处理完成') if __name__ == '__main__': diff --git a/vnpy/api/sec/pyscript/generate_td_functions.py b/vnpy/api/sec/pyscript/generate_td_functions.py index 1a574fdb..c3bc68dd 100644 --- a/vnpy/api/sec/pyscript/generate_td_functions.py +++ b/vnpy/api/sec/pyscript/generate_td_functions.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function from generate_data_type import pre_process import sec_struct @@ -69,7 +70,7 @@ def process_function(cpp_line): args_type_list.append(l[0]) args_name_list.append(l[1]) - print args_type_list + print(args_type_list) if args_type_list and args_type_list[0] in STRUCT_DICT: create_function(fc_name, args_type_list, args_name_list) @@ -298,4 +299,4 @@ header_process_f.close() header_on_f.close() header_function_f.close() -print API_NAME + u'处理完成' \ No newline at end of file +print(API_NAME + u'处理完成') \ No newline at end of file diff --git a/vnpy/api/sec/test/md_test.py b/vnpy/api/sec/test/md_test.py index cd2ab5a4..c304e392 100644 --- a/vnpy/api/sec/test/md_test.py +++ b/vnpy/api/sec/test/md_test.py @@ -1,7 +1,9 @@ # encoding: UTF-8 +from __future__ import print_function import sys +from six.moves import input from vnsecmd import MdApi @@ -9,7 +11,7 @@ from vnsecmd import MdApi def print_dict(d): """输出字典""" for k, v in d.items(): - print '%s:%s' %(k, v) + print('%s:%s' %(k, v)) ######################################################################## @@ -24,119 +26,119 @@ class TestMdApi(MdApi): #---------------------------------------------------------------------- def onFrontConnected(self): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onFrontDisconnected(self, reason): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRtnNotice(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspError(self, error): """""" - print sys._getframe().f_code.co_name - print locals() - print dict(error) + print(sys._getframe().f_code.co_name) + print(locals()) + print(dict(error)) #---------------------------------------------------------------------- def onRspStockUserLogin(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockUserLogout(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPUserLogin(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) print_dict(data) print_dict(error) #---------------------------------------------------------------------- def onRspSOPUserLogout(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLUserLogin(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLUserLogout(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockSubMarketData(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockUnSubMarketData(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPSubMarketData(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPUnSubMarketData(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onStockMarketData(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onSOPMarketData(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockAvailableQuot(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSopAvailableQuot(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspUserMDPasswordUpdate(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) if __name__ == '__main__': @@ -157,5 +159,5 @@ if __name__ == '__main__': } api.reqSOPUserLogin(req) - - raw_input() \ No newline at end of file + + input() diff --git a/vnpy/api/sec/test/td_test.py b/vnpy/api/sec/test/td_test.py index 742845ce..b12474b3 100644 --- a/vnpy/api/sec/test/td_test.py +++ b/vnpy/api/sec/test/td_test.py @@ -1,7 +1,9 @@ # encoding: UTF-8 +from __future__ import print_function import sys +from six.moves import input from vnsectd import TdApi @@ -9,7 +11,7 @@ from vnsectd import TdApi def print_dict(d): """输出字典""" for k, v in d.items(): - print '%s:%s' %(k, v) + print('%s:%s' %(k, v)) ######################################################################## @@ -24,556 +26,556 @@ class TestTdApi(TdApi): #---------------------------------------------------------------------- def onFrontConnected(self, ): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onFrontDisconnected(self, reason): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRtnNotice(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspError(self, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockUserLogin(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockUserLogout(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockUserPasswordUpdate(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockEntrustOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockWithdrawOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryEntrustOrder(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryRealTimeTrade(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQrySerialTrade(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryPosition(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryCapitalAccountInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryAccountInfo(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryShareholderInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockTransferFunds(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockEntrustBatchOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockWithdrawBatchOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockCalcAbleEntrustQty(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockCalcAblePurchaseETFQty(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryFreezeFundsDetail(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryFreezeStockDetail(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryTransferStockDetail(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryTransferFundsDetail(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryStockInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryStockStaticInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspStockQryTradeTime(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onStockEntrustOrderRtn(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onStockTradeRtn(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onStockWithdrawOrderRtn(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPUserLogin(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) print_dict(data) print_dict(error) #---------------------------------------------------------------------- def onRspSOPUserLogout(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPUserPasswordUpdate(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPEntrustOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPGroupSplit(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryGroupPosition(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPLockOUnLockStock(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPWithdrawOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryEntrustOrder(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQrySerialTrade(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryPosition(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryCollateralPosition(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryCapitalAccountInfo(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryAccountInfo(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryShareholderInfo(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPCalcAbleEntrustQty(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryAbleLockStock(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryContactInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPExectueOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryExecAssiInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryTradeTime(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryExchangeInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryCommission(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryDeposit(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspSOPQryContractObjectInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onSOPEntrustOrderRtn(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onSOPTradeRtn(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onSOPWithdrawOrderRtn(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLUserLogin(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLUserLogout(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryAbleFinInfo(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryAbleSloInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLTransferCollateral(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLDirectRepayment(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLRepayStockTransfer(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLEntrustCrdtOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLEntrustOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLCalcAbleEntrustCrdtQty(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryCrdtFunds(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryCrdtContract(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryCrdtConChangeInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLTransferFunds(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryAccountInfo(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryCapitalAccountInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryShareholderInfo(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryPosition(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryEntrustOrder(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQrySerialTrade(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryRealTimeTrade(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryFreezeFundsDetail(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryFreezeStockDetail(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryTransferFundsDetail(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLWithdrawOrder(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQrySystemTime(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryTransferredContract(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLDesirableFundsOut(self, data, error): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryGuaranteedContract(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onRspFASLQryUnderlyingContract(self, data, error, flag): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onFASLEntrustOrderRtn(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onFASLTradeRtn(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def onFASLWithdrawOrderRtn(self, data): """""" - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) @@ -595,5 +597,5 @@ if __name__ == '__main__': } api.reqSOPUserLogin(req) - - raw_input() \ No newline at end of file + + input() From 7a4432e02eb40a986f50b6456d512cba7513f4ee Mon Sep 17 00:00:00 2001 From: cclauss Date: Thu, 17 May 2018 08:33:04 +0200 Subject: [PATCH 008/135] Modernize vnpy/api/h* and vnpy/api/i* --- vnpy/api/huobi/__init__.py | 3 +- vnpy/api/huobi/testmd.py | 3 +- vnpy/api/huobi/testtd.py | 12 +- vnpy/api/huobi/vnhuobi.py | 37 +++--- vnpy/api/ib/__init__.py | 3 +- vnpy/api/ib/test/test.py | 237 +++++++++++++++++++------------------ 6 files changed, 152 insertions(+), 143 deletions(-) diff --git a/vnpy/api/huobi/__init__.py b/vnpy/api/huobi/__init__.py index 66fc387d..4f930268 100644 --- a/vnpy/api/huobi/__init__.py +++ b/vnpy/api/huobi/__init__.py @@ -1,3 +1,4 @@ # encoding: UTF-8 -from vnhuobi import TradeApi, DataApi \ No newline at end of file +from __future__ import absolute_import +from .vnhuobi import TradeApi, DataApi \ No newline at end of file diff --git a/vnpy/api/huobi/testmd.py b/vnpy/api/huobi/testmd.py index 89b320fc..ec44543a 100644 --- a/vnpy/api/huobi/testmd.py +++ b/vnpy/api/huobi/testmd.py @@ -5,7 +5,8 @@ #import zlib #import time -from vnhuobi import DataApi +from __future__ import absolute_import +from .vnhuobi import DataApi #if __name__ == '__main__': #while(1): diff --git a/vnpy/api/huobi/testtd.py b/vnpy/api/huobi/testtd.py index b3b1f8cd..8ce61ce1 100644 --- a/vnpy/api/huobi/testtd.py +++ b/vnpy/api/huobi/testtd.py @@ -1,6 +1,8 @@ # encoding: utf-8 -from vnhuobi import * +from __future__ import print_function +from __future__ import absolute_import +from .vnhuobi import * #---------------------------------------------------------------------- def testTrade(): @@ -15,9 +17,9 @@ def testTrade(): api.start() # 查询 - print api.getSymbols() - print api.getCurrencys() - print api.getTimestamp() + print(api.getSymbols()) + print(api.getCurrencys()) + print(api.getTimestamp()) #accountid = '' @@ -27,7 +29,7 @@ def testTrade(): #api.getAccountBalance(accountid) #api.getOrders(symbol, 'pre-submitted,submitted,partial-filled,partial-canceled,filled,canceled') #api.getOrders(symbol, 'filled') - print api.getMatchResults(symbol) + print(api.getMatchResults(symbol)) #api.getOrder('2440401255') #api.getMatchResult('2440401255') diff --git a/vnpy/api/huobi/vnhuobi.py b/vnpy/api/huobi/vnhuobi.py index 0f5f8a5a..ca9db9a3 100644 --- a/vnpy/api/huobi/vnhuobi.py +++ b/vnpy/api/huobi/vnhuobi.py @@ -1,5 +1,6 @@ # encoding: utf-8 +from __future__ import print_function import urllib import hmac import base64 @@ -419,71 +420,71 @@ class TradeApi(object): #---------------------------------------------------------------------- def onError(self, msg, reqid): """错误回调""" - print msg, reqid + print(msg, reqid) #---------------------------------------------------------------------- def onGetSymbols(self, data, reqid): """查询代码回调""" #print reqid, data for d in data: - print d + print(d) #---------------------------------------------------------------------- def onGetCurrencys(self, data, reqid): """查询货币回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onGetTimestamp(self, data, reqid): """查询时间回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onGetAccounts(self, data, reqid): """查询账户回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onGetAccountBalance(self, data, reqid): """查询余额回调""" - print reqid, data + print(reqid, data) for d in data['data']['list']: - print d + print(d) #---------------------------------------------------------------------- def onGetOrders(self, data, reqid): """查询委托回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onGetMatchResults(self, data, reqid): """查询成交回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onGetOrder(self, data, reqid): """查询单一委托回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onGetMatchResult(self, data, reqid): """查询单一成交回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onPlaceOrder(self, data, reqid): """委托回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onCancelOrder(self, data, reqid): """撤单回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onBatchCancel(self, data, reqid): """批量撤单回调""" - print reqid, data + print(reqid, data) ######################################################################## @@ -644,7 +645,7 @@ class DataApi(object): #---------------------------------------------------------------------- def onError(self, msg): """错误推送""" - print msg + print(msg) #---------------------------------------------------------------------- def onData(self, data): @@ -664,14 +665,14 @@ class DataApi(object): #---------------------------------------------------------------------- def onMarketDepth(self, data): """行情深度推送 """ - print data + print(data) #---------------------------------------------------------------------- def onTradeDetail(self, data): """成交细节推送""" - print data + print(data) #---------------------------------------------------------------------- def onMarketDetail(self, data): """市场细节推送""" - print data \ No newline at end of file + print(data) \ No newline at end of file diff --git a/vnpy/api/ib/__init__.py b/vnpy/api/ib/__init__.py index 37b3bd13..75445132 100644 --- a/vnpy/api/ib/__init__.py +++ b/vnpy/api/ib/__init__.py @@ -1,3 +1,4 @@ # encoding: UTF-8 -from vnib import * \ No newline at end of file +from __future__ import absolute_import +from .vnib import * \ No newline at end of file diff --git a/vnpy/api/ib/test/test.py b/vnpy/api/ib/test/test.py index 6f4ab062..acb39a12 100644 --- a/vnpy/api/ib/test/test.py +++ b/vnpy/api/ib/test/test.py @@ -1,15 +1,18 @@ # encoding: UTF-8 +from __future__ import print_function import sys from time import sleep +from six.moves import input + from vnib import IbApi ######################################################################## class TestApi(IbApi): - print sys._getframe().f_code.co_name + print(sys._getframe().f_code.co_name) #---------------------------------------------------------------------- def __init__(self): @@ -18,283 +21,283 @@ class TestApi(IbApi): #---------------------------------------------------------------------- def nextValidId(self, orderId): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def currentTime(self, time): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def connectAck(self): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def error(self, id_, errorCode, errorString): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def accountSummary(self, reqId, account, tag, value, curency): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def accountSummaryEnd(self, reqId): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def tickPrice(self, tickerId, field, price, canAutoExecute): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def tickSize(self, tickerId, field, size): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def tickOptionComputation(self, tickerId, tickType, impliedVol, delta, optPrice, pvDividend, gamma, vega, theta, undPrice): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def tickGeneric(self, tickerId, tickType, value): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def tickString(self, tickerId, tickType, value): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def tickEFP(self, tickerId, tickType, basisPoints, formattedBasisPoints, totalDividends, holdDays, futureLastTradeDate, dividendImpact, dividendsToLastTradeDate): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def orderStatus(self, orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def openOrder(self, orderId, contract, order, orderState): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def openOrderEnd(self): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def winError(self, str_, lastError): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def connectionClosed(self): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def updateAccountValue(self, key, val, currency, accountName): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def updatePortfolio(self, contract, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accountName): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def updateAccountTime(self, timeStamp): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def accountDownloadEnd(self, accountName): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def contractDetails(self, reqId, contractDetails): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def bondContractDetails(self, reqId, contractDetails): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def contractDetailsEnd(self, reqId): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def execDetails(self, reqId, contract, execution): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def execDetailsEnd(self, reqId): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def updateMktDepth(self, id_, position, operation, side, price, size): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def updateMktDepthL2(self, id_, position, marketMaker, operation, side, price, size): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def updateNewsBulletin(self, msgId, msgType, newsMessage, originExch): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def managedAccounts(self, accountsList): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def receiveFA(self, pFaDataType, cxml): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def historicalData(self, reqId, date, open_, high, low, close, volume, barCount, WAP, hasGaps): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def scannerParameters(self, xml): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def scannerData(self, reqId, rank, contractDetails, distance, benchmark, projection, legsStr): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def scannerDataEnd(self, reqId): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def realtimeBar(self, reqId, time, open_, high, low, close, volume, wap, count): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def fundamentalData(self, reqId, data): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def deltaNeutralValidation(self, reqId, underComp): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def tickSnapshotEnd(self, reqId): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def marketDataType(self, reqId, marketDataType): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def commissionReport(self, commissionReport): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def position(self, account, contract, position, avgCost): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def positionEnd(self): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def verifyMessageAPI(self, apiData): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def verifyCompleted(self, isSuccessful, errorText): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def displayGroupList(self, reqId, groups): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def displayGroupUpdated(self, reqId, contractInfo): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def verifyAndAuthMessageAPI(self, apiData, xyzChallange): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def verifyAndAuthCompleted(self, isSuccessful, errorText): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def positionMulti(self, reqId, account, modelCode, contract, pos, avgCost): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def positionMultiEnd(self, reqId): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def accountUpdateMulti(self, reqId, account, modelCode, key, value, currency): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def accountUpdateMultiEnd(self, reqId): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def securityDefinitionOptionalParameter(self, reqId, exchange, underlyingConId, tradingClass, multiplier, expirations, strikes): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def securityDefinitionOptionalParameterEnd(self, reqId): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) #---------------------------------------------------------------------- def softDollarTiers(self, reqId, tiers): - print sys._getframe().f_code.co_name - print locals() + print(sys._getframe().f_code.co_name) + print(locals()) @@ -302,14 +305,14 @@ if __name__ == '__main__': api = TestApi() n = api.eConnect('127.0.0.1', 7497, 123, False) - print n + print(n) #t = api.TwsConnectionTime() #print t # sleep(1) - print 'req time' + print('req time') api.reqCurrentTime() # @@ -319,6 +322,6 @@ if __name__ == '__main__': #print 'disconnect' #api.eDisconnect() - raw_input() + input() + - \ No newline at end of file From 7a13aec4ecec431b7a86f6fe3a9e90d147912e91 Mon Sep 17 00:00:00 2001 From: cclauss Date: Mon, 14 May 2018 15:47:19 +0200 Subject: [PATCH 009/135] Modernize vnpy/trader/app --- vnpy/trader/app/ctaStrategy/ctaBacktesting.py | 7 ++-- vnpy/trader/app/ctaStrategy/ctaHistoryData.py | 39 ++++++++++--------- .../app/ctaStrategy/language/__init__.py | 3 +- .../app/ctaStrategy/strategy/__init__.py | 1 + vnpy/trader/app/dataRecorder/__init__.py | 5 ++- .../app/dataRecorder/language/__init__.py | 5 ++- vnpy/trader/app/jaqsService/__init__.py | 5 ++- vnpy/trader/app/jaqsService/jrpc_server.py | 23 +++++------ vnpy/trader/app/jaqsService/jsEngine.py | 3 +- vnpy/trader/app/jaqsService/service.py | 10 +++-- vnpy/trader/app/optionMaster/omDate.py | 10 +++-- .../app/optionMaster/strategy/__init__.py | 1 + .../app/optionMaster/uiOmManualTrader.py | 3 +- vnpy/trader/app/optionMaster/uiOmWidget.py | 4 +- vnpy/trader/app/riskManager/__init__.py | 3 +- .../app/riskManager/language/__init__.py | 3 +- vnpy/trader/app/spreadTrading/uiStWidget.py | 6 ++- 17 files changed, 76 insertions(+), 55 deletions(-) diff --git a/vnpy/trader/app/ctaStrategy/ctaBacktesting.py b/vnpy/trader/app/ctaStrategy/ctaBacktesting.py index c0bb5c6d..8adffc5d 100644 --- a/vnpy/trader/app/ctaStrategy/ctaBacktesting.py +++ b/vnpy/trader/app/ctaStrategy/ctaBacktesting.py @@ -5,6 +5,7 @@ 可以使用和实盘相同的代码进行回测。 ''' from __future__ import division +from __future__ import print_function from datetime import datetime, timedelta from collections import OrderedDict @@ -112,7 +113,7 @@ class BacktestingEngine(object): #---------------------------------------------------------------------- def output(self, content): """输出内容""" - print str(datetime.now()) + "\t" + content + print(str(datetime.now()) + "\t" + content) #------------------------------------------------ # 参数设置相关 @@ -1213,11 +1214,11 @@ class OptimizationSetting(object): return if end < start: - print u'参数起始点必须不大于终止点' + print(u'参数起始点必须不大于终止点') return if step <= 0: - print u'参数布进必须大于0' + print(u'参数布进必须大于0') return l = [] diff --git a/vnpy/trader/app/ctaStrategy/ctaHistoryData.py b/vnpy/trader/app/ctaStrategy/ctaHistoryData.py index aa49d82e..362a66e2 100644 --- a/vnpy/trader/app/ctaStrategy/ctaHistoryData.py +++ b/vnpy/trader/app/ctaStrategy/ctaHistoryData.py @@ -7,6 +7,7 @@ 3. 将交易开拓者导出的历史数据载入到MongoDB中的函数 4. 将OKEX下载的历史数据载入到MongoDB中的函数 """ +from __future__ import print_function import csv from datetime import datetime, timedelta @@ -26,7 +27,7 @@ def downloadEquityDailyBarts(self, symbol): """ 下载股票的日行情,symbol是股票代码 """ - print u'开始下载%s日行情' %symbol + print(u'开始下载%s日行情' %symbol) # 查询数据库中已有数据的最后日期 cl = self.dbClient[DAILY_DB_NAME][symbol] @@ -62,20 +63,20 @@ def downloadEquityDailyBarts(self, symbol): bar.datetime = datetime.strptime(bar.date, '%Y%m%d') bar.volume = d.get('volume') except KeyError: - print d + print(d) flt = {'datetime': bar.datetime} self.dbClient[DAILY_DB_NAME][symbol].update_one(flt, {'$set':bar.__dict__}, upsert=True) - print u'%s下载完成' %symbol + print(u'%s下载完成' %symbol) else: - print u'找不到合约%s' %symbol + print(u'找不到合约%s' %symbol) #---------------------------------------------------------------------- def loadMcCsv(fileName, dbName, symbol): """将Multicharts导出的csv格式的历史数据插入到Mongo数据库中""" start = time() - print u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) + print(u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol)) # 锁定集合,并创建索引 client = pymongo.MongoClient(globalSetting['mongoHost'], globalSetting['mongoPort']) @@ -99,15 +100,15 @@ def loadMcCsv(fileName, dbName, symbol): flt = {'datetime': bar.datetime} collection.update_one(flt, {'$set':bar.__dict__}, upsert=True) - print bar.date, bar.time + print(bar.date, bar.time) - print u'插入完毕,耗时:%s' % (time()-start) + print(u'插入完毕,耗时:%s' % (time()-start)) #---------------------------------------------------------------------- def loadTbCsv(fileName, dbName, symbol): """将TradeBlazer导出的csv格式的历史分钟数据插入到Mongo数据库中""" start = time() - print u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) + print(u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol)) # 锁定集合,并创建索引 client = pymongo.MongoClient(globalSetting['mongoHost'], globalSetting['mongoPort']) @@ -132,15 +133,15 @@ def loadTbCsv(fileName, dbName, symbol): flt = {'datetime': bar.datetime} collection.update_one(flt, {'$set':bar.__dict__}, upsert=True) - print bar.date, bar.time + print(bar.date, bar.time) - print u'插入完毕,耗时:%s' % (time()-start) + print(u'插入完毕,耗时:%s' % (time()-start)) #---------------------------------------------------------------------- def loadTbPlusCsv(fileName, dbName, symbol): """将TB极速版导出的csv格式的历史分钟数据插入到Mongo数据库中""" start = time() - print u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) + print(u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol)) # 锁定集合,并创建索引 client = pymongo.MongoClient(globalSetting['mongoHost'], globalSetting['mongoPort']) @@ -167,9 +168,9 @@ def loadTbPlusCsv(fileName, dbName, symbol): bar.openInterest = d[7] flt = {'datetime': bar.datetime} collection.update_one(flt, {'$set':bar.__dict__}, upsert=True) - print bar.date, bar.time + print(bar.date, bar.time) - print u'插入完毕,耗时:%s' % (time()-start) + print(u'插入完毕,耗时:%s' % (time()-start)) #---------------------------------------------------------------------- """ @@ -186,7 +187,7 @@ def loadTdxCsv(fileName, dbName, symbol): """将通达信导出的csv格式的历史分钟数据插入到Mongo数据库中""" start = time() date_correct = "" - print u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) + print(u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol)) # 锁定集合,并创建索引 client = pymongo.MongoClient(globalSetting['mongoHost'], globalSetting['mongoPort']) @@ -218,7 +219,7 @@ def loadTdxCsv(fileName, dbName, symbol): flt = {'datetime': bar.datetime} collection.update_one(flt, {'$set':bar.__dict__}, upsert=True) - print u'插入完毕,耗时:%s' % (time()-start) + print(u'插入完毕,耗时:%s' % (time()-start)) #---------------------------------------------------------------------- """ @@ -231,7 +232,7 @@ def loadTdxLc1(fileName, dbName, symbol): """将通达信导出的lc1格式的历史分钟数据插入到Mongo数据库中""" start = time() - print u'开始读取通达信Lc1文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) + print(u'开始读取通达信Lc1文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol)) # 锁定集合,并创建索引 client = pymongo.MongoClient(globalSetting['mongoHost'], globalSetting['mongoPort']) @@ -267,13 +268,13 @@ def loadTdxLc1(fileName, dbName, symbol): flt = {'datetime': bar.datetime} collection.update_one(flt, {'$set':bar.__dict__}, upsert=True) - print u'插入完毕,耗时:%s' % (time()-start) + print(u'插入完毕,耗时:%s' % (time()-start)) #---------------------------------------------------------------------- def loadOKEXCsv(fileName, dbName, symbol): """将OKEX导出的csv格式的历史分钟数据插入到Mongo数据库中""" start = time() - print u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol) + print(u'开始读取CSV文件%s中的数据插入到%s的%s中' %(fileName, dbName, symbol)) # 锁定集合,并创建索引 client = pymongo.MongoClient(globalSetting['mongoHost'], globalSetting['mongoPort']) @@ -304,5 +305,5 @@ def loadOKEXCsv(fileName, dbName, symbol): collection.update_one(flt, {'$set':bar.__dict__}, upsert=True) print('%s \t %s' % (bar.date, bar.time)) - print u'插入完毕,耗时:%s' % (time()-start) + print(u'插入完毕,耗时:%s' % (time()-start)) diff --git a/vnpy/trader/app/ctaStrategy/language/__init__.py b/vnpy/trader/app/ctaStrategy/language/__init__.py index f5c4ca18..e402c4f5 100644 --- a/vnpy/trader/app/ctaStrategy/language/__init__.py +++ b/vnpy/trader/app/ctaStrategy/language/__init__.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import absolute_import import json import os import traceback @@ -10,4 +11,4 @@ from .chinese import text # 是否要使用英文 from vnpy.trader.vtGlobal import globalSetting if globalSetting['language'] == 'english': - from english import text + from .english import text diff --git a/vnpy/trader/app/ctaStrategy/strategy/__init__.py b/vnpy/trader/app/ctaStrategy/strategy/__init__.py index 7a14afa9..5b10bc02 100644 --- a/vnpy/trader/app/ctaStrategy/strategy/__init__.py +++ b/vnpy/trader/app/ctaStrategy/strategy/__init__.py @@ -3,6 +3,7 @@ ''' 动态载入所有的策略类 ''' +from __future__ import print_function import os import importlib diff --git a/vnpy/trader/app/dataRecorder/__init__.py b/vnpy/trader/app/dataRecorder/__init__.py index 84020dfe..8d3ddf78 100644 --- a/vnpy/trader/app/dataRecorder/__init__.py +++ b/vnpy/trader/app/dataRecorder/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 -from drEngine import DrEngine -from uiDrWidget import DrEngineManager +from __future__ import absolute_import +from .drEngine import DrEngine +from .uiDrWidget import DrEngineManager appName = 'DataRecorder' appDisplayName = u'行情记录' diff --git a/vnpy/trader/app/dataRecorder/language/__init__.py b/vnpy/trader/app/dataRecorder/language/__init__.py index ecf72358..09d8fba7 100644 --- a/vnpy/trader/app/dataRecorder/language/__init__.py +++ b/vnpy/trader/app/dataRecorder/language/__init__.py @@ -1,13 +1,14 @@ # encoding: UTF-8 +from __future__ import absolute_import import json import os import traceback # 默认设置 -from chinese import text +from .chinese import text # 是否要使用英文 from vnpy.trader.vtGlobal import globalSetting if globalSetting['language'] == 'english': - from english import text \ No newline at end of file + from .english import text \ No newline at end of file diff --git a/vnpy/trader/app/jaqsService/__init__.py b/vnpy/trader/app/jaqsService/__init__.py index f0bf3d26..4f7abcdd 100644 --- a/vnpy/trader/app/jaqsService/__init__.py +++ b/vnpy/trader/app/jaqsService/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 -from jsEngine import JsEngine -from uiJsWidget import JsEngineManager +from __future__ import absolute_import +from .jsEngine import JsEngine +from .uiJsWidget import JsEngineManager appName = 'JaqsService' appDisplayName = u'Jaqs服务' diff --git a/vnpy/trader/app/jaqsService/jrpc_server.py b/vnpy/trader/app/jaqsService/jrpc_server.py index d49037c1..a80edda3 100644 --- a/vnpy/trader/app/jaqsService/jrpc_server.py +++ b/vnpy/trader/app/jaqsService/jrpc_server.py @@ -1,3 +1,4 @@ +from __future__ import print_function import zmq import Queue import threading @@ -108,11 +109,11 @@ class JRpcServer : #client_addr_map[client_id] = identity self._on_data_arrived(identity, data) - except zmq.error.Again, e: + except zmq.error.Again as e: #print "RECV timeout: ", e pass - except Exception, e: - print("_recv_run:", e) + except Exception as e: + print(("_recv_run:", e)) def _callback_run(self): while not self._should_close: @@ -120,12 +121,12 @@ class JRpcServer : r = self._callback_queue.get(timeout = 1) if r : r() - except Queue.Empty, e: + except Queue.Empty as e: pass - except Exception, e: + except Exception as e: traceback.print_exc(e) - print "_callback_run", type(e), e + print("_callback_run", type(e), e) def _async_call(self, func): self._callback_queue.put( func ) @@ -164,12 +165,12 @@ class JRpcServer : #print "RECV", msg if not msg: - print "wrong message format" + print("wrong message format") return - method = msg['method'] if msg.has_key('method') else None + method = msg['method'] if 'method' in msg else None - call_id = msg['id'] if msg.has_key('id') and msg['id'] else None + call_id = msg['id'] if 'id' in msg and msg['id'] else None if call_id and method: if method == ".sys.heartbeat": @@ -183,8 +184,8 @@ class JRpcServer : if self.on_call : self._async_call( lambda : self.on_call(identity, msg)) - except Exception, e: - print( "_on_data_arrived:", e) + except Exception as e: + print(( "_on_data_arrived:", e)) pass diff --git a/vnpy/trader/app/jaqsService/jsEngine.py b/vnpy/trader/app/jaqsService/jsEngine.py index 8a072b57..15c6265c 100644 --- a/vnpy/trader/app/jaqsService/jsEngine.py +++ b/vnpy/trader/app/jaqsService/jsEngine.py @@ -1,9 +1,10 @@ # encoding: UTF-8 +from __future__ import absolute_import import json from collections import defaultdict -import jrpc_server +from . import jrpc_server from vnpy.event import Event from vnpy.trader.vtFunction import getJsonPath diff --git a/vnpy/trader/app/jaqsService/service.py b/vnpy/trader/app/jaqsService/service.py index 39bf6485..38cbae97 100644 --- a/vnpy/trader/app/jaqsService/service.py +++ b/vnpy/trader/app/jaqsService/service.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- -import jrpc_server +from __future__ import print_function +from __future__ import absolute_import +from . import jrpc_server import time import pandas as pd from qdata.database import DatabaseConn @@ -15,7 +17,7 @@ db = None def on_call(client_id, req): if req['method'] != '.sys.heartbeat': - print "on_call", req + print("on_call", req) if req['method'] == 'auth.login': server.send_rsp(client_id, req, result = { "username" : "fixme", "name": "fixme" }) @@ -25,7 +27,7 @@ def on_call(client_id, req): server.send_rsp(client_id, req, error=[-1, "unknown method"]) return - if not req.has_key('params'): + if 'params' not in req: server.send_rsp(client_id, req, error=[-1, "missing params"]) return @@ -55,7 +57,7 @@ def run(): server = jrpc_server.JRpcServer() server.on_call = on_call addr = "tcp://%s:%s"%(st.HOST, st.PORT) - print "listen at " + addr + print("listen at " + addr) server.listen(addr) while True: diff --git a/vnpy/trader/app/optionMaster/omDate.py b/vnpy/trader/app/optionMaster/omDate.py index 1211c166..795f3068 100644 --- a/vnpy/trader/app/optionMaster/omDate.py +++ b/vnpy/trader/app/optionMaster/omDate.py @@ -159,9 +159,11 @@ class CalendarManager(QtWidgets.QWidget): #---------------------------------------------------------------------- def runCalendarEditor(): """运行日历编辑器""" - reload(sys) - sys.setdefaultencoding('utf8') - + try: # Python 2 + reload(sys) + sys.setdefaultencoding('utf8') + except NameError: # Python 3 + pass app = QtWidgets.QApplication(sys.argv) app.setFont(QtGui.QFont(u'微软雅黑', 12)) @@ -249,4 +251,4 @@ def getTimeToMaturity(expiryDate): if __name__ == '__main__': - runCalendarEditor() \ No newline at end of file + runCalendarEditor() diff --git a/vnpy/trader/app/optionMaster/strategy/__init__.py b/vnpy/trader/app/optionMaster/strategy/__init__.py index 5e349490..0035381d 100644 --- a/vnpy/trader/app/optionMaster/strategy/__init__.py +++ b/vnpy/trader/app/optionMaster/strategy/__init__.py @@ -3,6 +3,7 @@ ''' 动态载入所有的策略类 ''' +from __future__ import print_function import os import importlib diff --git a/vnpy/trader/app/optionMaster/uiOmManualTrader.py b/vnpy/trader/app/optionMaster/uiOmManualTrader.py index 1652be06..d29743f9 100644 --- a/vnpy/trader/app/optionMaster/uiOmManualTrader.py +++ b/vnpy/trader/app/optionMaster/uiOmManualTrader.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.event import Event from vnpy.trader.vtConstant import DIRECTION_LONG, DIRECTION_SHORT, OFFSET_OPEN, OFFSET_CLOSE, PRICETYPE_LIMITPRICE @@ -7,7 +8,7 @@ from vnpy.trader.vtObject import VtOrderReq from vnpy.trader.vtEvent import EVENT_TICK, EVENT_TRADE from vnpy.trader.uiBasicWidget import WorkingOrderMonitor, PositionMonitor -from uiOmBase import * +from .uiOmBase import * diff --git a/vnpy/trader/app/optionMaster/uiOmWidget.py b/vnpy/trader/app/optionMaster/uiOmWidget.py index ea770808..956a6372 100644 --- a/vnpy/trader/app/optionMaster/uiOmWidget.py +++ b/vnpy/trader/app/optionMaster/uiOmWidget.py @@ -5,6 +5,8 @@ from __future__ import division import os from datetime import datetime +from six import text_type + from vnpy.event import Event from .omBase import EVENT_OM_LOG @@ -113,7 +115,7 @@ class OmManager(QtWidgets.QWidget): def initOmEngine(self): """初始化引擎""" path = os.getcwd() - fileName = unicode(self.comboSettingFile.currentText()) + fileName = text_type(self.comboSettingFile.currentText()) fileName = os.path.join(path, fileName) result = self.omEngine.initEngine(fileName) diff --git a/vnpy/trader/app/riskManager/__init__.py b/vnpy/trader/app/riskManager/__init__.py index 04e66bc5..daa917c0 100644 --- a/vnpy/trader/app/riskManager/__init__.py +++ b/vnpy/trader/app/riskManager/__init__.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import absolute_import from .rmEngine import RmEngine from .uiRmWidget import RmEngineManager @@ -7,4 +8,4 @@ appName = 'RiskManager' appDisplayName = u'风险管理' appEngine = RmEngine appWidget = RmEngineManager -appIco = 'rm.ico' \ No newline at end of file +appIco = 'rm.ico' diff --git a/vnpy/trader/app/riskManager/language/__init__.py b/vnpy/trader/app/riskManager/language/__init__.py index f5c4ca18..e402c4f5 100644 --- a/vnpy/trader/app/riskManager/language/__init__.py +++ b/vnpy/trader/app/riskManager/language/__init__.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import absolute_import import json import os import traceback @@ -10,4 +11,4 @@ from .chinese import text # 是否要使用英文 from vnpy.trader.vtGlobal import globalSetting if globalSetting['language'] == 'english': - from english import text + from .english import text diff --git a/vnpy/trader/app/spreadTrading/uiStWidget.py b/vnpy/trader/app/spreadTrading/uiStWidget.py index 7f505c8b..76b932fd 100644 --- a/vnpy/trader/app/spreadTrading/uiStWidget.py +++ b/vnpy/trader/app/spreadTrading/uiStWidget.py @@ -2,6 +2,8 @@ from collections import OrderedDict +from six import text_type + from vnpy.event import Event from vnpy.trader.uiQt import QtWidgets, QtCore from vnpy.trader.uiBasicWidget import (BasicMonitor, BasicCell, PnlCell, @@ -338,7 +340,7 @@ class StModeComboBox(QtWidgets.QComboBox): #---------------------------------------------------------------------- def setMode(self): """设置模式""" - mode = unicode(self.currentText()) + mode = text_type(self.currentText()) self.algoEngine.setAlgoMode(self.spreadName, mode) #---------------------------------------------------------------------- @@ -582,4 +584,4 @@ class StManager(QtWidgets.QWidget): - \ No newline at end of file + From 314dece44b8864d4f61b460a516692f83720e703 Mon Sep 17 00:00:00 2001 From: cclauss Date: Mon, 14 May 2018 16:02:15 +0200 Subject: [PATCH 010/135] Modernize vnpy/trader/gateway --- .../trader/gateway/cshshlpGateway/__init__.py | 3 ++- vnpy/trader/gateway/ctpGateway/__init__.py | 3 ++- .../gateway/ctpGateway/language/__init__.py | 5 +++-- vnpy/trader/gateway/femasGateway/__init__.py | 3 ++- .../gateway/femasGateway/femasGateway.py | 5 +++-- vnpy/trader/gateway/futuGateway/__init__.py | 1 + vnpy/trader/gateway/fxcmGateway/__init__.py | 3 ++- .../trader/gateway/fxcmGateway/fxcmGateway.py | 9 ++++---- vnpy/trader/gateway/huobiGateway/__init__.py | 3 ++- .../gateway/huobiGateway/huobiGateway.py | 21 ++++++++----------- vnpy/trader/gateway/ibGateway/__init__.py | 3 ++- vnpy/trader/gateway/ibGateway/ibGateway.py | 5 +++-- .../gateway/ibGateway/language/__init__.py | 12 +++++------ vnpy/trader/gateway/ksgoldGateway/__init__.py | 3 ++- vnpy/trader/gateway/ksotpGateway/__init__.py | 3 ++- vnpy/trader/gateway/lbankGateway/__init__.py | 3 ++- .../gateway/lbankGateway/lbankGateway.py | 5 +++-- vnpy/trader/gateway/ltsGateway/__init__.py | 3 ++- vnpy/trader/gateway/ltsGateway/ltsGateway.py | 3 ++- vnpy/trader/gateway/oandaGateway/__init__.py | 3 ++- vnpy/trader/gateway/qdpGateway/__init__.py | 3 ++- vnpy/trader/gateway/secGateway/__init__.py | 3 ++- vnpy/trader/gateway/secGateway/secGateway.py | 5 +++-- vnpy/trader/gateway/sgitGateway/__init__.py | 3 ++- vnpy/trader/gateway/shzdGateway/__init__.py | 3 ++- .../trader/gateway/shzdGateway/shzdGateway.py | 5 +++-- .../gateway/tkproGateway/tkproGateway.py | 2 +- vnpy/trader/gateway/windGateway/__init__.py | 3 ++- .../trader/gateway/windGateway/windGateway.py | 3 ++- vnpy/trader/gateway/xspeedGateway/__init__.py | 3 ++- vnpy/trader/gateway/xtpGateway/__init__.py | 3 ++- 31 files changed, 80 insertions(+), 55 deletions(-) diff --git a/vnpy/trader/gateway/cshshlpGateway/__init__.py b/vnpy/trader/gateway/cshshlpGateway/__init__.py index ff1cd68e..e340ebe5 100644 --- a/vnpy/trader/gateway/cshshlpGateway/__init__.py +++ b/vnpy/trader/gateway/cshshlpGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from cshshlpGateway import CshshlpGateway +from .cshshlpGateway import CshshlpGateway gatewayClass = CshshlpGateway gatewayName = 'CSHSHLP' diff --git a/vnpy/trader/gateway/ctpGateway/__init__.py b/vnpy/trader/gateway/ctpGateway/__init__.py index 3078d854..8a8d5827 100644 --- a/vnpy/trader/gateway/ctpGateway/__init__.py +++ b/vnpy/trader/gateway/ctpGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from ctpGateway import CtpGateway +from .ctpGateway import CtpGateway gatewayClass = CtpGateway gatewayName = 'CTP' diff --git a/vnpy/trader/gateway/ctpGateway/language/__init__.py b/vnpy/trader/gateway/ctpGateway/language/__init__.py index 2ca0ef80..8f68ed7f 100644 --- a/vnpy/trader/gateway/ctpGateway/language/__init__.py +++ b/vnpy/trader/gateway/ctpGateway/language/__init__.py @@ -1,15 +1,16 @@ # encoding: UTF-8 +from __future__ import absolute_import import json import os import traceback # 默认设置 -from chinese import text +from .chinese import text # 获取全局配置 from vnpy.trader.vtGlobal import globalSetting # 打开配置文件,读取语言配置 if globalSetting['language'] == 'english': - from english import text + from .english import text diff --git a/vnpy/trader/gateway/femasGateway/__init__.py b/vnpy/trader/gateway/femasGateway/__init__.py index 646b363e..e6897beb 100644 --- a/vnpy/trader/gateway/femasGateway/__init__.py +++ b/vnpy/trader/gateway/femasGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from femasGateway import FemasGateway +from .femasGateway import FemasGateway gatewayClass = FemasGateway gatewayName = 'FEMAS' diff --git a/vnpy/trader/gateway/femasGateway/femasGateway.py b/vnpy/trader/gateway/femasGateway/femasGateway.py index c5962a49..88ba3d42 100644 --- a/vnpy/trader/gateway/femasGateway/femasGateway.py +++ b/vnpy/trader/gateway/femasGateway/femasGateway.py @@ -5,6 +5,7 @@ vn.femas的gateway接入 考虑到飞马只对接期货(目前只有中金所), vtSymbol直接使用symbol ''' +from __future__ import print_function import os @@ -599,10 +600,10 @@ class FemasTdApi(TdApi): # 如果登录成功,推送日志信息 if error['ErrorID'] == 0: for k, v in data.items(): - print k, ':', v + print(k, ':', v) if data['MaxOrderLocalID']: self.localID = int(data['MaxOrderLocalID']) # 目前最大本地报单号 - print 'id now', self.localID + print('id now', self.localID) self.loginStatus = True self.gateway.mdConnected = True diff --git a/vnpy/trader/gateway/futuGateway/__init__.py b/vnpy/trader/gateway/futuGateway/__init__.py index 827c2b6c..3778ebc6 100644 --- a/vnpy/trader/gateway/futuGateway/__init__.py +++ b/vnpy/trader/gateway/futuGateway/__init__.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant from .futuGateway import FutuGateway diff --git a/vnpy/trader/gateway/fxcmGateway/__init__.py b/vnpy/trader/gateway/fxcmGateway/__init__.py index 26e36c12..53254b47 100644 --- a/vnpy/trader/gateway/fxcmGateway/__init__.py +++ b/vnpy/trader/gateway/fxcmGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from fxcmGateway import FxcmGateway +from .fxcmGateway import FxcmGateway gatewayClass = FxcmGateway gatewayName = 'FXCM' diff --git a/vnpy/trader/gateway/fxcmGateway/fxcmGateway.py b/vnpy/trader/gateway/fxcmGateway/fxcmGateway.py index 712b0692..fc5f323f 100644 --- a/vnpy/trader/gateway/fxcmGateway/fxcmGateway.py +++ b/vnpy/trader/gateway/fxcmGateway/fxcmGateway.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function import os import json from datetime import datetime @@ -434,22 +435,22 @@ class Api(FxcmApi): #---------------------------------------------------------------------- def onOpenTrade(self, data, reqid): """开仓回调""" - print data, reqid + print(data, reqid) #---------------------------------------------------------------------- def onCloseTrade(self, data, reqid): """平仓回调""" - print data, reqid + print(data, reqid) #---------------------------------------------------------------------- def onChangeOrder(self, data, reqid): """改单回调""" - print data, reqid + print(data, reqid) #---------------------------------------------------------------------- def onDeleteOrder(self, data, reqid): """撤单回调""" - print data, reqid + print(data, reqid) #---------------------------------------------------------------------- def onPriceUpdate(self, data): diff --git a/vnpy/trader/gateway/huobiGateway/__init__.py b/vnpy/trader/gateway/huobiGateway/__init__.py index b6bb6efe..34044316 100644 --- a/vnpy/trader/gateway/huobiGateway/__init__.py +++ b/vnpy/trader/gateway/huobiGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from huobiGateway import HuobiGateway +from .huobiGateway import HuobiGateway gatewayClass = HuobiGateway gatewayName = 'HUOBI' diff --git a/vnpy/trader/gateway/huobiGateway/huobiGateway.py b/vnpy/trader/gateway/huobiGateway/huobiGateway.py index b3fc31d4..b84cf580 100644 --- a/vnpy/trader/gateway/huobiGateway/huobiGateway.py +++ b/vnpy/trader/gateway/huobiGateway/huobiGateway.py @@ -3,8 +3,8 @@ ''' vn.sec的gateway接入 ''' +from __future__ import print_function -import os import json from datetime import datetime, timedelta from copy import copy @@ -12,7 +12,7 @@ from math import pow from vnpy.api.huobi import TradeApi, DataApi from vnpy.trader.vtGateway import * -from vnpy.trader.vtFunction import getJsonPath, getTempPath +from vnpy.trader.vtFunction import getJsonPath # 委托状态类型映射 @@ -29,11 +29,9 @@ statusMapReverse['canceled'] = STATUS_CANCELLED #---------------------------------------------------------------------- def print_dict(d): """""" - print '-' * 30 - l = d.keys() - l.sort() - for k in l: - print '%s:%s' %(k, d[k]) + print('-' * 30) + for key in sorted(d): + print('%s:%s' % (key, d[key])) ######################################################################## @@ -60,7 +58,7 @@ class HuobiGateway(VtGateway): def connect(self): """连接""" try: - f = file(self.filePath) + f = open(self.filePath) except IOError: log = VtLogData() log.gatewayName = self.gatewayName @@ -294,7 +292,7 @@ class HuobiDataApi(DataApi): #---------------------------------------------------------------------- def onTradeDetail(self, data): """成交细节推送""" - print data + print(data) #---------------------------------------------------------------------- def onMarketDetail(self, data): @@ -322,7 +320,6 @@ class HuobiDataApi(DataApi): self.gateway.onTick(tick) - ######################################################################## class HuobiTradeApi(TradeApi): """交易API实现""" @@ -693,7 +690,7 @@ class HuobiTradeApi(TradeApi): #---------------------------------------------------------------------- def onGetMatchResult(self, data, reqid): """查询单一成交回调""" - print reqid, data + print(reqid, data) #---------------------------------------------------------------------- def onPlaceOrder(self, data, reqid): @@ -717,4 +714,4 @@ class HuobiTradeApi(TradeApi): #---------------------------------------------------------------------- def onBatchCancel(self, data, reqid): """批量撤单回调""" - print reqid, data + print(reqid, data) diff --git a/vnpy/trader/gateway/ibGateway/__init__.py b/vnpy/trader/gateway/ibGateway/__init__.py index c7be4fdd..8d6335a4 100644 --- a/vnpy/trader/gateway/ibGateway/__init__.py +++ b/vnpy/trader/gateway/ibGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from ibGateway import IbGateway +from .ibGateway import IbGateway gatewayClass = IbGateway gatewayName = 'IB' diff --git a/vnpy/trader/gateway/ibGateway/ibGateway.py b/vnpy/trader/gateway/ibGateway/ibGateway.py index 9c6c251e..3f70826b 100644 --- a/vnpy/trader/gateway/ibGateway/ibGateway.py +++ b/vnpy/trader/gateway/ibGateway/ibGateway.py @@ -10,6 +10,7 @@ Interactive Brokers的gateway接入,已经替换为vn.ib封装。 4. 目前只支持股票和期货交易,ib api里期权合约的确定是基于Contract对象的多个字段,比较复杂暂时没做 5. 海外市场的交易规则和国内有很多细节上的不同,所以一些字段类型的映射可能不合理,如果发现问题欢迎指出 ''' +from __future__ import print_function import os import json @@ -375,7 +376,7 @@ class IbWrapper(IbApi): newtick = copy(tick) self.gateway.onTick(newtick) else: - print field + print(field) #---------------------------------------------------------------------- def tickSize(self, tickerId, field, size): @@ -385,7 +386,7 @@ class IbWrapper(IbApi): key = tickFieldMap[field] tick.__setattr__(key, size) else: - print field + print(field) #---------------------------------------------------------------------- def tickOptionComputation(self, tickerId, tickType, impliedVol, delta, optPrice, pvDividend, gamma, vega, theta, undPrice): diff --git a/vnpy/trader/gateway/ibGateway/language/__init__.py b/vnpy/trader/gateway/ibGateway/language/__init__.py index 8325ae0c..e9efec4b 100644 --- a/vnpy/trader/gateway/ibGateway/language/__init__.py +++ b/vnpy/trader/gateway/ibGateway/language/__init__.py @@ -1,11 +1,12 @@ # encoding: UTF-8 +from __future__ import absolute_import import json import os import traceback # 默认设置 -from chinese import text +from .chinese import text # 获取目录上级路径 path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')) @@ -14,10 +15,9 @@ SETTING_FILENAME = os.path.join(path, SETTING_FILENAME) # 打开配置文件,读取语言配置 try: - f = file(SETTING_FILENAME) - setting = json.load(f) + with open(SETTING_FILENAME) as f: + setting = json.load(f) if setting['language'] == 'english': - from english import text - f.close() -except: + from .english import text +except Exception: traceback.print_exc() diff --git a/vnpy/trader/gateway/ksgoldGateway/__init__.py b/vnpy/trader/gateway/ksgoldGateway/__init__.py index b81b6ef3..cc9ef208 100644 --- a/vnpy/trader/gateway/ksgoldGateway/__init__.py +++ b/vnpy/trader/gateway/ksgoldGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from ksgoldGateway import KsgoldGateway +from .ksgoldGateway import KsgoldGateway gatewayClass = KsgoldGateway gatewayName = 'KSGOLD' diff --git a/vnpy/trader/gateway/ksotpGateway/__init__.py b/vnpy/trader/gateway/ksotpGateway/__init__.py index cf9c5dbb..d18f121a 100644 --- a/vnpy/trader/gateway/ksotpGateway/__init__.py +++ b/vnpy/trader/gateway/ksotpGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from ksotpGateway import KsotpGateway +from .ksotpGateway import KsotpGateway gatewayClass = KsotpGateway gatewayName = 'KSOTP' diff --git a/vnpy/trader/gateway/lbankGateway/__init__.py b/vnpy/trader/gateway/lbankGateway/__init__.py index 4d5d6e00..72ba49d8 100644 --- a/vnpy/trader/gateway/lbankGateway/__init__.py +++ b/vnpy/trader/gateway/lbankGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from lbankGateway import LbankGateway +from .lbankGateway import LbankGateway gatewayClass = LbankGateway gatewayName = 'LBANK' diff --git a/vnpy/trader/gateway/lbankGateway/lbankGateway.py b/vnpy/trader/gateway/lbankGateway/lbankGateway.py index 85f82ae3..3cade80e 100644 --- a/vnpy/trader/gateway/lbankGateway/lbankGateway.py +++ b/vnpy/trader/gateway/lbankGateway/lbankGateway.py @@ -3,6 +3,7 @@ ''' vn.lhang的gateway接入 ''' +from __future__ import print_function import os @@ -244,11 +245,11 @@ class LbankApi(LbankApi): # ---------------------------------------------------------------------- def onGetTrades(self, data, req, reqID): """查询历史成交""" - print data, reqID + print(data, reqID) # ---------------------------------------------------------------------- def onGetKline(self, data, req, reqID): - print data, reqID + print(data, reqID) # ---------------------------------------------------------------------- def onGetUserInfo(self, data, req, reqID): diff --git a/vnpy/trader/gateway/ltsGateway/__init__.py b/vnpy/trader/gateway/ltsGateway/__init__.py index cbb88696..72306493 100644 --- a/vnpy/trader/gateway/ltsGateway/__init__.py +++ b/vnpy/trader/gateway/ltsGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from ltsGateway import LtsGateway +from .ltsGateway import LtsGateway gatewayClass = LtsGateway gatewayName = 'LTS' diff --git a/vnpy/trader/gateway/ltsGateway/ltsGateway.py b/vnpy/trader/gateway/ltsGateway/ltsGateway.py index 83610ac2..00942059 100644 --- a/vnpy/trader/gateway/ltsGateway/ltsGateway.py +++ b/vnpy/trader/gateway/ltsGateway/ltsGateway.py @@ -3,6 +3,7 @@ ''' vn.lts的gateway接入 ''' +from __future__ import print_function import os import json @@ -980,7 +981,7 @@ class LtsQryApi(QryApi): elif data['ProductClass'] == '8': contract.productClass = PRODUCT_EQUITY else: - print data['ProductClass'] + print(data['ProductClass']) # 期权类型 if data['InstrumentType'] == '1': diff --git a/vnpy/trader/gateway/oandaGateway/__init__.py b/vnpy/trader/gateway/oandaGateway/__init__.py index 0ce2fb0d..7ba3dbdb 100644 --- a/vnpy/trader/gateway/oandaGateway/__init__.py +++ b/vnpy/trader/gateway/oandaGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from oandaGateway import OandaGateway +from .oandaGateway import OandaGateway gatewayClass = OandaGateway gatewayName = 'OANDA' diff --git a/vnpy/trader/gateway/qdpGateway/__init__.py b/vnpy/trader/gateway/qdpGateway/__init__.py index 4fb85e9b..2c8b6685 100644 --- a/vnpy/trader/gateway/qdpGateway/__init__.py +++ b/vnpy/trader/gateway/qdpGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from qdpGateway import QdpGateway +from .qdpGateway import QdpGateway gatewayClass = QdpGateway gatewayName = 'QDP' diff --git a/vnpy/trader/gateway/secGateway/__init__.py b/vnpy/trader/gateway/secGateway/__init__.py index c919bb29..ba50b638 100644 --- a/vnpy/trader/gateway/secGateway/__init__.py +++ b/vnpy/trader/gateway/secGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from secGateway import SecGateway +from .secGateway import SecGateway gatewayClass = SecGateway gatewayName = 'SEC' diff --git a/vnpy/trader/gateway/secGateway/secGateway.py b/vnpy/trader/gateway/secGateway/secGateway.py index 095341b8..a5ff3d01 100644 --- a/vnpy/trader/gateway/secGateway/secGateway.py +++ b/vnpy/trader/gateway/secGateway/secGateway.py @@ -3,6 +3,7 @@ ''' vn.sec的gateway接入 ''' +from __future__ import print_function import os import json @@ -50,11 +51,11 @@ exchangeMapReverse = {v:k for k,v in exchangeMap.items()} #---------------------------------------------------------------------- def print_dict(d): """""" - print '-' * 30 + print('-' * 30) l = d.keys() l.sort() for k in l: - print '%s:%s' %(k, d[k]) + print('%s:%s' %(k, d[k])) ######################################################################## diff --git a/vnpy/trader/gateway/sgitGateway/__init__.py b/vnpy/trader/gateway/sgitGateway/__init__.py index 2be6983b..52c55412 100644 --- a/vnpy/trader/gateway/sgitGateway/__init__.py +++ b/vnpy/trader/gateway/sgitGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from sgitGateway import SgitGateway +from .sgitGateway import SgitGateway gatewayClass = SgitGateway gatewayName = 'SGIT' diff --git a/vnpy/trader/gateway/shzdGateway/__init__.py b/vnpy/trader/gateway/shzdGateway/__init__.py index 7f5d3497..fd10bb58 100644 --- a/vnpy/trader/gateway/shzdGateway/__init__.py +++ b/vnpy/trader/gateway/shzdGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from shzdGateway import ShzdGateway +from .shzdGateway import ShzdGateway gatewayClass = ShzdGateway gatewayName = 'SHZD' diff --git a/vnpy/trader/gateway/shzdGateway/shzdGateway.py b/vnpy/trader/gateway/shzdGateway/shzdGateway.py index 3ed9f305..d49ba6b0 100644 --- a/vnpy/trader/gateway/shzdGateway/shzdGateway.py +++ b/vnpy/trader/gateway/shzdGateway/shzdGateway.py @@ -8,6 +8,7 @@ vn.shzd的gateway接入 3. 持仓全部平光后,再次查询时会没有该合约的推送(和CTP不同),为了避免最后平仓 不更新的情况,使用缓存机制来处理 ''' +from __future__ import print_function import os @@ -721,9 +722,9 @@ class ShzdGatewayApi(ShzdApi): #---------------------------------------------------------------------- def printDict(d): """打印字典""" - print '-' * 50 + print('-' * 50) l = d.keys() l.sort() for k in l: - print k, ':', d[k] + print(k, ':', d[k]) \ No newline at end of file diff --git a/vnpy/trader/gateway/tkproGateway/tkproGateway.py b/vnpy/trader/gateway/tkproGateway/tkproGateway.py index 85d406cc..ddd53e4a 100644 --- a/vnpy/trader/gateway/tkproGateway/tkproGateway.py +++ b/vnpy/trader/gateway/tkproGateway/tkproGateway.py @@ -570,7 +570,7 @@ class TkproDataApi(object): tick.lowerLimit = data['limit_down'] self.gateway.onTick(tick) - except Exception, e: + except Exception as e: self.writeLog(u'行情更新失败,错误信息:%s' % str(e)) #---------------------------------------------------------------------- diff --git a/vnpy/trader/gateway/windGateway/__init__.py b/vnpy/trader/gateway/windGateway/__init__.py index f2508aee..fc9949b4 100644 --- a/vnpy/trader/gateway/windGateway/__init__.py +++ b/vnpy/trader/gateway/windGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from windGateway import WindGateway +from .windGateway import WindGateway gatewayClass = WindGateway gatewayName = 'WIND' diff --git a/vnpy/trader/gateway/windGateway/windGateway.py b/vnpy/trader/gateway/windGateway/windGateway.py index 1a872cdf..e244b6c5 100644 --- a/vnpy/trader/gateway/windGateway/windGateway.py +++ b/vnpy/trader/gateway/windGateway/windGateway.py @@ -3,6 +3,7 @@ ''' Wind Python API的gateway接入 ''' +from __future__ import print_function from copy import copy @@ -11,7 +12,7 @@ w = None try: from WindPy import w except ImportError: - print u'请先安装WindPy接口' + print(u'请先安装WindPy接口') from vnpy.trader.vtGateway import * diff --git a/vnpy/trader/gateway/xspeedGateway/__init__.py b/vnpy/trader/gateway/xspeedGateway/__init__.py index 50d26714..ee1f848b 100644 --- a/vnpy/trader/gateway/xspeedGateway/__init__.py +++ b/vnpy/trader/gateway/xspeedGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from xspeedGateway import XspeedGateway +from .xspeedGateway import XspeedGateway gatewayClass = XspeedGateway gatewayName = 'XSPEED' diff --git a/vnpy/trader/gateway/xtpGateway/__init__.py b/vnpy/trader/gateway/xtpGateway/__init__.py index bd4dfe6d..b9ea0af4 100644 --- a/vnpy/trader/gateway/xtpGateway/__init__.py +++ b/vnpy/trader/gateway/xtpGateway/__init__.py @@ -1,7 +1,8 @@ # encoding: UTF-8 +from __future__ import absolute_import from vnpy.trader import vtConstant -from xtpGateway import XtpGateway +from .xtpGateway import XtpGateway gatewayClass = XtpGateway gatewayName = 'XTP' From 762a838b3d0acaa07f29c26f4afb6eb8f0211d20 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Fri, 1 Jun 2018 09:34:01 +0800 Subject: [PATCH 011/135] =?UTF-8?q?[Add]=E5=88=9D=E6=AD=A5=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=9B=B4=E6=96=B0=E5=90=8E=E7=9A=84OKEX=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/okex/README.md | 14 + vnpy/api/okex/__init__.py | 4 + vnpy/api/okex/vnokex.py | 332 ++++++++++ .../gateway/okexGateway/OKEX_connect.json | 6 + vnpy/trader/gateway/okexGateway/__init__.py | 12 + .../trader/gateway/okexGateway/okexGateway.py | 611 ++++++++++++++++++ 6 files changed, 979 insertions(+) create mode 100644 vnpy/api/okex/README.md create mode 100644 vnpy/api/okex/__init__.py create mode 100644 vnpy/api/okex/vnokex.py create mode 100644 vnpy/trader/gateway/okexGateway/OKEX_connect.json create mode 100644 vnpy/trader/gateway/okexGateway/__init__.py create mode 100644 vnpy/trader/gateway/okexGateway/okexGateway.py diff --git a/vnpy/api/okex/README.md b/vnpy/api/okex/README.md new file mode 100644 index 00000000..f98f155c --- /dev/null +++ b/vnpy/api/okex/README.md @@ -0,0 +1,14 @@ +### 简介 + +OKEX的比特币交易接口,基于Websocket API开发,实现了以下功能: + +1. 发送、撤销委托 + +2. 查询委托、持仓、资金、成交历史 + +3. 实时行情、成交、资金更新的推送 + +### API信息 + +链接:[https://www.okex.com/ws_getStarted.html](https://www.okex.com/ws_getStarted.html) + diff --git a/vnpy/api/okex/__init__.py b/vnpy/api/okex/__init__.py new file mode 100644 index 00000000..2b7ddb71 --- /dev/null +++ b/vnpy/api/okex/__init__.py @@ -0,0 +1,4 @@ +# encoding: UTF-8 + +from __future__ import absolute_import +from .vnokex import OkexSpotApi, SPOT_CURRENCY, SPOT_SYMBOL, OKEX_SPOT_HOST \ 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..b2b86db0 --- /dev/null +++ b/vnpy/api/okex/vnokex.py @@ -0,0 +1,332 @@ +# encoding: UTF-8 + +from __future__ import print_function + +import hashlib +import json +from threading import Thread +from time import sleep + +import websocket + +# 常量定义 +OKEX_SPOT_HOST = 'wss://real.okex.com:10441/websocket' + + +SPOT_CURRENCY = ["usdt", + "btc", + "ltc", + "eth", + "etc", + "bch"] + +SPOT_SYMBOL = ["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"] + +KLINE_PERIOD = ["1min", + "3min", + "5min", + "15min", + "30min", + "1hour", + "2hour", + "4hour", + "6hour", + "12hour", + "day", + "3day", + "week"] + + +######################################################################## +class OkexApi(object): + """交易接口""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.host = '' # 服务器 + self.apiKey = '' # 用户名 + self.secretKey = '' # 密码 + + self.active = False # 工作状态 + self.ws = None # websocket应用对象 + self.wsThread = None # websocket工作线程 + + self.heartbeatCount = 0 # 心跳计数 + self.heartbeatThread = None # 心跳线程 + self.heartbeatReceived = True # 心跳是否收到 + + #---------------------------------------------------------------------- + def heartbeat(self): + """""" + while self.active: + self.heartbeatCount += 1 + + if self.heartbeatCount < 30: + sleep(1) + else: + self.heartbeatCount = 0 + + if not self.heartbeatReceived: + self.reconnect() + else: + d = {'event': 'ping'} + j = json.dumps(d) + self.ws.send(j) + self.heartbeatReceived = False + + #---------------------------------------------------------------------- + def reconnect(self): + """重新连接""" + self.close() # 首先关闭之前的连接 + self.initWebsocket() + + #---------------------------------------------------------------------- + def connect(self, host, apiKey, secretKey, trace=False): + """连接""" + self.host = host + self.apiKey = apiKey + self.secretKey = secretKey + + websocket.enableTrace(trace) + + self.initWebsocket() + self.active = True + + #---------------------------------------------------------------------- + def initWebsocket(self): + """""" + self.ws = websocket.WebSocketApp(self.host, + on_message=self.onMessageCallback, + on_error=self.onErrorCallback, + on_close=self.onCloseCallback, + on_open=self.onOpenCallback) + + self.wsThread = Thread(target=self.ws.run_forever) + self.wsThread.start() + + #---------------------------------------------------------------------- + def readData(self, evt): + """解码推送收到的数据""" + data = json.loads(evt) + return data + + #---------------------------------------------------------------------- + def close(self): + """关闭接口""" + if self.heartbeatThread and self.heartbeatThread.isAlive(): + self.active = False + self.heartbeatThread.join() + + if self.wsThread and self.wsThread.isAlive(): + self.ws.close() + self.wsThread.join() + + #---------------------------------------------------------------------- + def onMessage(self, data): + """信息推送""" + print('onMessage') + print(evt) + + #---------------------------------------------------------------------- + def onError(self, data): + """错误推送""" + print('onError') + print(evt) + + #---------------------------------------------------------------------- + def onClose(self): + """接口断开""" + print('onClose') + + #---------------------------------------------------------------------- + def onOpen(self): + """接口打开""" + print('onOpen') + + #---------------------------------------------------------------------- + def onMessageCallback(self, ws, evt): + """""" + data = self.readData(evt) + if 'event' in data: + self.heartbeatReceived = True + else: + self.onMessage(data[0]) + + #---------------------------------------------------------------------- + def onErrorCallback(self, ws, evt): + """""" + self.onError(evt) + + #---------------------------------------------------------------------- + def onCloseCallback(self, ws): + """""" + self.onClose() + + #---------------------------------------------------------------------- + def onOpenCallback(self, ws): + """""" + self.heartbeatThread = Thread(target=self.heartbeat) + self.heartbeatThread.start() + + self.onOpen() + + #---------------------------------------------------------------------- + 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 sendRequest(self, channel, params=None): + """发送请求""" + # 生成请求 + d = {} + d['event'] = 'addChannel' + d['channel'] = channel + + # 如果有参数,在参数字典中加上api_key和签名字段 + if params is not None: + params['api_key'] = self.apiKey + params['sign'] = self.generateSign(params) + d['parameters'] = params + + # 使用json打包并发送 + j = json.dumps(d) + + # 若触发异常则重连 + try: + self.ws.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 + j = json.dumps(d) + + # 若触发异常则重连 + try: + self.ws.send(j) + return True + except websocket.WebSocketConnectionClosedException: + return False + + +######################################################################## +class OkexSpotApi(OkexApi): + """现货交易接口""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(OkexSpotApi, self).__init__() + + #---------------------------------------------------------------------- + def subscribeSpotTicker(self, symbol): + """订阅现货的Tick""" + channel = 'ok_sub_spot_%s_ticker' %symbol + self.sendRequest(channel) + + #---------------------------------------------------------------------- + def subscribeSpotDepth(self, symbol, depth=0): + """订阅现货的深度""" + channel = 'ok_sub_spot_%s_depth' %symbol + if depth: + channel = channel + '_' + str(depth) + self.sendRequest(channel) + + #---------------------------------------------------------------------- + def subscribeSpotDeals(self, symbol): + channel = 'ok_sub_spot_%s_deals' %symbol + self.sendRequest(channel) + + #---------------------------------------------------------------------- + def subscribeSpotKlines(self, symbol, period): + channel = 'ok_sub_spot_%s_kline_%s' %(symbol, period) + self.sendRequest(channel) + + #---------------------------------------------------------------------- + def spotOrder(self, symbol, type_, price, amount): + """现货委托""" + params = {} + params['symbol'] = str(symbol) + params['type'] = str(type_) + params['price'] = str(price) + params['amount'] = str(amount) + + channel = 'ok_spot_order' + + self.sendRequest(channel, params) + + #---------------------------------------------------------------------- + def spotCancelOrder(self, symbol, orderid): + """现货撤单""" + params = {} + params['symbol'] = str(symbol) + params['order_id'] = str(orderid) + + channel = 'ok_spot_cancel_order' + + self.sendRequest(channel, params) + + #---------------------------------------------------------------------- + def spotUserInfo(self): + """查询现货账户""" + channel = 'ok_spot_userinfo' + self.sendRequest(channel, {}) + + #---------------------------------------------------------------------- + def spotOrderInfo(self, symbol, orderid): + """查询现货委托信息""" + params = {} + params['symbol'] = str(symbol) + params['order_id'] = str(orderid) + + channel = 'ok_spot_orderinfo' + + self.sendRequest(channel, params) + + #---------------------------------------------------------------------- + def subSpotOrder(self, symbol): + """订阅委托推送""" + channel = 'ok_sub_spot_%s_order' %symbol + self.sendRequest(channel) + + #---------------------------------------------------------------------- + def subSpotBalance(self, symbol): + """订阅资金推送""" + channel = 'ok_sub_spot_%s_balance' %symbol + self.sendRequest(channel) + diff --git a/vnpy/trader/gateway/okexGateway/OKEX_connect.json b/vnpy/trader/gateway/okexGateway/OKEX_connect.json new file mode 100644 index 00000000..5c77f027 --- /dev/null +++ b/vnpy/trader/gateway/okexGateway/OKEX_connect.json @@ -0,0 +1,6 @@ +{ + "apiKey": "", + "secretKey": "", + "trace": false, + "symbols": ["eth_btc", "btc_usdt", "eth_usdt"] +} \ 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..5f0265a9 --- /dev/null +++ b/vnpy/trader/gateway/okexGateway/__init__.py @@ -0,0 +1,12 @@ +# encoding: UTF-8 + +from __future__ import absolute_import +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..9ee1cf79 --- /dev/null +++ b/vnpy/trader/gateway/okexGateway/okexGateway.py @@ -0,0 +1,611 @@ +# encoding: UTF-8 + +''' +vnpy.api.okex的gateway接入 +''' +from __future__ import print_function + + +import os +import json +from datetime import datetime +from time import sleep +from copy import copy +from threading import Condition +from queue import Queue, Empty +from threading import Thread +from time import sleep + +from vnpy.api.okex import OkexSpotApi, OKEX_SPOT_HOST +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): + """OKEX交易接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine, gatewayName='OKEX'): + """Constructor""" + super(OkexGateway, self).__init__(eventEngine, gatewayName) + + self.spotApi = SpotApi(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'] + symbols = setting['symbols'] + except KeyError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'连接配置缺少字段,请检查' + self.onLog(log) + return + + # 初始化接口 + self.spotApi.init(apiKey, secretKey, trace, symbols) + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """发单""" + return self.spotApi.sendOrder(orderReq) + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """撤单""" + self.spotApi.cancelOrder(cancelOrderReq) + + #---------------------------------------------------------------------- + def qryAccount(self): + """查询账户资金""" + pass + + #---------------------------------------------------------------------- + def qryPosition(self): + """查询持仓""" + self.spotApi.spotUserInfo() + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.spotApi.close() + + #---------------------------------------------------------------------- + def initQuery(self): + """初始化连续查询""" + if self.qryEnabled: + # 需要循环的查询函数列表 + self.qryFunctionList = [self.qryPosition] + + 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 SpotApi(OkexSpotApi): + """OKEX的API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(SpotApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.cbDict = {} + self.tickDict = {} + self.orderDict = {} + + self.channelSymbolMap = {} + + self.localNo = 0 # 本地委托号 + self.localNoQueue = Queue() # 未收到系统委托号的本地委托号队列 + self.localNoDict = {} # key为本地委托号,value为系统委托号 + self.localOrderDict = {} # key为本地委托号, value为委托对象 + self.orderIdDict = {} # key为系统委托号,value为本地委托号 + self.cancelDict = {} # key为本地委托号,value为撤单请求 + + self.recordOrderId_BefVolume = {} # 记录的之前处理的量 + + self.cache_some_order = {} + self.tradeID = 0 + + self.registerSymbolPairArray = set([]) + + #---------------------------------------------------------------------- + def onMessage(self, data): + """信息推送""" + channel = data.get('channel', '') + if not channel: + return + + if channel in self.cbDict: + callback = self.cbDict[channel] + callback(data) + + #---------------------------------------------------------------------- + def onError(self, data): + """错误推送""" + error = VtErrorData() + error.gatewayName = self.gatewayName + error.errorMsg = str(data) + self.gateway.onError(error) + + #---------------------------------------------------------------------- + def onClose(self): + """接口断开""" + self.gateway.connected = False + self.writeLog(u'服务器连接断开') + + #---------------------------------------------------------------------- + def onOpen(self): + """连接成功""" + self.gateway.connected = True + self.writeLog(u'服务器连接成功') + + self.login() + + # 推送合约数据 + for symbol in self.symbols: + contract = VtContractData() + contract.gatewayName = self.gatewayName + contract.symbol = symbol + contract.exchange = EXCHANGE_OKEX + contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) + contract.name = symbol + contract.size = 0.00001 + contract.priceTick = 0.00001 + contract.productClass = PRODUCT_SPOT + self.gateway.onContract(contract) + + #---------------------------------------------------------------------- + def initCallback(self): + """初始化回调函数""" + for symbol in self.symbols: + # channel和symbol映射 + self.channelSymbolMap["ok_sub_spot_%s_ticker" % symbol] = symbol + self.channelSymbolMap["ok_sub_spot_%s_depth_5" % symbol] = symbol + + # channel和callback映射 + self.cbDict["ok_sub_spot_%s_ticker" % symbol] = self.onTicker + self.cbDict["ok_sub_spot_%s_depth_5" % symbol] = self.onDepth + self.cbDict["ok_sub_spot_%s_order" % symbol] = self.onSubSpotOrder + self.cbDict["ok_sub_spot_%s_balance" % symbol] = self.onSubSpotBalance + + self.cbDict['ok_spot_userinfo'] = self.onSpotUserInfo + self.cbDict['ok_spot_orderinfo'] = self.onSpotOrderInfo + self.cbDict['ok_spot_order'] = self.onSpotOrder + self.cbDict['ok_spot_cancel_order'] = self.onSpotCancelOrder + self.cbDict['login'] = self.onLogin + + #---------------------------------------------------------------------- + def onLogin(self, data): + """""" + self.writeLog(u'服务器登录成功') + + # 查询持仓 + self.spotUserInfo() + + # 订阅推送 + for symbol in self.symbols: + self.subscribe(symbol) + + #---------------------------------------------------------------------- + def onTicker(self, data): + """""" + channel = data['channel'] + symbol = self.channelSymbolMap[channel] + + if symbol not in self.tickDict: + tick = VtTickData() + tick.symbol = symbol + tick.exchange = EXCHANGE_OKEX + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + tick.gatewayName = self.gatewayName + + self.tickDict[symbol] = tick + else: + tick = self.tickDict[symbol] + + d = data['data'] + tick.highPrice = float(d['high']) + tick.lowPrice = float(d['low']) + tick.lastPrice = float(d['last']) + tick.volume = float(d['vol'].replace(',', '')) + tick.date, tick.time = self.generateDateTime(d['timestamp']) + + if tick.bidPrice1: + newtick = copy(tick) + self.gateway.onTick(newtick) + + #---------------------------------------------------------------------- + def onDepth(self, data): + """""" + channel = data['channel'] + symbol = self.channelSymbolMap[channel] + + if symbol not in self.tickDict: + tick = VtTickData() + tick.symbol = symbol + tick.exchange = EXCHANGE_OKEX + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + tick.gatewayName = self.gatewayName + + self.tickDict[symbol] = tick + else: + tick = self.tickDict[symbol] + + d = data['data'] + + tick.bidPrice1, tick.bidVolume1 = d['bids'][0] + tick.bidPrice2, tick.bidVolume2 = d['bids'][1] + tick.bidPrice3, tick.bidVolume3 = d['bids'][2] + tick.bidPrice4, tick.bidVolume4 = d['bids'][3] + tick.bidPrice5, tick.bidVolume5 = d['bids'][4] + + tick.askPrice1, tick.askVolume1 = d['asks'][-1] + tick.askPrice2, tick.askVolume2 = d['asks'][-2] + tick.askPrice3, tick.askVolume3 = d['asks'][-3] + tick.askPrice4, tick.askVolume4 = d['asks'][-4] + tick.askPrice5, tick.askVolume5 = d['asks'][-5] + + tick.date, tick.time = self.generateDateTime(d['timestamp']) + + if tick.lastPrice: + newtick = copy(tick) + self.gateway.onTick(newtick) + + #---------------------------------------------------------------------- + def onSpotOrder(self, data): + """""" + # 如果委托失败,则通知委托被拒单的信息 + if self.checkDataError(data): + try: + localNo = self.localNoQueue.get_nowait() + except Empty: + return + + order = self.localOrderDict[localNo] + order.status = STATUS_REJECTED + self.gateway.onOrder(order) + + #---------------------------------------------------------------------- + def onSpotCancelOrder(self, data): + """""" + self.checkDataError(data) + + #---------------------------------------------------------------------- + def onSpotUserInfo(self, data): + """现货账户资金推送""" + if self.checkDataError(data): + return + + funds = data['data']['info']['funds'] + free = funds['free'] + freezed = funds['freezed'] + + # 持仓信息 + for symbol in free.keys(): + frozen = float(freezed[symbol]) + available = float(free[symbol]) + + if frozen or available: + pos = VtPositionData() + pos.gatewayName = self.gatewayName + + pos.symbol = symbol + pos.exchange = EXCHANGE_OKEX + pos.vtSymbol = '.'.join([pos.symbol, pos.exchange]) + pos.direction = DIRECTION_LONG + pos.vtPositionName = '.'.join([pos.vtSymbol, pos.direction]) + + pos.frozen = frozen + pos.position = frozen + available + + self.gateway.onPosition(pos) + + self.writeLog(u'持仓信息查询成功') + + # 查询委托 + for symbol in self.symbols: + self.spotOrderInfo(symbol, '-1') + + #---------------------------------------------------------------------- + def onSpotOrderInfo(self, data): + """委托信息查询回调""" + if self.checkDataError(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 = d['symbol'] + order.exchange = EXCHANGE_OKEX + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + order.orderID = localNo + order.vtOrderID = '.'.join([self.gatewayName, order.orderID]) + + order.price = d['price'] + order.totalVolume = d['amount'] + order.direction, priceType = priceTypeMap[d['type']] + date, order.orderTime = self.generateDateTime(d['create_date']) + + self.orderDict[orderId] = order + else: + order = self.orderDict[orderId] + + order.tradedVolume = d['deal_amount'] + order.status = statusMap[d['status']] + + self.gateway.onOrder(copy(order)) + + #---------------------------------------------------------------------- + def onSubSpotOrder(self, data): + """交易数据""" + rawData = data["data"] + orderId = str(rawData['orderId']) + + # 获取本地委托号 + if orderId in self.orderIdDict: + localNo = self.orderIdDict[orderId] + else: + try: + localNo = self.localNoQueue.get_nowait() + except Empty: + self.localNo += 1 + localNo = str(self.localNo) + + self.localNoDict[localNo] = orderId + self.orderIdDict[orderId] = localNo + + # 获取委托对象 + if orderId in self.orderDict: + order = self.orderDict[orderId] + else: + order = VtOrderData() + order.gatewayName = self.gatewayName + order.symbol = rawData['symbol'] + order.exchange = EXCHANGE_OKEX + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + order.orderID = localNo + order.vtOrderID = '.'.join([self.gatewayName, localNo]) + order.direction, priceType = priceTypeMap[rawData['tradeType']] + order.price = float(rawData['tradeUnitPrice']) + order.totalVolume = float(rawData['tradeAmount']) + date, order.orderTime = self.generateDateTime(rawData['createdDate']) + + lastTradedVolume = order.tradedVolume + + order.status = statusMap[rawData['status']] + order.tradedVolume = float(rawData['completedTradeAmount']) + self.gateway.onOrder(copy(order)) + + # 成交信息 + if order.tradedVolume > lastTradedVolume: + trade = VtTradeData() + trade.gatewayName = self.gatewayName + + trade.symbol = order.symbol + trade.exchange = order.exchange + trade.vtSymbol = order.vtSymbol + + self.tradeID += 1 + trade.tradeID = str(self.tradeID) + trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID]) + + trade.orderID = order.orderID + trade.vtOrderID = order.vtOrderID + + trade.direction = order.direction + trade.price = float(rawData['averagePrice']) + trade.volume = order.tradedVolume - lastTradedVolume + + trade.tradeTime = datetime.now().strftime('%H:%M:%S') + self.gateway.onTrade(trade) + + # 撤单 + if localNo in self.cancelDict: + req = self.cancelDict[localNo] + self.spotCancel(req) + del self.cancelDict[localNo] + + #---------------------------------------------------------------------- + def onSubSpotBalance(self, data): + """""" + rawData = data['data'] + free = rawData['info']['free'] + freezed = rawData['info']['freezed'] + + for symbol in free.keys(): + pos = VtPositionData() + pos.gatewayName = self.gatewayName + pos.symbol = symbol + pos.exchange = EXCHANGE_OKEX + pos.vtSymbol = '.'.join([pos.symbol, pos.exchange]) + pos.direction = DIRECTION_LONG + pos.vtPositionName = '.'.join([pos.vtSymbol, pos.direction]) + pos.frozen = float(freezed[symbol]) + pos.position = pos.frozen + float(free[symbol]) + + self.gateway.onPosition(pos) + + #---------------------------------------------------------------------- + def init(self, apiKey, secretKey, trace, symbols): + """初始化接口""" + self.symbols = symbols + self.initCallback() + + self.connect(OKEX_SPOT_HOST, apiKey, secretKey, trace) + self.writeLog(u'接口初始化成功') + + #---------------------------------------------------------------------- + def sendOrder(self, req): + """发单""" + type_ = priceTypeMapReverse[(req.direction, req.priceType)] + req.volume = 0.001 + self.spotOrder(req.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)]) + + # 缓存委托信息 + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.symbol = req.symbol + order.exchange = EXCHANGE_OKEX + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + order.orderID= str(self.localNo) + order.vtOrderID = vtOrderID + order.direction = req.direction + order.price = req.price + order.totalVolume = req.volume + + self.localOrderDict[str(self.localNo)] = order + + return vtOrderID + + #---------------------------------------------------------------------- + def cancelOrder(self, req): + """撤单""" + localNo = req.orderID + if localNo in self.localNoDict: + orderID = self.localNoDict[localNo] + self.spotCancelOrder(req.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 + + #---------------------------------------------------------------------- + def writeLog(self, content): + """快速记录日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def checkDataError(self, data): + """检查回报是否存在错误""" + rawData = data['data'] + if 'error_code' not in rawData: + return False + else: + error = VtErrorData() + error.gatewayName = self.gatewayName + error.errorID = rawData['error_code'] + error.errorMsg = u'请求失败,功能:%s' %data['channel'] + self.gateway.onError(error) + return True + + #---------------------------------------------------------------------- + def subscribe(self, symbol): + """订阅行情""" + symbol = symbol + + self.subscribeSpotTicker(symbol) + self.subscribeSpotDepth(symbol, 5) + self.subSpotOrder(symbol) + self.subSpotBalance(symbol) + \ No newline at end of file From 230688dea854662639f7c919b67292874fdae8f9 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Fri, 1 Jun 2018 12:27:36 +0800 Subject: [PATCH 012/135] =?UTF-8?q?[Del]=E7=A7=BB=E9=99=A4beta=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E4=B8=8B=E7=9A=84OKEX=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beta/api/okex/README.md | 14 - beta/api/okex/__init__.py | 4 - beta/api/okex/test.py | 52 - beta/api/okex/vnokex.py | 413 -------- beta/gateway/okexGateway/OKEX_connect.json | 6 - beta/gateway/okexGateway/__init__.py | 12 - beta/gateway/okexGateway/okexGateway.py | 965 ------------------ .../gateway/huobiGateway/huobiGateway.py | 1 - 8 files changed, 1467 deletions(-) delete mode 100644 beta/api/okex/README.md delete mode 100644 beta/api/okex/__init__.py delete mode 100644 beta/api/okex/test.py delete mode 100644 beta/api/okex/vnokex.py delete mode 100644 beta/gateway/okexGateway/OKEX_connect.json delete mode 100644 beta/gateway/okexGateway/__init__.py delete mode 100644 beta/gateway/okexGateway/okexGateway.py diff --git a/beta/api/okex/README.md b/beta/api/okex/README.md deleted file mode 100644 index f98f155c..00000000 --- a/beta/api/okex/README.md +++ /dev/null @@ -1,14 +0,0 @@ -### 简介 - -OKEX的比特币交易接口,基于Websocket API开发,实现了以下功能: - -1. 发送、撤销委托 - -2. 查询委托、持仓、资金、成交历史 - -3. 实时行情、成交、资金更新的推送 - -### API信息 - -链接:[https://www.okex.com/ws_getStarted.html](https://www.okex.com/ws_getStarted.html) - diff --git a/beta/api/okex/__init__.py b/beta/api/okex/__init__.py deleted file mode 100644 index a0b1b632..00000000 --- a/beta/api/okex/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# encoding: UTF-8 - -from __future__ import absolute_import -from .vnokex import OkexSpotApi, OkexFuturesApi, CONTRACT_SYMBOL, SPOT_CURRENCY \ No newline at end of file diff --git a/beta/api/okex/test.py b/beta/api/okex/test.py deleted file mode 100644 index 2e20cb82..00000000 --- a/beta/api/okex/test.py +++ /dev/null @@ -1,52 +0,0 @@ -# encoding: UTF-8 - -from __future__ import absolute_import -from .vnokex import * - -# 在OkCoin网站申请这两个Key,分别对应用户名和密码 -apiKey = '你的accessKey' -secretKey = '你的secretKey' - -# 创建API对象 -api = OkexSpotApi() - -api.connect(apiKey, secretKey, True) - -sleep(3) - -#api.login() -api.subscribeSpotTicker("bch_btc") -api.subscribeSpotDepth("bch_btc") -api.subscribeSpotDepth("bch_btc", 5) -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 = OkexFuturesApi() -# api.connect(apiKey, secretKey, True) - -# sleep(3) -#api.subsribeFutureTicker("btc","this_week") -#api.subscribeFutureKline("btc","this_week", "30min") -#api.subscribeFutureDepth("btc","this_week") -#api.subscribeFutureDepth("btc","this_week", 5) -#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() - -''' -合约账户信息、 持仓信息等,在登录后都会自动推送。。。官方文档这样写的,还没实际验证过 -''' - -input() \ No newline at end of file diff --git a/beta/api/okex/vnokex.py b/beta/api/okex/vnokex.py deleted file mode 100644 index 18267b97..00000000 --- a/beta/api/okex/vnokex.py +++ /dev/null @@ -1,413 +0,0 @@ -# encoding: UTF-8 - -from __future__ import print_function -import hashlib -import zlib -import json -from time import sleep -from threading import Thread - -import websocket - -# OKEX网站 -OKEX_USD_SPOT = 'wss://real.okex.com:10441/websocket' # OKEX 现货地址 -OKEX_USD_CONTRACT = 'wss://real.okex.com:10440/websocket/okexapi' # OKEX 期货地址 - -SPOT_CURRENCY = ["usdt", - "btc", - "ltc", - "eth", - "etc", - "bch"] - -SPOT_SYMBOL = ["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"] - -KLINE_PERIOD = ["1min", - "3min", - "5min", - "15min", - "30min", - "1hour", - "2hour", - "4hour", - "6hour", - "12hour", - "day", - "3day", - "week"] - -CONTRACT_SYMBOL = ["btc", - "ltc", - "eth", - "etc", - "bch"] - -CONTRACT_TYPE = ["this_week", - "next_week", - "quarter"] - - -######################################################################## -class OkexApi(object): - """交易接口""" - - #---------------------------------------------------------------------- - def __init__(self): - """Constructor""" - self.host = '' # 服务器 - self.apiKey = '' # 用户名 - self.secretKey = '' # 密码 - - self.ws = None # websocket应用对象 现货对象 - self.thread = None # 初始化线程 - - #---------------------------------------------------------------------- - 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 connect(self, apiKey, secretKey, trace=False): - self.host = OKEX_USD_SPOT - self.apiKey = apiKey - self.secretKey = secretKey - - websocket.enableTrace(trace) - - 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 readData(self, evt): - """解码推送收到的数据""" - data = json.loads(evt) - return data - - #---------------------------------------------------------------------- - def close(self): - """关闭接口""" - if self.thread and self.thread.isAlive(): - self.ws.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 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.send(j) - except websocket.WebSocketConnectionClosedException: - pass - - #---------------------------------------------------------------------- - def sendDataRequest(self, channel): - """发送数据请求""" - d = {} - d['event'] = 'addChannel' - d['channel'] = channel - j = json.dumps(d) - - # 若触发异常则重连 - try: - self.ws.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.send(j) - return True - except websocket.WebSocketConnectionClosedException: - return False - - -######################################################################## -class OkexSpotApi(OkexApi): - """现货交易接口""" - - #---------------------------------------------------------------------- - def __init__(self): - """Constructor""" - super(OkexSpotApi, self).__init__() - - #---------------------------------------------------------------------- - def subscribeSpotTicker(self, symbol): - """订阅现货的Tick""" - channel = 'ok_sub_spot_%s_ticker' %symbol - self.sendDataRequest(channel) - - #---------------------------------------------------------------------- - def subscribeSpotDepth(self, symbol, depth=0): - """订阅现货的深度""" - channel = 'ok_sub_spot_%s_depth' %symbol - if depth: - channel = channel + '_' + str(depth) - self.sendDataRequest(channel) - - #---------------------------------------------------------------------- - def subscribeSpotDeals(self, symbol): - channel = 'ok_sub_spot_%s_deals' %symbol - self.sendDataRequest(channel) - - #---------------------------------------------------------------------- - def subscribeSpotKlines(self, symbol, period): - channel = 'ok_sub_spot_%s_kline_%s' %(symbol, period) - self.sendDataRequest(channel) - - #---------------------------------------------------------------------- - def spotTrade(self, symbol, type_, price, amount): - """现货委托""" - params = {} - params['symbol'] = str(symbol) - params['type'] = str(type_) - params['price'] = str(price) - params['amount'] = str(amount) - - channel = 'ok_spot_order' - - self.sendTradingRequest(channel, params) - - #---------------------------------------------------------------------- - def spotCancelOrder(self, symbol, orderid): - """现货撤单""" - params = {} - params['symbol'] = str(symbol) - 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, orderid): - """查询现货委托信息""" - params = {} - params['symbol'] = str(symbol) - params['order_id'] = str(orderid) - - channel = 'ok_spot_orderinfo' - - self.sendTradingRequest(channel, params) - - - -######################################################################## -class OkexFuturesApi(OkexApi): - """期货交易接口 - - 交割推送信息: - [{ - "channel": "btc_forecast_price", - "timestamp":"1490341322021", - "data": "998.8" - }] - data(string): 预估交割价格 - timestamp(string): 时间戳 - - 无需订阅,交割前一小时自动返回 - """ - - #---------------------------------------------------------------------- - def __init__(self): - """Constructor""" - super(OkexFuturesApi, self).__init__() - - #---------------------------------------------------------------------- - def subsribeFuturesTicker(self, symbol, contractType): - """订阅期货行情""" - channel ='ok_sub_futureusd_%s_ticker_%s' %(symbol, contractType) - self.sendDataRequest(channel) - - #---------------------------------------------------------------------- - def subscribeFuturesKline(self, symbol, contractType, period): - """订阅期货K线""" - channel = 'ok_sub_futureusd_%s_kline_%s_%s' %(symbol, contractType, period) - self.sendDataRequest(channel) - - #---------------------------------------------------------------------- - def subscribeFuturesDepth(self, symbol, contractType, depth=0): - """订阅期货深度""" - channel = 'ok_sub_futureusd_%s_depth_%s' %(symbol, contractType) - if depth: - channel = channel + '_' + str(depth) - self.sendDataRequest(channel) - - #---------------------------------------------------------------------- - def subscribeFuturesTrades(self, symbol, contractType): - """订阅期货成交""" - channel = 'ok_sub_futureusd_%s_trade_%s' %(symbol, contractType) - self.sendDataRequest(channel) - - #---------------------------------------------------------------------- - def subscribeFuturesIndex(self, symbol): - """订阅期货指数""" - channel = 'ok_sub_futureusd_%s_index' %symbol - self.sendDataRequest(channel) - - #---------------------------------------------------------------------- - def futuresTrade(self, symbol, contractType, type_, price, amount, matchPrice='0', leverRate='10'): - """期货委托""" - params = {} - params['symbol'] = str(symbol) - params['contract_type'] = str(contractType) - params['price'] = str(price) - params['amount'] = str(amount) - params['type'] = type_ # 1:开多 2:开空 3:平多 4:平空 - params['match_price'] = matchPrice # 是否为对手价: 0:不是 1:是 当取值为1时,price无效 - params['lever_rate'] = leverRate - - channel = 'ok_futureusd_trade' - - self.sendTradingRequest(channel, params) - - #---------------------------------------------------------------------- - def futuresCancelOrder(self, symbol, orderid, contractType): - """期货撤单""" - params = {} - params['symbol'] = str(symbol) - params['order_id'] = str(orderid) - params['contract_type'] = str(contractType) - - channel = 'ok_futureusd_cancel_order' - - self.sendTradingRequest(channel, params) - - #---------------------------------------------------------------------- - def futuresUserInfo(self): - """查询期货账户""" - channel = 'ok_futureusd_userinfo' - self.sendTradingRequest(channel, {}) - - #---------------------------------------------------------------------- - def futuresOrderInfo(self, symbol, orderid, contractType, status, current_page, page_length=10): - """查询期货委托""" - params = {} - params['symbol'] = str(symbol) - params['order_id'] = str(orderid) - params['contract_type'] = str(contractType) - 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 subscribeFuturesTrades( self): - channel = 'ok_sub_futureusd_trades' - self.sendTradingRequest(channel, {}) - - #---------------------------------------------------------------------- - def subscribeFuturesUserInfo(self): - """订阅期货账户信息""" - channel = 'ok_sub_futureusd_userinfo' - self.sendTradingRequest(channel, {}) - - #---------------------------------------------------------------------- - def subscribeFuturesPositions(self): - """订阅期货持仓信息""" - channel = 'ok_sub_futureusd_positions' - self.sendTradingRequest(channel, {}) - \ No newline at end of file diff --git a/beta/gateway/okexGateway/OKEX_connect.json b/beta/gateway/okexGateway/OKEX_connect.json deleted file mode 100644 index a0224e4c..00000000 --- a/beta/gateway/okexGateway/OKEX_connect.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "apiKey": "你的apiKey", - "secretKey": "你的secretKey", - "trace": false, - "leverage": 10 -} \ No newline at end of file diff --git a/beta/gateway/okexGateway/__init__.py b/beta/gateway/okexGateway/__init__.py deleted file mode 100644 index bafb2366..00000000 --- a/beta/gateway/okexGateway/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# encoding: UTF-8 - -from __future__ import absolute_import -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/beta/gateway/okexGateway/okexGateway.py b/beta/gateway/okexGateway/okexGateway.py deleted file mode 100644 index ba8deb48..00000000 --- a/beta/gateway/okexGateway/okexGateway.py +++ /dev/null @@ -1,965 +0,0 @@ -# encoding: UTF-8 - -''' -vnpy.api.okex的gateway接入 - -注意: -1. 目前仅支持USD现货交易 -''' -from __future__ import print_function - -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 OkexSpotApi, CONTRACT_SYMBOL, SPOT_CURRENCY -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 - - -okex_all_symbol_pairs = ['ref_usdt', 'soc_usdt', 'light_usdt', 'avt_usdt', 'of_usdt', 'brd_usdt', 'ast_usdt', 'int_usdt', 'zrx_usdt', 'ctr_usdt', 'dgd_usdt', 'aidoc_usdt', 'wtc_usdt', 'swftc_usdt', 'wrc_usdt', 'sub_usdt', 'dna_usdt', 'knc_usdt', 'kcash_usdt', 'mdt_usdt', 'theta_usdt', 'ppt_usdt', 'utk_usdt', 'qvt_usdt', 'salt_usdt', 'la_usdt', 'itc_usdt', 'fair_usdt', 'yee_usdt', '1st_usdt', 'fun_usdt', 'iost_usdt', 'mkr_usdt', 'tio_usdt', 'req_usdt', 'ubtc_usdt', 'icx_usdt', 'tct_usdt', 'san_usdt', 'lrc_usdt', 'icn_usdt', 'cvc_usdt', 'eth_usdt', 'poe_usdt', 'xlm_usdt', 'iota_usdt', 'eos_usdt', 'nuls_usdt', 'mot_usdt', 'neo_usdt', 'gnx_usdt', 'dgb_usdt', 'evx_usdt', 'ltc_usdt', 'mda_usdt', 'etc_usdt', 'dpy_usdt', 'tnb_usdt', 'nas_usdt', 'btc_usdt', 'smt_usdt', 'ssc_usdt', 'oax_usdt', 'yoyo_usdt', 'snc_usdt', 'sngls_usdt', 'bch_usdt', 'mana_usdt', 'mof_usdt', 'mco_usdt', 'vib_usdt', 'topc_usdt', 'pra_usdt', 'bnt_usdt', 'xmr_usdt', 'edo_usdt', 'snt_usdt', 'eng_usdt', 'stc_usdt', 'qtum_usdt', 'key_usdt', 'ins_usdt', 'rnt_usdt', 'bcd_usdt', 'amm_usdt', 'lend_usdt', 'btm_usdt', 'elf_usdt', 'xuc_usdt', 'cag_usdt', 'snm_usdt', 'act_usdt', 'dash_usdt', 'zec_usdt', 'storj_usdt', 'pay_usdt', 'vee_usdt', 'show_usdt', 'trx_usdt', 'atl_usdt', 'ark_usdt', 'ost_usdt', 'gnt_usdt', 'dat_usdt', 'rcn_usdt', 'qun_usdt', 'mth_usdt', 'rct_usdt', 'read_usdt', 'gas_usdt', 'btg_usdt', 'mtl_usdt', 'cmt_usdt', 'xrp_usdt', 'spf_usdt', 'aac_usdt', 'can_usdt', 'omg_usdt', 'hsr_usdt', 'link_usdt', 'dnt_usdt', 'true_usdt', 'ukg_usdt', 'xem_usdt', 'ngc_usdt', 'lev_usdt', 'rdn_usdt', 'ace_usdt', 'ipc_usdt', 'ugc_usdt', 'viu_usdt', 'mag_usdt', 'hot_usdt', 'pst_usdt', -'ref_btc', 'soc_btc', 'light_btc', 'avt_btc', 'of_btc', 'brd_btc', 'ast_btc', 'int_btc', 'zrx_btc', 'ctr_btc', 'dgd_btc', 'aidoc_btc', 'wtc_btc', 'swftc_btc', 'wrc_btc', 'sub_btc', 'dna_btc', 'knc_btc', 'kcash_btc', 'mdt_btc', 'theta_btc', 'ppt_btc', 'utk_btc', 'qvt_btc', 'salt_btc', 'la_btc', 'itc_btc', 'fair_btc', 'yee_btc', '1st_btc', 'fun_btc', 'iost_btc', 'mkr_btc', 'tio_btc', 'req_btc', 'ubtc_btc', 'icx_btc', 'tct_btc', 'san_btc', 'lrc_btc', 'icn_btc', 'cvc_btc', 'eth_btc', 'poe_btc', 'xlm_btc', 'iota_btc', 'eos_btc', 'nuls_btc', 'mot_btc', 'neo_btc', 'gnx_btc', 'dgb_btc', 'evx_btc', 'ltc_btc', 'mda_btc', 'etc_btc', 'dpy_btc', 'tnb_btc', 'nas_btc', 'btc_btc', 'smt_btc', 'ssc_btc', 'oax_btc', 'yoyo_btc', 'snc_btc', 'sngls_btc', 'bch_btc', 'mana_btc', 'mof_btc', 'mco_btc', 'vib_btc', 'topc_btc', 'pra_btc', 'bnt_btc', 'xmr_btc', 'edo_btc', 'snt_btc', 'eng_btc', 'stc_btc', 'qtum_btc', 'key_btc', 'ins_btc', 'rnt_btc', 'bcd_btc', 'amm_btc', 'lend_btc', 'btm_btc', 'elf_btc', 'xuc_btc', 'cag_btc', 'snm_btc', 'act_btc', 'dash_btc', 'zec_btc', 'storj_btc', 'pay_btc', 'vee_btc', 'show_btc', 'trx_btc', 'atl_btc', 'ark_btc', 'ost_btc', 'gnt_btc', 'dat_btc', 'rcn_btc', 'qun_btc', 'mth_btc', 'rct_btc', 'read_btc', 'gas_btc', 'btg_btc', 'mtl_btc', 'cmt_btc', 'xrp_btc', 'spf_btc', 'aac_btc', 'can_btc', 'omg_btc', 'hsr_btc', 'link_btc', 'dnt_btc', 'true_btc', 'ukg_btc', 'xem_btc', 'ngc_btc', 'lev_btc', 'rdn_btc', 'ace_btc', 'ipc_btc', 'ugc_btc', 'viu_btc', 'mag_btc', 'hot_btc', 'pst_btc'] - - -######################################################################## -class OkexGateway(VtGateway): - """OKEX交易接口""" - - #---------------------------------------------------------------------- - def __init__(self, eventEngine, gatewayName='OKEX'): - """Constructor""" - super(OkexGateway, self).__init__(eventEngine, gatewayName) - - self.api_spot = SpotApi(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(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 SpotApi(OkexSpotApi): - """okex的API实现""" - - #---------------------------------------------------------------------- - def __init__(self, gateway): - """Constructor""" - super(SpotApi, 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 as 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 as 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 as 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 : - 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 SPOT_CURRENCY: - 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/huobiGateway/huobiGateway.py b/vnpy/trader/gateway/huobiGateway/huobiGateway.py index b3fc31d4..2b84a7ee 100644 --- a/vnpy/trader/gateway/huobiGateway/huobiGateway.py +++ b/vnpy/trader/gateway/huobiGateway/huobiGateway.py @@ -322,7 +322,6 @@ class HuobiDataApi(DataApi): self.gateway.onTick(tick) - ######################################################################## class HuobiTradeApi(TradeApi): """交易API实现""" From 653357e58233f4f6c1b88678caebc85a27a0e9ae Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Fri, 1 Jun 2018 12:31:12 +0800 Subject: [PATCH 013/135] =?UTF-8?q?[Add]OKEX=E6=8E=A5=E5=8F=A3=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E8=87=AA=E5=8A=A8=E9=87=8D=E8=BF=9E=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/okex/vnokex.py | 49 ++++++++++++++----- .../trader/gateway/okexGateway/okexGateway.py | 7 ++- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/vnpy/api/okex/vnokex.py b/vnpy/api/okex/vnokex.py index b2b86db0..09500e59 100644 --- a/vnpy/api/okex/vnokex.py +++ b/vnpy/api/okex/vnokex.py @@ -75,14 +75,16 @@ class OkexApi(object): self.heartbeatCount = 0 # 心跳计数 self.heartbeatThread = None # 心跳线程 self.heartbeatReceived = True # 心跳是否收到 + + self.reconnecting = False # 重新连接中 #---------------------------------------------------------------------- def heartbeat(self): """""" while self.active: self.heartbeatCount += 1 - - if self.heartbeatCount < 30: + + if self.heartbeatCount < 10: sleep(1) else: self.heartbeatCount = 0 @@ -90,16 +92,26 @@ class OkexApi(object): if not self.heartbeatReceived: self.reconnect() else: + self.heartbeatReceived = False d = {'event': 'ping'} j = json.dumps(d) - self.ws.send(j) - self.heartbeatReceived = False + + try: + self.ws.send(j) + except websocket.WebSocketConnectionClosedException: + self.reconnect() + #---------------------------------------------------------------------- def reconnect(self): """重新连接""" - self.close() # 首先关闭之前的连接 - self.initWebsocket() + if not self.reconnecting: + self.reconnecting = True + + self.closeWebsocket() # 首先关闭之前的连接 + self.initWebsocket() + + self.reconnecting = False #---------------------------------------------------------------------- def connect(self, host, apiKey, secretKey, trace=False): @@ -132,15 +144,24 @@ class OkexApi(object): return data #---------------------------------------------------------------------- - def close(self): + def closeHeartbeat(self): """关闭接口""" if self.heartbeatThread and self.heartbeatThread.isAlive(): self.active = False self.heartbeatThread.join() - + + #---------------------------------------------------------------------- + def closeWebsocket(self): + """关闭WS""" if self.wsThread and self.wsThread.isAlive(): self.ws.close() self.wsThread.join() + + #---------------------------------------------------------------------- + def close(self): + """""" + self.closeHeartbeat() + self.closeWebsocket() #---------------------------------------------------------------------- def onMessage(self, data): @@ -186,8 +207,9 @@ class OkexApi(object): #---------------------------------------------------------------------- def onOpenCallback(self, ws): """""" - self.heartbeatThread = Thread(target=self.heartbeat) - self.heartbeatThread.start() + if not self.heartbeatThread: + self.heartbeatThread = Thread(target=self.heartbeat) + self.heartbeatThread.start() self.onOpen() @@ -221,8 +243,10 @@ class OkexApi(object): # 若触发异常则重连 try: self.ws.send(j) + return True except websocket.WebSocketConnectionClosedException: - pass + self.reconnect() + return False #---------------------------------------------------------------------- def login(self): @@ -241,6 +265,7 @@ class OkexApi(object): self.ws.send(j) return True except websocket.WebSocketConnectionClosedException: + self.reconnect() return False @@ -288,7 +313,7 @@ class OkexSpotApi(OkexApi): channel = 'ok_spot_order' - self.sendRequest(channel, params) + return self.sendRequest(channel, params) #---------------------------------------------------------------------- def spotCancelOrder(self, symbol, orderid): diff --git a/vnpy/trader/gateway/okexGateway/okexGateway.py b/vnpy/trader/gateway/okexGateway/okexGateway.py index 9ee1cf79..0d0dad32 100644 --- a/vnpy/trader/gateway/okexGateway/okexGateway.py +++ b/vnpy/trader/gateway/okexGateway/okexGateway.py @@ -524,7 +524,6 @@ class SpotApi(OkexSpotApi): """初始化接口""" self.symbols = symbols self.initCallback() - self.connect(OKEX_SPOT_HOST, apiKey, secretKey, trace) self.writeLog(u'接口初始化成功') @@ -533,7 +532,11 @@ class SpotApi(OkexSpotApi): """发单""" type_ = priceTypeMapReverse[(req.direction, req.priceType)] req.volume = 0.001 - self.spotOrder(req.symbol, type_, str(req.price), str(req.volume)) + result = self.spotOrder(req.symbol, type_, str(req.price), str(req.volume)) + + # 若请求失败,则返回空字符串委托号 + if not result: + return '' # 本地委托号加1,并将对应字符串保存到队列中,返回基于本地委托号的vtOrderID self.localNo += 1 From fe211876dab1b9544eb491d60ecba37f858dd29e Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 2 Jun 2018 21:21:19 +0800 Subject: [PATCH 014/135] =?UTF-8?q?[Del]=E7=A7=BB=E9=99=A4okexGateway?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=97=B6=E7=9A=84=E5=BC=BA=E5=88=B6=E6=95=B0?= =?UTF-8?q?=E9=87=8F=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/gateway/okexGateway/okexGateway.py | 1 - 1 file changed, 1 deletion(-) diff --git a/vnpy/trader/gateway/okexGateway/okexGateway.py b/vnpy/trader/gateway/okexGateway/okexGateway.py index 0d0dad32..e50fa706 100644 --- a/vnpy/trader/gateway/okexGateway/okexGateway.py +++ b/vnpy/trader/gateway/okexGateway/okexGateway.py @@ -531,7 +531,6 @@ class SpotApi(OkexSpotApi): def sendOrder(self, req): """发单""" type_ = priceTypeMapReverse[(req.direction, req.priceType)] - req.volume = 0.001 result = self.spotOrder(req.symbol, type_, str(req.price), str(req.volume)) # 若请求失败,则返回空字符串委托号 From e587d4cf3b025233fbfdc19c4e8e0fd97d5f514d Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 2 Jun 2018 21:26:04 +0800 Subject: [PATCH 015/135] =?UTF-8?q?[Mod]=E4=BF=AE=E6=94=B9API=E9=83=A8?= =?UTF-8?q?=E5=88=86=E8=AF=B4=E6=98=8E=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/okex/README.md | 2 +- vnpy/api/okex/vnokex.py | 6 ++++++ vnpy/trader/gateway/okexGateway/okexGateway.py | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/vnpy/api/okex/README.md b/vnpy/api/okex/README.md index f98f155c..5fa5cc86 100644 --- a/vnpy/api/okex/README.md +++ b/vnpy/api/okex/README.md @@ -10,5 +10,5 @@ OKEX的比特币交易接口,基于Websocket API开发,实现了以下功能 ### API信息 -链接:[https://www.okex.com/ws_getStarted.html](https://www.okex.com/ws_getStarted.html) +链接:[https://github.com/okcoin-okex/API-docs-OKEx.com](https://github.com/okcoin-okex/API-docs-OKEx.com) diff --git a/vnpy/api/okex/vnokex.py b/vnpy/api/okex/vnokex.py index 09500e59..061c1b2e 100644 --- a/vnpy/api/okex/vnokex.py +++ b/vnpy/api/okex/vnokex.py @@ -1,5 +1,11 @@ # encoding: UTF-8 +''' +OKEX的高性能异步交易API + +Contributor:ipqhjjybj 大佳 +''' + from __future__ import print_function import hashlib diff --git a/vnpy/trader/gateway/okexGateway/okexGateway.py b/vnpy/trader/gateway/okexGateway/okexGateway.py index e50fa706..6fb5d077 100644 --- a/vnpy/trader/gateway/okexGateway/okexGateway.py +++ b/vnpy/trader/gateway/okexGateway/okexGateway.py @@ -2,6 +2,8 @@ ''' vnpy.api.okex的gateway接入 + +Contributor:ipqhjjybj 大佳 ''' from __future__ import print_function From 53748292244d5b2e9d2751e90e79034e24417c0a Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 2 Jun 2018 22:29:10 +0800 Subject: [PATCH 016/135] =?UTF-8?q?[Mod]=E8=A7=A3=E5=86=B3OkexGateway?= =?UTF-8?q?=E4=B8=AD=E6=B7=B1=E5=BA=A6=E4=BB=B7=E6=A0=BC=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E7=B1=BB=E5=9E=8B=E4=B8=BAunicode=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trader/gateway/okexGateway/okexGateway.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/vnpy/trader/gateway/okexGateway/okexGateway.py b/vnpy/trader/gateway/okexGateway/okexGateway.py index 6fb5d077..58856f35 100644 --- a/vnpy/trader/gateway/okexGateway/okexGateway.py +++ b/vnpy/trader/gateway/okexGateway/okexGateway.py @@ -329,6 +329,28 @@ class SpotApi(OkexSpotApi): tick.askPrice4, tick.askVolume4 = d['asks'][-4] tick.askPrice5, tick.askVolume5 = d['asks'][-5] + tick.bidPrice1 = float(tick.bidPrice1) + tick.bidPrice2 = float(tick.bidPrice2) + tick.bidPrice3 = float(tick.bidPrice3) + tick.bidPrice4 = float(tick.bidPrice4) + tick.bidPrice5 = float(tick.bidPrice5) + tick.askPrice1 = float(tick.askPrice1) + tick.askPrice2 = float(tick.askPrice2) + tick.askPrice3 = float(tick.askPrice3) + tick.askPrice4 = float(tick.askPrice4) + tick.askPrice5 = float(tick.askPrice5) + + tick.bidVolume1 = float(tick.bidVolume1) + tick.bidVolume2 = float(tick.bidVolume2) + tick.bidVolume3 = float(tick.bidVolume3) + tick.bidVolume4 = float(tick.bidVolume4) + tick.bidVolume5 = float(tick.bidVolume5) + tick.askVolume1 = float(tick.askVolume1) + tick.askVolume2 = float(tick.askVolume2) + tick.askVolume3 = float(tick.askVolume3) + tick.askVolume4 = float(tick.askVolume4) + tick.askVolume5 = float(tick.askVolume5) + tick.date, tick.time = self.generateDateTime(d['timestamp']) if tick.lastPrice: From ba647a1b631fcf328579a38ddf6bbf88c0c12be5 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 4 Jun 2018 14:12:25 +0800 Subject: [PATCH 017/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9E=E5=B8=81?= =?UTF-8?q?=E5=AE=89=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/binance/__init__.py | 1 + vnpy/api/binance/test.py | 43 ++ vnpy/api/binance/vnbinance.py | 624 ++++++++++++++++++ .../binanceGateway/BINANCE_connect.json | 5 + .../trader/gateway/binanceGateway/__init__.py | 10 + .../gateway/binanceGateway/binanceGateway.py | 498 ++++++++++++++ 6 files changed, 1181 insertions(+) create mode 100644 vnpy/api/binance/__init__.py create mode 100644 vnpy/api/binance/test.py create mode 100644 vnpy/api/binance/vnbinance.py create mode 100644 vnpy/trader/gateway/binanceGateway/BINANCE_connect.json create mode 100644 vnpy/trader/gateway/binanceGateway/__init__.py create mode 100644 vnpy/trader/gateway/binanceGateway/binanceGateway.py diff --git a/vnpy/api/binance/__init__.py b/vnpy/api/binance/__init__.py new file mode 100644 index 00000000..57a962fc --- /dev/null +++ b/vnpy/api/binance/__init__.py @@ -0,0 +1 @@ +from .vnbinance import BinanceApi \ No newline at end of file diff --git a/vnpy/api/binance/test.py b/vnpy/api/binance/test.py new file mode 100644 index 00000000..83dd365c --- /dev/null +++ b/vnpy/api/binance/test.py @@ -0,0 +1,43 @@ +from time import sleep + +from vnbinance import BinanceApi + + +if __name__ == '__main__': + apiKey = '' + secretKey = '' + + api = BinanceApi() + api.init(apiKey, secretKey) + api.start() + + #api.queryPing() + #api.queryTime() + #api.queryExchangeInfo() + + api.queryDepth('BTCUSDT') + #api.queryDepth('BTCUSDT') + #api.queryTrades('BTCUSDT') + #api.queryAggTrades('BTCUSDT') + #api.queryKlines('BTCUSDT', '1m') + #api.queryTicker24HR() + #api.queryTickerPrice() + #api.queryBookTicker() + + api.queryAccount() + #api.queryOrder('BTCUSDT', '1231231') + #api.queryOpenOrders('BTCUSDT') + #api.queryAllOrders('BTCUSDT') + #api.queryMyTrades('BTCUSDT') + + #api.startStream() + #api.keepaliveStream('12312312') + #api.closeStream('123213') + + #api.newOrder('BTCUSDT', 'BUY', 'LIMIT', 3000, 1, 'GTC') + #api.cancelOrder('BTCUSDT', '132213123') + + #api.initDataStream(['btcusdt@ticker', 'btcusdt@depth5']) + #api.initUserStream('NXvaiFwZz2LuKqINVerKOnWaQQG1JhdQNejiZKY9Kmgk4lZgTvm3nRAnXJK7') + + raw_input() \ No newline at end of file diff --git a/vnpy/api/binance/vnbinance.py b/vnpy/api/binance/vnbinance.py new file mode 100644 index 00000000..8e9ade96 --- /dev/null +++ b/vnpy/api/binance/vnbinance.py @@ -0,0 +1,624 @@ +# encoding: UTF-8 + +import json +import requests +import hmac +import hashlib +import traceback + +from queue import Queue, Empty +from threading import Thread +from multiprocessing.dummy import Pool +from time import time, sleep +from urllib import urlencode + +from websocket import create_connection + + + +REST_ENDPOINT = 'https://www.binance.com' +DATASTREAM_ENDPOINT = 'wss://stream.binance.com:9443/stream?streams=' +USERSTREAM_ENDPOINT = 'wss://stream.binance.com:9443/ws/' + + + +######################################################################## +class BinanceApi(object): + """""" + + ################################################### + ## Basic Function + ################################################### + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.apiKey = '' + self.secretKey = '' + + self.active = False + self.reqid = 0 + self.queue = Queue() + self.pool = None + + self.headers = {} + self.secret = '' + self.recvWindow = 5000 + + self.dataStreamNameList = [] + self.dataStreamUrl = '' + self.dataStreamActive = False + self.dataStreamWs = None + self.dataStreamThread = None + + self.userStreamKey = '' + self.userStreamUrl = '' + self.userStreamActive = False + self.userStreamWs = None + self.userStreamThread = None + + self.keepaliveCount = 0 + self.keepaliveThread = None + + #---------------------------------------------------------------------- + def init(self, apiKey, secretKey, recvWindow=5000): + """""" + self.apiKey = apiKey + self.secretKey = secretKey + + self.headers['X-MBX-APIKEY'] = apiKey + self.secret = bytes(secretKey.encode('utf-8')) + self.recvWindow = recvWindow + + #---------------------------------------------------------------------- + def start(self, n=10): + """""" + if self.active: + return + + self.active = True + + self.pool = Pool(n) + self.pool.map_async(self.run, range(n)) + + #---------------------------------------------------------------------- + def close(self): + """""" + self.active = False + self.pool.close() + self.pool.join() + + #---------------------------------------------------------------------- + def request(self, method, path, params=None, signed=False, stream=False): + """""" + if not signed: + url = REST_ENDPOINT + path + headers = {} + else: + if not stream: + params['recvWindow'] = self.recvWindow + params['timestamp'] = int(time()*1000) + query = urlencode(sorted(params.items())) + + signature = hmac.new(self.secret, query.encode('utf-8'), + hashlib.sha256).hexdigest() + query += "&signature={}".format(signature) + + url = REST_ENDPOINT + path + '?' + query + params = None # 参数添加到query中后,清空参数字典 + else: + if params: + query = urlencode(sorted(params.items())) + url = REST_ENDPOINT + path + '?' + query + params = None + else: + url = REST_ENDPOINT + path + + headers = self.headers + + try: + resp = requests.request(method, url, params=params, headers=headers) + + if resp.status_code == 200: + return True, resp.json() + else: + error = { + 'method': method, + 'params': params, + 'code': resp.status_code, + 'msg': resp.json()['msg'] + } + return False, error + except Exception as e: + error = { + 'method': method, + 'params': params, + 'code': e, + 'msg': traceback.format_exc() + } + return False, error + + #---------------------------------------------------------------------- + def addReq(self, method, path, params, callback, signed=False, stream=False): + """添加请求""" + self.reqid += 1 + req = (method, path, params, callback, signed, stream, self.reqid) + self.queue.put(req) + return self.reqid + + #---------------------------------------------------------------------- + def processReq(self, req): + """""" + method, path, params, callback, signed, stream, reqid = req + result, data = self.request(method, path, params, signed, stream) + + if result: + callback(data, reqid) + else: + self.onError(data, reqid) + + #---------------------------------------------------------------------- + def run(self, n): + """""" + while self.active: + try: + req = self.queue.get(timeout=1) + self.processReq(req) + except Empty: + pass + + ################################################### + ## REST Function + ################################################### + + #---------------------------------------------------------------------- + def queryPing(self): + """""" + path = '/api/v1/ping' + return self.addReq('GET', path, {}, self.onQueryPing) + + #---------------------------------------------------------------------- + def queryTime(self): + """""" + path = '/api/v1/time' + return self.addReq('GET', path, {}, self.onQueryTime) + + #---------------------------------------------------------------------- + def queryExchangeInfo(self): + """""" + path = '/api/v1/exchangeInfo' + return self.addReq('GET', path, {}, self.onQueryExchangeInfo) + + #---------------------------------------------------------------------- + def queryDepth(self, symbol, limit=0): + """""" + path = '/api/v1/depth' + params = {'symbol': symbol} + if limit: + params['limit'] = limit + return self.addReq('GET', path, params, self.onQueryDepth) + + #---------------------------------------------------------------------- + def queryTrades(self, symbol, limit=0): + """""" + path = '/api/v1/trades' + params = {'symbol': symbol} + if limit: + params['limit'] = limit + return self.addReq('GET', path, params, self.onQueryTrades) + + #---------------------------------------------------------------------- + def queryAggTrades(self, symbol, fromId=0, startTime=0, endTime=0, limit=0): + """""" + path = '/api/v1/aggTrades' + + params = {'symbol': symbol} + if fromId: + params['fromId'] = fromId + if startTime: + params['startTime'] = startTime + if endTime: + params['endTime'] = endTime + if limit: + params['limit'] = limit + + return self.addReq('GET', path, params, self.onQueryAggTrades) + + #---------------------------------------------------------------------- + def queryKlines(self, symbol, interval, limit=0, startTime=0, endTime=0): + """""" + path = '/api/v1/klines' + + params = { + 'symbol': symbol, + 'interval': interval + } + if limit: + params['limit'] = limit + if startTime: + params['startTime'] = startTime + if endTime: + params['endTime'] = endTime + + return self.addReq('GET', path, params, self.onQueryKlines) + + #---------------------------------------------------------------------- + def queryTicker24HR(self, symbol=''): + """""" + path = '/api/v1/ticker/24hr' + params = {} + if symbol: + params['symbol'] = symbol + return self.addReq('GET', path, params, self.onQueryTicker24HR) + + #---------------------------------------------------------------------- + def queryTickerPrice(self, symbol=''): + """""" + path = '/api/v3/ticker/price' + params = {} + if symbol: + params['symbol'] = symbol + return self.addReq('GET', path, params, self.onQueryTickerPrice) + + #---------------------------------------------------------------------- + def queryBookTicker(self, symbol=''): + """""" + path = '/api/v3/ticker/bookTicker' + params = {} + if symbol: + params['symbol'] = symbol + return self.addReq('GET', path, params, self.onQueryBookTicker) + + #---------------------------------------------------------------------- + def newOrder(self, symbol, side, type_, price, quantity, timeInForce, + newClientOrderId='', stopPrice=0, icebergQty=0, newOrderRespType=''): + """""" + path = '/api/v3/order' + + params = { + 'symbol': symbol, + 'side': side, + 'type': type_, + 'price': price, + 'quantity': quantity, + 'timeInForce': timeInForce + } + if newClientOrderId: + params['newClientOrderId'] = newClientOrderId + if timeInForce: + params['timeInForce'] = timeInForce + if stopPrice: + params['stopPrice'] = stopPrice + if icebergQty: + params['icebergQty'] = icebergQty + if newOrderRespType: + params['newOrderRespType'] = newOrderRespType + + return self.addReq('POST', path, params, self.onNewOrder, signed=True) + + #---------------------------------------------------------------------- + def queryOrder(self, symbol, orderId=0, origClientOrderId=0): + """""" + path = '/api/v3/order' + params = {'symbol': symbol} + if orderId: + params['orderId'] = orderId + if origClientOrderId: + params['origClientOrderId'] = origClientOrderId + return self.addReq('GET', path, params, self.onQueryOrder, signed=True) + + #---------------------------------------------------------------------- + def cancelOrder(self, symbol, orderId=0, origClientOrderId='', + newClientOrderId=''): + """""" + path = '/api/v3/order' + params = {'symbol': symbol} + if orderId: + params['orderId'] = orderId + if origClientOrderId: + params['origClientOrderId'] = origClientOrderId + if newClientOrderId: + params['newClientOrderId'] = newClientOrderId + return self.addReq('DELETE', path, params, self.onCancelOrder, signed=True) + + #---------------------------------------------------------------------- + def queryOpenOrders(self, symbol=''): + """""" + path = '/api/v3/openOrders' + params = {} + if symbol: + params['symbol'] = symbol + return self.addReq('GET', path, params, self.onQueryOpenOrders, signed=True) + + #---------------------------------------------------------------------- + def queryAllOrders(self, symbol, orderId=0, limit=0): + """""" + path = '/api/v3/allOrders' + params = {'symbol': symbol} + if orderId: + params['orderId'] = orderId + if limit: + params['limit'] = limit + return self.addReq('GET', path, params, self.onQueryAllOrders, signed=True) + + #---------------------------------------------------------------------- + def queryAccount(self): + """""" + path = '/api/v3/account' + params = {} + return self.addReq('GET', path, params, self.onQueryAccount, signed=True) + + #---------------------------------------------------------------------- + def queryMyTrades(self, symbol, limit=0, fromId=0): + """""" + path = '/api/v3/myTrades' + params = {'symbol': symbol} + if limit: + params['limit'] = limit + if fromId: + params['fromId'] = fromId + return self.addReq('GET', path, params, self.onQueryMyTrades, signed=True) + + #---------------------------------------------------------------------- + def startStream(self): + """""" + path = '/api/v1/userDataStream' + return self.addReq('POST', path, {}, self.onStartStream, signed=True, stream=True) + + #---------------------------------------------------------------------- + def keepaliveStream(self, listenKey): + """""" + path = '/api/v1/userDataStream' + params = {'listenKey': listenKey} + return self.addReq('PUT', path, params, self.onKeepaliveStream, signed=True, stream=True) + + #---------------------------------------------------------------------- + def closeStream(self, listenKey): + """""" + path = '/api/v1/userDataStream' + params = {'listenKey': listenKey} + return self.addReq('DELETE', path, params, self.onCloseStream, signed=True, stream=True) + + ################################################### + ## REST Callback + ################################################### + + #---------------------------------------------------------------------- + def onError(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryPing(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryTime(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryExchangeInfo(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryDepth(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryTrades(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryAggTrades(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryKlines(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryTicker24HR(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryTickerPrice(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryBookTicker(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onNewOrder(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryOrder(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onCancelOrder(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryOpenOrders(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryAllOrders(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryAccount(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onQueryMyTrades(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onStartStream(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onKeepaliveStream(self, data, reqid): + """""" + print(data, reqid) + + #---------------------------------------------------------------------- + def onCloseStream(self, data, reqid): + """""" + print(data, reqid) + + ################################################### + ## Websocket Function + ################################################### + + #---------------------------------------------------------------------- + def initDataStream(self, nameList=None): + """""" + if nameList: + self.dataStreamNameList = nameList + s = '/'.join(self.dataStreamNameList) + self.dataStreamUrl = DATASTREAM_ENDPOINT + s + + result = self.connectDataStream() + if result: + self.dataStreamActive = True + self.dataStreamThread = Thread(target=self.runDataStream) + self.dataStreamThread.start() + + #---------------------------------------------------------------------- + def runDataStream(self): + """""" + while self.dataStreamActive: + try: + stream = self.dataStreamWs.recv() + data = json.loads(stream) + self.onMarketData(data) + except: + self.onDataStreamError('Data stream connection lost') + result = self.connectDataStream() + if not result: + self.onDataStreamError(u'Waiting 3 seconds to reconnect') + sleep(3) + else: + self.onDataStreamError(u'Data stream reconnected') + + #---------------------------------------------------------------------- + def closeDataStream(self): + """""" + self.dataStreamActive = False + self.dataStreamThread.join() + + #---------------------------------------------------------------------- + def connectDataStream(self): + """""" + try: + self.dataStreamWs = create_connection(self.dataStreamUrl) + return True + except: + msg = traceback.format_exc() + self.onDataStreamError('Connecting data stream falied: %s' %msg) + return False + + #---------------------------------------------------------------------- + def onDataStreamError(self, msg): + """""" + print msg + + #---------------------------------------------------------------------- + def onMarketData(self, data): + """""" + print data + + #---------------------------------------------------------------------- + def initUserStream(self, key): + """""" + self.userStreamKey = key + self.userStreamUrl = USERSTREAM_ENDPOINT + key + + result = self.connectUserStream() + if result: + self.userStreamActive = True + self.userStreamThread = Thread(target=self.runUserStream) + self.userStreamThread.start() + + self.keepaliveThread = Thread(target=self.runKeepalive) + self.keepaliveThread.start() + + #---------------------------------------------------------------------- + def runUserStream(self): + """""" + while self.userStreamActive: + try: + stream = self.userStreamWs.recv() + data = json.loads(stream) + self.onUserData(data) + except: + self.onUserStreamError('User stream connection lost') + result = self.connectUserStream() + if not result: + self.onUserStreamError(u'Waiting 3 seconds to reconnect') + sleep(3) + else: + self.onUserStreamError(u'User stream reconnected') + + #---------------------------------------------------------------------- + def closeUserStream(self): + """""" + self.userStreamActive = False + self.userStreamThread.join() + self.keepaliveThread.join() + + #---------------------------------------------------------------------- + def connectUserStream(self): + """""" + try: + self.userStreamWs = create_connection(self.userStreamUrl) + return True + except: + msg = traceback.format_exc() + self.onUserStreamError('Connecting user stream falied: %s' %msg) + return False + + #---------------------------------------------------------------------- + def onUserStreamError(self, msg): + """""" + print msg + + #---------------------------------------------------------------------- + def onUserData(self, data): + """""" + print data + + #---------------------------------------------------------------------- + def runKeepalive(self): + """""" + while self.userStreamActive: + self.keepaliveCount += 1 + + if self.keepaliveCount >= 1800: + self.keepaliveCount = 0 + self.keepaliveStream(self.userStreamKey) + + sleep(1) + \ No newline at end of file diff --git a/vnpy/trader/gateway/binanceGateway/BINANCE_connect.json b/vnpy/trader/gateway/binanceGateway/BINANCE_connect.json new file mode 100644 index 00000000..01d370e4 --- /dev/null +++ b/vnpy/trader/gateway/binanceGateway/BINANCE_connect.json @@ -0,0 +1,5 @@ +{ + "apiKey": "", + "secretKey": "", + "symbols": ["BTCUSDT", "ETHUSDT", "ETHBTC"] +} \ No newline at end of file diff --git a/vnpy/trader/gateway/binanceGateway/__init__.py b/vnpy/trader/gateway/binanceGateway/__init__.py new file mode 100644 index 00000000..05256200 --- /dev/null +++ b/vnpy/trader/gateway/binanceGateway/__init__.py @@ -0,0 +1,10 @@ +# encoding: UTF-8 + +from vnpy.trader import vtConstant +from .binanceGateway import BinanceGateway + +gatewayClass = BinanceGateway +gatewayName = 'BINANCE' +gatewayDisplayName = u'币安' +gatewayType = vtConstant.GATEWAYTYPE_BTC +gatewayQryEnabled = True \ No newline at end of file diff --git a/vnpy/trader/gateway/binanceGateway/binanceGateway.py b/vnpy/trader/gateway/binanceGateway/binanceGateway.py new file mode 100644 index 00000000..7bc1e0c4 --- /dev/null +++ b/vnpy/trader/gateway/binanceGateway/binanceGateway.py @@ -0,0 +1,498 @@ +# encoding: UTF-8 + +''' +vnpy.api.binance的gateway接入 +''' + +import os +import json +from datetime import datetime, timedelta +from copy import copy + +from vnpy.api.binance import BinanceApi +from vnpy.trader.vtGateway import * +from vnpy.trader.vtFunction import getJsonPath, getTempPath + + +# 委托状态类型映射 +statusMapReverse = {} +statusMapReverse['NEW'] = STATUS_NOTTRADED +statusMapReverse['PARTIALLY_FILLED'] = STATUS_PARTTRADED +statusMapReverse['FILLED'] = STATUS_ALLTRADED +statusMapReverse['CANCELED'] = STATUS_CANCELLED +statusMapReverse['REJECTED'] = STATUS_REJECTED +statusMapReverse['EXPIRED'] = STATUS_CANCELLED + +# 方向映射 +directionMap = {} +directionMap[DIRECTION_LONG] = 'BUY' +directionMap[DIRECTION_SHORT] = 'SELL' +directionMapReverse = {v:k for k,v in directionMap.items()} + +# 价格类型映射 +priceTypeMap = {} +priceTypeMap[PRICETYPE_LIMITPRICE] = 'LIMIT' +priceTypeMap[PRICETYPE_MARKETPRICE] = 'MARKET' + + + + +#---------------------------------------------------------------------- +def print_dict(d): + """""" + print '-' * 30 + l = d.keys() + l.sort() + for k in l: + print '%s:%s' %(k, d[k]) + + +######################################################################## +class BinanceGateway(VtGateway): + """币安接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine, gatewayName=''): + """Constructor""" + super(BinanceGateway, self).__init__(eventEngine, gatewayName) + + self.api = GatewayApi(self) + + self.qryEnabled = False # 是否要启动循环查询 + + self.fileName = self.gatewayName + '_connect.json' + self.filePath = getJsonPath(self.fileName, __file__) + + #---------------------------------------------------------------------- + def connect(self): + """连接""" + try: + f = file(self.filePath) + except IOError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'读取连接配置出错,请检查' + self.onLog(log) + return + + # 解析json文件 + setting = json.load(f) + try: + apiKey = str(setting['apiKey']) + secretKey = str(setting['secretKey']) + symbols = setting['symbols'] + except KeyError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'连接配置缺少字段,请检查' + self.onLog(log) + return + + # 创建行情和交易接口对象 + self.api.connect(apiKey, secretKey, symbols) + + # 初始化并启动查询 + #self.initQuery() + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """发单""" + return self.api.sendOrder(orderReq) + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """撤单""" + self.api.cancel(cancelOrderReq) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.api.close() + + #---------------------------------------------------------------------- + def queryAccount(self): + """""" + self.api.queryAccount() + + #---------------------------------------------------------------------- + def initQuery(self): + """初始化连续查询""" + if self.qryEnabled: + # 需要循环的查询函数列表 + self.qryFunctionList = [self.queryAccount] + + self.qryCount = 0 # 查询触发倒计时 + self.qryTrigger = 1 # 查询触发点 + self.qryNextFunction = 0 # 上次运行的查询函数索引 + + self.startQuery() + + #---------------------------------------------------------------------- + def query(self, event): + """注册到事件处理引擎上的查询函数""" + self.qryCount += 1 + + if self.qryCount > self.qryTrigger: + # 清空倒计时 + self.qryCount = 0 + + # 执行查询函数 + function = self.qryFunctionList[self.qryNextFunction] + function() + + # 计算下次查询函数的索引,如果超过了列表长度,则重新设为0 + self.qryNextFunction += 1 + if self.qryNextFunction == len(self.qryFunctionList): + self.qryNextFunction = 0 + + #---------------------------------------------------------------------- + def startQuery(self): + """启动连续查询""" + self.eventEngine.register(EVENT_TIMER, self.query) + + #---------------------------------------------------------------------- + def setQryEnabled(self, qryEnabled): + """设置是否要启动循环查询""" + self.qryEnabled = qryEnabled + + +######################################################################## +class GatewayApi(BinanceApi): + """API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(GatewayApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.date = datetime.now().strftime('%y%m%d%H%M%S') + self.orderId = 0 + + self.tickDict = {} + + #---------------------------------------------------------------------- + def connect(self, apiKey, secretKey, symbols): + """连接服务器""" + self.init(apiKey, secretKey) + self.start() + self.writeLog(u'交易API启动成功') + + l = [] + for symbol in symbols: + symbol = symbol.lower() + l.append(symbol+'@ticker') + l.append(symbol+'@depth5') + self.initDataStream(l) + self.writeLog(u'行情推送订阅成功') + + self.startStream() + + # 初始化查询 + self.queryExchangeInfo() + self.queryAccount() + + for symbol in symbols: + self.queryOpenOrders(symbol.upper()) + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def onError(self, data, reqid): + """""" + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = data['code'] + err.errorMsg = data['msg'] + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onQueryExchangeInfo(self, data, reqid): + """""" + for d in data['symbols']: + if str(d['symbol']) == 'ETHUSDT': + print d + + contract = VtContractData() + contract.gatewayName = self.gatewayName + + contract.symbol = d['symbol'] + contract.exchange = EXCHANGE_BINANCE + contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) + contract.name = contract.vtSymbol + contract.productClass = PRODUCT_SPOT + contract.size = 1 + + for f in d['filters']: + if f['filterType'] == 'PRICE_FILTER': + contract.priceTick = float(f['tickSize']) + + self.gateway.onContract(contract) + + #---------------------------------------------------------------------- + def onNewOrder(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onCancelOrder(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onQueryOpenOrders(self, data, reqid): + """""" + for d in data: + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.symbol = d['symbol'] + order.exchange = EXCHANGE_BINANCE + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + order.orderID = d['clientOrderId'] + order.vtOrderID = '.'.join([order.gatewayName, order.orderID]) + + order.direction = directionMapReverse[d['side']] + order.price = float(d['price']) + order.totalVolume = float(d['origQty']) + order.tradedVolume = float(d['executedQty']) + date, order.orderTime = self.generateDateTime(d['time']) + order.status = statusMapReverse[d['status']] + + self.gateway.onOrder(order) + + #---------------------------------------------------------------------- + def onQueryAllOrders(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onQueryAccount(self, data, reqid): + """""" + for d in data['balances']: + free = float(d['free']) + locked = float(d['locked']) + + if free or locked: + pos = VtPositionData() + pos.gatewayName = self.gatewayName + pos.symbol = d['asset'] + pos.exchange = EXCHANGE_BINANCE + pos.vtSymbol = '.'.join([pos.vtSymbol, pos.direction]) + pos.direction = DIRECTION_LONG + pos.vtPositionName = '.'.join([pos.symbol, pos.direction]) + pos.frozen = locked + pos.position = free + locked + self.gateway.onPosition(pos) + + #---------------------------------------------------------------------- + def onQueryMyTrades(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onStartStream(self, data, reqid): + """""" + key = data['listenKey'] + self.initUserStream(key) + self.writeLog(u'交易推送订阅成功') + + #---------------------------------------------------------------------- + def onKeepaliveStream(self, data, reqid): + """""" + self.writeLog(u'交易推送刷新成功') + + #---------------------------------------------------------------------- + def onCloseStream(self, data, reqid): + """""" + self.writeLog(u'交易推送关闭') + + #---------------------------------------------------------------------- + def onUserData(self, data): + """""" + if data['e'] == 'outboundAccountInfo': + self.onPushAccount(data) + elif data['e'] == 'executionReport': + self.onPushOrder(data) + + #---------------------------------------------------------------------- + def onPushAccount(self, data): + """""" + for d in data['B']: + free = float(d['f']) + locked = float(d['l']) + + if free or locked: + pos = VtPositionData() + pos.gatewayName = self.gatewayName + pos.symbol = d['a'] + pos.exchange = EXCHANGE_BINANCE + pos.vtSymbol = '.'.join([pos.vtSymbol, pos.direction]) + pos.direction = DIRECTION_LONG + pos.vtPositionName = '.'.join([pos.symbol, pos.direction]) + pos.frozen = locked + pos.position = free + locked + self.gateway.onPosition(pos) + + #---------------------------------------------------------------------- + def onPushOrder(self, d): + """""" + # 委托更新 + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.symbol = d['s'] + order.exchange = EXCHANGE_BINANCE + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + if d['C'] != 'null': + order.orderID = d['C'] # 撤单原始委托号 + else: + order.orderID = d['c'] + order.vtOrderID = '.'.join([order.gatewayName, order.orderID]) + + order.direction = directionMapReverse[d['S']] + order.price = float(d['p']) + order.totalVolume = float(d['q']) + order.tradedVolume = float(d['z']) + date, order.orderTime = self.generateDateTime(d['T']) + order.status = statusMapReverse[d['X']] + + self.gateway.onOrder(order) + + # 成交更新 + if float(d['l']): + trade = VtTradeData() + trade.gatewayName = self.gatewayName + + trade.symbol = order.symbol + trade.exchange = order.exchange + trade.vtSymbol = order.vtSymbol + trade.orderID = order.orderID + trade.vtOrderID = order.vtOrderID + trade.tradeID = str(d['t']) + trade.vtTradeID = '.'.join([trade.gatewayName, trade.tradeID]) + trade.direction = order.direction + trade.price = float(d['L']) + trade.volume = float(d['l']) + date, trade.tradeTime = self.generateDateTime(d['E']) + + self.gateway.onTrade(trade) + + #---------------------------------------------------------------------- + def onMarketData(self, data): + """""" + name = data['stream'] + symbol, channel = name.split('@') + symbol = symbol.upper() + + if symbol in self.tickDict: + tick = self.tickDict[symbol] + else: + tick = VtTickData() + tick.gatewayName = self.gatewayName + tick.symbol = symbol + tick.exchange = EXCHANGE_BINANCE + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + + self.tickDict[symbol] = tick + + data = data['data'] + if channel == 'ticker': + tick.volume = float(data['v']) + tick.openPrice = float(data['o']) + tick.highPrice = float(data['h']) + tick.lowPrice = float(data['l']) + tick.lastPrice = float(data['c']) + tick.date, tick.time = self.generateDateTime(data['E']) + else: + tick.askPrice1, tick.askVolume1, buf = data['asks'][0] + tick.askPrice2, tick.askVolume2, buf = data['asks'][1] + tick.askPrice3, tick.askVolume3, buf = data['asks'][2] + tick.askPrice4, tick.askVolume4, buf = data['asks'][3] + tick.askPrice5, tick.askVolume5, buf = data['asks'][4] + + tick.bidPrice1, tick.bidVolume1, buf = data['bids'][0] + tick.bidPrice2, tick.bidVolume2, buf = data['bids'][1] + tick.bidPrice3, tick.bidVolume3, buf = data['bids'][2] + tick.bidPrice4, tick.bidVolume4, buf = data['bids'][3] + tick.bidPrice5, tick.bidVolume5, buf = data['bids'][4] + + tick.askPrice1 = float(tick.askPrice1) + tick.askPrice2 = float(tick.askPrice2) + tick.askPrice3 = float(tick.askPrice3) + tick.askPrice4 = float(tick.askPrice4) + tick.askPrice5 = float(tick.askPrice5) + + tick.bidPrice1 = float(tick.bidPrice1) + tick.bidPrice2 = float(tick.bidPrice2) + tick.bidPrice3 = float(tick.bidPrice3) + tick.bidPrice4 = float(tick.bidPrice4) + tick.bidPrice5 = float(tick.bidPrice5) + + tick.askVolume1 = float(tick.askVolume1) + tick.askVolume2 = float(tick.askVolume2) + tick.askVolume3 = float(tick.askVolume3) + tick.askVolume4 = float(tick.askVolume4) + tick.askVolume5 = float(tick.askVolume5) + + tick.bidVolume1 = float(tick.bidVolume1) + tick.bidVolume2 = float(tick.bidVolume2) + tick.bidVolume3 = float(tick.bidVolume3) + tick.bidVolume4 = float(tick.bidVolume4) + tick.bidVolume5 = float(tick.bidVolume5) + + self.gateway.onTick(copy(tick)) + + #---------------------------------------------------------------------- + def onDataStreamError(self, msg): + """""" + self.writeLog(msg) + + #---------------------------------------------------------------------- + def onUserStreamError(self, msg): + """""" + self.writeLog(msg) + + #---------------------------------------------------------------------- + def generateDateTime(self, s): + """生成时间""" + dt = datetime.fromtimestamp(float(s)/1e3) + time = dt.strftime("%H:%M:%S.%f") + date = dt.strftime("%Y%m%d") + return date, time + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """""" + orderReq.volume = 0.02 + + self.orderId += 1 + orderId = self.date + str(self.orderId).rjust(6, '0') + vtOrderID = '.'.join([self.gatewayName, orderId]) + side = directionMap.get(orderReq.direction, '') + type_ = priceTypeMap.get(orderReq.priceType, PRICETYPE_LIMITPRICE) + + self.newOrder(orderReq.symbol, side, type_, orderReq.price, + orderReq.volume, 'GTC', newClientOrderId=orderId) + + return vtOrderID + + #---------------------------------------------------------------------- + def cancel(self, cancelOrderReq): + """""" + self.cancelOrder(cancelOrderReq.symbol, origClientOrderId=cancelOrderReq.orderID) \ No newline at end of file From c578d4e04de5d2cddc110a1f7a1bac487e2d6831 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 5 Jun 2018 09:03:55 +0800 Subject: [PATCH 018/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9EBitfinex?= =?UTF-8?q?=E6=95=B0=E5=AD=97=E8=B4=A7=E5=B8=81=E4=BA=A4=E6=98=93=E6=89=80?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/bitfinex/__init__.py | 1 + vnpy/api/bitfinex/vnbitfinex.py | 95 ++++ .../gateway/binanceGateway/binanceGateway.py | 2 - .../bitfinexGateway/BITFINEX_connect.json | 5 + .../gateway/bitfinexGateway/__init__.py | 10 + .../bitfinexGateway/bitfinexGateway.py | 526 ++++++++++++++++++ vnpy/trader/language/chinese/constant.py | 2 + vnpy/trader/language/english/constant.py | 2 + 8 files changed, 641 insertions(+), 2 deletions(-) create mode 100644 vnpy/api/bitfinex/__init__.py create mode 100644 vnpy/api/bitfinex/vnbitfinex.py create mode 100644 vnpy/trader/gateway/bitfinexGateway/BITFINEX_connect.json create mode 100644 vnpy/trader/gateway/bitfinexGateway/__init__.py create mode 100644 vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py diff --git a/vnpy/api/bitfinex/__init__.py b/vnpy/api/bitfinex/__init__.py new file mode 100644 index 00000000..15180a7a --- /dev/null +++ b/vnpy/api/bitfinex/__init__.py @@ -0,0 +1 @@ +from .vnbitfinex import BitfinexApi \ No newline at end of file diff --git a/vnpy/api/bitfinex/vnbitfinex.py b/vnpy/api/bitfinex/vnbitfinex.py new file mode 100644 index 00000000..23bc3a9d --- /dev/null +++ b/vnpy/api/bitfinex/vnbitfinex.py @@ -0,0 +1,95 @@ +# encoding: UTF-8 + +import json +import requests +from threading import Thread +from queue import Queue, Empty + +import websocket + + +WEBSOCKET_V2_URL = 'wss://api.bitfinex.com/ws/2' +RESTFUL_V1_URL = 'https://api.bitfinex.com/v1' + + +######################################################################## +class BitfinexApi(object): + """""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.ws = None + self.thread = None + self.active = False + + self.restQueue = Queue() + self.restThread = None + + #---------------------------------------------------------------------- + def start(self): + """""" + self.ws = websocket.create_connection(WEBSOCKET_V2_URL) + + self.active =True + self.thread = Thread(target=self.run) + self.thread.start() + + self.restThread = Thread(target=self.runRest) + self.restThread.start() + + #---------------------------------------------------------------------- + def run(self): + """""" + while self.active: + stream = self.ws.recv() + data = json.loads(stream) + self.onData(data) + + #---------------------------------------------------------------------- + def onData(self, data): + """""" + print data + + #---------------------------------------------------------------------- + def sendReq(self, req): + """""" + self.ws.send(json.dumps(req)) + + #---------------------------------------------------------------------- + def sendRestReq(self, path, callback): + """""" + self.restQueue.put((path, callback)) + + #---------------------------------------------------------------------- + def runRest(self): + """""" + while self.active: + try: + path, callback = self.restQueue.get(timeout=1) + self.httpGet(path, callback) + except Empty: + pass + + #---------------------------------------------------------------------- + def httpGet(self, path, callback): + """""" + url = RESTFUL_V1_URL + path + resp = requests.get(url) + callback(resp.json()) + + + +if __name__ == '__main__': + api = BitfinexApi() + api.start() + + d = { + 'event': 'subscribe', + 'channel': 'book', + 'symbol': 'BTCUSD' + } + api.sendReq(d) + + raw_input() + \ No newline at end of file diff --git a/vnpy/trader/gateway/binanceGateway/binanceGateway.py b/vnpy/trader/gateway/binanceGateway/binanceGateway.py index 7bc1e0c4..897a7877 100644 --- a/vnpy/trader/gateway/binanceGateway/binanceGateway.py +++ b/vnpy/trader/gateway/binanceGateway/binanceGateway.py @@ -479,8 +479,6 @@ class GatewayApi(BinanceApi): #---------------------------------------------------------------------- def sendOrder(self, orderReq): """""" - orderReq.volume = 0.02 - self.orderId += 1 orderId = self.date + str(self.orderId).rjust(6, '0') vtOrderID = '.'.join([self.gatewayName, orderId]) diff --git a/vnpy/trader/gateway/bitfinexGateway/BITFINEX_connect.json b/vnpy/trader/gateway/bitfinexGateway/BITFINEX_connect.json new file mode 100644 index 00000000..ae25ff9c --- /dev/null +++ b/vnpy/trader/gateway/bitfinexGateway/BITFINEX_connect.json @@ -0,0 +1,5 @@ +{ + "apiKey": "", + "secretKey": "", + "symbols": ["BTCUSD", "ETHUSD", "ETHBTC"] +} \ No newline at end of file diff --git a/vnpy/trader/gateway/bitfinexGateway/__init__.py b/vnpy/trader/gateway/bitfinexGateway/__init__.py new file mode 100644 index 00000000..831aa287 --- /dev/null +++ b/vnpy/trader/gateway/bitfinexGateway/__init__.py @@ -0,0 +1,10 @@ +# encoding: UTF-8 + +from vnpy.trader import vtConstant +from .bitfinexGateway import BitfinexGateay + +gatewayClass = BitfinexGateay +gatewayName = 'BITFINEX' +gatewayDisplayName = u'BITFINEX' +gatewayType = vtConstant.GATEWAYTYPE_BTC +gatewayQryEnabled = True \ No newline at end of file diff --git a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py new file mode 100644 index 00000000..b8ce5be4 --- /dev/null +++ b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py @@ -0,0 +1,526 @@ +# encoding: UTF-8 + +''' +vnpy.api.bitfinex的gateway接入 +''' + +import os +import json +import hashlib +import hmac +import time +from datetime import datetime, timedelta +from copy import copy +from math import pow + +from vnpy.api.bitfinex import BitfinexApi +from vnpy.trader.vtGateway import * +from vnpy.trader.vtFunction import getJsonPath, getTempPath + +# 委托状态类型映射 +statusMapReverse = {} +statusMapReverse['ACTIVE'] = STATUS_NOTTRADED +statusMapReverse['PARTIALLY FILLED'] = STATUS_PARTTRADED +statusMapReverse['EXECUTED'] = STATUS_ALLTRADED +statusMapReverse['CANCELED'] = STATUS_CANCELLED + +# 价格类型映射 +priceTypeMap = {} +priceTypeMap[PRICETYPE_LIMITPRICE] = 'EXCHANGE LIMIT' +priceTypeMap[PRICETYPE_MARKETPRICE] = 'EXCHANGE MARKET' + + + +######################################################################## +class BitfinexGateay(VtGateway): + """Bitfinex接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine, gatewayName=''): + """Constructor""" + super(BitfinexGateay, self).__init__(eventEngine, gatewayName) + + self.api = GatewayApi(self) + + self.qryEnabled = False # 是否要启动循环查询 + + self.fileName = self.gatewayName + '_connect.json' + self.filePath = getJsonPath(self.fileName, __file__) + + #---------------------------------------------------------------------- + def connect(self): + """连接""" + try: + f = file(self.filePath) + except IOError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'读取连接配置出错,请检查' + self.onLog(log) + return + + # 解析json文件 + setting = json.load(f) + try: + apiKey = str(setting['apiKey']) + secretKey = str(setting['secretKey']) + symbols = setting['symbols'] + except KeyError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'连接配置缺少字段,请检查' + self.onLog(log) + return + + # 创建行情和交易接口对象 + self.api.connect(apiKey, secretKey, symbols) + + # 初始化并启动查询 + #self.initQuery() + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """发单""" + return self.api.sendOrder(orderReq) + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """撤单""" + self.api.cancelOrder(cancelOrderReq) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.api.close() + + #---------------------------------------------------------------------- + def queryAccount(self): + """""" + self.api.queryAccount() + + #---------------------------------------------------------------------- + def initQuery(self): + """初始化连续查询""" + if self.qryEnabled: + # 需要循环的查询函数列表 + self.qryFunctionList = [self.queryAccount] + + self.qryCount = 0 # 查询触发倒计时 + self.qryTrigger = 1 # 查询触发点 + self.qryNextFunction = 0 # 上次运行的查询函数索引 + + self.startQuery() + + #---------------------------------------------------------------------- + def query(self, event): + """注册到事件处理引擎上的查询函数""" + self.qryCount += 1 + + if self.qryCount > self.qryTrigger: + # 清空倒计时 + self.qryCount = 0 + + # 执行查询函数 + function = self.qryFunctionList[self.qryNextFunction] + function() + + # 计算下次查询函数的索引,如果超过了列表长度,则重新设为0 + self.qryNextFunction += 1 + if self.qryNextFunction == len(self.qryFunctionList): + self.qryNextFunction = 0 + + #---------------------------------------------------------------------- + def startQuery(self): + """启动连续查询""" + self.eventEngine.register(EVENT_TIMER, self.query) + + #---------------------------------------------------------------------- + def setQryEnabled(self, qryEnabled): + """设置是否要启动循环查询""" + self.qryEnabled = qryEnabled + + +######################################################################## +class GatewayApi(BitfinexApi): + """API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(GatewayApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.orderId = 0 + self.date = int(datetime.now().strftime('%y%m%d%H%M%S')) * self.orderId + + self.apiKey = '' + self.secretKey = '' + self.symbols = [] + + self.tickDict = {} + self.bidDict = {} + self.askDict = {} + self.orderLocalDict = {} + + self.channelDict = {} # ChannelID : (Channel, Symbol) + + #---------------------------------------------------------------------- + def connect(self, apiKey, secretKey, symbols): + """连接服务器""" + self.apiKey = apiKey + self.secretKey = secretKey + self.symbols = symbols + + self.start() + self.writeLog(u'交易API启动成功') + + for symbol in symbols: + self.subscribe(symbol, 'ticker') + self.subscribe(symbol, 'book') + self.writeLog(u'行情推送订阅成功') + + self.authenticate() + self.writeLog(u'交易推送订阅成功') + + self.sendRestReq('/symbols_details', self.onSymbolDetails) + + #---------------------------------------------------------------------- + def subscribe(self, symbol, channel): + """""" + req = { + 'event': 'subscribe', + 'channel': channel, + 'symbol': symbol + } + self.sendReq(req) + + #---------------------------------------------------------------------- + def authenticate(self): + """""" + nonce = int(time.time() * 1000000) + authPayload = 'AUTH' + str(nonce) + signature = hmac.new( + self.secretKey.encode(), + msg = authPayload.encode(), + digestmod = hashlib.sha384 + ).hexdigest() + + req = { + 'apiKey': self.apiKey, + 'event': 'auth', + 'authPayload': authPayload, + 'authNonce': nonce, + 'authSig': signature + } + + self.sendReq(req) + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def generateDateTime(self, s): + """生成时间""" + dt = datetime.fromtimestamp(s/1000.0) + date = dt.strftime('%Y-%m-%d') + time = dt.strftime("%H:%M:%S.%f") + return date, time + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """""" + self.orderId += 1 + orderId = self.date + self.orderId + vtOrderID = '.'.join([self.gatewayName, str(orderId)]) + + if orderReq.direction == DIRECTION_LONG: + amount = orderReq.volume + else: + amount = -orderReq.volume + + o = { + 'CID': orderId, + 'type': priceTypeMap[orderReq.priceType], + 'symbol': 't' + orderReq.symbol, + 'amount': str(amount), + 'price': str(orderReq.price) + } + + req = [0, 'on', None, o] + self.sendReq(req) + + return vtOrderID + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """""" + orderId = int(cancelOrderReq.orderID) + date = cancelOrderReq.sessionID + + req = [ + 0, + 'oc', + None, + { + 'cid': orderId, + 'cid_date': date, + } + ] + + self.sendReq(req) + + #---------------------------------------------------------------------- + def onData(self, data): + """""" + if isinstance(data, dict): + self.onResponse(data) + else: + self.onUpdate(data) + + #---------------------------------------------------------------------- + def onResponse(self, data): + """""" + if 'event' not in data: + return + + if data['event'] == 'subscribed': + symbol = str(data['symbol'].replace('t', '')) + #symbol = str(data['symbol']) + self.channelDict[data['chanId']] = (data['channel'], symbol) + + #---------------------------------------------------------------------- + def onUpdate(self, data): + """""" + if data[1] == u'hb': + return + + channelID = data[0] + + if not channelID: + self.onTradeUpdate(data) + else: + self.onDataUpdate(data) + + #---------------------------------------------------------------------- + def onDataUpdate(self, data): + """""" + channelID = data[0] + channel, symbol = self.channelDict[channelID] + symbol = str(symbol.replace('t', '')) + #symbol = str(symbol) + + # 获取Tick对象 + if symbol in self.tickDict: + tick = self.tickDict[symbol] + else: + tick = VtTickData() + tick.gatewayName = self.gatewayName + tick.symbol = symbol + tick.exchange = EXCHANGE_BITFINEX + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + + self.tickDict[symbol] = tick + + l = data[1] + + # 常规行情更新 + if channel == 'ticker': + tick.volume = l[-3] + tick.highPrice = l[-2] + tick.lowPrice = l[-1] + tick.lastPrice = l[-4] + tick.openPrice = tick.lastPrice - l[4] + # 深度报价更新 + elif channel == 'book': + bid = self.bidDict.setdefault(symbol, {}) + ask = self.askDict.setdefault(symbol, {}) + + if len(l) > 3: + for price, count, amount in l: + if amount > 0: + bid[price] = amount + else: + ask[price] = -amount + else: + price, count, amount = l + if not count: + if price in bid: + del bid[price] + elif price in ask: + del ask[price] + else: + if amount > 0: + bid[price ] = amount + else: + ask[price] = amount + + # BID + bidPriceList = bid.keys() + bidPriceList.sort() + + tick.bidPrice1 = bidPriceList[0] + tick.bidPrice2 = bidPriceList[1] + tick.bidPrice3 = bidPriceList[2] + tick.bidPrice4 = bidPriceList[3] + tick.bidPrice5 = bidPriceList[4] + + tick.bidVolume1 = bid[tick.bidPrice1] + tick.bidVolume2 = bid[tick.bidPrice2] + tick.bidVolume3 = bid[tick.bidPrice3] + tick.bidVolume4 = bid[tick.bidPrice4] + tick.bidVolume5 = bid[tick.bidPrice5] + + # ASK + askPriceList = ask.keys() + askPriceList.sort() + askPriceList.reverse() + + tick.askPrice1 = askPriceList[0] + tick.askPrice2 = askPriceList[1] + tick.askPrice3 = askPriceList[2] + tick.askPrice4 = askPriceList[3] + tick.askPrice5 = askPriceList[4] + + tick.askVolume1 = ask[tick.askPrice1] + tick.askVolume2 = ask[tick.askPrice2] + tick.askVolume3 = ask[tick.askPrice3] + tick.askVolume4 = ask[tick.askPrice4] + tick.askVolume5 = ask[tick.askPrice5] + + dt = datetime.now() + tick.date = dt.strftime('%Y%m%d') + tick.time = dt.strftime('%H:%M:%S.%f') + tick.datetime = dt + + # 推送 + self.gateway.onTick(copy(tick)) + + #---------------------------------------------------------------------- + def onTradeUpdate(self, data): + """""" + name = data[1] + info = data[2] + + if name == 'ws': + for l in info: + self.onWallet(l) + elif name == 'wu': + self.onWallet(info) + elif name == 'os': + for l in info: + self.onOrder(l) + elif name in ['on', 'ou', 'oc']: + self.onOrder(info) + elif name == 'te': + self.onTrade(info) + + #---------------------------------------------------------------------- + def onWallet(self, data): + """""" + if str(data[0]) == 'exchange': + pos = VtPositionData() + pos.gatewayName = self.gatewayName + + pos.symbol = str(data[1]) + pos.exchange = EXCHANGE_BITFINEX + pos.vtSymbol = '.'.join([pos.vtSymbol, pos.direction]) + pos.direction = DIRECTION_LONG + pos.vtPositionName = '.'.join([pos.symbol, pos.direction]) + pos.position = float(data[2]) + + if data[-1] is None: + pos.frozen = 0 + else: + pos.frozen = pos.position - float(data[-1]) + + self.gateway.onPosition(pos) + + #---------------------------------------------------------------------- + def onOrder(self, data): + """""" + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.symbol = str(data[3].replace('t', '')) + order.exchange = EXCHANGE_BITFINEX + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + order.orderID = str(data[2]) + order.vtOrderID = '.'.join([order.gatewayName, order.orderID]) + + if data[7] > 0: + order.direction = DIRECTION_LONG + elif data[7] < 0: + order.direction = DIRECTION_SHORT + + order.price = float(data[16]) + order.totalVolume = abs(data[7]) + order.tradedVolume = order.totalVolume - abs(data[6]) + + orderStatus = str(data[13].split('@')[0]) + orderStatus = orderStatus.replace(' ', '') + order.status = statusMapReverse[orderStatus] + + order.sessionID, order.orderTime = self.generateDateTime(data[4]) + if order.status == STATUS_CANCELLED: + buf, order.cancelTime = self.generateDateTime(data[5]) + + self.orderLocalDict[data[0]] = order.orderID + + self.gateway.onOrder(order) + + #---------------------------------------------------------------------- + def onTrade(self, data): + """""" + trade = VtTradeData() + trade.gatewayName = self.gatewayName + + trade.symbol = data[1].replace('t', '') + trade.exchange = EXCHANGE_BITFINEX + trade.vtSymbol = '.'.join([trade.symbol, trade.exchange]) + trade.orderID = self.orderLocalDict[data[3]] + trade.vtOrderID = '.'.join([trade.gatewayName, trade.orderID]) + trade.tradeID = str(data[0]) + trade.vtTradeID = '.'.join([trade.gatewayName, trade.tradeID]) + + if data[4] > 0: + trade.direction = DIRECTION_LONG + else: + trade.direction = DIRECTION_SHORT + + trade.price = data[5] + trade.volume = abs(data[4]) + buf, trade.tradeTime = self.generateDateTime(data[2]) + + self.gateway.onTrade(trade) + + #---------------------------------------------------------------------- + def onSymbolDetails(self, data): + """""" + for d in data: + contract = VtContractData() + contract.gatewayName = self.gatewayName + + contract.symbol = d['pair'].upper() + contract.exchange = EXCHANGE_BITFINEX + contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) + contract.name = contract.vtSymbol + contract.productClass = PRODUCT_SPOT + contract.priceTick = pow(10, d["price_precision"]) + contract.size = 1 + + self.gateway.onContract(contract) + + self.writeLog(u'合约信息查询成功') diff --git a/vnpy/trader/language/chinese/constant.py b/vnpy/trader/language/chinese/constant.py index ef49b3dd..0297f79a 100644 --- a/vnpy/trader/language/chinese/constant.py +++ b/vnpy/trader/language/chinese/constant.py @@ -91,6 +91,8 @@ EXCHANGE_ZB = 'ZB' # 比特币中国比特币交易所 EXCHANGE_OKEX = 'OKEX' # OKEX比特币交易所 EXCHANGE_ZAIF = "ZAIF" # ZAIF日本比特币交易所 EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK日本比特币交易所 +EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所 +EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 diff --git a/vnpy/trader/language/english/constant.py b/vnpy/trader/language/english/constant.py index 276cabee..d62757e7 100644 --- a/vnpy/trader/language/english/constant.py +++ b/vnpy/trader/language/english/constant.py @@ -87,6 +87,8 @@ EXCHANGE_ZB = 'ZB' # 比特币中国比特币交易所 EXCHANGE_OKEX = 'OKEX' # OKEX比特币交易所 EXCHANGE_ZAIF = "ZAIF" # ZAIF日本比特币交易所 EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK日本比特币交易所 +EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所 +EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 From 98dcecb470462f19db071d623fba0933da4650c4 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 5 Jun 2018 14:25:35 +0800 Subject: [PATCH 019/135] =?UTF-8?q?[Mod]=E4=BF=AE=E6=94=B9vnokex=E7=9A=84?= =?UTF-8?q?=E6=96=AD=E7=BA=BF=E9=87=8D=E8=BF=9E=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/okex/vnokex.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/vnpy/api/okex/vnokex.py b/vnpy/api/okex/vnokex.py index 061c1b2e..0c17d3eb 100644 --- a/vnpy/api/okex/vnokex.py +++ b/vnpy/api/okex/vnokex.py @@ -1,15 +1,10 @@ # encoding: UTF-8 -''' -OKEX的高性能异步交易API - -Contributor:ipqhjjybj 大佳 -''' - from __future__ import print_function import hashlib import json +import traceback from threading import Thread from time import sleep @@ -104,9 +99,10 @@ class OkexApi(object): try: self.ws.send(j) - except websocket.WebSocketConnectionClosedException: + except: + msg = traceback.format_exc() + self.onError(msg) self.reconnect() - #---------------------------------------------------------------------- def reconnect(self): From 08e836b0707744e8ece4072b5523ec4b6cf246f7 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 5 Jun 2018 14:26:17 +0800 Subject: [PATCH 020/135] =?UTF-8?q?[Add]Bitfinex=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=87=AA=E5=8A=A8=E9=87=8D=E8=BF=9E=E5=92=8C?= =?UTF-8?q?=E6=8C=81=E4=BB=93=E6=9B=B4=E6=96=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/bitfinex/vnbitfinex.py | 22 ++++++++++++++++--- .../bitfinexGateway/bitfinexGateway.py | 20 ++++++++++++++++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/vnpy/api/bitfinex/vnbitfinex.py b/vnpy/api/bitfinex/vnbitfinex.py index 23bc3a9d..8c55b08a 100644 --- a/vnpy/api/bitfinex/vnbitfinex.py +++ b/vnpy/api/bitfinex/vnbitfinex.py @@ -2,6 +2,7 @@ import json import requests +import traceback from threading import Thread from queue import Queue, Empty @@ -37,20 +38,35 @@ class BitfinexApi(object): self.restThread = Thread(target=self.runRest) self.restThread.start() + + #---------------------------------------------------------------------- + def reconnect(self): + """""" + self.ws = websocket.create_connection(WEBSOCKET_V2_URL) #---------------------------------------------------------------------- def run(self): """""" while self.active: - stream = self.ws.recv() - data = json.loads(stream) - self.onData(data) + try: + stream = self.ws.recv() + data = json.loads(stream) + self.onData(data) + except: + msg = traceback.format_exc() + self.onError(msg) + self.reconnect() #---------------------------------------------------------------------- def onData(self, data): """""" print data + #---------------------------------------------------------------------- + def onError(self, msg): + """""" + print msg + #---------------------------------------------------------------------- def sendReq(self, req): """""" diff --git a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py index b8ce5be4..d51f1cbe 100644 --- a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py +++ b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py @@ -163,6 +163,7 @@ class GatewayApi(BitfinexApi): self.apiKey = '' self.secretKey = '' self.symbols = [] + self.currencys = [] self.tickDict = {} self.bidDict = {} @@ -241,6 +242,8 @@ class GatewayApi(BitfinexApi): #---------------------------------------------------------------------- def sendOrder(self, orderReq): """""" + orderReq.volume = 0.02 + self.orderId += 1 orderId = self.date + self.orderId vtOrderID = '.'.join([self.gatewayName, str(orderId)]) @@ -281,6 +284,16 @@ class GatewayApi(BitfinexApi): self.sendReq(req) + #---------------------------------------------------------------------- + def calc(self): + """""" + l = [] + for currency in self.currencys: + l.append(['wallet_exchange_' + currency]) + + req = [0, 'calc', None, l] + self.sendReq(req) + #---------------------------------------------------------------------- def onData(self, data): """""" @@ -416,11 +429,13 @@ class GatewayApi(BitfinexApi): if name == 'ws': for l in info: self.onWallet(l) + self.writeLog(u'账户资金获取成功') elif name == 'wu': self.onWallet(info) elif name == 'os': for l in info: self.onOrder(l) + self.writeLog(u'活动委托获取成功') elif name in ['on', 'ou', 'oc']: self.onOrder(info) elif name == 'te': @@ -445,6 +460,7 @@ class GatewayApi(BitfinexApi): else: pos.frozen = pos.position - float(data[-1]) + self.currencys.append(pos.symbol) self.gateway.onPosition(pos) #---------------------------------------------------------------------- @@ -479,7 +495,9 @@ class GatewayApi(BitfinexApi): self.orderLocalDict[data[0]] = order.orderID - self.gateway.onOrder(order) + self.gateway.onOrder(order) + + self.calc() #---------------------------------------------------------------------- def onTrade(self, data): From 8b1bc4e2eabb075f07be02e957048457c80ffe97 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 5 Jun 2018 21:23:56 +0800 Subject: [PATCH 021/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9EAlgoTrading?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E4=BA=A4=E6=98=93=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/__init__.py | 12 + vnpy/trader/app/algoTrading/algoEngine.py | 221 +++++++++++++++++ vnpy/trader/app/algoTrading/algoTemplate.py | 168 +++++++++++++ vnpy/trader/app/algoTrading/twapAlgo.py | 232 ++++++++++++++++++ vnpy/trader/app/algoTrading/uiAlgoManager.py | 243 +++++++++++++++++++ 5 files changed, 876 insertions(+) create mode 100644 vnpy/trader/app/algoTrading/__init__.py create mode 100644 vnpy/trader/app/algoTrading/algoEngine.py create mode 100644 vnpy/trader/app/algoTrading/algoTemplate.py create mode 100644 vnpy/trader/app/algoTrading/twapAlgo.py create mode 100644 vnpy/trader/app/algoTrading/uiAlgoManager.py diff --git a/vnpy/trader/app/algoTrading/__init__.py b/vnpy/trader/app/algoTrading/__init__.py new file mode 100644 index 00000000..5d8bab7c --- /dev/null +++ b/vnpy/trader/app/algoTrading/__init__.py @@ -0,0 +1,12 @@ +# encoding: UTF-8 + +import os + +from .algoEngine import AlgoEngine +from .uiAlgoManager import AlgoManager + +appName = 'AlgoTrading' +appDisplayName = u'算法交易' +appEngine = AlgoEngine +appWidget = AlgoManager +appIco = 'ct.ico' diff --git a/vnpy/trader/app/algoTrading/algoEngine.py b/vnpy/trader/app/algoTrading/algoEngine.py new file mode 100644 index 00000000..e6ac3617 --- /dev/null +++ b/vnpy/trader/app/algoTrading/algoEngine.py @@ -0,0 +1,221 @@ +# encoding: UTF-8 + +''' +''' + +from __future__ import division + +from vnpy.event import Event +from vnpy.trader.vtEvent import EVENT_TIMER, EVENT_TICK, EVENT_ORDER, EVENT_TRADE +from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, PRICETYPE_LIMITPRICE, + OFFSET_OPEN, OFFSET_CLOSE, + OFFSET_CLOSETODAY, OFFSET_CLOSEYESTERDAY) +from vnpy.trader.vtObject import VtSubscribeReq, VtOrderReq, VtCancelOrderReq, VtLogData + +from .twapAlgo import TwapAlgo + + +EVENT_ALGO_LOG = 'eAlgoLog' # 算法日志事件 +EVENT_ALGO_PARAM = 'eAlgoParam' # 算法参数事件 +EVENT_ALGO_VAR = 'eAlgoVar' # 算法变量事件 + + +ALGO_DICT = { + TwapAlgo.name: TwapAlgo +} + + + +######################################################################## +class AlgoEngine(object): + """算法交易引擎""" + + #---------------------------------------------------------------------- + def __init__(self, mainEngine, eventEngine): + """""" + self.mainEngine = mainEngine + self.eventEngine = eventEngine + + self.algoDict = {} # algoName:algo + self.orderAlgoDict = {} # vtOrderID:algo + self.symbolAlgoDict = {} # vtSymbol:algo set + + self.registerEvent() + + #---------------------------------------------------------------------- + def registerEvent(self): + """注册事件监听""" + self.eventEngine.register(EVENT_TICK, self.processTickEvent) + self.eventEngine.register(EVENT_TIMER, self.processTimerEvent) + self.eventEngine.register(EVENT_ORDER, self.processOrderEvent) + self.eventEngine.register(EVENT_TRADE, self.processTradeEvent) + + #---------------------------------------------------------------------- + def stop(self): + """停止""" + pass + + #---------------------------------------------------------------------- + def processTickEvent(self, event): + """行情事件""" + tick = event.dict_['data'] + + l = self.symbolAlgoDict.get(tick.vtSymbol, None) + if l: + for algo in l: + algo.updateTick(tick) + + #---------------------------------------------------------------------- + def processOrderEvent(self, event): + """委托事件""" + order = event.dict_['data'] + + algo = self.orderAlgoDict.get(order.vtOrderID, None) + if algo: + algo.updateOrder(order) + + #---------------------------------------------------------------------- + def processTradeEvent(self, event): + """成交事件""" + trade = event.dict_['data'] + + algo = self.orderAlgoDict.get(trade.vtOrderID, None) + if algo: + algo.updateTrade(trade) + + #---------------------------------------------------------------------- + def processTimerEvent(self, event): + """定时事件""" + for algo in self.algoDict.values(): + algo.updateTimer() + + #---------------------------------------------------------------------- + def addAlgo(self, name, algoSetting): + """新增算法""" + algoClass = ALGO_DICT[name] + algo = algoClass.new(self, algoSetting) + self.algoDict[algo.algoName] = algo + return algo.algoName + + #---------------------------------------------------------------------- + def stopAlgo(self, algoName): + """停止算法""" + self.algoDict[algoName].stop() + del self.algoDict[algoName] + + #---------------------------------------------------------------------- + def stopAll(self): + """全部停止""" + l = self.algoDict.keys() + for algoName in l: + self.stopAlgo(algoName) + + #---------------------------------------------------------------------- + def subscribe(self, algo, vtSymbol): + """""" + contract = self.mainEngine.getContract(vtSymbol) + if not contract: + self.writeLog(u'%s订阅行情失败,找不到合约%s' %(algo.algoName, vtSymbol)) + return + + # 如果vtSymbol已存在于字典,说明已经订阅过 + if vtSymbol in self.symbolAlgoDict: + s = self.symbolAlgoDict[vtSymbol] + s.add(algo) + return + # 否则需要添加到字典中并执行订阅 + else: + s = set() + self.symbolAlgoDict[vtSymbol] = s + s.add(algo) + + req = VtSubscribeReq() + req.symbol = contract.symbol + req.exchange = contract.exchange + self.mainEngine.subscribe(req, contract.gatewayName) + + #---------------------------------------------------------------------- + def sendOrder(self, algo, vtSymbol, direction, price, volume): + """发单""" + contract = self.mainEngine.getContract(vtSymbol) + if not contract: + self.writeLog(u'%s委托下单失败,找不到合约:%s' %(algo.algoName, vtSymbol)) + + req = VtOrderReq() + req.vtSymbol = vtSymbol + req.symbol = contract.symbol + req.exchange = contract.exchange + req.direction = direction + req.priceType = PRICETYPE_LIMITPRICE + req.offset = OFFSET_CLOSETODAY + req.price = price + req.volume = volume + vtOrderID = self.mainEngine.sendOrder(req, contract.gatewayName) + + return vtOrderID + + #---------------------------------------------------------------------- + def buy(self, algo, vtSymbol, price, volume): + """买入""" + return self.sendOrder(algo, vtSymbol, DIRECTION_LONG, price, volume) + + #---------------------------------------------------------------------- + def sell(self, algo, vtSymbol, price, volume): + """卖出""" + return self.sendOrder(algo, vtSymbol, DIRECTION_SHORT, price, volume) + + #---------------------------------------------------------------------- + def cancelOrder(self, algo, vtOrderID): + """撤单""" + order = self.mainEngine.getOrder(vtOrderID) + if not order: + self.writeLog(u'%s委托撤单失败,找不到委托:%s' %(algo.algoName, vtOrderID)) + return + + req = VtCancelOrderReq() + req.symbol = order.symbol + req.exchange = order.exchange + req.orderID = order.orderID + req.frontID = order.frontID + req.sessionID = order.sessionID + self.mainEngine.cancelOrder(req, order.gatewayName) + + #---------------------------------------------------------------------- + def writeLog(self, content, algo=None): + """输出日志""" + log = VtLogData() + log.logContent = content + + if algo: + log.gatewayName = algo.algoName + + event = Event(EVENT_ALGO_LOG) + event.dict_['data'] = log + self.eventEngine.put(event) + + #---------------------------------------------------------------------- + def putVarEvent(self, algo, d): + """更新变量""" + d['algoName'] = algo.algoName + event = Event(EVENT_ALGO_VAR) + event.dict_['data'] = d + self.eventEngine.put(event) + + #---------------------------------------------------------------------- + def putParamEvent(self, algo, d): + """更新参数""" + d['algoName'] = algo.algoName + event = Event(EVENT_ALGO_PARAM) + event.dict_['data'] = d + self.eventEngine.put(event) + + #---------------------------------------------------------------------- + def getTick(self, algo, vtSymbol): + """查询行情""" + tick = self.mainEngine.getTick(vtSymbol) + if not tick: + self.writeLog(u'%s查询行情失败,找不到报价:%s' %(algo.algoName, vtSymbol)) + return + + return tick + diff --git a/vnpy/trader/app/algoTrading/algoTemplate.py b/vnpy/trader/app/algoTrading/algoTemplate.py new file mode 100644 index 00000000..9866669f --- /dev/null +++ b/vnpy/trader/app/algoTrading/algoTemplate.py @@ -0,0 +1,168 @@ +# encoding: UTF-8 + +from __future__ import division + +from vnpy.trader.vtConstant import STATUS_NOTTRADED, STATUS_PARTTRADED, STATUS_UNKNOWN + + +# 活动委托状态 +STATUS_ACTIVE = [STATUS_NOTTRADED, STATUS_PARTTRADED, STATUS_UNKNOWN] + + +######################################################################## +class AlgoTemplate(object): + """算法模板""" + name = 'AlgoTemplate' + count = 0 + + @classmethod + #---------------------------------------------------------------------- + def new(cls, engine, setting): + """创建新对象""" + cls.count += 1 + algoName = '%s_%s' %(cls.name, cls.count) + + algo = cls(engine, setting, algoName) + return algo + + #---------------------------------------------------------------------- + def __init__(self, engine, setting, algoName): + """Constructor""" + self.engine = engine + self.active = True + self.algoName = algoName + self.activeOrderDict = {} # vtOrderID:order + + #---------------------------------------------------------------------- + def updateTick(self, tick): + """""" + if not self.active: + return + + self.onTick(tick) + + #---------------------------------------------------------------------- + def updateTrade(self, trade): + """""" + if not self.active: + return + + self.onTrade(trade) + + #---------------------------------------------------------------------- + def updateOrder(self, order): + """""" + if not self.active: + return + + # 活动委托需要缓存 + if order.status in STATUS_ACTIVE: + self.activeOrderDict[order.vtOrderID] = order + # 结束委托需要移除 + elif order.vtOrderID in self.activeOrderDict: + del self.activeOrderDict[order.vtOrderID] + + self.onOrder(order) + + #---------------------------------------------------------------------- + def updateTimer(self): + """""" + if not self.active: + return + + self.onTimer() + + #---------------------------------------------------------------------- + def stop(self): + """""" + self.active = False + self.cancelAll() + + self.onStop() + + #---------------------------------------------------------------------- + def onTick(self, tick): + """""" + pass + + #---------------------------------------------------------------------- + def onTrade(self, trade): + """""" + pass + + #---------------------------------------------------------------------- + def onOrder(self, order): + """""" + pass + + #---------------------------------------------------------------------- + def onTimer(self): + """""" + pass + + #---------------------------------------------------------------------- + def onStop(self): + """""" + pass + + #---------------------------------------------------------------------- + def subscribe(self, vtSymbol): + """""" + self.engine.subscribe(self, vtSymbol) + + #---------------------------------------------------------------------- + def buy(self, vtSymbol, price, volume): + """""" + return self.engine.buy(self, vtSymbol, price, volume) + + #---------------------------------------------------------------------- + def sell(self, vtSymbol, price, volume): + """""" + return self.engine.sell(self, vtSymbol, price, volume) + + #---------------------------------------------------------------------- + def cancelOrder(self, vtOrderID): + """""" + self.engine.cancelOrder(self, vtOrderID) + + #---------------------------------------------------------------------- + def cancelAll(self): + """""" + if not self.activeOrderDict: + return False + + for order in self.activeOrderDict.values(): + self.cancelOrder(order.vtOrderID) + return True + + #---------------------------------------------------------------------- + def getTick(self, vtSymbol): + """""" + return self.engine.getTick(self, vtSymbol) + + #---------------------------------------------------------------------- + def roundValue(self, value, change): + """标准化价格或者数量""" + if not change: + return value + + n = value / change + v = round(n, 0) * change + return v + + #---------------------------------------------------------------------- + def putVarEvent(self, d): + """更新变量""" + self.engine.putVarEvent(self, d) + + #---------------------------------------------------------------------- + def putParamEvent(self, d): + """更新参数""" + self.engine.putParamEvent(self, d) + + #---------------------------------------------------------------------- + def writeLog(self, content): + """输出日志""" + self.engine.writeLog(content, self) + + \ No newline at end of file diff --git a/vnpy/trader/app/algoTrading/twapAlgo.py b/vnpy/trader/app/algoTrading/twapAlgo.py new file mode 100644 index 00000000..f7723ddc --- /dev/null +++ b/vnpy/trader/app/algoTrading/twapAlgo.py @@ -0,0 +1,232 @@ +# encoding: UTF-8 + +from __future__ import division +from collections import OrderedDict + +from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT) +from vnpy.trader.uiQt import QtWidgets + +from algoTemplate import AlgoTemplate + + +######################################################################## +class TwapAlgo(AlgoTemplate): + """TWAP算法""" + + name = 'TWAP' + + #---------------------------------------------------------------------- + def __init__(self, engine, setting, algoName): + """Constructor""" + super(TwapAlgo, self).__init__(engine, setting, algoName) + + # 参数 + self.vtSymbol = setting['vtSymbol'] # 合约代码 + self.direction = setting['direction'] # 买卖 + self.price = setting['price'] # 目标价格 + self.totalVolume = setting['volume'] # 总数量 + self.time = setting['time'] # 执行时间 + self.interval = setting['interval'] # 执行间隔 + self.minVolume = setting['minVolume'] # 最小委托数量 + + # 变量 + self.orderSize = self.totalVolume / (self.time / self.interval) + self.orderSize = self.roundValue(self.orderSize, self.minVolume) + if self.minVolume >= 1: + self.orderSize = int(self.orderSize) + + self.timerCount = 0 + self.timerTotal = 0 + self.tradedVolume = 0 + + self.subscribe(self.vtSymbol) + self.paramEvent() + self.varEvent() + + #---------------------------------------------------------------------- + def onTick(self, tick): + """""" + pass + + #---------------------------------------------------------------------- + def onTrade(self, trade): + """""" + self.tradedVolume += trade.volume + + if self.tradedVolume >= self.totalVolume: + self.stop() + else: + self.varEvent() + + #---------------------------------------------------------------------- + def onOrder(self, order): + """""" + pass + + #---------------------------------------------------------------------- + def onTimer(self): + """""" + self.timerCount += 1 + self.timerTotal += 1 + + # 总时间结束,停止算法 + if self.timerTotal >= self.time: + self.stop() + return + + # 每到间隔发一次委托 + if self.timerCount >= self.interval: + self.timerCount = 0 + + tick = self.getTick(self.vtSymbol) + if not tick: + return + + size = min(self.orderSize, self.totalVolume-self.tradedVolume) + + # 买入 + if self.direction == DIRECTION_LONG: + # 市场买1价小于目标买价 + if tick.bidPrice1 < self.price: + self.buy(self.vtSymbol, self.price, size) + self.writeLog(u'委托买入%s,数量%,价格%s' %(self.vtSymbol, self.orderSize, self.price)) + # 卖出 + if self.direction == DIRECTION_SHORT: + # 市场卖1价大于目标价 + if tick.askPrice1 > self.price: + self.sell(self.vtSymbol, self.price, size) + self.writeLog(u'委托卖出%s,数量%s,价格%s' %(self.vtSymbol, self.orderSize, self.price)) + + # 委托后等待到间隔一半的时间撤单 + elif self.timerCount == round(self.interval/2, 0): + result = self.cancelAll() + if result: + self.writeLog(u'撤销之前的委托') + + self.varEvent() + + #---------------------------------------------------------------------- + def onStop(self): + """""" + self.writeLog(u'运行时间已到,停止算法') + self.varEvent() + + #---------------------------------------------------------------------- + def varEvent(self): + """更新变量""" + d = OrderedDict() + d[u'算法状态'] = self.active + d[u'成交数量'] = self.tradedVolume + d[u'单笔委托'] = self.orderSize + d[u'本轮读秒'] = self.timerCount + d[u'累计读秒'] = self.timerTotal + d['active'] = self.active + self.putVarEvent(d) + + #---------------------------------------------------------------------- + def paramEvent(self): + """更新参数""" + d = OrderedDict() + d[u'代码'] = self.vtSymbol + d[u'方向'] = self.direction + d[u'目标价格'] = self.price + d[u'总数量'] = self.totalVolume + d[u'总时间(秒)'] = self.time + d[u'间隔(秒)'] = self.interval + self.putParamEvent(d) + + + +######################################################################## +class TwapWidget(QtWidgets.QWidget): + """""" + name = TwapAlgo.name + + #---------------------------------------------------------------------- + def __init__(self, algoEngine): + """Constructor""" + super(TwapWidget, self).__init__() + + self.algoEngine = algoEngine + + self.initUi() + + #---------------------------------------------------------------------- + def initUi(self): + """""" + self.lineSymbol = QtWidgets.QLineEdit() + + self.comboDirection = QtWidgets.QComboBox() + self.comboDirection.addItem(DIRECTION_LONG) + self.comboDirection.addItem(DIRECTION_SHORT) + self.comboDirection.setCurrentIndex(0) + + self.spinPrice = QtWidgets.QDoubleSpinBox() + self.spinPrice.setMinimum(0) + self.spinPrice.setMaximum(1000000000) + self.spinPrice.setDecimals(8) + + self.spinVolume = QtWidgets.QDoubleSpinBox() + self.spinVolume.setMinimum(0) + self.spinVolume.setMaximum(1000000000) + self.spinVolume.setDecimals(6) + + self.spinTime = QtWidgets.QSpinBox() + self.spinTime.setMinimum(30) + self.spinTime.setMaximum(86400) + + self.spinInterval = QtWidgets.QSpinBox() + self.spinInterval.setMinimum(10) + self.spinInterval.setMaximum(3600) + + self.spinMinVolume = QtWidgets.QDoubleSpinBox() + self.spinMinVolume.setMinimum(0) + self.spinMinVolume.setMaximum(10000) + self.spinMinVolume.setDecimals(6) + self.spinMinVolume.setValue(1) + + buttonStart = QtWidgets.QPushButton(u'启动') + buttonStart.clicked.connect(self.addAlgo) + buttonStart.setMinimumHeight(100) + + Label = QtWidgets.QLabel + + grid = QtWidgets.QGridLayout() + grid.addWidget(Label(u'交易代码'), 0, 0) + grid.addWidget(self.lineSymbol, 0, 1) + grid.addWidget(Label(u'方向'), 1, 0) + grid.addWidget(self.comboDirection, 1, 1) + grid.addWidget(Label(u'目标价格'), 2, 0) + grid.addWidget(self.spinPrice, 2, 1) + grid.addWidget(Label(u'总数量'), 3, 0) + grid.addWidget(self.spinVolume, 3, 1) + grid.addWidget(Label(u'总时间(秒)'), 4, 0) + grid.addWidget(self.spinTime, 4, 1) + grid.addWidget(Label(u'间隔(秒)'), 5, 0) + grid.addWidget(self.spinInterval, 5, 1) + grid.addWidget(Label(u'数量取整'), 6, 0) + grid.addWidget(self.spinMinVolume, 6, 1) + grid.addWidget(buttonStart, 7, 0, 1, 2) + + self.setLayout(grid) + + #---------------------------------------------------------------------- + def addAlgo(self): + """""" + setting = { + 'vtSymbol': str(self.lineSymbol.text()), + 'direction': str(self.comboDirection.currentText()), + 'price': float(self.spinPrice.value()), + 'volume': float(self.spinVolume.value()), + 'time': int(self.spinTime.value()), + 'interval': int(self.spinInterval.value()), + 'minVolume': float(self.spinMinVolume.value()) + } + + self.algoEngine.addAlgo(TwapAlgo.name, setting) + + + + + + \ No newline at end of file diff --git a/vnpy/trader/app/algoTrading/uiAlgoManager.py b/vnpy/trader/app/algoTrading/uiAlgoManager.py new file mode 100644 index 00000000..1c0baea3 --- /dev/null +++ b/vnpy/trader/app/algoTrading/uiAlgoManager.py @@ -0,0 +1,243 @@ +# encoding: UTF-8 + +from vnpy.event import Event +from vnpy.trader.uiQt import QtCore, QtWidgets + +from .algoEngine import EVENT_ALGO_LOG, EVENT_ALGO_PARAM, EVENT_ALGO_VAR +from .twapAlgo import TwapWidget + + +######################################################################## +class StopButton(QtWidgets.QPushButton): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, algoName=''): + """Constructor""" + super(StopButton, self).__init__() + + self.algoEngine = algoEngine + self.algoName = algoName + + self.setStyleSheet("color:black;background-color:yellow") + + if algoName: + self.setText(u'停止') + self.clicked.connect(self.stopAlgo) + else: + self.setText(u'全部停止') + self.clicked.connect(self.stopAll) + + #---------------------------------------------------------------------- + def stopAlgo(self): + """""" + self.algoEngine.stopAlgo(self.algoName) + self.disable() + + #---------------------------------------------------------------------- + def stopAll(self): + """""" + self.algoEngine.stopAll() + + #---------------------------------------------------------------------- + def disable(self): + """""" + self.setEnabled(False) + self.setStyleSheet("color:black;background-color:grey") + + + +AlgoCell = QtWidgets.QTableWidgetItem + + +######################################################################## +class AlgoStatusMonitor(QtWidgets.QTableWidget): + """""" + signalParam = QtCore.Signal(type(Event())) + signalVar = QtCore.Signal(type(Event())) + + #---------------------------------------------------------------------- + def __init__(self, algoEngine): + """Constructor""" + super(AlgoStatusMonitor, self).__init__() + + self.algoEngine = algoEngine + self.eventEngine = algoEngine.eventEngine + + self.cellDict = {} + + self.initUi() + self.registerEvent() + + #---------------------------------------------------------------------- + def initUi(self): + """初始化界面""" + labels = [u'', + u'名称', + u'参数', + u'变量'] + + self.setColumnCount(len(labels)) + self.setHorizontalHeaderLabels(labels) + self.setRowCount(0) + self.verticalHeader().setVisible(False) + self.setEditTriggers(self.NoEditTriggers) + + #---------------------------------------------------------------------- + def registerEvent(self): + """注册事件监听""" + self.signalParam.connect(self.processParamEvent) + self.signalVar.connect(self.processVarEvent) + + self.eventEngine.register(EVENT_ALGO_PARAM, self.signalParam.emit) + self.eventEngine.register(EVENT_ALGO_VAR, self.signalVar.emit) + + #---------------------------------------------------------------------- + def addAlgo(self, algoName): + """新增算法""" + self.insertRow(0) + + buttonStop = StopButton(self.algoEngine, algoName) + cellName = AlgoCell(algoName) + cellParam = AlgoCell() + cellVar = AlgoCell() + + self.setCellWidget(0, 0, buttonStop) + self.setItem(0, 1, cellName) + self.setItem(0, 2, cellParam) + self.setItem(0, 3, cellVar) + + self.cellDict[algoName] = { + 'param': cellParam, + 'var': cellVar, + 'button': buttonStop + } + + #---------------------------------------------------------------------- + def processParamEvent(self, event): + """""" + d = event.dict_['data'] + + algoName = d.pop('algoName') + if algoName not in self.cellDict: + self.addAlgo(algoName) + + l = [] + for k, v in d.items(): + msg = u'%s:%s' %(k, v) + l.append(msg) + text = ','.join(l) + + cell = self.cellDict[algoName]['param'] + cell.setText(text) + + self.resizeColumnsToContents() + + #---------------------------------------------------------------------- + def processVarEvent(self, event): + """""" + d = event.dict_['data'] + + algoName = d.pop('algoName') + if algoName not in self.cellDict: + self.addAlgo(algoName) + + if 'active' in d: + active = d.pop('active') + if not active: + button = self.cellDict[algoName]['button'] + button.disable() + + l = [] + for k, v in d.items(): + msg = u'%s:%s' %(k, v) + l.append(msg) + text = ','.join(l) + + cell = self.cellDict[algoName]['var'] + cell.setText(text) + + self.resizeColumnsToContents() + + +######################################################################## +class AlgoLogMonitor(QtWidgets.QTextEdit): + """""" + signal = QtCore.Signal(type(Event())) + + #---------------------------------------------------------------------- + def __init__(self, algoEngine): + """Constructor""" + super(AlgoLogMonitor, self).__init__() + + self.eventEngine = algoEngine.eventEngine + + self.registerEvent() + + #---------------------------------------------------------------------- + def registerEvent(self): + """""" + self.signal.connect(self.processEvent) + + self.eventEngine.register(EVENT_ALGO_LOG, self.signal.emit) + + #---------------------------------------------------------------------- + def processEvent(self, event): + """""" + log = event.dict_['data'] + if not log.gatewayName: + log.gatewayName = u'算法引擎' + msg = u'%s\t%s:%s' %(log.logTime, log.gatewayName, log.logContent) + self.append(msg) + + + +######################################################################## +class AlgoManager(QtWidgets.QWidget): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, eventEngine): + """Constructor""" + super(AlgoManager, self).__init__() + + self.algoEngine = algoEngine + self.eventEngine = eventEngine + + self.initUi() + self.addAlgoWidget(TwapWidget) + + #---------------------------------------------------------------------- + def initUi(self): + """""" + self.setWindowTitle(u'算法交易') + + self.statusMonitor = AlgoStatusMonitor(self.algoEngine) + self.logMonitor = AlgoLogMonitor(self.algoEngine) + self.tab = QtWidgets.QTabWidget() + self.buttonStop = StopButton(self.algoEngine) + + self.tab.setMaximumWidth(400) + self.buttonStop.setMaximumWidth(400) + self.buttonStop.setFixedHeight(100) + + vbox1 = QtWidgets.QVBoxLayout() + vbox1.addWidget(self.tab) + vbox1.addStretch() + vbox1.addWidget(self.buttonStop) + + vbox2 = QtWidgets.QVBoxLayout() + vbox2.addWidget(self.statusMonitor) + vbox2.addWidget(self.logMonitor) + + hbox = QtWidgets.QHBoxLayout() + hbox.addLayout(vbox1) + hbox.addLayout(vbox2) + + self.setLayout(hbox) + + #---------------------------------------------------------------------- + def addAlgoWidget(self, widgetClass): + """""" + w = widgetClass(self.algoEngine) + self.tab.addTab(w, w.name) \ No newline at end of file From f0c9500a0e0d4bcd47df3587dd4c4b457338b63a Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 9 Jun 2018 15:12:30 +0800 Subject: [PATCH 022/135] =?UTF-8?q?[Mod]=E7=AE=80=E5=8C=96=E5=A4=A7?= =?UTF-8?q?=E5=95=86=E6=89=80=E7=9A=84Tick=E6=97=A5=E6=9C=9F=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/gateway/ctpGateway/ctpGateway.py | 25 +------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/vnpy/trader/gateway/ctpGateway/ctpGateway.py b/vnpy/trader/gateway/ctpGateway/ctpGateway.py index 2d49c8c3..38185763 100644 --- a/vnpy/trader/gateway/ctpGateway/ctpGateway.py +++ b/vnpy/trader/gateway/ctpGateway/ctpGateway.py @@ -247,10 +247,6 @@ class CtpMdApi(MdApi): self.brokerID = EMPTY_STRING # 经纪商代码 self.address = EMPTY_STRING # 服务器地址 - self.tradingDt = None # 交易日datetime对象 - self.tradingDate = EMPTY_STRING # 交易日期字符串 - self.tickTime = None # 最新行情time对象 - #---------------------------------------------------------------------- def onFrontConnected(self): """服务器连接""" @@ -298,14 +294,6 @@ class CtpMdApi(MdApi): for subscribeReq in self.subscribedSymbols: self.subscribe(subscribeReq) - # 获取交易日 - #self.tradingDate = data['TradingDay'] - #self.tradingDt = datetime.strptime(self.tradingDate, '%Y%m%d') - - # 登录时通过本地时间来获取当前的日期 - self.tradingDt = datetime.now() - self.tradingDate = self.tradingDt.strftime('%Y%m%d') - # 否则,推送错误信息 else: err = VtErrorData() @@ -388,18 +376,7 @@ class CtpMdApi(MdApi): # 大商所日期转换 if tick.exchange is EXCHANGE_DCE: - newTime = datetime.strptime(tick.time, '%H:%M:%S.%f').time() # 最新tick时间戳 - - # 如果新tick的时间小于夜盘分隔,且上一个tick的时间大于夜盘分隔,则意味着越过了12点 - if (self.tickTime and - newTime < NIGHT_TRADING and - self.tickTime > NIGHT_TRADING): - self.tradingDt += timedelta(1) # 日期加1 - self.tradingDate = self.tradingDt.strftime('%Y%m%d') # 生成新的日期字符串 - - tick.date = self.tradingDate # 使用本地维护的日期 - - self.tickTime = newTime # 更新上一个tick时间 + tick.date = datetime.now().strftime('%Y%m%d') self.gateway.onTick(tick) From 143ab33068d88761df1326af6d90f3d4d48b155b Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 9 Jun 2018 16:40:13 +0800 Subject: [PATCH 023/135] =?UTF-8?q?[ADD]TWAP=E7=AE=97=E6=B3=95=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=A7=94=E6=89=98=E6=B7=B1=E5=BA=A6=E6=A1=A3=E4=BD=8D?= =?UTF-8?q?=E7=9A=84=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/__init__.py | 2 +- vnpy/trader/app/algoTrading/at.ico | Bin 0 -> 67646 bytes vnpy/trader/app/algoTrading/twapAlgo.py | 78 ++++++++++++++++++------ 3 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 vnpy/trader/app/algoTrading/at.ico diff --git a/vnpy/trader/app/algoTrading/__init__.py b/vnpy/trader/app/algoTrading/__init__.py index 5d8bab7c..0529d815 100644 --- a/vnpy/trader/app/algoTrading/__init__.py +++ b/vnpy/trader/app/algoTrading/__init__.py @@ -9,4 +9,4 @@ appName = 'AlgoTrading' appDisplayName = u'算法交易' appEngine = AlgoEngine appWidget = AlgoManager -appIco = 'ct.ico' +appIco = 'at.ico' diff --git a/vnpy/trader/app/algoTrading/at.ico b/vnpy/trader/app/algoTrading/at.ico new file mode 100644 index 0000000000000000000000000000000000000000..83114df81f5fca19c9462e166dd97f5f032d20b2 GIT binary patch literal 67646 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 self.price: - self.sell(self.vtSymbol, self.price, size) - self.writeLog(u'委托卖出%s,数量%s,价格%s' %(self.vtSymbol, self.orderSize, self.price)) + if tick.askPrice1 > self.targetPrice: + # 计算委托价格 + priceMap = { + 1: tick.bidPrice1, + 2: tick.bidPrice2, + 3: tick.bidPrice3, + 4: tick.bidPrice4, + 5: tick.bidPrice5, + } + price = priceMap[self.priceLevel] + if price: + price = max(price, self.targetPrice) + else: + price = self.targetPrice + + # 发出委托 + self.sell(self.vtSymbol, price, size) + self.writeLog(u'委托卖出%s,数量%s,价格%s' %(self.vtSymbol, self.orderSize, price)) # 委托后等待到间隔一半的时间撤单 elif self.timerCount == round(self.interval/2, 0): @@ -129,10 +160,11 @@ class TwapAlgo(AlgoTemplate): d = OrderedDict() d[u'代码'] = self.vtSymbol d[u'方向'] = self.direction - d[u'目标价格'] = self.price + d[u'目标价格'] = self.targetPrice d[u'总数量'] = self.totalVolume d[u'总时间(秒)'] = self.time d[u'间隔(秒)'] = self.interval + d[u'委托档位'] = self.priceLevel self.putParamEvent(d) @@ -185,6 +217,11 @@ class TwapWidget(QtWidgets.QWidget): self.spinMinVolume.setDecimals(6) self.spinMinVolume.setValue(1) + self.spinPriceLevel = QtWidgets.QSpinBox() + self.spinPriceLevel.setMinimum(1) + self.spinPriceLevel.setMaximum(5) + self.spinPriceLevel.setValue(1) + buttonStart = QtWidgets.QPushButton(u'启动') buttonStart.clicked.connect(self.addAlgo) buttonStart.setMinimumHeight(100) @@ -204,9 +241,11 @@ class TwapWidget(QtWidgets.QWidget): grid.addWidget(self.spinTime, 4, 1) grid.addWidget(Label(u'间隔(秒)'), 5, 0) grid.addWidget(self.spinInterval, 5, 1) - grid.addWidget(Label(u'数量取整'), 6, 0) - grid.addWidget(self.spinMinVolume, 6, 1) - grid.addWidget(buttonStart, 7, 0, 1, 2) + grid.addWidget(Label(u'委托档位'), 6, 0) + grid.addWidget(self.spinPriceLevel, 6, 1) + grid.addWidget(Label(u'数量取整'), 7, 0) + grid.addWidget(self.spinMinVolume, 7, 1) + grid.addWidget(buttonStart, 8, 0, 1, 2) self.setLayout(grid) @@ -216,10 +255,11 @@ class TwapWidget(QtWidgets.QWidget): setting = { 'vtSymbol': str(self.lineSymbol.text()), 'direction': str(self.comboDirection.currentText()), - 'price': float(self.spinPrice.value()), - 'volume': float(self.spinVolume.value()), + 'targetPrice': float(self.spinPrice.value()), + 'totalVolume': float(self.spinVolume.value()), 'time': int(self.spinTime.value()), 'interval': int(self.spinInterval.value()), + 'priceLevel': int(self.spinPriceLevel.value()), 'minVolume': float(self.spinMinVolume.value()) } From 32d8a257baf5e14fb9ee7bbb9ed1da898efd0903 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 9 Jun 2018 18:54:41 +0800 Subject: [PATCH 024/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9ECryptoTrader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/CryptoTrader/VT_setting.json | 20 +++++++++ examples/CryptoTrader/run.py | 61 +++++++++++++++++++++++++++ examples/VnTrader/run.py | 4 +- 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 examples/CryptoTrader/VT_setting.json create mode 100644 examples/CryptoTrader/run.py diff --git a/examples/CryptoTrader/VT_setting.json b/examples/CryptoTrader/VT_setting.json new file mode 100644 index 00000000..473017fb --- /dev/null +++ b/examples/CryptoTrader/VT_setting.json @@ -0,0 +1,20 @@ +{ + "fontFamily": "微软雅黑", + "fontSize": 12, + + "mongoHost": "localhost", + "mongoPort": 27017, + "mongoLogging": true, + + "darkStyle": true, + "language": "chinese", + + "logActive": true, + "logLevel": "debug", + "logConsole": true, + "logFile": true, + + "tdPenalty": ["IF", "IH", "IC"], + + "maxDecimal": 4 +} \ No newline at end of file diff --git a/examples/CryptoTrader/run.py b/examples/CryptoTrader/run.py new file mode 100644 index 00000000..a09cb3dc --- /dev/null +++ b/examples/CryptoTrader/run.py @@ -0,0 +1,61 @@ +# encoding: UTF-8 + +# 重载sys模块,设置默认字符串编码方式为utf8 +try: + reload # Python 2 +except NameError: # Python 3 + from importlib import reload +import sys +reload(sys) +sys.setdefaultencoding('utf8') + +# 判断操作系统 +import platform +system = platform.system() + +# vn.trader模块 +from vnpy.event import EventEngine +from vnpy.trader.vtEngine import MainEngine +from vnpy.trader.uiQt import createQApp +from vnpy.trader.uiMainWindow import MainWindow + +# 加载底层接口 +from vnpy.trader.gateway import (huobiGateway, okexGateway, + binanceGateway, bitfinexGateway) + +# 加载上层应用 +from vnpy.trader.app import (riskManager, algoTrading) + + +#---------------------------------------------------------------------- +def main(): + """主程序入口""" + # 创建Qt应用对象 + qApp = createQApp() + + # 创建事件引擎 + ee = EventEngine() + + # 创建主引擎 + me = MainEngine(ee) + + # 添加交易接口 + me.addGateway(huobiGateway) + me.addGateway(okexGateway) + me.addGateway(binanceGateway) + me.addGateway(bitfinexGateway) + + # 添加上层应用 + me.addApp(riskManager) + me.addApp(algoTrading) + + # 创建主窗口 + mw = MainWindow(me, ee) + mw.showMaximized() + + # 在主线程中启动Qt事件循环 + sys.exit(qApp.exec_()) + + +if __name__ == '__main__': + main() diff --git a/examples/VnTrader/run.py b/examples/VnTrader/run.py index 24a54f97..9eaf85d3 100644 --- a/examples/VnTrader/run.py +++ b/examples/VnTrader/run.py @@ -30,7 +30,8 @@ elif system == 'Windows': secGateway) # 加载上层应用 -from vnpy.trader.app import (riskManager, ctaStrategy, spreadTrading) +from vnpy.trader.app import (riskManager, ctaStrategy, + spreadTrading, algoTrading) #---------------------------------------------------------------------- @@ -62,6 +63,7 @@ def main(): me.addApp(riskManager) me.addApp(ctaStrategy) me.addApp(spreadTrading) + me.addApp(algoTrading) # 创建主窗口 mw = MainWindow(me, ee) From 08253b087b0f7598f7d771cbe676e7d3710a983c Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 9 Jun 2018 18:55:04 +0800 Subject: [PATCH 025/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9E=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E9=85=8D=E7=BD=AE=E4=BF=9D=E5=AD=98=E5=92=8C=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/algoEngine.py | 49 +++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/vnpy/trader/app/algoTrading/algoEngine.py b/vnpy/trader/app/algoTrading/algoEngine.py index e6ac3617..5b69fb23 100644 --- a/vnpy/trader/app/algoTrading/algoEngine.py +++ b/vnpy/trader/app/algoTrading/algoEngine.py @@ -18,10 +18,16 @@ from .twapAlgo import TwapAlgo EVENT_ALGO_LOG = 'eAlgoLog' # 算法日志事件 EVENT_ALGO_PARAM = 'eAlgoParam' # 算法参数事件 EVENT_ALGO_VAR = 'eAlgoVar' # 算法变量事件 +EVENT_ALGO_SETTING = 'eAlgoSetting' # 算法配置事件 + + +ALGOTRADING_DB_NAME = 'VnTrader_AlgoTrading_Db' + +SETTING_COLLECTION_NAME = 'AlgoSetting' ALGO_DICT = { - TwapAlgo.name: TwapAlgo + TwapAlgo.templateName: TwapAlgo } @@ -39,6 +45,7 @@ class AlgoEngine(object): self.algoDict = {} # algoName:algo self.orderAlgoDict = {} # vtOrderID:algo self.symbolAlgoDict = {} # vtSymbol:algo set + self.settingDict = {} # settingName:setting self.registerEvent() @@ -90,9 +97,10 @@ class AlgoEngine(object): algo.updateTimer() #---------------------------------------------------------------------- - def addAlgo(self, name, algoSetting): + def addAlgo(self, algoSetting): """新增算法""" - algoClass = ALGO_DICT[name] + templateName = algoSetting['templateName'] + algoClass = ALGO_DICT[templateName] algo = algoClass.new(self, algoSetting) self.algoDict[algo.algoName] = algo return algo.algoName @@ -218,4 +226,39 @@ class AlgoEngine(object): return return tick + + #---------------------------------------------------------------------- + def saveAlgoSetting(self, settingName, algoSetting): + """保存算法配置""" + algoSetting['settingName'] = settingName + self.settingDict[settingName] = algoSetting + + self.mainEngine.dbUpdate(ALGOTRADING_DB_NAME, + SETTING_COLLECTION_NAME, + algoSetting, + {'settingName': settingName}, + True) + + self.putSettingEvent(settingName, algoSetting) + + #---------------------------------------------------------------------- + def loadAlgoSetting(self): + """加载算法配置""" + l = self.mainEngine.dbQuery(ALGOTRADING_DB_NAME, + SETTING_COLLECTION_NAME, + {}, + 'templateName') + for algoSetting in l: + settingName = algoSetting['settingName'] + self.settingDict[settingName] = algoSetting + self.putSettingEvent(settingName, algoSetting) + + #---------------------------------------------------------------------- + def putSettingEvent(self, settingName, algoSetting): + """发出算法配置更新事件""" + algoSetting['settingName'] = settingName + + event = Event(EVENT_ALGO_SETTING) + event.dict_['data'] = algoSetting + self.eventEngine.put(event) From 19f559f36ade04186d335eecb8ea6d9a50fc9652 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 9 Jun 2018 18:55:31 +0800 Subject: [PATCH 026/135] =?UTF-8?q?[Add]=E6=8A=BD=E8=B1=A1=E5=89=A5?= =?UTF-8?q?=E7=A6=BB=E7=AE=97=E6=B3=95=E6=8E=A7=E5=88=B6=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E7=88=B6=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/algoTemplate.py | 4 +- vnpy/trader/app/algoTrading/twapAlgo.py | 35 +++++------ vnpy/trader/app/algoTrading/uiAlgoManager.py | 2 +- vnpy/trader/app/algoTrading/uiAlgoWidget.py | 66 ++++++++++++++++++++ 4 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 vnpy/trader/app/algoTrading/uiAlgoWidget.py diff --git a/vnpy/trader/app/algoTrading/algoTemplate.py b/vnpy/trader/app/algoTrading/algoTemplate.py index 9866669f..29e89a2a 100644 --- a/vnpy/trader/app/algoTrading/algoTemplate.py +++ b/vnpy/trader/app/algoTrading/algoTemplate.py @@ -12,7 +12,7 @@ STATUS_ACTIVE = [STATUS_NOTTRADED, STATUS_PARTTRADED, STATUS_UNKNOWN] ######################################################################## class AlgoTemplate(object): """算法模板""" - name = 'AlgoTemplate' + templateName = 'AlgoTemplate' count = 0 @classmethod @@ -20,7 +20,7 @@ class AlgoTemplate(object): def new(cls, engine, setting): """创建新对象""" cls.count += 1 - algoName = '%s_%s' %(cls.name, cls.count) + algoName = '%s_%s' %(cls.templateName, cls.count) algo = cls(engine, setting, algoName) return algo diff --git a/vnpy/trader/app/algoTrading/twapAlgo.py b/vnpy/trader/app/algoTrading/twapAlgo.py index e5e93112..6e06c9b9 100644 --- a/vnpy/trader/app/algoTrading/twapAlgo.py +++ b/vnpy/trader/app/algoTrading/twapAlgo.py @@ -6,14 +6,15 @@ from collections import OrderedDict from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT) from vnpy.trader.uiQt import QtWidgets -from algoTemplate import AlgoTemplate +from .algoTemplate import AlgoTemplate +from .uiAlgoWidget import AlgoWidget, QtWidgets ######################################################################## class TwapAlgo(AlgoTemplate): """TWAP算法""" - name = 'TWAP' + templateName = 'TWAP' #---------------------------------------------------------------------- def __init__(self, engine, setting, algoName): @@ -168,23 +169,19 @@ class TwapAlgo(AlgoTemplate): self.putParamEvent(d) - ######################################################################## -class TwapWidget(QtWidgets.QWidget): +class TwapWidget(AlgoWidget): """""" - name = TwapAlgo.name - + #---------------------------------------------------------------------- - def __init__(self, algoEngine): + def __init__(self, algoEngine, parent=None): """Constructor""" - super(TwapWidget, self).__init__() + super(TwapWidget, self).__init__(algoEngine, parent) - self.algoEngine = algoEngine - - self.initUi() + self.templateName = TwapAlgo.templateName #---------------------------------------------------------------------- - def initUi(self): + def initAlgoLayout(self): """""" self.lineSymbol = QtWidgets.QLineEdit() @@ -245,14 +242,14 @@ class TwapWidget(QtWidgets.QWidget): grid.addWidget(self.spinPriceLevel, 6, 1) grid.addWidget(Label(u'数量取整'), 7, 0) grid.addWidget(self.spinMinVolume, 7, 1) - grid.addWidget(buttonStart, 8, 0, 1, 2) - - self.setLayout(grid) + + return grid #---------------------------------------------------------------------- - def addAlgo(self): + def getAlgoSetting(self): """""" setting = { + 'templateName': TwapAlgo.templateName, 'vtSymbol': str(self.lineSymbol.text()), 'direction': str(self.comboDirection.currentText()), 'targetPrice': float(self.spinPrice.value()), @@ -263,10 +260,6 @@ class TwapWidget(QtWidgets.QWidget): 'minVolume': float(self.spinMinVolume.value()) } - self.algoEngine.addAlgo(TwapAlgo.name, setting) - - - - + return setting \ No newline at end of file diff --git a/vnpy/trader/app/algoTrading/uiAlgoManager.py b/vnpy/trader/app/algoTrading/uiAlgoManager.py index 1c0baea3..7459339d 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoManager.py +++ b/vnpy/trader/app/algoTrading/uiAlgoManager.py @@ -240,4 +240,4 @@ class AlgoManager(QtWidgets.QWidget): def addAlgoWidget(self, widgetClass): """""" w = widgetClass(self.algoEngine) - self.tab.addTab(w, w.name) \ No newline at end of file + self.tab.addTab(w, w.templateName) \ No newline at end of file diff --git a/vnpy/trader/app/algoTrading/uiAlgoWidget.py b/vnpy/trader/app/algoTrading/uiAlgoWidget.py new file mode 100644 index 00000000..7df9ddb1 --- /dev/null +++ b/vnpy/trader/app/algoTrading/uiAlgoWidget.py @@ -0,0 +1,66 @@ +# encoding: UTF-8 + +from vnpy.trader.uiQt import QtWidgets + + +######################################################################## +class AlgoWidget(QtWidgets.QWidget): + """算法启动组件""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, parent=None): + """Constructor""" + super(AlgoWidget, self).__init__(parent) + + self.templateName = '' + self.algoEngine = algoEngine + + self.initUi() + + #---------------------------------------------------------------------- + def initUi(self): + """""" + algoLayout = self.initAlgoLayout() + + buttonStart = QtWidgets.QPushButton(u'启动算法') + buttonStart.clicked.connect(self.addAlgo) + buttonStart.setMinimumHeight(100) + + buttonSave = QtWidgets.QPushButton(u'保存配置') + buttonSave.clicked.connect(self.saveAlgoSetting) + buttonSave.setMinimumHeight(100) + + self.lineSettingName = QtWidgets.QLineEdit() + self.lineSettingName.setPlaceholderText(u'算法配置名称') + + vbox = QtWidgets.QVBoxLayout() + vbox.addLayout(algoLayout) + vbox.addWidget(buttonStart) + vbox.addWidget(QtWidgets.QLabel('')) + vbox.addWidget(QtWidgets.QLabel('')) + vbox.addWidget(self.lineSettingName) + vbox.addWidget(buttonSave) + + self.setLayout(vbox) + + #---------------------------------------------------------------------- + def addAlgo(self): + """启动算法""" + setting = self.getAlgoSetting() + self.algoEngine.addAlgo(setting) + + #---------------------------------------------------------------------- + def saveAlgoSetting(self): + """保存算法配置""" + setting = self.getAlgoSetting() + self.algoEngine.saveAlgoSetting(setting) + + #---------------------------------------------------------------------- + def initAlgoLayout(self): + """初始化算法相关的组件部分""" + pass + + #---------------------------------------------------------------------- + def getAlgoSetting(self): + """获取算法配置""" + pass From b92eea8978d3b604be2848eaab5fdb87dc81b3b4 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 9 Jun 2018 22:03:10 +0800 Subject: [PATCH 027/135] =?UTF-8?q?[Add]MainEngine=E5=A2=9E=E5=8A=A0dbDele?= =?UTF-8?q?te=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/language/chinese/text.py | 1 + vnpy/trader/language/english/text.py | 3 ++- vnpy/trader/vtEngine.py | 14 ++++++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/vnpy/trader/language/chinese/text.py b/vnpy/trader/language/chinese/text.py index 61850491..f9729083 100644 --- a/vnpy/trader/language/chinese/text.py +++ b/vnpy/trader/language/chinese/text.py @@ -137,3 +137,4 @@ DATABASE_CONNECTING_FAILED = u'MongoDB连接失败' DATA_INSERT_FAILED = u'数据插入失败,MongoDB没有连接' DATA_QUERY_FAILED = u'数据查询失败,MongoDB没有连接' DATA_UPDATE_FAILED = u'数据更新失败,MongoDB没有连接' +DATA_DELETE_FAILED = u'数据删除失败,MongoDB没有连接' \ No newline at end of file diff --git a/vnpy/trader/language/english/text.py b/vnpy/trader/language/english/text.py index 6268d9c8..68ed2985 100644 --- a/vnpy/trader/language/english/text.py +++ b/vnpy/trader/language/english/text.py @@ -137,4 +137,5 @@ DATABASE_CONNECTING_COMPLETED = u'MongoDB is connected.' DATABASE_CONNECTING_FAILED = u'Failed to connect to MongoDB.' DATA_INSERT_FAILED = u'Data insert failed,please connect MongoDB first.' DATA_QUERY_FAILED = u'Data query failed, please connect MongoDB first.' -DATA_UPDATE_FAILED = u'Data update failed, please connect MongoDB first.' \ No newline at end of file +DATA_UPDATE_FAILED = u'Data update failed, please connect MongoDB first.' +DATA_DELETE_FAILED = u'Data delete failed, please connect MongoDB first.' \ No newline at end of file diff --git a/vnpy/trader/vtEngine.py b/vnpy/trader/vtEngine.py index 75584a1f..e86d2c8f 100644 --- a/vnpy/trader/vtEngine.py +++ b/vnpy/trader/vtEngine.py @@ -251,7 +251,17 @@ class MainEngine(object): collection = db[collectionName] collection.replace_one(flt, d, upsert) else: - self.writeLog(text.DATA_UPDATE_FAILED) + self.writeLog(text.DATA_UPDATE_FAILED) + + #---------------------------------------------------------------------- + def dbDelete(self, dbName, collectionName, flt): + """从数据库中删除数据,flt是过滤条件""" + if self.dbClient: + db = self.dbClient[dbName] + collection = db[collectionName] + collection.delete_one(flt) + else: + self.writeLog(text.DATA_DELETE_FAILED) #---------------------------------------------------------------------- def dbLogging(self, event): @@ -735,7 +745,7 @@ class LogEngine(object): function = self.levelFunctionDict[log.logLevel] # 获取日志级别对应的处理函数 msg = '\t'.join([log.gatewayName, log.logContent]) function(msg) - + ######################################################################## class PositionDetail(object): From d33a585b0f810b96b6e1c3a41c597ad67583bdd3 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 9 Jun 2018 22:03:42 +0800 Subject: [PATCH 028/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9E=E4=BA=A4?= =?UTF-8?q?=E6=98=93=E7=AE=97=E6=B3=95=E9=85=8D=E7=BD=AE=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/algoEngine.py | 16 +- vnpy/trader/app/algoTrading/twapAlgo.py | 21 +- vnpy/trader/app/algoTrading/uiAlgoManager.py | 263 ++++++++++++++++--- vnpy/trader/app/algoTrading/uiAlgoWidget.py | 4 + 4 files changed, 256 insertions(+), 48 deletions(-) diff --git a/vnpy/trader/app/algoTrading/algoEngine.py b/vnpy/trader/app/algoTrading/algoEngine.py index 5b69fb23..3c360cd8 100644 --- a/vnpy/trader/app/algoTrading/algoEngine.py +++ b/vnpy/trader/app/algoTrading/algoEngine.py @@ -228,9 +228,9 @@ class AlgoEngine(object): return tick #---------------------------------------------------------------------- - def saveAlgoSetting(self, settingName, algoSetting): + def saveAlgoSetting(self, algoSetting): """保存算法配置""" - algoSetting['settingName'] = settingName + settingName = algoSetting['settingName'] self.settingDict[settingName] = algoSetting self.mainEngine.dbUpdate(ALGOTRADING_DB_NAME, @@ -252,6 +252,18 @@ class AlgoEngine(object): settingName = algoSetting['settingName'] self.settingDict[settingName] = algoSetting self.putSettingEvent(settingName, algoSetting) + + #---------------------------------------------------------------------- + def deleteAlgoSetting(self, algoSetting): + """删除算法配置""" + settingName = algoSetting['settingName'] + + del self.settingDict[settingName] + self.mainEngine.dbDelete(ALGOTRADING_DB_NAME, + SETTING_COLLECTION_NAME, + {'settingName': settingName}) + + self.putSettingEvent(settingName, {}) #---------------------------------------------------------------------- def putSettingEvent(self, settingName, algoSetting): diff --git a/vnpy/trader/app/algoTrading/twapAlgo.py b/vnpy/trader/app/algoTrading/twapAlgo.py index 6e06c9b9..b4bc9f7d 100644 --- a/vnpy/trader/app/algoTrading/twapAlgo.py +++ b/vnpy/trader/app/algoTrading/twapAlgo.py @@ -248,17 +248,16 @@ class TwapWidget(AlgoWidget): #---------------------------------------------------------------------- def getAlgoSetting(self): """""" - setting = { - 'templateName': TwapAlgo.templateName, - 'vtSymbol': str(self.lineSymbol.text()), - 'direction': str(self.comboDirection.currentText()), - 'targetPrice': float(self.spinPrice.value()), - 'totalVolume': float(self.spinVolume.value()), - 'time': int(self.spinTime.value()), - 'interval': int(self.spinInterval.value()), - 'priceLevel': int(self.spinPriceLevel.value()), - 'minVolume': float(self.spinMinVolume.value()) - } + setting = OrderedDict() + setting['templateName'] = TwapAlgo.templateName + setting['vtSymbol'] = str(self.lineSymbol.text()) + setting['direction'] = str(self.comboDirection.currentText()) + setting['targetPrice'] = float(self.spinPrice.value()) + setting['totalVolume'] = float(self.spinVolume.value()) + setting['time'] = int(self.spinTime.value()) + setting['interval'] = int(self.spinInterval.value()) + setting['priceLevel'] = int(self.spinPriceLevel.value()) + setting['minVolume'] = float(self.spinMinVolume.value()) return setting diff --git a/vnpy/trader/app/algoTrading/uiAlgoManager.py b/vnpy/trader/app/algoTrading/uiAlgoManager.py index 7459339d..fa92fae3 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoManager.py +++ b/vnpy/trader/app/algoTrading/uiAlgoManager.py @@ -3,13 +3,14 @@ from vnpy.event import Event from vnpy.trader.uiQt import QtCore, QtWidgets -from .algoEngine import EVENT_ALGO_LOG, EVENT_ALGO_PARAM, EVENT_ALGO_VAR +from .algoEngine import (EVENT_ALGO_LOG, EVENT_ALGO_PARAM, + EVENT_ALGO_VAR, EVENT_ALGO_SETTING) from .twapAlgo import TwapWidget ######################################################################## class StopButton(QtWidgets.QPushButton): - """""" + """停止算法用按钮""" #---------------------------------------------------------------------- def __init__(self, algoEngine, algoName=''): @@ -30,18 +31,18 @@ class StopButton(QtWidgets.QPushButton): #---------------------------------------------------------------------- def stopAlgo(self): - """""" + """停止某一算法""" self.algoEngine.stopAlgo(self.algoName) self.disable() #---------------------------------------------------------------------- def stopAll(self): - """""" + """停止全部算法""" self.algoEngine.stopAll() #---------------------------------------------------------------------- def disable(self): - """""" + """禁用按钮""" self.setEnabled(False) self.setStyleSheet("color:black;background-color:grey") @@ -52,17 +53,21 @@ AlgoCell = QtWidgets.QTableWidgetItem ######################################################################## class AlgoStatusMonitor(QtWidgets.QTableWidget): - """""" + """算法状态监控""" signalParam = QtCore.Signal(type(Event())) signalVar = QtCore.Signal(type(Event())) + + MODE_WORKING = 'working' + MODE_HISTORY = 'history' #---------------------------------------------------------------------- - def __init__(self, algoEngine): + def __init__(self, algoEngine, mode): """Constructor""" super(AlgoStatusMonitor, self).__init__() self.algoEngine = algoEngine self.eventEngine = algoEngine.eventEngine + self.mode = mode self.cellDict = {} @@ -81,7 +86,11 @@ class AlgoStatusMonitor(QtWidgets.QTableWidget): self.setHorizontalHeaderLabels(labels) self.setRowCount(0) self.verticalHeader().setVisible(False) - self.setEditTriggers(self.NoEditTriggers) + self.setEditTriggers(self.NoEditTriggers) + self.setAlternatingRowColors(True) + + if self.mode == self.MODE_HISTORY: + self.hideColumn(0) #---------------------------------------------------------------------- def registerEvent(self): @@ -113,21 +122,19 @@ class AlgoStatusMonitor(QtWidgets.QTableWidget): 'button': buttonStop } + if self.mode == self.MODE_HISTORY: + self.hideRow(0) + #---------------------------------------------------------------------- def processParamEvent(self, event): - """""" + """处理参数事件""" d = event.dict_['data'] - algoName = d.pop('algoName') + algoName = d['algoName'] if algoName not in self.cellDict: self.addAlgo(algoName) - l = [] - for k, v in d.items(): - msg = u'%s:%s' %(k, v) - l.append(msg) - text = ','.join(l) - + text = self.generateText(d) cell = self.cellDict[algoName]['param'] cell.setText(text) @@ -135,29 +142,47 @@ class AlgoStatusMonitor(QtWidgets.QTableWidget): #---------------------------------------------------------------------- def processVarEvent(self, event): - """""" + """处理变量事件""" d = event.dict_['data'] - algoName = d.pop('algoName') + algoName = d['algoName'] if algoName not in self.cellDict: self.addAlgo(algoName) if 'active' in d: - active = d.pop('active') + active = d['active'] + + # 若算法已经结束 if not active: - button = self.cellDict[algoName]['button'] + # 禁用按钮 + cells = self.cellDict[algoName] + button = cells['button'] button.disable() + + # 根据模式决定显示或者隐藏该行 + cell = cells['var'] + row = self.row(cell) + if self.mode == self.MODE_WORKING: + self.hideRow(row) + else: + self.showRow(row) - l = [] - for k, v in d.items(): - msg = u'%s:%s' %(k, v) - l.append(msg) - text = ','.join(l) - + text = self.generateText(d) cell = self.cellDict[algoName]['var'] cell.setText(text) self.resizeColumnsToContents() + + #---------------------------------------------------------------------- + def generateText(self, d): + """从字典生成字符串""" + l = [] + for k, v in d.items(): + if k not in ['algoName']: + msg = u'%s:%s' %(k, v) + l.append(msg) + text = ','.join(l) + return text ######################################################################## @@ -191,10 +216,159 @@ class AlgoLogMonitor(QtWidgets.QTextEdit): self.append(msg) +######################################################################## +class StartButton(QtWidgets.QPushButton): + """基于配置启动算法用按钮""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, setting): + """Constructor""" + super(StartButton, self).__init__() + + self.algoEngine = algoEngine + self.setting = setting + + self.setStyleSheet("color:black;background-color:green") + self.setText(u'启动') + + self.clicked.connect(self.startAlgo) + + #---------------------------------------------------------------------- + def startAlgo(self): + """启动算法""" + self.algoEngine.addAlgo(self.setting) + + #---------------------------------------------------------------------- + def updateSetting(self, setting): + """更新配置""" + self.setting = setting + + +######################################################################## +class DeleteButton(QtWidgets.QPushButton): + """删除算法用按钮""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, setting): + """Constructor""" + super(DeleteButton, self).__init__() + + self.algoEngine = algoEngine + self.setting = setting + + self.setStyleSheet("color:black;background-color:red") + self.setText(u'删除') + + self.clicked.connect(self.deleteAlgoSetting) + + #---------------------------------------------------------------------- + def deleteAlgoSetting(self): + """删除算法配置""" + self.algoEngine.deleteAlgoSetting(self.setting) + + #---------------------------------------------------------------------- + def updateSetting(self, setting): + """更新配置""" + self.setting = setting + + +######################################################################## +class AlgoSettingMonitor(QtWidgets.QTableWidget): + """""" + signal = QtCore.Signal(type(Event())) + + #---------------------------------------------------------------------- + def __init__(self, algoEngine): + """Constructor""" + super(AlgoSettingMonitor, self).__init__() + + self.algoEngine = algoEngine + self.eventEngine = algoEngine.eventEngine + + self.cellDict = {} + + self.initUi() + self.registerEvent() + + #---------------------------------------------------------------------- + def initUi(self): + """初始化界面""" + labels = [u'', + u'名称', + u'算法', + u'参数', + ''] + + self.setColumnCount(len(labels)) + self.setHorizontalHeaderLabels(labels) + self.setRowCount(0) + self.verticalHeader().setVisible(False) + self.setEditTriggers(self.NoEditTriggers) + + #---------------------------------------------------------------------- + def registerEvent(self): + """注册事件监听""" + self.signal.connect(self.processEvent) + + self.eventEngine.register(EVENT_ALGO_SETTING, self.signal.emit) + + #---------------------------------------------------------------------- + def processEvent(self, event): + """处理事件""" + setting = event.dict_['data'] + settingName = setting['settingName'] + + # 删除配置行 + if len(setting) == 1: + d = self.cellDict.pop(settingName) + cell = d['text'] + row = self.row(cell) + self.removeRow(row) + # 新增配置行 + elif settingName not in self.cellDict: + self.insertRow(0) + + buttonStart = StartButton(self.algoEngine, setting) + cellSettingName = AlgoCell(settingName) + cellTemplateName = AlgoCell(setting['templateName']) + cellSettingText = AlgoCell(self.generateText(setting)) + buttonDelete = DeleteButton(self.algoEngine, setting) + + self.setCellWidget(0, 0, buttonStart) + self.setItem(0, 1, cellSettingName) + self.setItem(0, 2, cellTemplateName) + self.setItem(0, 3, cellSettingText) + self.setCellWidget(0, 4, buttonDelete) + + self.cellDict[settingName] = { + 'start': buttonStart, + 'text': cellSettingText, + 'delete': buttonDelete + } + # 更新已有配置行 + else: + d = self.cellDict[settingName] + d['start'].updateSetting(setting) + d['text'].setText(self.generateText(setting)) + d['delete'].updateSetting(setting) + + self.resizeColumnsToContents() + + #---------------------------------------------------------------------- + def generateText(self, d): + """从字典生成字符串""" + l = [] + for k, v in d.items(): + if k not in ['settingName', 'templateName', '_id']: + msg = u'%s:%s' %(k, v) + l.append(msg) + text = ','.join(l) + return text + ######################################################################## class AlgoManager(QtWidgets.QWidget): - """""" + """算法交易管理组件""" #---------------------------------------------------------------------- def __init__(self, algoEngine, eventEngine): @@ -207,13 +381,13 @@ class AlgoManager(QtWidgets.QWidget): self.initUi() self.addAlgoWidget(TwapWidget) + self.algoEngine.loadAlgoSetting() # 界面初始化后,再加载算法配置 + #---------------------------------------------------------------------- def initUi(self): """""" self.setWindowTitle(u'算法交易') - self.statusMonitor = AlgoStatusMonitor(self.algoEngine) - self.logMonitor = AlgoLogMonitor(self.algoEngine) self.tab = QtWidgets.QTabWidget() self.buttonStop = StopButton(self.algoEngine) @@ -226,15 +400,34 @@ class AlgoManager(QtWidgets.QWidget): vbox1.addStretch() vbox1.addWidget(self.buttonStop) - vbox2 = QtWidgets.QVBoxLayout() - vbox2.addWidget(self.statusMonitor) - vbox2.addWidget(self.logMonitor) + workingMonitor = AlgoStatusMonitor(self.algoEngine, AlgoStatusMonitor.MODE_WORKING) + historyMonitor = AlgoStatusMonitor(self.algoEngine, AlgoStatusMonitor.MODE_HISTORY) + logMonitor = AlgoLogMonitor(self.algoEngine) + settingMonitor = AlgoSettingMonitor(self.algoEngine) + + tab1 = QtWidgets.QTabWidget() + tab1.addTab(workingMonitor, u'运行中') + tab1.addTab(historyMonitor, u'已结束') + + tab2 = QtWidgets.QTabWidget() + tab2.addTab(logMonitor, u'日志信息') + + tab3 = QtWidgets.QTabWidget() + tab3.addTab(settingMonitor, u'算法配置') hbox = QtWidgets.QHBoxLayout() - hbox.addLayout(vbox1) - hbox.addLayout(vbox2) + hbox.addWidget(tab2) + hbox.addWidget(tab3) - self.setLayout(hbox) + vbox2 = QtWidgets.QVBoxLayout() + vbox2.addWidget(tab1) + vbox2.addLayout(hbox) + + hbox2 = QtWidgets.QHBoxLayout() + hbox2.addLayout(vbox1) + hbox2.addLayout(vbox2) + + self.setLayout(hbox2) #---------------------------------------------------------------------- def addAlgoWidget(self, widgetClass): diff --git a/vnpy/trader/app/algoTrading/uiAlgoWidget.py b/vnpy/trader/app/algoTrading/uiAlgoWidget.py index 7df9ddb1..addb3090 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoWidget.py +++ b/vnpy/trader/app/algoTrading/uiAlgoWidget.py @@ -53,7 +53,11 @@ class AlgoWidget(QtWidgets.QWidget): def saveAlgoSetting(self): """保存算法配置""" setting = self.getAlgoSetting() + setting['templateName'] = self.templateName + setting['settingName'] = unicode(self.lineSettingName.text()) self.algoEngine.saveAlgoSetting(setting) + + self.lineSettingName.setText('') #---------------------------------------------------------------------- def initAlgoLayout(self): From 5fb1cafc0d297c11ec969a235ec0ca2d9d99f3e4 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 11:18:35 +0800 Subject: [PATCH 029/135] [Fix]Close #909 --- vnpy/trader/app/ctaStrategy/ctaBacktesting.py | 2 +- vnpy/trader/app/ctaStrategy/ctaEngine.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/vnpy/trader/app/ctaStrategy/ctaBacktesting.py b/vnpy/trader/app/ctaStrategy/ctaBacktesting.py index c0bb5c6d..5d7239bb 100644 --- a/vnpy/trader/app/ctaStrategy/ctaBacktesting.py +++ b/vnpy/trader/app/ctaStrategy/ctaBacktesting.py @@ -234,8 +234,8 @@ class BacktestingEngine(object): self.output(u'开始回测') - self.strategy.inited = True self.strategy.onInit() + self.strategy.inited = True self.output(u'策略初始化完成') self.strategy.trading = True diff --git a/vnpy/trader/app/ctaStrategy/ctaEngine.py b/vnpy/trader/app/ctaStrategy/ctaEngine.py index ba25d273..bf927fcd 100644 --- a/vnpy/trader/app/ctaStrategy/ctaEngine.py +++ b/vnpy/trader/app/ctaStrategy/ctaEngine.py @@ -430,8 +430,9 @@ class CtaEngine(object): strategy = self.strategyDict[name] if not strategy.inited: - strategy.inited = True self.callStrategyFunc(strategy, strategy.onInit) + strategy.inited = True + self.loadSyncData(strategy) # 初始化完成后加载同步数据 self.subscribeMarketData(strategy) # 加载同步数据后再订阅行情 else: From dfe0304f74b40d380eee0786d6095e90d605822e Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 11:47:08 +0800 Subject: [PATCH 030/135] [Fix]Close #907 --- vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py index d51f1cbe..5aef41be 100644 --- a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py +++ b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py @@ -381,7 +381,7 @@ class GatewayApi(BitfinexApi): # BID bidPriceList = bid.keys() - bidPriceList.sort() + bidPriceList.sort(reverse=True) tick.bidPrice1 = bidPriceList[0] tick.bidPrice2 = bidPriceList[1] @@ -398,7 +398,6 @@ class GatewayApi(BitfinexApi): # ASK askPriceList = ask.keys() askPriceList.sort() - askPriceList.reverse() tick.askPrice1 = askPriceList[0] tick.askPrice2 = askPriceList[1] From 70ed2dfd66561f9ef1eae5d6415645ef0364e084 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 11:53:09 +0800 Subject: [PATCH 031/135] =?UTF-8?q?[Fix]Close=20#906,Bitfinex=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=96=B0=E5=A2=9EonConnect=E5=9B=9E=E8=B0=83=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/bitfinex/vnbitfinex.py | 12 +++++++++++- .../gateway/bitfinexGateway/bitfinexGateway.py | 7 +++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/vnpy/api/bitfinex/vnbitfinex.py b/vnpy/api/bitfinex/vnbitfinex.py index 8c55b08a..4e1b10bb 100644 --- a/vnpy/api/bitfinex/vnbitfinex.py +++ b/vnpy/api/bitfinex/vnbitfinex.py @@ -38,11 +38,15 @@ class BitfinexApi(object): self.restThread = Thread(target=self.runRest) self.restThread.start() + + self.onConnect() #---------------------------------------------------------------------- def reconnect(self): """""" - self.ws = websocket.create_connection(WEBSOCKET_V2_URL) + self.ws = websocket.create_connection(WEBSOCKET_V2_URL) + + self.onConnect() #---------------------------------------------------------------------- def run(self): @@ -57,6 +61,12 @@ class BitfinexApi(object): self.onError(msg) self.reconnect() + #---------------------------------------------------------------------- + def onConnect(self): + """""" + print 'connected' + + #---------------------------------------------------------------------- def onData(self, data): """""" diff --git a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py index 5aef41be..005ca85b 100644 --- a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py +++ b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py @@ -182,10 +182,13 @@ class GatewayApi(BitfinexApi): self.start() self.writeLog(u'交易API启动成功') - for symbol in symbols: + #---------------------------------------------------------------------- + def onConnect(self): + """""" + for symbol in self.symbols: self.subscribe(symbol, 'ticker') self.subscribe(symbol, 'book') - self.writeLog(u'行情推送订阅成功') + self.writeLog(u'行情推送订阅成功') self.authenticate() self.writeLog(u'交易推送订阅成功') From c42ecb6ec87af14fba2e632ee5f757980e9b8056 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 14:59:03 +0800 Subject: [PATCH 032/135] =?UTF-8?q?[Fix]=E4=BF=AE=E5=A4=8D=E5=B8=81?= =?UTF-8?q?=E5=AE=89API=E5=85=B3=E9=97=AD=E6=97=B6=E7=9A=84=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E6=B1=A0=E5=85=B3=E9=97=AD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/binance/vnbinance.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vnpy/api/binance/vnbinance.py b/vnpy/api/binance/vnbinance.py index 8e9ade96..9d9decfd 100644 --- a/vnpy/api/binance/vnbinance.py +++ b/vnpy/api/binance/vnbinance.py @@ -85,8 +85,10 @@ class BinanceApi(object): def close(self): """""" self.active = False - self.pool.close() - self.pool.join() + + if self.pool: + self.pool.close() + self.pool.join() #---------------------------------------------------------------------- def request(self, method, path, params=None, signed=False, stream=False): From bbabd742ff3add740dc329489b99c3dd09aeca5b Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 14:59:33 +0800 Subject: [PATCH 033/135] =?UTF-8?q?[Add]BitfinexApi=E6=96=B0=E5=A2=9Eclose?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/bitfinex/vnbitfinex.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/vnpy/api/bitfinex/vnbitfinex.py b/vnpy/api/bitfinex/vnbitfinex.py index 4e1b10bb..bd5aa189 100644 --- a/vnpy/api/bitfinex/vnbitfinex.py +++ b/vnpy/api/bitfinex/vnbitfinex.py @@ -32,7 +32,7 @@ class BitfinexApi(object): """""" self.ws = websocket.create_connection(WEBSOCKET_V2_URL) - self.active =True + self.active = True self.thread = Thread(target=self.run) self.thread.start() @@ -61,11 +61,21 @@ class BitfinexApi(object): self.onError(msg) self.reconnect() + #---------------------------------------------------------------------- + def close(self): + """""" + self.active = False + + if self.thread: + self.thread.join() + + if self.restThread: + self.thread.join() + #---------------------------------------------------------------------- def onConnect(self): """""" print 'connected' - #---------------------------------------------------------------------- def onData(self, data): From 8cdfcef62ba5dc3b3c87277b16edee96f981f42d Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 15:00:39 +0800 Subject: [PATCH 034/135] =?UTF-8?q?[Mod]=E7=81=AB=E5=B8=81=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E7=A7=BB=E9=99=A4=E4=BB=A3=E7=90=86=E6=94=AF=E6=8C=81?= =?UTF-8?q?,=E5=A2=9E=E5=8A=A0=E8=87=AA=E5=8A=A8=E8=AE=A2=E9=98=85?= =?UTF-8?q?=E8=A1=8C=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/CryptoTrader/VT_setting.json | 6 ++-- vnpy/api/huobi/vnhuobi.py | 21 ++--------- .../gateway/huobiGateway/huobiGateway.py | 35 ++++++++++--------- 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/examples/CryptoTrader/VT_setting.json b/examples/CryptoTrader/VT_setting.json index 473017fb..40495eca 100644 --- a/examples/CryptoTrader/VT_setting.json +++ b/examples/CryptoTrader/VT_setting.json @@ -9,12 +9,12 @@ "darkStyle": true, "language": "chinese", - "logActive": true, + "logActive": false, "logLevel": "debug", "logConsole": true, "logFile": true, - "tdPenalty": ["IF", "IH", "IC"], + "tdPenalty": [], - "maxDecimal": 4 + "maxDecimal": 10 } \ No newline at end of file diff --git a/vnpy/api/huobi/vnhuobi.py b/vnpy/api/huobi/vnhuobi.py index ca9db9a3..f6b37697 100644 --- a/vnpy/api/huobi/vnhuobi.py +++ b/vnpy/api/huobi/vnhuobi.py @@ -504,8 +504,6 @@ class DataApi(object): self.subDict = {} self.url = '' - self.proxyHost = '' - self.proxyPort = 0 #---------------------------------------------------------------------- def run(self): @@ -532,12 +530,7 @@ class DataApi(object): def reconnect(self): """重连""" try: - if not self.proxyHost: - self.ws = create_connection(self.url) - else: - self.ws = create_connection(self.url, - http_proxy_host=self.proxyHost, - http_proxy_port=self.proxyPort) + self.ws = create_connection(self.url) return True except: msg = traceback.format_exc() @@ -553,20 +546,12 @@ class DataApi(object): self.subTopic(topic) #---------------------------------------------------------------------- - def connect(self, url, proxyHost='', proxyPort=0): + def connect(self, url): """连接""" self.url = url - self.proxyHost = proxyHost - self.proxyPort = proxyPort try: - if not self.proxyHost: - self.ws = create_connection(self.url) - else: - self.ws = create_connection(self.url, - http_proxy_host=self.proxyHost, - http_proxy_port=self.proxyPort) - + self.ws = create_connection(self.url) self.active = True self.thread.start() diff --git a/vnpy/trader/gateway/huobiGateway/huobiGateway.py b/vnpy/trader/gateway/huobiGateway/huobiGateway.py index 2b84a7ee..7e50940c 100644 --- a/vnpy/trader/gateway/huobiGateway/huobiGateway.py +++ b/vnpy/trader/gateway/huobiGateway/huobiGateway.py @@ -75,8 +75,6 @@ class HuobiGateway(VtGateway): accessKey = str(setting['accessKey']) secretKey = str(setting['secretKey']) symbols = setting['symbols'] - proxyHost = str(setting['proxyHost']) - proxyPort = int(setting['proxyPort']) except KeyError: log = VtLogData() log.gatewayName = self.gatewayName @@ -85,7 +83,7 @@ class HuobiGateway(VtGateway): return # 创建行情和交易接口对象 - self.dataApi.connect(exchange, proxyHost, proxyPort) + self.dataApi.connect(exchange, symbols) self.tradeApi.connect(exchange, accessKey, secretKey, symbols) # 初始化并启动查询 @@ -94,7 +92,8 @@ class HuobiGateway(VtGateway): #---------------------------------------------------------------------- def subscribe(self, subscribeReq): """订阅行情""" - self.dataApi.subscribe(subscribeReq) + pass + #self.dataApi.subscribe(subscribeReq) #---------------------------------------------------------------------- def sendOrder(self, orderReq): @@ -179,38 +178,40 @@ class HuobiDataApi(DataApi): self.tickDict = {} - self.subscribeDict = {} + #self.subscribeDict = {} #---------------------------------------------------------------------- - def connect(self, exchange, proxyHost, proxyPort): + def connect(self, exchange, symbols): """连接服务器""" if exchange == 'huobi': url = 'wss://api.huobi.pro/ws' else: url = 'wss://api.hadax.com/ws' + + self.symbols = symbols - if proxyHost: - self.connectionStatus = super(HuobiDataApi, self).connect(url, proxyHost, proxyPort) - else: - self.connectionStatus = super(HuobiDataApi, self).connect(url) + self.connectionStatus = super(HuobiDataApi, self).connect(url) self.gateway.mdConnected = True if self.connectionStatus: self.writeLog(u'行情服务器连接成功') - + + for symbol in self.symbols: + self.subscribe(symbol) # 订阅所有之前订阅过的行情 - for req in self.subscribeDict.values(): - self.subscribe(req) + #for req in self.subscribeDict.values(): + # self.subscribe(req) + #---------------------------------------------------------------------- - def subscribe(self, subscribeReq): + def subscribe(self, symbol): """订阅合约""" - self.subscribeDict[subscribeReq.symbol] = subscribeReq + #self.subscribeDict[subscribeReq.symbol] = subscribeReq if not self.connectionStatus: return - symbol = subscribeReq.symbol + #symbol = subscribeReq.symbol if symbol in self.tickDict: return @@ -354,7 +355,7 @@ class HuobiTradeApi(TradeApi): #self.activeOrderSet = set() # 活动委托集合 #---------------------------------------------------------------------- - def connect(self, exchange, accessKey, secretKey, symbols=''): + def connect(self, exchange, symbols, accessKey, secretKey): """初始化连接""" if not self.connectionStatus: self.symbols = symbols From a80f44100f197a2904ec0c1b6e05a62768f47cb9 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 16:03:31 +0800 Subject: [PATCH 035/135] =?UTF-8?q?[Mod]HuobiGateway=E5=8F=AA=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E7=8E=B0=E8=B4=A7=E4=BA=A4=E6=98=93=E8=B4=A6=E6=88=B7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/gateway/huobiGateway/huobiGateway.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vnpy/trader/gateway/huobiGateway/huobiGateway.py b/vnpy/trader/gateway/huobiGateway/huobiGateway.py index 7e50940c..64041e7d 100644 --- a/vnpy/trader/gateway/huobiGateway/huobiGateway.py +++ b/vnpy/trader/gateway/huobiGateway/huobiGateway.py @@ -84,7 +84,7 @@ class HuobiGateway(VtGateway): # 创建行情和交易接口对象 self.dataApi.connect(exchange, symbols) - self.tradeApi.connect(exchange, accessKey, secretKey, symbols) + self.tradeApi.connect(exchange, symbols, accessKey, secretKey) # 初始化并启动查询 self.initQuery() @@ -495,8 +495,9 @@ class HuobiTradeApi(TradeApi): def onGetAccounts(self, data, reqid): """查询账户回调""" for d in data: - self.accountid = str(d['id']) - self.writeLog(u'交易账户%s查询成功' %self.accountid) + if str(d['type']) == 'spot': + self.accountid = str(d['id']) + self.writeLog(u'交易账户%s查询成功' %self.accountid) #---------------------------------------------------------------------- def onGetAccountBalance(self, data, reqid): From 8ba6206be07dd068f3648d99c4cf073239051d0d Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 16:06:12 +0800 Subject: [PATCH 036/135] =?UTF-8?q?[Mod]=E7=81=AB=E5=B8=81=E5=80=9F?= =?UTF-8?q?=E5=8F=A3=E8=BF=87=E6=BB=A4=E8=AF=B7=E6=B1=82=E8=B6=85=E6=97=B6?= =?UTF-8?q?429=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/gateway/huobiGateway/huobiGateway.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vnpy/trader/gateway/huobiGateway/huobiGateway.py b/vnpy/trader/gateway/huobiGateway/huobiGateway.py index 64041e7d..d4b72e90 100644 --- a/vnpy/trader/gateway/huobiGateway/huobiGateway.py +++ b/vnpy/trader/gateway/huobiGateway/huobiGateway.py @@ -452,6 +452,10 @@ class HuobiTradeApi(TradeApi): #---------------------------------------------------------------------- def onError(self, msg, reqid): """错误回调""" + # 忽略请求超时错误 + if '429' in msg: + return + err = VtErrorData() err.gatewayName = self.gatewayName err.errorID = 'Trade' From 603d9e855f3613654502ad137226a69c53103542 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 16:08:57 +0800 Subject: [PATCH 037/135] =?UTF-8?q?[Mod]vnhuobi=E7=9A=84=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E8=B6=85=E6=97=B6=E6=94=B9=E4=B8=BA10?= =?UTF-8?q?=E7=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/huobi/vnhuobi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnpy/api/huobi/vnhuobi.py b/vnpy/api/huobi/vnhuobi.py index f6b37697..bb82d80f 100644 --- a/vnpy/api/huobi/vnhuobi.py +++ b/vnpy/api/huobi/vnhuobi.py @@ -20,7 +20,7 @@ from websocket import create_connection, _exceptions # 常量定义 -TIMEOUT = 5 +TIMEOUT = 10 HUOBI_API_HOST = "api.huobi.pro" HADAX_API_HOST = "api.hadax.com" LANG = 'zh-CN' From 50609547e8fedfc1638476206d1a211b84226599 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 16:09:25 +0800 Subject: [PATCH 038/135] =?UTF-8?q?[Mod]HuobiGateway=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E7=94=B1=E4=BA=8E=E8=AF=B7=E6=B1=82=E8=B6=85=E6=97=B6=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E7=9A=84API=E7=AD=BE=E5=90=8D=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/gateway/huobiGateway/huobiGateway.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnpy/trader/gateway/huobiGateway/huobiGateway.py b/vnpy/trader/gateway/huobiGateway/huobiGateway.py index d4b72e90..c1c587c9 100644 --- a/vnpy/trader/gateway/huobiGateway/huobiGateway.py +++ b/vnpy/trader/gateway/huobiGateway/huobiGateway.py @@ -453,7 +453,7 @@ class HuobiTradeApi(TradeApi): def onError(self, msg, reqid): """错误回调""" # 忽略请求超时错误 - if '429' in msg: + if '429' in msg or 'api-signature-not-valid' in msg: return err = VtErrorData() From ff8a6c96028061a51cf92dc32ad60a9e145ab201 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 16:19:36 +0800 Subject: [PATCH 039/135] =?UTF-8?q?[Mod]=E5=8D=87=E7=BA=A7=E6=95=B0?= =?UTF-8?q?=E5=AD=97=E8=B4=A7=E5=B8=81=E6=8E=A5=E5=8F=A3=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=9A=84websocket-client=E5=88=B0=E6=9C=80=E6=96=B0=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/binance/vnbinance.py | 7 +++++-- vnpy/api/bitfinex/vnbitfinex.py | 7 +++++-- vnpy/api/huobi/vnhuobi.py | 2 +- vnpy/api/okex/vnokex.py | 4 +++- vnpy/trader/app/riskManager/RM_setting.json | 4 ++-- vnpy/trader/gateway/binanceGateway/binanceGateway.py | 3 --- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/vnpy/api/binance/vnbinance.py b/vnpy/api/binance/vnbinance.py index 9d9decfd..da67de8e 100644 --- a/vnpy/api/binance/vnbinance.py +++ b/vnpy/api/binance/vnbinance.py @@ -5,6 +5,7 @@ import requests import hmac import hashlib import traceback +import ssl from queue import Queue, Empty from threading import Thread @@ -535,7 +536,8 @@ class BinanceApi(object): def connectDataStream(self): """""" try: - self.dataStreamWs = create_connection(self.dataStreamUrl) + self.dataStreamWs = create_connection(self.dataStreamUrl, + sslopt={'cert_reqs': ssl.CERT_NONE}) return True except: msg = traceback.format_exc() @@ -595,7 +597,8 @@ class BinanceApi(object): def connectUserStream(self): """""" try: - self.userStreamWs = create_connection(self.userStreamUrl) + self.userStreamWs = create_connection(self.userStreamUrl, + sslopt={'cert_reqs': ssl.CERT_NONE}) return True except: msg = traceback.format_exc() diff --git a/vnpy/api/bitfinex/vnbitfinex.py b/vnpy/api/bitfinex/vnbitfinex.py index bd5aa189..ee308d68 100644 --- a/vnpy/api/bitfinex/vnbitfinex.py +++ b/vnpy/api/bitfinex/vnbitfinex.py @@ -3,6 +3,7 @@ import json import requests import traceback +import ssl from threading import Thread from queue import Queue, Empty @@ -30,7 +31,8 @@ class BitfinexApi(object): #---------------------------------------------------------------------- def start(self): """""" - self.ws = websocket.create_connection(WEBSOCKET_V2_URL) + self.ws = websocket.create_connection(WEBSOCKET_V2_URL, + sslopt={'cert_reqs': ssl.CERT_NONE}) self.active = True self.thread = Thread(target=self.run) @@ -44,7 +46,8 @@ class BitfinexApi(object): #---------------------------------------------------------------------- def reconnect(self): """""" - self.ws = websocket.create_connection(WEBSOCKET_V2_URL) + self.ws = websocket.create_connection(WEBSOCKET_V2_URL, + sslopt={'cert_reqs': ssl.CERT_NONE}) self.onConnect() diff --git a/vnpy/api/huobi/vnhuobi.py b/vnpy/api/huobi/vnhuobi.py index bb82d80f..8877a371 100644 --- a/vnpy/api/huobi/vnhuobi.py +++ b/vnpy/api/huobi/vnhuobi.py @@ -551,7 +551,7 @@ class DataApi(object): self.url = url try: - self.ws = create_connection(self.url) + self.ws = create_connection(self.url, sslopt={'cert_reqs': ssl.CERT_NONE}) self.active = True self.thread.start() diff --git a/vnpy/api/okex/vnokex.py b/vnpy/api/okex/vnokex.py index 0c17d3eb..9a21ef4b 100644 --- a/vnpy/api/okex/vnokex.py +++ b/vnpy/api/okex/vnokex.py @@ -2,6 +2,7 @@ from __future__ import print_function +import ssl import hashlib import json import traceback @@ -136,7 +137,8 @@ class OkexApi(object): on_close=self.onCloseCallback, on_open=self.onOpenCallback) - self.wsThread = Thread(target=self.ws.run_forever) + kwargs = {'sslopt': {'cert_reqs': ssl.CERT_NONE}} + self.wsThread = Thread(target=self.ws.run_forever, kwargs=kwargs) self.wsThread.start() #---------------------------------------------------------------------- diff --git a/vnpy/trader/app/riskManager/RM_setting.json b/vnpy/trader/app/riskManager/RM_setting.json index 20625b38..6dc35464 100644 --- a/vnpy/trader/app/riskManager/RM_setting.json +++ b/vnpy/trader/app/riskManager/RM_setting.json @@ -1,10 +1,10 @@ { "orderFlowClear": 1, "orderCancelLimit": 10, + "marginRatioLimit": 0.95, "workingOrderLimit": 20, "tradeLimit": 1000, "orderSizeLimit": 100, "active": true, - "orderFlowLimit": 50, - "marginRatioLimit": 0.95 + "orderFlowLimit": 50 } \ No newline at end of file diff --git a/vnpy/trader/gateway/binanceGateway/binanceGateway.py b/vnpy/trader/gateway/binanceGateway/binanceGateway.py index 897a7877..229c2494 100644 --- a/vnpy/trader/gateway/binanceGateway/binanceGateway.py +++ b/vnpy/trader/gateway/binanceGateway/binanceGateway.py @@ -223,9 +223,6 @@ class GatewayApi(BinanceApi): def onQueryExchangeInfo(self, data, reqid): """""" for d in data['symbols']: - if str(d['symbol']) == 'ETHUSDT': - print d - contract = VtContractData() contract.gatewayName = self.gatewayName From 893940de6f85c231c1b2817dd3c78477fde305b3 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 21:12:59 +0800 Subject: [PATCH 040/135] =?UTF-8?q?[Mod]=E4=BF=AE=E6=94=B9AlgoName?= =?UTF-8?q?=E7=94=9F=E6=88=90=E8=A7=84=E5=88=99=EF=BC=8C=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=9C=BA=E5=88=B6=E4=BF=9D=E8=AF=81=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E5=94=AF=E4=B8=80=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/algoTemplate.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/vnpy/trader/app/algoTrading/algoTemplate.py b/vnpy/trader/app/algoTrading/algoTemplate.py index 29e89a2a..8ffcbf16 100644 --- a/vnpy/trader/app/algoTrading/algoTemplate.py +++ b/vnpy/trader/app/algoTrading/algoTemplate.py @@ -1,6 +1,7 @@ # encoding: UTF-8 from __future__ import division +from datetime import datetime from vnpy.trader.vtConstant import STATUS_NOTTRADED, STATUS_PARTTRADED, STATUS_UNKNOWN @@ -13,15 +14,22 @@ STATUS_ACTIVE = [STATUS_NOTTRADED, STATUS_PARTTRADED, STATUS_UNKNOWN] class AlgoTemplate(object): """算法模板""" templateName = 'AlgoTemplate' + + timestamp = '' count = 0 @classmethod #---------------------------------------------------------------------- def new(cls, engine, setting): """创建新对象""" - cls.count += 1 - algoName = '%s_%s' %(cls.templateName, cls.count) - + timestamp = datetime.now().strftime('%Y%m%d%H%M%S') + if timestamp != cls.timestamp: + cls.timestamp = timestamp + cls.count = 0 + else: + cls.count += 1 + + algoName = '_'.join([cls.templateName, cls.timestamp, str(cls.count)]) algo = cls(engine, setting, algoName) return algo From 252e78996cf7a049fce9b2eaa21ba59e676d6327 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 21:13:21 +0800 Subject: [PATCH 041/135] =?UTF-8?q?[Mod]AlgoSettingMonitor=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=9B=B4=E6=96=B0templateName?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/uiAlgoManager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vnpy/trader/app/algoTrading/uiAlgoManager.py b/vnpy/trader/app/algoTrading/uiAlgoManager.py index fa92fae3..0ab47ebf 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoManager.py +++ b/vnpy/trader/app/algoTrading/uiAlgoManager.py @@ -342,6 +342,7 @@ class AlgoSettingMonitor(QtWidgets.QTableWidget): self.cellDict[settingName] = { 'start': buttonStart, + 'template': cellTemplateName, 'text': cellSettingText, 'delete': buttonDelete } @@ -349,6 +350,7 @@ class AlgoSettingMonitor(QtWidgets.QTableWidget): else: d = self.cellDict[settingName] d['start'].updateSetting(setting) + d['template'].setText(setting['templateName']) d['text'].setText(self.generateText(setting)) d['delete'].updateSetting(setting) From 9b076e2a0ad638019eeb64d91d9ae60a1c37bc13 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Jun 2018 21:45:31 +0800 Subject: [PATCH 042/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9EAlgoTrading?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E5=8E=86=E5=8F=B2=E5=85=A5=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/algoEngine.py | 38 ++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/vnpy/trader/app/algoTrading/algoEngine.py b/vnpy/trader/app/algoTrading/algoEngine.py index 3c360cd8..f4d0844e 100644 --- a/vnpy/trader/app/algoTrading/algoEngine.py +++ b/vnpy/trader/app/algoTrading/algoEngine.py @@ -21,9 +21,10 @@ EVENT_ALGO_VAR = 'eAlgoVar' # 算法变量事件 EVENT_ALGO_SETTING = 'eAlgoSetting' # 算法配置事件 -ALGOTRADING_DB_NAME = 'VnTrader_AlgoTrading_Db' +ALGOTRADING_DB_NAME = 'VnTrader_AlgoTrading_Db' # AlgoTrading数据库名 -SETTING_COLLECTION_NAME = 'AlgoSetting' +SETTING_COLLECTION_NAME = 'AlgoSetting' # 算法配置集合名 +HISTORY_COLLECTION_NAME = 'AlgoHistory' # 算法历史集合名 ALGO_DICT = { @@ -46,6 +47,7 @@ class AlgoEngine(object): self.orderAlgoDict = {} # vtOrderID:algo self.symbolAlgoDict = {} # vtSymbol:algo set self.settingDict = {} # settingName:setting + self.historyDict = {} # algoName:dict self.registerEvent() @@ -102,7 +104,9 @@ class AlgoEngine(object): templateName = algoSetting['templateName'] algoClass = ALGO_DICT[templateName] algo = algoClass.new(self, algoSetting) + self.algoDict[algo.algoName] = algo + return algo.algoName #---------------------------------------------------------------------- @@ -204,18 +208,44 @@ class AlgoEngine(object): #---------------------------------------------------------------------- def putVarEvent(self, algo, d): """更新变量""" - d['algoName'] = algo.algoName + algoName = algo.algoName + + d['algoName'] = algoName event = Event(EVENT_ALGO_VAR) event.dict_['data'] = d self.eventEngine.put(event) + + # 保存数据到数据库 + history = self.historyDict.setdefault(algoName, {}) + history['algoName'] = algoName + history['var'] = d + + self.mainEngine.dbUpdate(ALGOTRADING_DB_NAME, + HISTORY_COLLECTION_NAME, + history, + {'algoName': algoName}, + True) #---------------------------------------------------------------------- def putParamEvent(self, algo, d): """更新参数""" - d['algoName'] = algo.algoName + algoName = algo.algoName + + d['algoName'] = algoName event = Event(EVENT_ALGO_PARAM) event.dict_['data'] = d self.eventEngine.put(event) + + # 保存数据到数据库 + history = self.historyDict.setdefault(algoName, {}) + history['algoName'] = algoName + history['param'] = d + + self.mainEngine.dbUpdate(ALGOTRADING_DB_NAME, + HISTORY_COLLECTION_NAME, + history, + {'algoName': algoName}, + True) #---------------------------------------------------------------------- def getTick(self, algo, vtSymbol): From 49d22b47beed8d94043e4667f1bf5a3ce27b9c66 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 11 Jun 2018 13:06:41 +0800 Subject: [PATCH 043/135] =?UTF-8?q?[Mod]=E5=A2=9E=E5=8A=A0BitfinexGateway?= =?UTF-8?q?=E8=A1=8C=E6=83=85=E6=95=B0=E6=8D=AE=E7=9A=84=E5=BC=BA=E5=88=B6?= =?UTF-8?q?=E6=95=B0=E5=AD=97=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/bitfinexGateway/bitfinexGateway.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py index 5aef41be..31c9cf52 100644 --- a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py +++ b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py @@ -350,11 +350,11 @@ class GatewayApi(BitfinexApi): # 常规行情更新 if channel == 'ticker': - tick.volume = l[-3] - tick.highPrice = l[-2] - tick.lowPrice = l[-1] - tick.lastPrice = l[-4] - tick.openPrice = tick.lastPrice - l[4] + tick.volume = float(l[-3]) + tick.highPrice = float(l[-2]) + tick.lowPrice = float(l[-1]) + tick.lastPrice = float(l[-4]) + tick.openPrice = float(tick.lastPrice - l[4]) # 深度报价更新 elif channel == 'book': bid = self.bidDict.setdefault(symbol, {}) @@ -362,12 +362,20 @@ class GatewayApi(BitfinexApi): if len(l) > 3: for price, count, amount in l: + price = float(price) + count = int(count) + amount = float(amount) + if amount > 0: bid[price] = amount else: ask[price] = -amount else: price, count, amount = l + price = float(price) + count = int(count) + amount = float(amount) + if not count: if price in bid: del bid[price] From 920ee7fbb1ab6f609ed31160fafc2e2c440b05ed Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 11 Jun 2018 14:09:16 +0800 Subject: [PATCH 044/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9E=E4=BB=8ECSV?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E7=AF=AE=E5=AD=90=E7=AE=97=E6=B3=95=E6=88=96?= =?UTF-8?q?=E8=80=85=E5=8A=A0=E8=BD=BD=E7=AE=97=E6=B3=95=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=88=B0=E6=95=B0=E6=8D=AE=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/CryptoTrader/algoBasket.csv | 3 + vnpy/trader/app/algoTrading/twapAlgo.py | 18 ++--- vnpy/trader/app/algoTrading/uiAlgoManager.py | 74 +++++++++++++++++--- vnpy/trader/uiBasicWidget.py | 2 +- 4 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 examples/CryptoTrader/algoBasket.csv diff --git a/examples/CryptoTrader/algoBasket.csv b/examples/CryptoTrader/algoBasket.csv new file mode 100644 index 00000000..dfecd5b2 --- /dev/null +++ b/examples/CryptoTrader/algoBasket.csv @@ -0,0 +1,3 @@ +templateName,settingName,vtSymbol,direction,targetPrice,totalVolume,time,interval,priceLevel,minVolume +TWAP,BUY_BTC_TWAP,BTCUSD.BITFINEX,多,4000,10,200,10,3,1 +TWAP,SELL_BTC_TWAP,BTCUSD.BITFINEX,空,9000,10,200,10,3,1 diff --git a/vnpy/trader/app/algoTrading/twapAlgo.py b/vnpy/trader/app/algoTrading/twapAlgo.py index b4bc9f7d..d3727136 100644 --- a/vnpy/trader/app/algoTrading/twapAlgo.py +++ b/vnpy/trader/app/algoTrading/twapAlgo.py @@ -21,15 +21,15 @@ class TwapAlgo(AlgoTemplate): """Constructor""" super(TwapAlgo, self).__init__(engine, setting, algoName) - # 参数 - self.vtSymbol = setting['vtSymbol'] # 合约代码 - self.direction = setting['direction'] # 买卖 - self.targetPrice = setting['targetPrice'] # 目标价格 - self.totalVolume = setting['totalVolume'] # 总数量 - self.time = setting['time'] # 执行时间 - self.interval = setting['interval'] # 执行间隔 - self.minVolume = setting['minVolume'] # 最小委托数量 - self.priceLevel = setting['priceLevel'] # 使用第几档价格委托 + # 参数,强制类型转换,保证从CSV加载的配置正确 + self.vtSymbol = str(setting['vtSymbol']) # 合约代码 + self.direction = unicode(setting['direction']) # 买卖 + self.targetPrice = float(setting['targetPrice']) # 目标价格 + self.totalVolume = float(setting['totalVolume']) # 总数量 + self.time = int(setting['time']) # 执行时间 + self.interval = int(setting['interval']) # 执行间隔 + self.minVolume = float(setting['minVolume']) # 最小委托数量 + self.priceLevel = int(setting['priceLevel']) # 使用第几档价格委托 # 变量 self.orderSize = self.totalVolume / (self.time / self.interval) diff --git a/vnpy/trader/app/algoTrading/uiAlgoManager.py b/vnpy/trader/app/algoTrading/uiAlgoManager.py index fa92fae3..811d1b08 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoManager.py +++ b/vnpy/trader/app/algoTrading/uiAlgoManager.py @@ -1,5 +1,9 @@ # encoding: UTF-8 +import csv +import traceback +from collections import OrderedDict + from vnpy.event import Event from vnpy.trader.uiQt import QtCore, QtWidgets @@ -26,7 +30,7 @@ class StopButton(QtWidgets.QPushButton): self.setText(u'停止') self.clicked.connect(self.stopAlgo) else: - self.setText(u'全部停止') + self.setText(u'停止全部算法') self.clicked.connect(self.stopAll) #---------------------------------------------------------------------- @@ -388,17 +392,32 @@ class AlgoManager(QtWidgets.QWidget): """""" self.setWindowTitle(u'算法交易') - self.tab = QtWidgets.QTabWidget() - self.buttonStop = StopButton(self.algoEngine) + buttonWidth = 400 + buttonHeight = 60 - self.tab.setMaximumWidth(400) - self.buttonStop.setMaximumWidth(400) - self.buttonStop.setFixedHeight(100) + self.tab = QtWidgets.QTabWidget() + self.tab.setMaximumWidth(buttonWidth) + + self.buttonStop = StopButton(self.algoEngine) + self.buttonStop.setMaximumWidth(buttonWidth) + self.buttonStop.setFixedHeight(buttonHeight) + + self.buttonAddAlgo = QtWidgets.QPushButton(u'启动篮子算法') + self.buttonAddAlgo.setStyleSheet("color:white;background-color:green") + self.buttonAddAlgo.clicked.connect(self.addAlgoFromCsv) + self.buttonAddAlgo.setFixedHeight(buttonHeight) + + self.buttonSaveSetting = QtWidgets.QPushButton(u'加载算法配置') + self.buttonSaveSetting.setStyleSheet("color:white;background-color:blue") + self.buttonSaveSetting.clicked.connect(self.saveSettingFromCsv) + self.buttonSaveSetting.setFixedHeight(buttonHeight) vbox1 = QtWidgets.QVBoxLayout() vbox1.addWidget(self.tab) vbox1.addStretch() vbox1.addWidget(self.buttonStop) + vbox1.addWidget(self.buttonAddAlgo) + vbox1.addWidget(self.buttonSaveSetting) workingMonitor = AlgoStatusMonitor(self.algoEngine, AlgoStatusMonitor.MODE_WORKING) historyMonitor = AlgoStatusMonitor(self.algoEngine, AlgoStatusMonitor.MODE_HISTORY) @@ -431,6 +450,45 @@ class AlgoManager(QtWidgets.QWidget): #---------------------------------------------------------------------- def addAlgoWidget(self, widgetClass): - """""" + """添加算法控制组件 """ w = widgetClass(self.algoEngine) - self.tab.addTab(w, w.templateName) \ No newline at end of file + self.tab.addTab(w, w.templateName) + + #---------------------------------------------------------------------- + def loadCsv(self, path): + """读取CSV配置文件""" + try: + with open(unicode(path)) as f: + buf = [line.encode('UTF-8') for line in f] + + reader = csv.DictReader(buf) + l = [] + + for d in reader: + setting = OrderedDict() + for name in reader.fieldnames: + setting[str(name)] = d[name] + l.append(setting) + + return l + + except: + msg = traceback.format_exc() + self.algoEngine.writeLog(u'读取CSV文件失败:\n' + msg) + return [] + + #---------------------------------------------------------------------- + def saveSettingFromCsv(self): + """从CSV加载配置到数据库""" + path, fileType = QtWidgets.QFileDialog.getOpenFileName(self, u'加载算法配置', '', 'CSV(*.csv)') + l = self.loadCsv(path) + for setting in l: + self.algoEngine.saveAlgoSetting(setting) + + #---------------------------------------------------------------------- + def addAlgoFromCsv(self): + """从CSV启动一篮子算法""" + path, fileType = QtWidgets.QFileDialog.getOpenFileName(self, u'启动篮子算法', '', 'CSV(*.csv) ') + l = self.loadCsv(path) + for setting in l: + self.algoEngine.addAlgo(setting) \ No newline at end of file diff --git a/vnpy/trader/uiBasicWidget.py b/vnpy/trader/uiBasicWidget.py index 6ef2dcee..4cde79f1 100644 --- a/vnpy/trader/uiBasicWidget.py +++ b/vnpy/trader/uiBasicWidget.py @@ -375,7 +375,7 @@ class BasicMonitor(QtWidgets.QTableWidget): self.menu.close() # 获取想要保存的文件名 - path = QtWidgets.QFileDialog.getSaveFileName(self, vtText.SAVE_DATA, '', 'CSV(*.csv)') + path, fileType = QtWidgets.QFileDialog.getSaveFileName(self, vtText.SAVE_DATA, '', 'CSV(*.csv)') try: #if not path.isEmpty(): From 93ebff5e43eeaea36ca762c3480464da007935df Mon Sep 17 00:00:00 2001 From: cclauss Date: Mon, 11 Jun 2018 10:03:50 +0200 Subject: [PATCH 045/135] Modernize Python 2 code to get ready for Python 3 --- vnpy/api/binance/test.py | 7 ++- vnpy/api/binance/vnbinance.py | 51 ++++++++++--------- vnpy/api/bitfinex/vnbitfinex.py | 12 +++-- vnpy/api/huobi/vnhuobi.py | 20 ++++---- vnpy/api/sgit/__init__.py | 7 +-- vnpy/event/eventEngine.py | 1 + vnpy/event/eventType.py | 1 + vnpy/rpc/testClient.py | 3 +- vnpy/rpc/testServer.py | 3 +- vnpy/trader/app/algoTrading/twapAlgo.py | 6 ++- vnpy/trader/app/algoTrading/uiAlgoManager.py | 6 ++- vnpy/trader/app/algoTrading/uiAlgoWidget.py | 4 +- .../gateway/binanceGateway/binanceGateway.py | 5 +- vnpy/trader/vtFunction.py | 4 +- 14 files changed, 76 insertions(+), 54 deletions(-) diff --git a/vnpy/api/binance/test.py b/vnpy/api/binance/test.py index 83dd365c..d755fd21 100644 --- a/vnpy/api/binance/test.py +++ b/vnpy/api/binance/test.py @@ -1,6 +1,9 @@ +from __future__ import absolute_import from time import sleep -from vnbinance import BinanceApi +from six.moves import input + +from .vnbinance import BinanceApi if __name__ == '__main__': @@ -40,4 +43,4 @@ if __name__ == '__main__': #api.initDataStream(['btcusdt@ticker', 'btcusdt@depth5']) #api.initUserStream('NXvaiFwZz2LuKqINVerKOnWaQQG1JhdQNejiZKY9Kmgk4lZgTvm3nRAnXJK7') - raw_input() \ No newline at end of file + input() diff --git a/vnpy/api/binance/vnbinance.py b/vnpy/api/binance/vnbinance.py index da67de8e..618e428a 100644 --- a/vnpy/api/binance/vnbinance.py +++ b/vnpy/api/binance/vnbinance.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function import json import requests import hmac @@ -389,107 +390,107 @@ class BinanceApi(object): #---------------------------------------------------------------------- def onError(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryPing(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryTime(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryExchangeInfo(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryDepth(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryTrades(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryAggTrades(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryKlines(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryTicker24HR(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryTickerPrice(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryBookTicker(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onNewOrder(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryOrder(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onCancelOrder(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryOpenOrders(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryAllOrders(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryAccount(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onQueryMyTrades(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onStartStream(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onKeepaliveStream(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) #---------------------------------------------------------------------- def onCloseStream(self, data, reqid): """""" - print(data, reqid) + print((data, reqid)) ################################################### ## Websocket Function @@ -547,12 +548,12 @@ class BinanceApi(object): #---------------------------------------------------------------------- def onDataStreamError(self, msg): """""" - print msg + print(msg) #---------------------------------------------------------------------- def onMarketData(self, data): """""" - print data + print(data) #---------------------------------------------------------------------- def initUserStream(self, key): @@ -608,12 +609,12 @@ class BinanceApi(object): #---------------------------------------------------------------------- def onUserStreamError(self, msg): """""" - print msg + print(msg) #---------------------------------------------------------------------- def onUserData(self, data): """""" - print data + print(data) #---------------------------------------------------------------------- def runKeepalive(self): diff --git a/vnpy/api/bitfinex/vnbitfinex.py b/vnpy/api/bitfinex/vnbitfinex.py index ee308d68..0132c000 100644 --- a/vnpy/api/bitfinex/vnbitfinex.py +++ b/vnpy/api/bitfinex/vnbitfinex.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function import json import requests import traceback @@ -9,6 +10,8 @@ from queue import Queue, Empty import websocket +from six.moves import input + WEBSOCKET_V2_URL = 'wss://api.bitfinex.com/ws/2' RESTFUL_V1_URL = 'https://api.bitfinex.com/v1' @@ -78,17 +81,17 @@ class BitfinexApi(object): #---------------------------------------------------------------------- def onConnect(self): """""" - print 'connected' + print('connected') #---------------------------------------------------------------------- def onData(self, data): """""" - print data + print(data) #---------------------------------------------------------------------- def onError(self, msg): """""" - print msg + print(msg) #---------------------------------------------------------------------- def sendReq(self, req): @@ -130,5 +133,4 @@ if __name__ == '__main__': } api.sendReq(d) - raw_input() - \ No newline at end of file + input() diff --git a/vnpy/api/huobi/vnhuobi.py b/vnpy/api/huobi/vnhuobi.py index 8877a371..514db58a 100644 --- a/vnpy/api/huobi/vnhuobi.py +++ b/vnpy/api/huobi/vnhuobi.py @@ -1,23 +1,25 @@ # encoding: utf-8 from __future__ import print_function -import urllib -import hmac + import base64 import hashlib -import requests +import hmac +import json +import ssl import traceback +import urllib +import zlib from copy import copy from datetime import datetime -from threading import Thread -from queue import Queue, Empty from multiprocessing.dummy import Pool +from queue import Empty, Queue +from threading import Thread from time import sleep -import json -import zlib -from websocket import create_connection, _exceptions +import requests +from websocket import _exceptions, create_connection # 常量定义 TIMEOUT = 10 @@ -660,4 +662,4 @@ class DataApi(object): #---------------------------------------------------------------------- def onMarketDetail(self, data): """市场细节推送""" - print(data) \ No newline at end of file + print(data) diff --git a/vnpy/api/sgit/__init__.py b/vnpy/api/sgit/__init__.py index 7bdd52cd..906277fd 100644 --- a/vnpy/api/sgit/__init__.py +++ b/vnpy/api/sgit/__init__.py @@ -1,5 +1,6 @@ # encoding: UTF-8 -from vnsgitmd import MdApi -from vnsgittd import TdApi -from sgit_data_type import defineDict \ No newline at end of file +from __future__ import absolute_import +from .vnsgitmd import MdApi +from .vnsgittd import TdApi +from .sgit_data_type import defineDict \ No newline at end of file diff --git a/vnpy/event/eventEngine.py b/vnpy/event/eventEngine.py index 5d259ee0..dff82489 100644 --- a/vnpy/event/eventEngine.py +++ b/vnpy/event/eventEngine.py @@ -1,6 +1,7 @@ # encoding: UTF-8 # 系统模块 +from __future__ import print_function from queue import Queue, Empty from threading import Thread from time import sleep diff --git a/vnpy/event/eventType.py b/vnpy/event/eventType.py index 0515bcb1..acda0150 100644 --- a/vnpy/event/eventType.py +++ b/vnpy/event/eventType.py @@ -10,6 +10,7 @@ 建议将所有的常量定义放在该文件中,便于检查是否存在重复的现象。 ''' +from __future__ import print_function EVENT_TIMER = 'eTimer' # 计时器事件,每隔1秒发送一次 diff --git a/vnpy/rpc/testClient.py b/vnpy/rpc/testClient.py index d4d03fd3..f1bcf5b9 100644 --- a/vnpy/rpc/testClient.py +++ b/vnpy/rpc/testClient.py @@ -1,9 +1,10 @@ # encoding: UTF-8 from __future__ import print_function +from __future__ import absolute_import from time import sleep -from vnrpc import RpcClient +from .vnrpc import RpcClient ######################################################################## diff --git a/vnpy/rpc/testServer.py b/vnpy/rpc/testServer.py index cb5760b1..0a8a39c2 100644 --- a/vnpy/rpc/testServer.py +++ b/vnpy/rpc/testServer.py @@ -1,9 +1,10 @@ # encoding: UTF-8 from __future__ import print_function +from __future__ import absolute_import from time import sleep, time -from vnrpc import RpcServer +from .vnrpc import RpcServer ######################################################################## diff --git a/vnpy/trader/app/algoTrading/twapAlgo.py b/vnpy/trader/app/algoTrading/twapAlgo.py index d3727136..8093e355 100644 --- a/vnpy/trader/app/algoTrading/twapAlgo.py +++ b/vnpy/trader/app/algoTrading/twapAlgo.py @@ -3,6 +3,8 @@ from __future__ import division from collections import OrderedDict +from six import text_type + from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT) from vnpy.trader.uiQt import QtWidgets @@ -23,7 +25,7 @@ class TwapAlgo(AlgoTemplate): # 参数,强制类型转换,保证从CSV加载的配置正确 self.vtSymbol = str(setting['vtSymbol']) # 合约代码 - self.direction = unicode(setting['direction']) # 买卖 + self.direction = text_type(setting['direction']) # 买卖 self.targetPrice = float(setting['targetPrice']) # 目标价格 self.totalVolume = float(setting['totalVolume']) # 总数量 self.time = int(setting['time']) # 执行时间 @@ -261,4 +263,4 @@ class TwapWidget(AlgoWidget): return setting - \ No newline at end of file + diff --git a/vnpy/trader/app/algoTrading/uiAlgoManager.py b/vnpy/trader/app/algoTrading/uiAlgoManager.py index 811d1b08..b71c671e 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoManager.py +++ b/vnpy/trader/app/algoTrading/uiAlgoManager.py @@ -4,6 +4,8 @@ import csv import traceback from collections import OrderedDict +from six import text_type + from vnpy.event import Event from vnpy.trader.uiQt import QtCore, QtWidgets @@ -458,7 +460,7 @@ class AlgoManager(QtWidgets.QWidget): def loadCsv(self, path): """读取CSV配置文件""" try: - with open(unicode(path)) as f: + with open(text_type(path)) as f: buf = [line.encode('UTF-8') for line in f] reader = csv.DictReader(buf) @@ -491,4 +493,4 @@ class AlgoManager(QtWidgets.QWidget): path, fileType = QtWidgets.QFileDialog.getOpenFileName(self, u'启动篮子算法', '', 'CSV(*.csv) ') l = self.loadCsv(path) for setting in l: - self.algoEngine.addAlgo(setting) \ No newline at end of file + self.algoEngine.addAlgo(setting) diff --git a/vnpy/trader/app/algoTrading/uiAlgoWidget.py b/vnpy/trader/app/algoTrading/uiAlgoWidget.py index addb3090..1f4a1baa 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoWidget.py +++ b/vnpy/trader/app/algoTrading/uiAlgoWidget.py @@ -1,5 +1,7 @@ # encoding: UTF-8 +from six import text_type + from vnpy.trader.uiQt import QtWidgets @@ -54,7 +56,7 @@ class AlgoWidget(QtWidgets.QWidget): """保存算法配置""" setting = self.getAlgoSetting() setting['templateName'] = self.templateName - setting['settingName'] = unicode(self.lineSettingName.text()) + setting['settingName'] = text_type(self.lineSettingName.text()) self.algoEngine.saveAlgoSetting(setting) self.lineSettingName.setText('') diff --git a/vnpy/trader/gateway/binanceGateway/binanceGateway.py b/vnpy/trader/gateway/binanceGateway/binanceGateway.py index 229c2494..64fd6a5d 100644 --- a/vnpy/trader/gateway/binanceGateway/binanceGateway.py +++ b/vnpy/trader/gateway/binanceGateway/binanceGateway.py @@ -3,6 +3,7 @@ ''' vnpy.api.binance的gateway接入 ''' +from __future__ import print_function import os import json @@ -40,11 +41,11 @@ priceTypeMap[PRICETYPE_MARKETPRICE] = 'MARKET' #---------------------------------------------------------------------- def print_dict(d): """""" - print '-' * 30 + print('-' * 30) l = d.keys() l.sort() for k in l: - print '%s:%s' %(k, d[k]) + print('%s:%s' %(k, d[k])) ######################################################################## diff --git a/vnpy/trader/vtFunction.py b/vnpy/trader/vtFunction.py index 1fc55c9a..7673177a 100644 --- a/vnpy/trader/vtFunction.py +++ b/vnpy/trader/vtFunction.py @@ -11,6 +11,8 @@ import traceback from datetime import datetime from math import isnan +from six import text_type + #---------------------------------------------------------------------- def safeUnicode(value): @@ -26,7 +28,7 @@ def safeUnicode(value): if abs(d.as_tuple().exponent) > MAX_DECIMAL: value = round(value, ndigits=MAX_DECIMAL) - return unicode(value) + return text_type(value) #---------------------------------------------------------------------- From 799e47407deb96f0d24a4d3aba470707b5775f1a Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 11 Jun 2018 22:07:12 +0800 Subject: [PATCH 046/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9E=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E6=89=A7=E8=A1=8C=E5=A7=94=E6=89=98=E7=9A=84DMA?= =?UTF-8?q?=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/algoEngine.py | 30 ++- vnpy/trader/app/algoTrading/algoTemplate.py | 8 +- vnpy/trader/app/algoTrading/dmaAlgo.py | 192 +++++++++++++++++++ vnpy/trader/app/algoTrading/twapAlgo.py | 1 - vnpy/trader/app/algoTrading/uiAlgoManager.py | 2 + 5 files changed, 219 insertions(+), 14 deletions(-) create mode 100644 vnpy/trader/app/algoTrading/dmaAlgo.py diff --git a/vnpy/trader/app/algoTrading/algoEngine.py b/vnpy/trader/app/algoTrading/algoEngine.py index 3c360cd8..ce20b3ad 100644 --- a/vnpy/trader/app/algoTrading/algoEngine.py +++ b/vnpy/trader/app/algoTrading/algoEngine.py @@ -7,13 +7,14 @@ from __future__ import division from vnpy.event import Event from vnpy.trader.vtEvent import EVENT_TIMER, EVENT_TICK, EVENT_ORDER, EVENT_TRADE -from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, PRICETYPE_LIMITPRICE, +from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, + PRICETYPE_LIMITPRICE, PRICETYPE_MARKETPRICE, OFFSET_OPEN, OFFSET_CLOSE, OFFSET_CLOSETODAY, OFFSET_CLOSEYESTERDAY) from vnpy.trader.vtObject import VtSubscribeReq, VtOrderReq, VtCancelOrderReq, VtLogData from .twapAlgo import TwapAlgo - +from .dmaAlgo import DmaAlgo EVENT_ALGO_LOG = 'eAlgoLog' # 算法日志事件 EVENT_ALGO_PARAM = 'eAlgoParam' # 算法参数事件 @@ -143,7 +144,8 @@ class AlgoEngine(object): self.mainEngine.subscribe(req, contract.gatewayName) #---------------------------------------------------------------------- - def sendOrder(self, algo, vtSymbol, direction, price, volume): + def sendOrder(self, algo, vtSymbol, direction, price, volume, + priceType=None, offset=None): """发单""" contract = self.mainEngine.getContract(vtSymbol) if not contract: @@ -153,24 +155,34 @@ class AlgoEngine(object): req.vtSymbol = vtSymbol req.symbol = contract.symbol req.exchange = contract.exchange - req.direction = direction - req.priceType = PRICETYPE_LIMITPRICE + req.direction = direction req.offset = OFFSET_CLOSETODAY req.price = price req.volume = volume + + if priceType: + req.priceType = priceType + else: + req.priceType = PRICETYPE_LIMITPRICE + + if offset: + req.offset = offset + else: + req.offset = OFFSET_OPEN + vtOrderID = self.mainEngine.sendOrder(req, contract.gatewayName) return vtOrderID #---------------------------------------------------------------------- - def buy(self, algo, vtSymbol, price, volume): + def buy(self, algo, vtSymbol, price, volume, priceType=None, offset=None): """买入""" - return self.sendOrder(algo, vtSymbol, DIRECTION_LONG, price, volume) + return self.sendOrder(algo, vtSymbol, DIRECTION_LONG, price, volume, priceType, offset) #---------------------------------------------------------------------- - def sell(self, algo, vtSymbol, price, volume): + def sell(self, algo, vtSymbol, price, volume, priceType=None, offset=None): """卖出""" - return self.sendOrder(algo, vtSymbol, DIRECTION_SHORT, price, volume) + return self.sendOrder(algo, vtSymbol, DIRECTION_SHORT, price, volume, priceType, offset) #---------------------------------------------------------------------- def cancelOrder(self, algo, vtOrderID): diff --git a/vnpy/trader/app/algoTrading/algoTemplate.py b/vnpy/trader/app/algoTrading/algoTemplate.py index 29e89a2a..6e2865c6 100644 --- a/vnpy/trader/app/algoTrading/algoTemplate.py +++ b/vnpy/trader/app/algoTrading/algoTemplate.py @@ -111,14 +111,14 @@ class AlgoTemplate(object): self.engine.subscribe(self, vtSymbol) #---------------------------------------------------------------------- - def buy(self, vtSymbol, price, volume): + def buy(self, vtSymbol, price, volume, priceType=None, offset=None): """""" - return self.engine.buy(self, vtSymbol, price, volume) + return self.engine.buy(self, vtSymbol, price, volume, priceType, offset) #---------------------------------------------------------------------- - def sell(self, vtSymbol, price, volume): + def sell(self, vtSymbol, price, volume, priceType=None, offset=None): """""" - return self.engine.sell(self, vtSymbol, price, volume) + return self.engine.sell(self, vtSymbol, price, volume, priceType, offset) #---------------------------------------------------------------------- def cancelOrder(self, vtOrderID): diff --git a/vnpy/trader/app/algoTrading/dmaAlgo.py b/vnpy/trader/app/algoTrading/dmaAlgo.py new file mode 100644 index 00000000..753794f3 --- /dev/null +++ b/vnpy/trader/app/algoTrading/dmaAlgo.py @@ -0,0 +1,192 @@ +# encoding: UTF-8 + +from __future__ import division +from collections import OrderedDict + +from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, + OFFSET_OPEN, OFFSET_CLOSE, + PRICETYPE_LIMITPRICE, PRICETYPE_MARKETPRICE, + STATUS_REJECTED, STATUS_CANCELLED, STATUS_ALLTRADED) +from vnpy.trader.uiQt import QtWidgets + +from .algoTemplate import AlgoTemplate +from .uiAlgoWidget import AlgoWidget, QtWidgets + + + +STATUS_FINISHED = set([STATUS_ALLTRADED, STATUS_CANCELLED, STATUS_REJECTED]) + + +######################################################################## +class DmaAlgo(AlgoTemplate): + """DMA算法,直接发出限价或者市价委托""" + + templateName = 'DMA' + + #---------------------------------------------------------------------- + def __init__(self, engine, setting, algoName): + """Constructor""" + super(DmaAlgo, self).__init__(engine, setting, algoName) + + # 参数,强制类型转换,保证从CSV加载的配置正确 + self.vtSymbol = str(setting['vtSymbol']) # 合约代码 + self.direction = unicode(setting['direction']) # 买卖 + self.offset = float(setting['offset']) # 开平 + self.priceType = float(setting['priceType']) # 价格类型 + self.price = float(setting['price']) # 价格 + self.totalVolume = int(setting['totalVolume']) # 数量 + + self.vtOrderID = '' # 委托号 + self.tradedVolume = 0 # 成交数量 + self.orderStatus = '' # 委托状态 + + self.subscribe(self.vtSymbol) + self.paramEvent() + self.varEvent() + + #---------------------------------------------------------------------- + def onTick(self, tick): + """""" + # 发出委托 + if not self.vtOrderID: + if self.direction == DIRECTION_LONG: + func = self.buy + else: + func = self.sell + + self.vtOrderID = func(self.vtSymbol, self.price, self.volume, + self.priceType, self.offset) + + # 更新变量 + self.varEvent() + + #---------------------------------------------------------------------- + def onTrade(self, trade): + """""" + pass + + #---------------------------------------------------------------------- + def onOrder(self, order): + """""" + self.tradedVolume = order.tradedVolume + self.orderStatus = order.status + + if self.orderStatus in STATUS_FINISHED: + self.stop() + + self.paramEvent() + + #---------------------------------------------------------------------- + def onTimer(self): + """""" + pass + + #---------------------------------------------------------------------- + def onStop(self): + """""" + if self.orderStatus not in STATUS_FINISHED: + self.cancelAll() + + # 处理这里的逻辑,区分用户停止和到期停止 + + self.writeLog(u'委托已%s,停止算法' %self.status) + + #---------------------------------------------------------------------- + def varEvent(self): + """更新变量""" + d = OrderedDict() + d[u'算法状态'] = self.active + d[u'委托号'] = self.vtOrderID + d[u'成交数量'] = self.tradedVolume + d[u'委托状态'] = self.orderStatus + self.putVarEvent(d) + + #---------------------------------------------------------------------- + def paramEvent(self): + """更新参数""" + d = OrderedDict() + d[u'代码'] = self.vtSymbol + d[u'方向'] = self.direction + d[u'价格'] = self.price + d[u'数量'] = self.totalVolume + d[u'价格类型'] = self.priceType + d[u'开平'] = self.offset + self.putParamEvent(d) + + +######################################################################## +class DmaWidget(AlgoWidget): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, parent=None): + """Constructor""" + super(DmaWidget, self).__init__(algoEngine, parent) + + self.templateName = DmaAlgo.templateName + + #---------------------------------------------------------------------- + def initAlgoLayout(self): + """""" + self.lineSymbol = QtWidgets.QLineEdit() + + self.comboDirection = QtWidgets.QComboBox() + self.comboDirection.addItem(DIRECTION_LONG) + self.comboDirection.addItem(DIRECTION_SHORT) + self.comboDirection.setCurrentIndex(0) + + self.spinPrice = QtWidgets.QDoubleSpinBox() + self.spinPrice.setMinimum(0) + self.spinPrice.setMaximum(1000000000) + self.spinPrice.setDecimals(8) + + self.spinVolume = QtWidgets.QDoubleSpinBox() + self.spinVolume.setMinimum(0) + self.spinVolume.setMaximum(1000000000) + self.spinVolume.setDecimals(6) + + self.comboPriceType = QtWidgets.QComboBox() + self.comboPriceType.addItems([PRICETYPE_LIMITPRICE, PRICETYPE_MARKETPRICE]) + self.comboPriceType.setCurrentIndex(0) + + self.comboOffset = QtWidgets.QComboBox() + self.comboOffset.addItems(['', OFFSET_OPEN, OFFSET_CLOSE]) + self.comboOffset.setCurrentIndex(0) + + buttonStart = QtWidgets.QPushButton(u'启动') + buttonStart.clicked.connect(self.addAlgo) + buttonStart.setMinimumHeight(100) + + Label = QtWidgets.QLabel + + grid = QtWidgets.QGridLayout() + grid.addWidget(Label(u'代码'), 0, 0) + grid.addWidget(self.lineSymbol, 0, 1) + grid.addWidget(Label(u'方向'), 1, 0) + grid.addWidget(self.comboDirection, 1, 1) + grid.addWidget(Label(u'价格'), 2, 0) + grid.addWidget(self.spinPrice, 2, 1) + grid.addWidget(Label(u'数量'), 3, 0) + grid.addWidget(self.spinVolume, 3, 1) + grid.addWidget(Label(u'类型'), 4, 0) + grid.addWidget(self.comboPriceType, 4, 1) + grid.addWidget(Label(u'开平'), 5, 0) + grid.addWidget(self.comboOffset, 5, 1) + + return grid + + #---------------------------------------------------------------------- + def getAlgoSetting(self): + """""" + setting = OrderedDict() + setting['templateName'] = DmaAlgo.templateName + setting['vtSymbol'] = str(self.lineSymbol.text()) + setting['direction'] = unicode(self.comboDirection.currentText()) + setting['price'] = float(self.spinPrice.value()) + setting['totalVolume'] = float(self.spinVolume.value()) + setting['priceType'] = unicode(self.comboPriceType.currentText()) + setting['offset'] = unicode(self.comboOffset.currentText()) + + return setting + + \ No newline at end of file diff --git a/vnpy/trader/app/algoTrading/twapAlgo.py b/vnpy/trader/app/algoTrading/twapAlgo.py index d3727136..d38d29af 100644 --- a/vnpy/trader/app/algoTrading/twapAlgo.py +++ b/vnpy/trader/app/algoTrading/twapAlgo.py @@ -152,7 +152,6 @@ class TwapAlgo(AlgoTemplate): d[u'单笔委托'] = self.orderSize d[u'本轮读秒'] = self.timerCount d[u'累计读秒'] = self.timerTotal - d['active'] = self.active self.putVarEvent(d) #---------------------------------------------------------------------- diff --git a/vnpy/trader/app/algoTrading/uiAlgoManager.py b/vnpy/trader/app/algoTrading/uiAlgoManager.py index 811d1b08..1e07c1ec 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoManager.py +++ b/vnpy/trader/app/algoTrading/uiAlgoManager.py @@ -10,6 +10,7 @@ from vnpy.trader.uiQt import QtCore, QtWidgets from .algoEngine import (EVENT_ALGO_LOG, EVENT_ALGO_PARAM, EVENT_ALGO_VAR, EVENT_ALGO_SETTING) from .twapAlgo import TwapWidget +from .dmaAlgo import DmaWidget ######################################################################## @@ -384,6 +385,7 @@ class AlgoManager(QtWidgets.QWidget): self.initUi() self.addAlgoWidget(TwapWidget) + self.addAlgoWidget(DmaWidget) self.algoEngine.loadAlgoSetting() # 界面初始化后,再加载算法配置 From 2d94151efe8caf91c2781dc14202916459ddfa15 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 11 Jun 2018 22:16:26 +0800 Subject: [PATCH 047/135] =?UTF-8?q?[Add]HuobiGateway=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=A1=8C=E6=83=85=E6=95=B0=E6=8D=AE=E5=AD=97=E6=AE=B5=E5=BC=BA?= =?UTF-8?q?=E5=88=B6=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/huobiGateway/huobiGateway.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/vnpy/trader/gateway/huobiGateway/huobiGateway.py b/vnpy/trader/gateway/huobiGateway/huobiGateway.py index 27371ca6..1d383342 100644 --- a/vnpy/trader/gateway/huobiGateway/huobiGateway.py +++ b/vnpy/trader/gateway/huobiGateway/huobiGateway.py @@ -257,14 +257,14 @@ class HuobiDataApi(DataApi): bids = data['tick']['bids'] for n in range(5): l = bids[n] - tick.__setattr__('bidPrice' + str(n+1), l[0]) - tick.__setattr__('bidVolume' + str(n+1), l[1]) + tick.__setattr__('bidPrice' + str(n+1), float(l[0])) + tick.__setattr__('bidVolume' + str(n+1), float(l[1])) asks = data['tick']['asks'] for n in range(5): l = asks[n] - tick.__setattr__('askPrice' + str(n+1), l[0]) - tick.__setattr__('askVolume' + str(n+1), l[1]) + tick.__setattr__('askPrice' + str(n+1), float(l[0])) + tick.__setattr__('askVolume' + str(n+1), float(l[1])) #print '-' * 50 #for d in data['tick']['asks']: @@ -309,12 +309,12 @@ class HuobiDataApi(DataApi): tick.time = tick.datetime.strftime('%H:%M:%S.%f') t = data['tick'] - tick.openPrice = t['open'] - tick.highPrice = t['high'] - tick.lowPrice = t['low'] - tick.lastPrice = t['close'] - tick.volume = t['vol'] - tick.preClosePrice = tick.openPrice + tick.openPrice = float(t['open']) + tick.highPrice = float(t['high']) + tick.lowPrice = float(t['low']) + tick.lastPrice = float(t['close']) + tick.volume = float(t['vol']) + tick.preClosePrice = float(tick.openPrice) if tick.bidPrice1: newtick = copy(tick) From 11a69f79ce129add55fdc5edbc941b3cdcd8f62e Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 11 Jun 2018 22:18:45 +0800 Subject: [PATCH 048/135] =?UTF-8?q?[Add]BitfinexGateway=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=AF=B9=E6=B7=B1=E5=BA=A6=E8=A1=8C=E6=83=85=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=97=B6=E6=B2=A1=E6=9C=89BID=E6=88=96=E8=80=85ASK=E6=8C=82?= =?UTF-8?q?=E5=8D=95=E6=83=85=E5=86=B5=E7=9A=84=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bitfinexGateway/bitfinexGateway.py | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py index e2ca00d2..217d93e3 100644 --- a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py +++ b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py @@ -390,37 +390,43 @@ class GatewayApi(BitfinexApi): else: ask[price] = amount - # BID - bidPriceList = bid.keys() - bidPriceList.sort(reverse=True) - - tick.bidPrice1 = bidPriceList[0] - tick.bidPrice2 = bidPriceList[1] - tick.bidPrice3 = bidPriceList[2] - tick.bidPrice4 = bidPriceList[3] - tick.bidPrice5 = bidPriceList[4] - - tick.bidVolume1 = bid[tick.bidPrice1] - tick.bidVolume2 = bid[tick.bidPrice2] - tick.bidVolume3 = bid[tick.bidPrice3] - tick.bidVolume4 = bid[tick.bidPrice4] - tick.bidVolume5 = bid[tick.bidPrice5] - - # ASK - askPriceList = ask.keys() - askPriceList.sort() - - tick.askPrice1 = askPriceList[0] - tick.askPrice2 = askPriceList[1] - tick.askPrice3 = askPriceList[2] - tick.askPrice4 = askPriceList[3] - tick.askPrice5 = askPriceList[4] - - tick.askVolume1 = ask[tick.askPrice1] - tick.askVolume2 = ask[tick.askPrice2] - tick.askVolume3 = ask[tick.askPrice3] - tick.askVolume4 = ask[tick.askPrice4] - tick.askVolume5 = ask[tick.askPrice5] + # Bitfinex的深度数据更新是逐档推送变动情况,而非5档一起推 + # 因此会出现没有Bid或者Ask的情况,这里使用try...catch过滤 + # 只有买卖深度满足5档时才做推送 + try: + # BID + bidPriceList = bid.keys() + bidPriceList.sort(reverse=True) + + tick.bidPrice1 = bidPriceList[0] + tick.bidPrice2 = bidPriceList[1] + tick.bidPrice3 = bidPriceList[2] + tick.bidPrice4 = bidPriceList[3] + tick.bidPrice5 = bidPriceList[4] + + tick.bidVolume1 = bid[tick.bidPrice1] + tick.bidVolume2 = bid[tick.bidPrice2] + tick.bidVolume3 = bid[tick.bidPrice3] + tick.bidVolume4 = bid[tick.bidPrice4] + tick.bidVolume5 = bid[tick.bidPrice5] + + # ASK + askPriceList = ask.keys() + askPriceList.sort() + + tick.askPrice1 = askPriceList[0] + tick.askPrice2 = askPriceList[1] + tick.askPrice3 = askPriceList[2] + tick.askPrice4 = askPriceList[3] + tick.askPrice5 = askPriceList[4] + + tick.askVolume1 = ask[tick.askPrice1] + tick.askVolume2 = ask[tick.askPrice2] + tick.askVolume3 = ask[tick.askPrice3] + tick.askVolume4 = ask[tick.askPrice4] + tick.askVolume5 = ask[tick.askPrice5] + except IndexError: + return dt = datetime.now() tick.date = dt.strftime('%Y%m%d') From 584990a7edf6e0afabc054955dcdeaa3721f0b2c Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 11 Jun 2018 23:26:53 +0800 Subject: [PATCH 049/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9E=E5=81=9C?= =?UTF-8?q?=E6=AD=A2=E5=8D=95=E7=AE=97=E6=B3=95StopAlgo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/algoEngine.py | 10 +- vnpy/trader/app/algoTrading/dmaAlgo.py | 17 +- vnpy/trader/app/algoTrading/stopAlgo.py | 206 +++++++++++++++++++ vnpy/trader/app/algoTrading/twapAlgo.py | 1 + vnpy/trader/app/algoTrading/uiAlgoManager.py | 3 + 5 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 vnpy/trader/app/algoTrading/stopAlgo.py diff --git a/vnpy/trader/app/algoTrading/algoEngine.py b/vnpy/trader/app/algoTrading/algoEngine.py index de3bcb34..f83c475b 100644 --- a/vnpy/trader/app/algoTrading/algoEngine.py +++ b/vnpy/trader/app/algoTrading/algoEngine.py @@ -15,6 +15,7 @@ from vnpy.trader.vtObject import VtSubscribeReq, VtOrderReq, VtCancelOrderReq, V from .twapAlgo import TwapAlgo from .dmaAlgo import DmaAlgo +from .stopAlgo import StopAlgo EVENT_ALGO_LOG = 'eAlgoLog' # 算法日志事件 EVENT_ALGO_PARAM = 'eAlgoParam' # 算法参数事件 @@ -29,7 +30,9 @@ HISTORY_COLLECTION_NAME = 'AlgoHistory' # 算法历史集合名 ALGO_DICT = { - TwapAlgo.templateName: TwapAlgo + TwapAlgo.templateName: TwapAlgo, + DmaAlgo.templateName: DmaAlgo, + StopAlgo.templateName: StopAlgo } @@ -113,8 +116,9 @@ class AlgoEngine(object): #---------------------------------------------------------------------- def stopAlgo(self, algoName): """停止算法""" - self.algoDict[algoName].stop() - del self.algoDict[algoName] + if algoName in self.algoDict: + self.algoDict[algoName].stop() + del self.algoDict[algoName] #---------------------------------------------------------------------- def stopAll(self): diff --git a/vnpy/trader/app/algoTrading/dmaAlgo.py b/vnpy/trader/app/algoTrading/dmaAlgo.py index 753794f3..fdb42a82 100644 --- a/vnpy/trader/app/algoTrading/dmaAlgo.py +++ b/vnpy/trader/app/algoTrading/dmaAlgo.py @@ -31,10 +31,10 @@ class DmaAlgo(AlgoTemplate): # 参数,强制类型转换,保证从CSV加载的配置正确 self.vtSymbol = str(setting['vtSymbol']) # 合约代码 self.direction = unicode(setting['direction']) # 买卖 - self.offset = float(setting['offset']) # 开平 - self.priceType = float(setting['priceType']) # 价格类型 + self.offset = unicode(setting['offset']) # 开平 + self.priceType = unicode(setting['priceType']) # 价格类型 self.price = float(setting['price']) # 价格 - self.totalVolume = int(setting['totalVolume']) # 数量 + self.totalVolume = float(setting['totalVolume']) # 数量 self.vtOrderID = '' # 委托号 self.tradedVolume = 0 # 成交数量 @@ -74,7 +74,7 @@ class DmaAlgo(AlgoTemplate): if self.orderStatus in STATUS_FINISHED: self.stop() - self.paramEvent() + self.varEvent() #---------------------------------------------------------------------- def onTimer(self): @@ -84,12 +84,8 @@ class DmaAlgo(AlgoTemplate): #---------------------------------------------------------------------- def onStop(self): """""" - if self.orderStatus not in STATUS_FINISHED: - self.cancelAll() - - # 处理这里的逻辑,区分用户停止和到期停止 - - self.writeLog(u'委托已%s,停止算法' %self.status) + self.writeLog(u'停止算法') + self.varEvent() #---------------------------------------------------------------------- def varEvent(self): @@ -99,6 +95,7 @@ class DmaAlgo(AlgoTemplate): d[u'委托号'] = self.vtOrderID d[u'成交数量'] = self.tradedVolume d[u'委托状态'] = self.orderStatus + d['active'] = self.active self.putVarEvent(d) #---------------------------------------------------------------------- diff --git a/vnpy/trader/app/algoTrading/stopAlgo.py b/vnpy/trader/app/algoTrading/stopAlgo.py new file mode 100644 index 00000000..3b2e662f --- /dev/null +++ b/vnpy/trader/app/algoTrading/stopAlgo.py @@ -0,0 +1,206 @@ +# encoding: UTF-8 + +from __future__ import division +from collections import OrderedDict + +from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, + OFFSET_OPEN, OFFSET_CLOSE) +from vnpy.trader.uiQt import QtWidgets + +from .algoTemplate import AlgoTemplate +from .uiAlgoWidget import AlgoWidget, QtWidgets + + + +######################################################################## +class StopAlgo(AlgoTemplate): + """停止单算法,也可以用于止损单""" + + templateName = 'STOP' + + #---------------------------------------------------------------------- + def __init__(self, engine, setting, algoName): + """Constructor""" + super(StopAlgo, self).__init__(engine, setting, algoName) + + # 参数,强制类型转换,保证从CSV加载的配置正确 + self.vtSymbol = str(setting['vtSymbol']) # 合约代码 + self.direction = unicode(setting['direction']) # 买卖 + self.stopPrice = float(setting['stopPrice']) # 触发价格 + self.totalVolume = float(setting['totalVolume']) # 数量 + self.offset = unicode(setting['offset']) # 开平 + self.priceAdd = float(setting['priceAdd']) # 下单时的超价 + + self.vtOrderID = '' # 委托号 + self.tradedVolume = 0 # 成交数量 + self.orderStatus = '' # 委托状态 + + self.subscribe(self.vtSymbol) + self.paramEvent() + self.varEvent() + + #---------------------------------------------------------------------- + def onTick(self, tick): + """""" + # 如果已经发出委托,则忽略行情事件 + if self.vtOrderID: + return + + # 如果到达止损位,才触发委托 + if (self.direction == DIRECTION_LONG and + tick.lastPrice >= self.price): + # 计算超价委托价格 + price = self.stopPrice + self.priceAdd + + # 避免价格超过涨停价 + if tick.upperLimit: + price = min(price, tick.upperLimit) + + func = self.buy + else: + price = self.stopPrice - self.priceAdd + + if tick.lowerLimit: + price = max(price, tick.lowerLimit) + + func = self.sell + + self.vtOrderID = func(self.vtSymbol, price, self.volume, offset=self.offset) + + msg = u'停止单已触发,代码:%s,方向:%s, 价格:%s,数量:%s,开平:%s' %(self.vtSymbol, + self.direction, + self.stopPrice, + self.totalVolume, + self.offset) + self.writeLog(msg) + + # 更新变量 + self.varEvent() + + #---------------------------------------------------------------------- + def onTrade(self, trade): + """""" + pass + + #---------------------------------------------------------------------- + def onOrder(self, order): + """""" + self.tradedVolume = order.tradedVolume + self.orderStatus = order.status + + if self.orderStatus in STATUS_FINISHED: + self.stop() + + self.varEvent() + + #---------------------------------------------------------------------- + def onTimer(self): + """""" + pass + + #---------------------------------------------------------------------- + def onStop(self): + """""" + self.writeLog(u'停止算法') + self.varEvent() + + #---------------------------------------------------------------------- + def varEvent(self): + """更新变量""" + d = OrderedDict() + d[u'算法状态'] = self.active + d[u'委托号'] = self.vtOrderID + d[u'成交数量'] = self.tradedVolume + d[u'委托状态'] = self.orderStatus + d['active'] = self.active + self.putVarEvent(d) + + #---------------------------------------------------------------------- + def paramEvent(self): + """更新参数""" + d = OrderedDict() + d[u'代码'] = self.vtSymbol + d[u'方向'] = self.direction + d[u'触发价格'] = self.stopPrice + d[u'数量'] = self.totalVolume + d[u'开平'] = self.offset + self.putParamEvent(d) + + +######################################################################## +class StopWidget(AlgoWidget): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, parent=None): + """Constructor""" + super(StopWidget, self).__init__(algoEngine, parent) + + self.templateName = StopAlgo.templateName + + #---------------------------------------------------------------------- + def initAlgoLayout(self): + """""" + self.lineSymbol = QtWidgets.QLineEdit() + + self.comboDirection = QtWidgets.QComboBox() + self.comboDirection.addItem(DIRECTION_LONG) + self.comboDirection.addItem(DIRECTION_SHORT) + self.comboDirection.setCurrentIndex(0) + + self.spinPrice = QtWidgets.QDoubleSpinBox() + self.spinPrice.setMinimum(0) + self.spinPrice.setMaximum(1000000000) + self.spinPrice.setDecimals(8) + + self.spinVolume = QtWidgets.QDoubleSpinBox() + self.spinVolume.setMinimum(0) + self.spinVolume.setMaximum(1000000000) + self.spinVolume.setDecimals(6) + + self.comboOffset = QtWidgets.QComboBox() + self.comboOffset.addItems(['', OFFSET_OPEN, OFFSET_CLOSE]) + self.comboOffset.setCurrentIndex(0) + + self.spinPriceAdd = QtWidgets.QDoubleSpinBox() + self.spinPriceAdd.setMinimum(0) + self.spinPriceAdd.setMaximum(1000000000) + self.spinPriceAdd.setDecimals(8) + + buttonStart = QtWidgets.QPushButton(u'启动') + buttonStart.clicked.connect(self.addAlgo) + buttonStart.setMinimumHeight(100) + + Label = QtWidgets.QLabel + + grid = QtWidgets.QGridLayout() + grid.addWidget(Label(u'代码'), 0, 0) + grid.addWidget(self.lineSymbol, 0, 1) + grid.addWidget(Label(u'方向'), 1, 0) + grid.addWidget(self.comboDirection, 1, 1) + grid.addWidget(Label(u'价格'), 2, 0) + grid.addWidget(self.spinPrice, 2, 1) + grid.addWidget(Label(u'数量'), 3, 0) + grid.addWidget(self.spinVolume, 3, 1) + grid.addWidget(Label(u'开平'), 4, 0) + grid.addWidget(self.comboOffset, 4, 1) + grid.addWidget(Label(u'超价'), 5, 0) + grid.addWidget(self.spinPriceAdd, 5, 1) + + return grid + + #---------------------------------------------------------------------- + def getAlgoSetting(self): + """""" + setting = OrderedDict() + setting['templateName'] = StopAlgo.templateName + setting['vtSymbol'] = str(self.lineSymbol.text()) + setting['direction'] = unicode(self.comboDirection.currentText()) + setting['stopPrice'] = float(self.spinPrice.value()) + setting['totalVolume'] = float(self.spinVolume.value()) + setting['offset'] = unicode(self.comboOffset.currentText()) + setting['priceAdd'] = float(self.spinPriceAdd.value()) + + return setting + + \ No newline at end of file diff --git a/vnpy/trader/app/algoTrading/twapAlgo.py b/vnpy/trader/app/algoTrading/twapAlgo.py index d38d29af..d3727136 100644 --- a/vnpy/trader/app/algoTrading/twapAlgo.py +++ b/vnpy/trader/app/algoTrading/twapAlgo.py @@ -152,6 +152,7 @@ class TwapAlgo(AlgoTemplate): d[u'单笔委托'] = self.orderSize d[u'本轮读秒'] = self.timerCount d[u'累计读秒'] = self.timerTotal + d['active'] = self.active self.putVarEvent(d) #---------------------------------------------------------------------- diff --git a/vnpy/trader/app/algoTrading/uiAlgoManager.py b/vnpy/trader/app/algoTrading/uiAlgoManager.py index 8d8f8ac4..895e3fa0 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoManager.py +++ b/vnpy/trader/app/algoTrading/uiAlgoManager.py @@ -9,8 +9,10 @@ from vnpy.trader.uiQt import QtCore, QtWidgets from .algoEngine import (EVENT_ALGO_LOG, EVENT_ALGO_PARAM, EVENT_ALGO_VAR, EVENT_ALGO_SETTING) + from .twapAlgo import TwapWidget from .dmaAlgo import DmaWidget +from .stopAlgo import StopWidget ######################################################################## @@ -388,6 +390,7 @@ class AlgoManager(QtWidgets.QWidget): self.initUi() self.addAlgoWidget(TwapWidget) self.addAlgoWidget(DmaWidget) + self.addAlgoWidget(StopWidget) self.algoEngine.loadAlgoSetting() # 界面初始化后,再加载算法配置 From 17f777b84c4982b86d40adb3acbeb2e73ff36aa6 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Wed, 13 Jun 2018 11:55:33 +0800 Subject: [PATCH 050/135] =?UTF-8?q?[Fix]=E4=BF=AE=E5=A4=8D=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E4=BA=A4=E6=98=93=E5=BC=95=E6=93=8E=E7=9A=84=E5=A7=94?= =?UTF-8?q?=E6=89=98=E5=8F=B7=E5=92=8C=E7=AE=97=E6=B3=95=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E6=98=A0=E5=B0=84=E7=BC=BA=E5=A4=B1=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/algoEngine.py | 1 + 1 file changed, 1 insertion(+) diff --git a/vnpy/trader/app/algoTrading/algoEngine.py b/vnpy/trader/app/algoTrading/algoEngine.py index f83c475b..f1da8037 100644 --- a/vnpy/trader/app/algoTrading/algoEngine.py +++ b/vnpy/trader/app/algoTrading/algoEngine.py @@ -179,6 +179,7 @@ class AlgoEngine(object): req.offset = OFFSET_OPEN vtOrderID = self.mainEngine.sendOrder(req, contract.gatewayName) + self.orderAlgoDict[vtOrderID] = algo return vtOrderID From c14ae4d5e08de73d8124a2e35fea5293e002b4af Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Thu, 14 Jun 2018 13:12:05 +0800 Subject: [PATCH 051/135] =?UTF-8?q?[Fix]=E4=BF=AE=E6=94=B9=E5=A7=94?= =?UTF-8?q?=E6=89=98=E9=87=8F=E8=B4=9F=E5=8F=B7=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py index 217d93e3..dd9178bd 100644 --- a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py +++ b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py @@ -388,7 +388,7 @@ class GatewayApi(BitfinexApi): if amount > 0: bid[price ] = amount else: - ask[price] = amount + ask[price] = -amount # Bitfinex的深度数据更新是逐档推送变动情况,而非5档一起推 # 因此会出现没有Bid或者Ask的情况,这里使用try...catch过滤 From 1ae001cfa7fbfa1ba7f72a021d4acb09f61ce8e0 Mon Sep 17 00:00:00 2001 From: cooooo Date: Thu, 14 Jun 2018 16:57:09 +0800 Subject: [PATCH 052/135] =?UTF-8?q?[Mod]=E6=9B=B4=E6=96=B0WebTrader?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/WebTrader/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/WebTrader/README.md b/examples/WebTrader/README.md index 0563187c..25bd5cbf 100644 --- a/examples/WebTrader/README.md +++ b/examples/WebTrader/README.md @@ -6,17 +6,17 @@ 1. 修改CTP_connect.json中的账号和服务器地址 2. 修改WEB_setting.json中的网页登录用户名和密码 -3. 打开cmd,运行python server.py -4. 打开另一个cmd,运行python run.py -5. 用浏览器(推荐Chrome)访问http://localhost:5000/ -6. 输入2中的用户名和密码登录后,点击左下角的“连接CTP” -7. 网页前端的使用方法和常规版本的VnTrader基本一致 -8. 如需运行CTA策略,请修改CTA_setting.json中的配置 +3. 打开cmd,运行python run.py +4. 用浏览器(推荐Chrome)访问http://localhost:5000/ +5. 输入2中的用户名和密码登录后,点击左下角的“连接CTP” +6. 网页前端的使用方法和常规版本的VnTrader基本一致 +7. 如需运行CTA策略,请修改CTA_setting.json中的配置 ## 文件功能 -* server.py:基于vnpy.rpc模块实现的交易服务器,包含CTP接口和CTA策略模块 -* run.py:基于Flask实现的Web服务器,内部通过vnpy.rpc客户端来访问交易服务器 +* tradingServer.py:基于vnpy.rpc模块实现的交易服务器,包含CTP接口和CTA策略模块 +* webServer.py:基于Flask实现的Web服务器,内部通过vnpy.rpc客户端来访问交易服务器 +* run.py: 无人值守服务 ## 架构设计 From c3d482bfd724729b58fccec9def3068d24877683 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Thu, 14 Jun 2018 20:03:53 +0800 Subject: [PATCH 053/135] =?UTF-8?q?[Add]=E5=88=9D=E6=AD=A5=E5=AE=8C?= =?UTF-8?q?=E6=88=90vnbitmex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/bitmex/__init__.py | 1 + vnpy/api/bitmex/vnbitmex.py | 248 ++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 vnpy/api/bitmex/__init__.py create mode 100644 vnpy/api/bitmex/vnbitmex.py diff --git a/vnpy/api/bitmex/__init__.py b/vnpy/api/bitmex/__init__.py new file mode 100644 index 00000000..857efbac --- /dev/null +++ b/vnpy/api/bitmex/__init__.py @@ -0,0 +1 @@ +from .vnbitmex import BitmexRestApi, BitmexWebsocketApi \ No newline at end of file diff --git a/vnpy/api/bitmex/vnbitmex.py b/vnpy/api/bitmex/vnbitmex.py new file mode 100644 index 00000000..a7b9e6b6 --- /dev/null +++ b/vnpy/api/bitmex/vnbitmex.py @@ -0,0 +1,248 @@ +# encoding: UTF-8 + +import hashlib +import hmac +import json +import ssl + +from queue import Queue, Empty +from multiprocessing.dummy import Pool +from time import time +from urlparse import urlparse +from copy import copy +from urllib import urlencode +from threading import Thread + +import requests +import websocket + + +REST_HOST = 'https://www.bitmex.com/api/v1' +WEBSOCKET_HOST = 'wss://www.bitmex.com/realtime' + + + + +######################################################################## +class BitmexRestApi(object): + """REST API""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.apiKey = '' + self.apiSecret = '' + + self.active = False + self.reqid = 0 + self.queue = Queue() + self.pool = None + self.sessionDict = {} # 会话对象字典 + + self.header = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json' + } + + #---------------------------------------------------------------------- + def init(self, apiKey, apiSecret): + """初始化""" + self.apiKey = apiKey + self.apiSecret = apiSecret + + #---------------------------------------------------------------------- + def start(self, n=10): + """启动""" + if self.active: + return + + self.active = True + self.pool = Pool(n) + self.pool.map_async(self.run, range(n)) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.active = False + + if self.pool: + self.pool.close() + self.pool.join() + + #---------------------------------------------------------------------- + def addReq(self, method, path, callback, params=None, postdict=None): + """添加请求""" + self.reqid += 1 + req = (method, path, callback, params, postdict, self.reqid) + self.queue.put(req) + return self.reqid + + #---------------------------------------------------------------------- + def processReq(self, req, i): + """处理请求""" + method, path, callback, params, postdict, reqid = req + url = REST_HOST + path + expires = int(time() + 5) + + header = copy(self.header) + header['api-expires'] = str(expires) + header['api-key'] = self.apiKey + header['api-signature'] = self.generateSignature(method, path, expires, params, postdict) + + # 使用长连接的session,比短连接的耗时缩短80% + session = self.sessionDict[i] + resp = session.request(method, url, headers=header, params=params, json=postdict) + code = resp.status_code + d = resp.json() + + if code == 200: + callback(d, reqid) + else: + self.onError(code, d) + + #---------------------------------------------------------------------- + def run(self, i): + """连续运行""" + self.sessionDict[i] = requests.Session() + + while self.active: + try: + req = self.queue.get(timeout=1) + self.processReq(req, i) + except Empty: + pass + + #---------------------------------------------------------------------- + def generateSignature(self, method, path, expires, params=None, postdict=None): + """生成签名""" + # 对params在HTTP报文路径中,以请求字段方式序列化 + if params: + query = urlencode(sorted(params.items())) + path = path + '?' + query + + msg = method + '/api/v1' + path + str(expires) + + # 对postdict在HTTP报文体中,是作为表单内容以json序列化 + # 因此这里需要采用同样方式,不能直接用str()来转换 + if postdict: + buf = json.dumps(postdict) + msg += buf + + signature = hmac.new(self.apiSecret, msg, + digestmod=hashlib.sha256).hexdigest() + return signature + + #---------------------------------------------------------------------- + def onError(self, code, error): + """错误回调""" + print 'on error' + print code, error + + #---------------------------------------------------------------------- + def onData(self, data, reqid): + """通用回调""" + print 'on data' + print data, reqid + + +######################################################################## +class BitmexWebsocketApi(object): + """Websocket API""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.ws = None + self.thread = None + self.active = False + + #---------------------------------------------------------------------- + def start(self): + """启动""" + self.ws = websocket.create_connection(WEBSOCKET_HOST, + sslopt={'cert_reqs': ssl.CERT_NONE}) + + self.active = True + self.thread = Thread(target=self.run) + self.thread.start() + + self.onConnect() + + #---------------------------------------------------------------------- + def reconnect(self): + """重连""" + self.ws = websocket.create_connection(WEBSOCKET_V2_URL, + sslopt={'cert_reqs': ssl.CERT_NONE}) + + self.onConnect() + + #---------------------------------------------------------------------- + def run(self): + """运行""" + while self.active: + try: + stream = self.ws.recv() + data = json.loads(stream) + self.onData(data) + except: + msg = traceback.format_exc() + self.onError(msg) + self.reconnect() + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.active = False + + if self.thread: + self.thread.join() + + #---------------------------------------------------------------------- + def onConnect(self): + """连接回调""" + print 'connected' + + #---------------------------------------------------------------------- + def onData(self, data): + """数据回调""" + print data + + #---------------------------------------------------------------------- + def onError(self, msg): + """错误回调""" + print msg + + #---------------------------------------------------------------------- + def sendReq(self, req): + """发出请求""" + self.ws.send(json.dumps(req)) + + + + + +if __name__ == '__main__': + API_KEY = '08zsKDmvaNHAqey0eMMHEG4t' + API_SECRET = 'lIzHW2wpG6Pk7hBNUXc6onwNfxFYKOPQZC4EItf6MeHTa9yC' + + ## REST测试 + #rest = BitmexRestApi() + #rest.init(API_KEY, API_SECRET) + #rest.start(3) + + #data = { + #'token': 'test' + #} + #rest.addReq('POST', '/confirmEmail', rest.onData, postdict=data) + #rest.addReq('GET', '/instrument', rest.onData) + + # WEBSOCKET测试 + ws = BitmexWebsocketApi() + ws.start() + + req = {"op": "subscribe", "args": ['quote']} + ws.sendReq(req) + + raw_input() + + \ No newline at end of file From 1befa5c16b3a8cb3f929f110e94255a595549053 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 16 Jun 2018 11:30:11 +0800 Subject: [PATCH 054/135] [Fix]Close #915 --- vnpy/api/okex/vnokex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vnpy/api/okex/vnokex.py b/vnpy/api/okex/vnokex.py index 9a21ef4b..555df99f 100644 --- a/vnpy/api/okex/vnokex.py +++ b/vnpy/api/okex/vnokex.py @@ -171,13 +171,13 @@ class OkexApi(object): def onMessage(self, data): """信息推送""" print('onMessage') - print(evt) + print(data) #---------------------------------------------------------------------- def onError(self, data): """错误推送""" print('onError') - print(evt) + print(data) #---------------------------------------------------------------------- def onClose(self): From fb91279b62bd15d641b378bebdd9000ed81dac43 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 16 Jun 2018 11:30:59 +0800 Subject: [PATCH 055/135] =?UTF-8?q?[Mod]vnbitmex=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=95=B0=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/bitmex/vnbitmex.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vnpy/api/bitmex/vnbitmex.py b/vnpy/api/bitmex/vnbitmex.py index a7b9e6b6..6791e7fb 100644 --- a/vnpy/api/bitmex/vnbitmex.py +++ b/vnpy/api/bitmex/vnbitmex.py @@ -51,7 +51,7 @@ class BitmexRestApi(object): self.apiSecret = apiSecret #---------------------------------------------------------------------- - def start(self, n=10): + def start(self, n=3): """启动""" if self.active: return @@ -222,8 +222,8 @@ class BitmexWebsocketApi(object): if __name__ == '__main__': - API_KEY = '08zsKDmvaNHAqey0eMMHEG4t' - API_SECRET = 'lIzHW2wpG6Pk7hBNUXc6onwNfxFYKOPQZC4EItf6MeHTa9yC' + API_KEY = '' + API_SECRET = '' ## REST测试 #rest = BitmexRestApi() From 87ad5d40581312f905244be38ae74a8a402aa2b3 Mon Sep 17 00:00:00 2001 From: cclauss Date: Sat, 16 Jun 2018 08:28:08 +0200 Subject: [PATCH 056/135] Disable allow_failures on Python 3 This PR should make the flake8 tests pass on both Python 2 and Python 3. --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c4cb042..e5b57753 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,6 @@ cache: pip python: - 2.7 - 3.6 -matrix: - allow_failures: - - python: 3.6 install: - pip install -r requirements.txt - pip install flake8 # pytest # add another testing frameworks later From 8db91fa52383f18a43245d5da84e92988c2fbcac Mon Sep 17 00:00:00 2001 From: cclauss Date: Sat, 16 Jun 2018 08:32:44 +0200 Subject: [PATCH 057/135] Align comment --- vnpy/trader/app/algoTrading/twapAlgo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnpy/trader/app/algoTrading/twapAlgo.py b/vnpy/trader/app/algoTrading/twapAlgo.py index 8093e355..86515d45 100644 --- a/vnpy/trader/app/algoTrading/twapAlgo.py +++ b/vnpy/trader/app/algoTrading/twapAlgo.py @@ -25,7 +25,7 @@ class TwapAlgo(AlgoTemplate): # 参数,强制类型转换,保证从CSV加载的配置正确 self.vtSymbol = str(setting['vtSymbol']) # 合约代码 - self.direction = text_type(setting['direction']) # 买卖 + self.direction = text_type(setting['direction']) # 买卖 self.targetPrice = float(setting['targetPrice']) # 目标价格 self.totalVolume = float(setting['totalVolume']) # 总数量 self.time = int(setting['time']) # 执行时间 From f408cb30cade7bd21af5cda213cab15d9ec88a29 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 16 Jun 2018 22:10:36 +0800 Subject: [PATCH 058/135] =?UTF-8?q?[Add]=E5=88=9D=E6=AD=A5=E5=AE=8C?= =?UTF-8?q?=E6=88=90BitMEX=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/CryptoTrader/BINANCE_connect.json | 5 + examples/CryptoTrader/BITFINEX_connect.json | 5 + examples/CryptoTrader/BITMEX_connect.json | 6 + examples/CryptoTrader/HUOBI_connect.json | 6 + examples/CryptoTrader/OKEX_connect.json | 6 + examples/CryptoTrader/run.py | 4 +- vnpy/api/bitmex/vnbitmex.py | 70 ++- .../bitfinexGateway/bitfinexGateway.py | 6 +- vnpy/trader/gateway/bitmexGateway/__init__.py | 10 + .../gateway/bitmexGateway/bitmexGateway.py | 536 ++++++++++++++++++ vnpy/trader/language/chinese/constant.py | 1 + vnpy/trader/language/english/constant.py | 1 + 12 files changed, 628 insertions(+), 28 deletions(-) create mode 100644 examples/CryptoTrader/BINANCE_connect.json create mode 100644 examples/CryptoTrader/BITFINEX_connect.json create mode 100644 examples/CryptoTrader/BITMEX_connect.json create mode 100644 examples/CryptoTrader/HUOBI_connect.json create mode 100644 examples/CryptoTrader/OKEX_connect.json create mode 100644 vnpy/trader/gateway/bitmexGateway/__init__.py create mode 100644 vnpy/trader/gateway/bitmexGateway/bitmexGateway.py diff --git a/examples/CryptoTrader/BINANCE_connect.json b/examples/CryptoTrader/BINANCE_connect.json new file mode 100644 index 00000000..01d370e4 --- /dev/null +++ b/examples/CryptoTrader/BINANCE_connect.json @@ -0,0 +1,5 @@ +{ + "apiKey": "", + "secretKey": "", + "symbols": ["BTCUSDT", "ETHUSDT", "ETHBTC"] +} \ No newline at end of file diff --git a/examples/CryptoTrader/BITFINEX_connect.json b/examples/CryptoTrader/BITFINEX_connect.json new file mode 100644 index 00000000..ae25ff9c --- /dev/null +++ b/examples/CryptoTrader/BITFINEX_connect.json @@ -0,0 +1,5 @@ +{ + "apiKey": "", + "secretKey": "", + "symbols": ["BTCUSD", "ETHUSD", "ETHBTC"] +} \ No newline at end of file diff --git a/examples/CryptoTrader/BITMEX_connect.json b/examples/CryptoTrader/BITMEX_connect.json new file mode 100644 index 00000000..4aef1612 --- /dev/null +++ b/examples/CryptoTrader/BITMEX_connect.json @@ -0,0 +1,6 @@ +{ + "apiKey": "", + "apiSecret": "", + "sessionCount": 3, + "symbols": ["XBTUSD", "EOSM18", "XRPM18"] +} \ No newline at end of file diff --git a/examples/CryptoTrader/HUOBI_connect.json b/examples/CryptoTrader/HUOBI_connect.json new file mode 100644 index 00000000..8255a46f --- /dev/null +++ b/examples/CryptoTrader/HUOBI_connect.json @@ -0,0 +1,6 @@ +{ + "exchange": "huobi", + "accessKey": "", + "secretKey": "", + "symbols": ["btcusdt","ethusdt","ethbtc"] +} \ No newline at end of file diff --git a/examples/CryptoTrader/OKEX_connect.json b/examples/CryptoTrader/OKEX_connect.json new file mode 100644 index 00000000..5c77f027 --- /dev/null +++ b/examples/CryptoTrader/OKEX_connect.json @@ -0,0 +1,6 @@ +{ + "apiKey": "", + "secretKey": "", + "trace": false, + "symbols": ["eth_btc", "btc_usdt", "eth_usdt"] +} \ No newline at end of file diff --git a/examples/CryptoTrader/run.py b/examples/CryptoTrader/run.py index a09cb3dc..9088f66e 100644 --- a/examples/CryptoTrader/run.py +++ b/examples/CryptoTrader/run.py @@ -21,7 +21,8 @@ from vnpy.trader.uiMainWindow import MainWindow # 加载底层接口 from vnpy.trader.gateway import (huobiGateway, okexGateway, - binanceGateway, bitfinexGateway) + binanceGateway, bitfinexGateway, + bitmexGateway) # 加载上层应用 from vnpy.trader.app import (riskManager, algoTrading) @@ -40,6 +41,7 @@ def main(): me = MainEngine(ee) # 添加交易接口 + me.addGateway(bitmexGateway) me.addGateway(huobiGateway) me.addGateway(okexGateway) me.addGateway(binanceGateway) diff --git a/vnpy/api/bitmex/vnbitmex.py b/vnpy/api/bitmex/vnbitmex.py index 6791e7fb..91e5c303 100644 --- a/vnpy/api/bitmex/vnbitmex.py +++ b/vnpy/api/bitmex/vnbitmex.py @@ -4,6 +4,7 @@ import hashlib import hmac import json import ssl +import traceback from queue import Queue, Empty from multiprocessing.dummy import Pool @@ -84,14 +85,20 @@ class BitmexRestApi(object): url = REST_HOST + path expires = int(time() + 5) + rq = requests.Request(url=url, data=postdict) + p = rq.prepare() + header = copy(self.header) header['api-expires'] = str(expires) header['api-key'] = self.apiKey - header['api-signature'] = self.generateSignature(method, path, expires, params, postdict) + header['api-signature'] = self.generateSignature(method, path, expires, params, body=p.body) # 使用长连接的session,比短连接的耗时缩短80% session = self.sessionDict[i] - resp = session.request(method, url, headers=header, params=params, json=postdict) + resp = session.request(method, url, headers=header, params=params, data=postdict) + + #resp = requests.request(method, url, headers=header, params=params, data=postdict) + code = resp.status_code d = resp.json() @@ -113,21 +120,17 @@ class BitmexRestApi(object): pass #---------------------------------------------------------------------- - def generateSignature(self, method, path, expires, params=None, postdict=None): + def generateSignature(self, method, path, expires, params=None, body=None): """生成签名""" # 对params在HTTP报文路径中,以请求字段方式序列化 if params: query = urlencode(sorted(params.items())) path = path + '?' + query - - msg = method + '/api/v1' + path + str(expires) - # 对postdict在HTTP报文体中,是作为表单内容以json序列化 - # 因此这里需要采用同样方式,不能直接用str()来转换 - if postdict: - buf = json.dumps(postdict) - msg += buf + if body is None: + body = '' + msg = method + '/api/v1' + path + str(expires) + body signature = hmac.new(self.apiSecret, msg, digestmod=hashlib.sha256).hexdigest() return signature @@ -171,7 +174,7 @@ class BitmexWebsocketApi(object): #---------------------------------------------------------------------- def reconnect(self): """重连""" - self.ws = websocket.create_connection(WEBSOCKET_V2_URL, + self.ws = websocket.create_connection(WEBSOCKET_HOST, sslopt={'cert_reqs': ssl.CERT_NONE}) self.onConnect() @@ -205,7 +208,11 @@ class BitmexWebsocketApi(object): #---------------------------------------------------------------------- def onData(self, data): """数据回调""" - print data + print '-' * 30 + l = data.keys() + l.sort() + for k in l: + print k, data[k] #---------------------------------------------------------------------- def onError(self, msg): @@ -226,22 +233,39 @@ if __name__ == '__main__': API_SECRET = '' ## REST测试 - #rest = BitmexRestApi() - #rest.init(API_KEY, API_SECRET) - #rest.start(3) + rest = BitmexRestApi() + rest.init(API_KEY, API_SECRET) + rest.start(3) - #data = { - #'token': 'test' - #} - #rest.addReq('POST', '/confirmEmail', rest.onData, postdict=data) + data = { + 'symbol': 'XBTUSD' + } + rest.addReq('POST', '/position/isolate', rest.onData, postdict=data) #rest.addReq('GET', '/instrument', rest.onData) # WEBSOCKET测试 - ws = BitmexWebsocketApi() - ws.start() + #ws = BitmexWebsocketApi() + #ws.start() - req = {"op": "subscribe", "args": ['quote']} - ws.sendReq(req) + #req = {"op": "subscribe", "args": ['order', 'trade', 'position', 'margin']} + #ws.sendReq(req) + + #expires = int(time()) + #method = 'GET' + #path = '/realtime' + #msg = method + path + str(expires) + #signature = hmac.new(API_SECRET, msg, digestmod=hashlib.sha256).hexdigest() + + #req = { + #'op': 'authKey', + #'args': [API_KEY, expires, signature] + #} + + #ws.sendReq(req) + + #req = {"op": "subscribe", "args": ['order', 'execution', 'position', 'margin']} + #req = {"op": "subscribe", "args": ['instrument']} + #ws.sendReq(req) raw_input() diff --git a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py index dd9178bd..1e622ad4 100644 --- a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py +++ b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py @@ -157,7 +157,7 @@ class GatewayApi(BitfinexApi): self.gateway = gateway # gateway对象 self.gatewayName = gateway.gatewayName # gateway对象名称 - self.orderId = 0 + self.orderId = 1000000 self.date = int(datetime.now().strftime('%y%m%d%H%M%S')) * self.orderId self.apiKey = '' @@ -245,8 +245,6 @@ class GatewayApi(BitfinexApi): #---------------------------------------------------------------------- def sendOrder(self, orderReq): """""" - orderReq.volume = 0.02 - self.orderId += 1 orderId = self.date + self.orderId vtOrderID = '.'.join([self.gatewayName, str(orderId)]) @@ -468,7 +466,7 @@ class GatewayApi(BitfinexApi): pos.exchange = EXCHANGE_BITFINEX pos.vtSymbol = '.'.join([pos.vtSymbol, pos.direction]) pos.direction = DIRECTION_LONG - pos.vtPositionName = '.'.join([pos.symbol, pos.direction]) + pos.vtPositionName = '.'.join([pos.vtSymbol, pos.direction]) pos.position = float(data[2]) if data[-1] is None: diff --git a/vnpy/trader/gateway/bitmexGateway/__init__.py b/vnpy/trader/gateway/bitmexGateway/__init__.py new file mode 100644 index 00000000..4c149c8f --- /dev/null +++ b/vnpy/trader/gateway/bitmexGateway/__init__.py @@ -0,0 +1,10 @@ +# encoding: UTF-8 + +from vnpy.trader import vtConstant +from .bitmexGateway import BitmexGateay + +gatewayClass = BitmexGateay +gatewayName = 'BITMEX' +gatewayDisplayName = 'BITMEX' +gatewayType = vtConstant.GATEWAYTYPE_BTC +gatewayQryEnabled = False \ No newline at end of file diff --git a/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py b/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py new file mode 100644 index 00000000..2fd9b262 --- /dev/null +++ b/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py @@ -0,0 +1,536 @@ +# encoding: UTF-8 + +''' +vnpy.api.bitmex的gateway接入 +''' + +import os +import json +import hashlib +import hmac +import time +import traceback +from datetime import datetime, timedelta +from copy import copy +from math import pow + +from vnpy.api.bitmex import BitmexRestApi, BitmexWebsocketApi +from vnpy.trader.vtGateway import * +from vnpy.trader.vtFunction import getJsonPath, getTempPath + +# 委托状态类型映射 +statusMapReverse = {} +statusMapReverse['New'] = STATUS_NOTTRADED +statusMapReverse['Partially filled'] = STATUS_PARTTRADED +statusMapReverse['Filled'] = STATUS_ALLTRADED +statusMapReverse['Canceled'] = STATUS_CANCELLED +statusMapReverse['Rejected'] = STATUS_REJECTED + +# 方向映射 +directionMap = {} +directionMap[DIRECTION_LONG] = 'Buy' +directionMap[DIRECTION_SHORT] = 'Sell' +directionMapReverse = {v:k for k,v in directionMap.items()} + +# 价格类型映射 +priceTypeMap = {} +priceTypeMap[PRICETYPE_LIMITPRICE] = 'Limit' +priceTypeMap[PRICETYPE_MARKETPRICE] = 'Market' + + + +######################################################################## +class BitmexGateay(VtGateway): + """Bitfinex接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine, gatewayName=''): + """Constructor""" + super(BitmexGateay, self).__init__(eventEngine, gatewayName) + + self.restApi = RestApi(self) + self.wsApi = WebsocketApi(self) + + self.qryEnabled = False # 是否要启动循环查询 + + self.fileName = self.gatewayName + '_connect.json' + self.filePath = getJsonPath(self.fileName, __file__) + + #---------------------------------------------------------------------- + def connect(self): + """连接""" + try: + f = file(self.filePath) + except IOError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'读取连接配置出错,请检查' + self.onLog(log) + return + + # 解析json文件 + setting = json.load(f) + try: + apiKey = str(setting['apiKey']) + apiSecret = str(setting['apiSecret']) + sessionCount = int(setting['sessionCount']) + symbols = setting['symbols'] + except KeyError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'连接配置缺少字段,请检查' + self.onLog(log) + return + + # 创建行情和交易接口对象 + self.restApi.connect(apiKey, apiSecret, sessionCount) + self.wsApi.connect(apiKey, apiSecret, symbols) + + # 初始化并启动查询 + #self.initQuery() + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """发单""" + return self.restApi.sendOrder(orderReq) + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """撤单""" + self.restApi.cancelOrder(cancelOrderReq) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.restApi.close() + self.wsApi.close() + + #---------------------------------------------------------------------- + def initQuery(self): + """初始化连续查询""" + if self.qryEnabled: + # 需要循环的查询函数列表 + self.qryFunctionList = [self.queryAccount] + + self.qryCount = 0 # 查询触发倒计时 + self.qryTrigger = 1 # 查询触发点 + self.qryNextFunction = 0 # 上次运行的查询函数索引 + + self.startQuery() + + #---------------------------------------------------------------------- + def query(self, event): + """注册到事件处理引擎上的查询函数""" + self.qryCount += 1 + + if self.qryCount > self.qryTrigger: + # 清空倒计时 + self.qryCount = 0 + + # 执行查询函数 + function = self.qryFunctionList[self.qryNextFunction] + function() + + # 计算下次查询函数的索引,如果超过了列表长度,则重新设为0 + self.qryNextFunction += 1 + if self.qryNextFunction == len(self.qryFunctionList): + self.qryNextFunction = 0 + + #---------------------------------------------------------------------- + def startQuery(self): + """启动连续查询""" + self.eventEngine.register(EVENT_TIMER, self.query) + + #---------------------------------------------------------------------- + def setQryEnabled(self, qryEnabled): + """设置是否要启动循环查询""" + self.qryEnabled = qryEnabled + + +######################################################################## +class RestApi(BitmexRestApi): + """REST API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(RestApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.orderId = 1000000 + self.date = int(datetime.now().strftime('%y%m%d%H%M%S')) * self.orderId + + #---------------------------------------------------------------------- + def connect(self, apiKey, apiSecret, sessionCount): + """连接服务器""" + self.init(apiKey, apiSecret) + self.start(sessionCount) + + self.writeLog(u'REST API启动成功') + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """""" + self.orderId += 1 + orderId = self.date + self.orderId + vtOrderID = '.'.join([self.gatewayName, str(orderId)]) + + req = { + 'symbol': orderReq.symbol, + 'side': directionMap[orderReq.direction], + 'ordType': priceTypeMap[orderReq.priceType], + 'price': orderReq.price, + 'orderQty': orderReq.volume, + 'clOrdID': str(orderId) + } + self.addReq('POST', '/order', self.onSendOrder, postdict=req) + + return vtOrderID + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """""" + orderID = cancelOrderReq.orderID + if orderID.isdigit(): + req = {'clOrdID': orderID} + else: + req = {'orderID': orderID} + + self.addReq('DELETE', '/order', self.onCancelOrder, params=req) + + #---------------------------------------------------------------------- + def onSendOrder(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onCancelOrder(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onError(self, code, error): + """""" + e = VtErrorData() + e.errorID = code + e.errorID = error + self.gateway.onError(e) + + +######################################################################## +class WebsocketApi(BitmexWebsocketApi): + """""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(WebsocketApi, self).__init__() + + self.gateway = gateway + self.gatewayName = gateway.gatewayName + + self.apiKey = '' + self.apiSecret = '' + + self.callbackDict = { + 'trade': self.onTick, + 'orderBook10': self.onDepth, + 'execution': self.onTrade, + 'order': self.onOrder, + 'position': self.onPosition, + 'margin': self.onAccount, + 'instrument': self.onContract + } + + self.tickDict = {} + self.accountDict = {} + self.orderDict = {} + self.tradeSet = set() + + #---------------------------------------------------------------------- + def connect(self, apiKey, apiSecret, symbols): + """""" + self.apiKey = apiKey + self.apiSecret = apiSecret + + for symbol in symbols: + tick = VtTickData() + tick.gatewayName = self.gatewayName + tick.symbol = symbol + tick.exchange = EXCHANGE_BITMEX + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + self.tickDict[symbol] = tick + + self.start() + + #---------------------------------------------------------------------- + def onConnect(self): + """连接回调""" + self.writeLog(u'Websocket API连接成功') + self.authenticate() + + #---------------------------------------------------------------------- + def onData(self, data): + """数据回调""" + if 'request' in data: + req = data['request'] + success = data['success'] + + if success: + if req['op'] == 'authKey': + self.writeLog(u'Websocket API验证授权成功') + self.subscribe() + + elif 'table' in data: + name = data['table'] + callback = self.callbackDict[name] + + if isinstance(data['data'], list): + for d in data['data']: + callback(d) + else: + callback(data['data']) + + #if data['action'] == 'update' and data['table'] != 'instrument': + #callback(data['data']) + #elif data['action'] == 'partial': + #for d in data['data']: + #callback(d) + + + #---------------------------------------------------------------------- + def onError(self, msg): + """错误回调""" + self.writeLog(msg) + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def authenticate(self): + """""" + expires = int(time.time()) + method = 'GET' + path = '/realtime' + msg = method + path + str(expires) + signature = hmac.new(self.apiSecret, msg, digestmod=hashlib.sha256).hexdigest() + + req = { + 'op': 'authKey', + 'args': [self.apiKey, expires, signature] + } + self.sendReq(req) + + #---------------------------------------------------------------------- + def subscribe(self): + """""" + req = { + 'op': 'subscribe', + 'args': ['instrument', 'trade', 'orderBook10', 'execution', 'order', 'position', 'margin'] + } + self.sendReq(req) + + #---------------------------------------------------------------------- + def onTick(self, d): + """""" + symbol = d['symbol'] + + tick = self.tickDict.get(symbol, None) + if not tick: + return + + tick.lastPrice = d['price'] + + date, time = str(d['timestamp']).split('T') + tick.date = date.replace('-', '') + tick.time = time.replace('Z', '') + + self.gateway.onTick(tick) + + #---------------------------------------------------------------------- + def onDepth(self, d): + """""" + symbol = d['symbol'] + tick = self.tickDict.get(symbol, None) + if not tick: + return + + for n, buf in enumerate(d['bids'][:5]): + price, volume = buf + tick.__setattr__('bidPrice%s' %(n+1), price) + tick.__setattr__('bidVolume%s' %(n+1), volume) + + for n, buf in enumerate(d['asks'][:5]): + price, volume = buf + tick.__setattr__('askPrice%s' %(n+1), price) + tick.__setattr__('askVolume%s' %(n+1), volume) + + date, time = str(d['timestamp']).split('T') + tick.date = date.replace('-', '') + tick.time = time.replace('Z', '') + + self.gateway.onTick(tick) + + #---------------------------------------------------------------------- + def onTrade(self, d): + """""" + if not d['lastQty']: + return + + tradeID = d['execID'] + if tradeID in self.tradeSet: + return + self.tradeSet.add(tradeID) + + trade = VtTradeData() + trade.gatewayName = self.gatewayName + + trade.symbol = d['symbol'] + trade.exchange = EXCHANGE_BITMEX + trade.vtSymbol = '.'.join([trade.symbol, trade.exchange]) + if d['clOrdID']: + orderID = d['clOrdID'] + else: + orderID = d['orderID'] + trade.orderID = orderID + trade.vtOrderID = '.'.join([trade.gatewayName, trade.orderID]) + + + trade.tradeID = tradeID + trade.vtTradeID = '.'.join([trade.gatewayName, trade.tradeID]) + + trade.direction = directionMapReverse[d['side']] + trade.price = d['lastPx'] + trade.volume = d['lastQty'] + trade.tradeTime = d['timestamp'][0:10].replace('-', '') + + self.gateway.onTrade(trade) + + #---------------------------------------------------------------------- + def onOrder(self, d): + """""" + if 'ordStatus' not in d: + return + + sysID = d['orderID'] + if sysID in self.orderDict: + order = self.orderDict[sysID] + else: + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.symbol = d['symbol'] + order.exchange = EXCHANGE_BITMEX + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + if d['clOrdID']: + orderID = d['clOrdID'] + else: + orderID = sysID + order.orderID = orderID + order.vtOrderID = '.'.join([self.gatewayName, order.orderID]) + + order.direction = directionMapReverse[d['side']] + + if d['price']: + order.price = d['price'] + + order.totalVolume = d['orderQty'] + order.orderTime = d['timestamp'][0:10].replace('-', '') + + self.orderDict[sysID] = order + + order.tradedVolume = d.get('cumQty', order.tradedVolume) + order.status = statusMapReverse.get(d['ordStatus'], STATUS_UNKNOWN) + + self.gateway.onOrder(order) + + #---------------------------------------------------------------------- + def onPosition(self, d): + """""" + pos = VtPositionData() + pos.gatewayName = self.gatewayName + + pos.symbol = d['symbol'] + pos.exchange = EXCHANGE_BITMEX + pos.vtSymbol = '.'.join([pos.symbol, pos.exchange]) + + pos.direction = DIRECTION_NET + pos.vtPositionName = '.'.join([pos.vtSymbol, pos.direction]) + pos.position = d['currentQty'] + pos.frozen = 0 # 期货没有冻结概念,会直接反向开仓 + + self.gateway.onPosition(pos) + + #---------------------------------------------------------------------- + def onAccount(self, d): + """""" + accoundID = str(d['account']) + + if accoundID in self.accountDict: + account = self.accountDict[accoundID] + else: + account = VtAccountData() + account.gatewayName = self.gatewayName + + account.accountID = accoundID + account.vtAccountID = '.'.join([account.gatewayName, account.accountID]) + + self.accountDict[accoundID] = account + + account.balance = d.get('marginBalance', account.balance) + account.available = d.get('availableMargin', account.available) + account.closeProfit = d.get('realisedPnl', account.closeProfit) + account.positionProfit = d.get('unrealisedPnl', account.positionProfit) + + self.gateway.onAccount(account) + + #---------------------------------------------------------------------- + def onContract(self, d): + """""" + if 'tickSize' not in d: + return + + contract = VtContractData() + contract.gatewayName = self.gatewayName + + contract.symbol = d['symbol'] + contract.exchange = EXCHANGE_BITMEX + contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) + contract.name = contract.vtSymbol + contract.productClass = PRODUCT_FUTURES + contract.priceTick = d['tickSize'] + contract.size = d['multiplier'] + + self.gateway.onContract(contract) + + +#---------------------------------------------------------------------- +def printDict(d): + """""" + print '-' * 30 + l = d.keys() + l.sort() + for k in l: + print k, d[k] + \ No newline at end of file diff --git a/vnpy/trader/language/chinese/constant.py b/vnpy/trader/language/chinese/constant.py index 0297f79a..e71970db 100644 --- a/vnpy/trader/language/chinese/constant.py +++ b/vnpy/trader/language/chinese/constant.py @@ -93,6 +93,7 @@ EXCHANGE_ZAIF = "ZAIF" # ZAIF日本比特币交易所 EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK日本比特币交易所 EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所 EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 +EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 diff --git a/vnpy/trader/language/english/constant.py b/vnpy/trader/language/english/constant.py index d62757e7..0676c8f5 100644 --- a/vnpy/trader/language/english/constant.py +++ b/vnpy/trader/language/english/constant.py @@ -89,6 +89,7 @@ EXCHANGE_ZAIF = "ZAIF" # ZAIF日本比特币交易所 EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK日本比特币交易所 EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所 EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 +EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 From a7f5e8307b3d7cf9a4dfad18a4798da3dc027ab1 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 18 Jun 2018 21:20:24 +0800 Subject: [PATCH 059/135] =?UTF-8?q?[Mod]=E5=A4=84=E7=90=86=E9=83=A8?= =?UTF-8?q?=E5=88=86FutuOpenD=E7=9A=84Python3=E5=85=BC=E5=AE=B9=E6=80=A7?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/futuGateway/Futu_connect.json | 2 +- .../trader/gateway/futuGateway/futuGateway.py | 28 +++++-------------- vnpy/trader/uiBasicWidget.py | 2 +- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/vnpy/trader/gateway/futuGateway/Futu_connect.json b/vnpy/trader/gateway/futuGateway/Futu_connect.json index d26a492d..723cefb8 100644 --- a/vnpy/trader/gateway/futuGateway/Futu_connect.json +++ b/vnpy/trader/gateway/futuGateway/Futu_connect.json @@ -3,5 +3,5 @@ "port": 11111, "market": "HK", "password": "123123", - "env": 1 + "env": "REAL" } \ No newline at end of file diff --git a/vnpy/trader/gateway/futuGateway/futuGateway.py b/vnpy/trader/gateway/futuGateway/futuGateway.py index d557abcb..f4a5431a 100644 --- a/vnpy/trader/gateway/futuGateway/futuGateway.py +++ b/vnpy/trader/gateway/futuGateway/futuGateway.py @@ -12,14 +12,9 @@ from datetime import datetime from copy import copy import futuquant as ft -from futuquant import (RET_ERROR, RET_OK, PriceRegularMode, +from futuquant import (RET_ERROR, RET_OK, TrdEnv, StockQuoteHandlerBase, OrderBookHandlerBase, TradeOrderHandlerBase, TradeDealHandlerBase) - -#from futuquant.open_context import (RET_ERROR, RET_OK, PriceRegularMode, - #StockQuoteHandlerBase, OrderBookHandlerBase, - #USTradeOrderHandlerBase, USTradeDealHandlerBase, - #HKTradeOrderHandlerBase, HKTradeDealHandlerBase) from vnpy.trader.vtGateway import * from vnpy.trader.vtConstant import GATEWAYTYPE_INTERNATIONAL @@ -182,15 +177,11 @@ class FutuGateway(VtGateway): # 连接交易接口 if self.market == 'US': self.tradeCtx = ft.OpenUSTradeContext(self.host, self.port) - OrderHandlerBase = USTradeOrderHandlerBase - DealHandlerBase = USTradeDealHandlerBase else: self.tradeCtx = ft.OpenHKTradeContext(self.host, self.port) - OrderHandlerBase = HKTradeOrderHandlerBase - DealHandlerBase = HKTradeDealHandlerBase - + # 继承实现处理器类 - class OrderHandler(OrderHandlerBase): + class OrderHandler(TradeOrderHandlerBase): """委托处理器""" gateway = self # 缓存Gateway对象 @@ -201,7 +192,7 @@ class FutuGateway(VtGateway): self.gateway.processOrder(content) return RET_OK, content - class DealHandler(DealHandlerBase): + class DealHandler(TradeDealHandlerBase): """订单簿处理器""" gateway = self @@ -223,11 +214,6 @@ class FutuGateway(VtGateway): # 启动交易接口 self.tradeCtx.start() - # 订阅委托推送 - self.tradeCtx.subscribe_order_deal_push([], - order_deal_push=True, - envtype=self.env) - self.writeLog(u'交易接口连接成功') #---------------------------------------------------------------------- @@ -306,7 +292,7 @@ class FutuGateway(VtGateway): #---------------------------------------------------------------------- def qryAccount(self): """查询账户资金""" - code, data = self.tradeCtx.accinfo_query(self.env) + code, data = self.tradeCtx.accinfo_query(trd_env=self.env, acc_id=0) if code: self.writeError(code, u'查询账户资金失败:%s' %data) @@ -327,7 +313,7 @@ class FutuGateway(VtGateway): #---------------------------------------------------------------------- def qryPosition(self): """查询持仓""" - code, data = self.tradeCtx.position_list_query(envtype=self.env) + code, data = self.tradeCtx.position_list_query(trd_env=self.env, acc_id=0) if code: self.writeError(code, u'查询持仓失败:%s' %data) @@ -356,7 +342,7 @@ class FutuGateway(VtGateway): #---------------------------------------------------------------------- def qryOrder(self): """查询委托""" - code, data = self.tradeCtx.order_list_query("", envtype=self.env) + code, data = self.tradeCtx.order_list_query("", trd_env=self.env) if code: self.writeError(code, u'查询委托失败:%s' %data) diff --git a/vnpy/trader/uiBasicWidget.py b/vnpy/trader/uiBasicWidget.py index 4cde79f1..138e9ccf 100644 --- a/vnpy/trader/uiBasicWidget.py +++ b/vnpy/trader/uiBasicWidget.py @@ -1144,7 +1144,7 @@ class ContractMonitor(BasicMonitor): """显示所有合约数据""" l = self.mainEngine.getAllContracts() d = {'.'.join([contract.exchange, contract.symbol]):contract for contract in l} - l2 = d.keys() + l2 = list(d.keys()) l2.sort(reverse=True) self.setRowCount(len(l2)) From 2742de7d4b34c40f43adc573ce3173d437a59134 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 19 Jun 2018 13:48:52 +0800 Subject: [PATCH 060/135] =?UTF-8?q?[Fix]=E4=BF=AE=E5=A4=8DTWAP=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F=E5=8C=96typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/app/algoTrading/twapAlgo.py | 2 +- vnpy/trader/gateway/bitmexGateway/BITMEX_connect.json | 6 ++++++ vnpy/trader/gateway/huobiGateway/huobiGateway.py | 7 ++++--- vnpy/trader/language/chinese/constant.py | 1 + vnpy/trader/language/english/constant.py | 1 + 5 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 vnpy/trader/gateway/bitmexGateway/BITMEX_connect.json diff --git a/vnpy/trader/app/algoTrading/twapAlgo.py b/vnpy/trader/app/algoTrading/twapAlgo.py index d3727136..a2c309c5 100644 --- a/vnpy/trader/app/algoTrading/twapAlgo.py +++ b/vnpy/trader/app/algoTrading/twapAlgo.py @@ -106,7 +106,7 @@ class TwapAlgo(AlgoTemplate): # 发出委托 self.buy(self.vtSymbol, price, size) - self.writeLog(u'委托买入%s,数量%,价格%s' %(self.vtSymbol, self.orderSize, price)) + self.writeLog(u'委托买入%s,数量%s,价格%s' %(self.vtSymbol, self.orderSize, price)) # 卖出 if self.direction == DIRECTION_SHORT: # 市场卖1价大于目标价 diff --git a/vnpy/trader/gateway/bitmexGateway/BITMEX_connect.json b/vnpy/trader/gateway/bitmexGateway/BITMEX_connect.json new file mode 100644 index 00000000..4aef1612 --- /dev/null +++ b/vnpy/trader/gateway/bitmexGateway/BITMEX_connect.json @@ -0,0 +1,6 @@ +{ + "apiKey": "", + "apiSecret": "", + "sessionCount": 3, + "symbols": ["XBTUSD", "EOSM18", "XRPM18"] +} \ No newline at end of file diff --git a/vnpy/trader/gateway/huobiGateway/huobiGateway.py b/vnpy/trader/gateway/huobiGateway/huobiGateway.py index 1d383342..7a0c837f 100644 --- a/vnpy/trader/gateway/huobiGateway/huobiGateway.py +++ b/vnpy/trader/gateway/huobiGateway/huobiGateway.py @@ -600,13 +600,14 @@ class HuobiTradeApi(TradeApi): if d['canceled-at']: order.cancelTime = datetime.fromtimestamp(d['canceled-at']/1000).strftime('%H:%M:%S') - newTradedVolume = d['field-amount'] + newTradedVolume = float(d['field-amount']) newStatus = statusMapReverse.get(d['state'], STATUS_UNKNOWN) if newTradedVolume != order.tradedVolume or newStatus != order.status: updated = True - order.tradedVolume = float(newTradedVolume) - order.status = newStatus + + order.tradedVolume = newTradedVolume + order.status = newStatus # 只推送有更新的数据 if updated: diff --git a/vnpy/trader/language/chinese/constant.py b/vnpy/trader/language/chinese/constant.py index e71970db..3bd294bc 100644 --- a/vnpy/trader/language/chinese/constant.py +++ b/vnpy/trader/language/chinese/constant.py @@ -94,6 +94,7 @@ EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK日本比特币交易所 EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所 EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所 +EXCHANGE_FCOIN = 'FCOIN' # FCoin比特币交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 diff --git a/vnpy/trader/language/english/constant.py b/vnpy/trader/language/english/constant.py index 0676c8f5..852cfc56 100644 --- a/vnpy/trader/language/english/constant.py +++ b/vnpy/trader/language/english/constant.py @@ -90,6 +90,7 @@ EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK日本比特币交易所 EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所 EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所 +EXCHANGE_FCOIN = 'FCOIN' # FCoin比特币交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 From eabcee9eed40bdeba1ab2f24a471b9cb8b0d39a3 Mon Sep 17 00:00:00 2001 From: Si Feng Date: Wed, 20 Jun 2018 01:30:55 +0800 Subject: [PATCH 061/135] =?UTF-8?q?=E5=BF=BD=E7=95=A5pyenv=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=88=9B=E5=BB=BA=E7=9A=84.python-version=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0c5cb077..a218a899 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ vn.ctp/build/* vn.lts/build/* .idea .vscode +.python-version .gitignore vn.trader/ctaAlgo/data/* From 6b85efa3d9ca831b47d82812e3ab4b465ea5fd85 Mon Sep 17 00:00:00 2001 From: cclauss Date: Wed, 20 Jun 2018 09:29:28 +0200 Subject: [PATCH 062/135] Modernize recent changes --- vnpy/api/bitmex/vnbitmex.py | 27 ++++++++++--------- vnpy/trader/app/algoTrading/dmaAlgo.py | 15 ++++++----- vnpy/trader/app/algoTrading/stopAlgo.py | 11 ++++---- .../gateway/bitmexGateway/bitmexGateway.py | 5 ++-- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/vnpy/api/bitmex/vnbitmex.py b/vnpy/api/bitmex/vnbitmex.py index 91e5c303..962b0c33 100644 --- a/vnpy/api/bitmex/vnbitmex.py +++ b/vnpy/api/bitmex/vnbitmex.py @@ -1,5 +1,6 @@ # encoding: UTF-8 +from __future__ import print_function import hashlib import hmac import json @@ -14,6 +15,8 @@ from copy import copy from urllib import urlencode from threading import Thread +from six.moves import input + import requests import websocket @@ -138,14 +141,14 @@ class BitmexRestApi(object): #---------------------------------------------------------------------- def onError(self, code, error): """错误回调""" - print 'on error' - print code, error + print('on error') + print(code, error) #---------------------------------------------------------------------- def onData(self, data, reqid): """通用回调""" - print 'on data' - print data, reqid + print('on data') + print(data, reqid) ######################################################################## @@ -203,21 +206,21 @@ class BitmexWebsocketApi(object): #---------------------------------------------------------------------- def onConnect(self): """连接回调""" - print 'connected' + print('connected') #---------------------------------------------------------------------- def onData(self, data): """数据回调""" - print '-' * 30 + print('-' * 30) l = data.keys() l.sort() for k in l: - print k, data[k] + print(k, data[k]) #---------------------------------------------------------------------- def onError(self, msg): """错误回调""" - print msg + print(msg) #---------------------------------------------------------------------- def sendReq(self, req): @@ -265,8 +268,6 @@ if __name__ == '__main__': #req = {"op": "subscribe", "args": ['order', 'execution', 'position', 'margin']} #req = {"op": "subscribe", "args": ['instrument']} - #ws.sendReq(req) - - raw_input() - - \ No newline at end of file + #ws.sendReq(req) + + input() diff --git a/vnpy/trader/app/algoTrading/dmaAlgo.py b/vnpy/trader/app/algoTrading/dmaAlgo.py index fdb42a82..72c3bea4 100644 --- a/vnpy/trader/app/algoTrading/dmaAlgo.py +++ b/vnpy/trader/app/algoTrading/dmaAlgo.py @@ -12,6 +12,7 @@ from vnpy.trader.uiQt import QtWidgets from .algoTemplate import AlgoTemplate from .uiAlgoWidget import AlgoWidget, QtWidgets +from six import text_type STATUS_FINISHED = set([STATUS_ALLTRADED, STATUS_CANCELLED, STATUS_REJECTED]) @@ -30,9 +31,9 @@ class DmaAlgo(AlgoTemplate): # 参数,强制类型转换,保证从CSV加载的配置正确 self.vtSymbol = str(setting['vtSymbol']) # 合约代码 - self.direction = unicode(setting['direction']) # 买卖 - self.offset = unicode(setting['offset']) # 开平 - self.priceType = unicode(setting['priceType']) # 价格类型 + self.direction = text_type(setting['direction']) # 买卖 + self.offset = text_type(setting['offset']) # 开平 + self.priceType = text_type(setting['priceType']) # 价格类型 self.price = float(setting['price']) # 价格 self.totalVolume = float(setting['totalVolume']) # 数量 @@ -178,12 +179,12 @@ class DmaWidget(AlgoWidget): setting = OrderedDict() setting['templateName'] = DmaAlgo.templateName setting['vtSymbol'] = str(self.lineSymbol.text()) - setting['direction'] = unicode(self.comboDirection.currentText()) + setting['direction'] = text_type(self.comboDirection.currentText()) setting['price'] = float(self.spinPrice.value()) setting['totalVolume'] = float(self.spinVolume.value()) - setting['priceType'] = unicode(self.comboPriceType.currentText()) - setting['offset'] = unicode(self.comboOffset.currentText()) + setting['priceType'] = text_type(self.comboPriceType.currentText()) + setting['offset'] = text_type(self.comboOffset.currentText()) return setting - \ No newline at end of file + diff --git a/vnpy/trader/app/algoTrading/stopAlgo.py b/vnpy/trader/app/algoTrading/stopAlgo.py index 3b2e662f..cb54bf4c 100644 --- a/vnpy/trader/app/algoTrading/stopAlgo.py +++ b/vnpy/trader/app/algoTrading/stopAlgo.py @@ -10,6 +10,7 @@ from vnpy.trader.uiQt import QtWidgets from .algoTemplate import AlgoTemplate from .uiAlgoWidget import AlgoWidget, QtWidgets +from six import text_type ######################################################################## @@ -25,10 +26,10 @@ class StopAlgo(AlgoTemplate): # 参数,强制类型转换,保证从CSV加载的配置正确 self.vtSymbol = str(setting['vtSymbol']) # 合约代码 - self.direction = unicode(setting['direction']) # 买卖 + self.direction = text_type(setting['direction']) # 买卖 self.stopPrice = float(setting['stopPrice']) # 触发价格 self.totalVolume = float(setting['totalVolume']) # 数量 - self.offset = unicode(setting['offset']) # 开平 + self.offset = text_type(setting['offset']) # 开平 self.priceAdd = float(setting['priceAdd']) # 下单时的超价 self.vtOrderID = '' # 委托号 @@ -195,12 +196,12 @@ class StopWidget(AlgoWidget): setting = OrderedDict() setting['templateName'] = StopAlgo.templateName setting['vtSymbol'] = str(self.lineSymbol.text()) - setting['direction'] = unicode(self.comboDirection.currentText()) + setting['direction'] = text_type(self.comboDirection.currentText()) setting['stopPrice'] = float(self.spinPrice.value()) setting['totalVolume'] = float(self.spinVolume.value()) - setting['offset'] = unicode(self.comboOffset.currentText()) + setting['offset'] = text_type(self.comboOffset.currentText()) setting['priceAdd'] = float(self.spinPriceAdd.value()) return setting - \ No newline at end of file + diff --git a/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py b/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py index 2fd9b262..b8803692 100644 --- a/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py +++ b/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py @@ -3,6 +3,7 @@ ''' vnpy.api.bitmex的gateway接入 ''' +from __future__ import print_function import os import json @@ -528,9 +529,9 @@ class WebsocketApi(BitmexWebsocketApi): #---------------------------------------------------------------------- def printDict(d): """""" - print '-' * 30 + print('-' * 30) l = d.keys() l.sort() for k in l: - print k, d[k] + print(k, d[k]) \ No newline at end of file From 10e9be7c03d1c47c6d80ad6c933cf58ca5c977cd Mon Sep 17 00:00:00 2001 From: cclauss Date: Wed, 20 Jun 2018 09:33:47 +0200 Subject: [PATCH 063/135] Update stopAlgo.py --- vnpy/trader/app/algoTrading/stopAlgo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vnpy/trader/app/algoTrading/stopAlgo.py b/vnpy/trader/app/algoTrading/stopAlgo.py index cb54bf4c..a08d09d7 100644 --- a/vnpy/trader/app/algoTrading/stopAlgo.py +++ b/vnpy/trader/app/algoTrading/stopAlgo.py @@ -26,10 +26,10 @@ class StopAlgo(AlgoTemplate): # 参数,强制类型转换,保证从CSV加载的配置正确 self.vtSymbol = str(setting['vtSymbol']) # 合约代码 - self.direction = text_type(setting['direction']) # 买卖 + self.direction = text_type(setting['direction']) # 买卖 self.stopPrice = float(setting['stopPrice']) # 触发价格 self.totalVolume = float(setting['totalVolume']) # 数量 - self.offset = text_type(setting['offset']) # 开平 + self.offset = text_type(setting['offset']) # 开平 self.priceAdd = float(setting['priceAdd']) # 下单时的超价 self.vtOrderID = '' # 委托号 From ecb86676c17c2987e10194a2820e2d5c0fd870c9 Mon Sep 17 00:00:00 2001 From: cclauss Date: Wed, 20 Jun 2018 09:34:19 +0200 Subject: [PATCH 064/135] Update dmaAlgo.py --- vnpy/trader/app/algoTrading/dmaAlgo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vnpy/trader/app/algoTrading/dmaAlgo.py b/vnpy/trader/app/algoTrading/dmaAlgo.py index 72c3bea4..2f0736f2 100644 --- a/vnpy/trader/app/algoTrading/dmaAlgo.py +++ b/vnpy/trader/app/algoTrading/dmaAlgo.py @@ -31,9 +31,9 @@ class DmaAlgo(AlgoTemplate): # 参数,强制类型转换,保证从CSV加载的配置正确 self.vtSymbol = str(setting['vtSymbol']) # 合约代码 - self.direction = text_type(setting['direction']) # 买卖 - self.offset = text_type(setting['offset']) # 开平 - self.priceType = text_type(setting['priceType']) # 价格类型 + self.direction = text_type(setting['direction']) # 买卖 + self.offset = text_type(setting['offset']) # 开平 + self.priceType = text_type(setting['priceType']) # 价格类型 self.price = float(setting['price']) # 价格 self.totalVolume = float(setting['totalVolume']) # 数量 From 8db7b42f34f413a15eda7fd2c490ed7683277665 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 26 Jun 2018 23:56:50 +0800 Subject: [PATCH 065/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9E=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=9F=BA=E7=A1=80=E5=87=BD=E6=95=B0=E5=92=8C=E5=B8=B8?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 3 ++- vnpy/trader/app/algoTrading/algoTemplate.py | 5 +++++ vnpy/trader/language/chinese/constant.py | 4 +--- vnpy/trader/language/english/constant.py | 4 +--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index 97bdf730..a22cf5c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,5 @@ future flask-socketio flask-restful flask-cors -gevent-websocket \ No newline at end of file +gevent-websocket +pyjwt \ No newline at end of file diff --git a/vnpy/trader/app/algoTrading/algoTemplate.py b/vnpy/trader/app/algoTrading/algoTemplate.py index 92c2a9f4..ba044ec3 100644 --- a/vnpy/trader/app/algoTrading/algoTemplate.py +++ b/vnpy/trader/app/algoTrading/algoTemplate.py @@ -147,6 +147,11 @@ class AlgoTemplate(object): def getTick(self, vtSymbol): """""" return self.engine.getTick(self, vtSymbol) + + #---------------------------------------------------------------------- + def getContract(self, vtSymbol): + """""" + return self.engine.getContract(self, vtSymbol) #---------------------------------------------------------------------- def roundValue(self, value, change): diff --git a/vnpy/trader/language/chinese/constant.py b/vnpy/trader/language/chinese/constant.py index 3bd294bc..37417925 100644 --- a/vnpy/trader/language/chinese/constant.py +++ b/vnpy/trader/language/chinese/constant.py @@ -86,15 +86,13 @@ EXCHANGE_FXCM = 'FXCM' # FXCM外汇做市商 EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所 EXCHANGE_HUOBI = 'HUOBI' # 火币比特币交易所 EXCHANGE_LBANK = 'LBANK' # LBANK比特币交易所 -EXCHANGE_KORBIT = 'KORBIT' # KORBIT韩国交易所 EXCHANGE_ZB = 'ZB' # 比特币中国比特币交易所 EXCHANGE_OKEX = 'OKEX' # OKEX比特币交易所 -EXCHANGE_ZAIF = "ZAIF" # ZAIF日本比特币交易所 -EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK日本比特币交易所 EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所 EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所 EXCHANGE_FCOIN = 'FCOIN' # FCoin比特币交易所 +EXCHANGE_BIGONE = 'BIGONE' # BigOne比特币交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 diff --git a/vnpy/trader/language/english/constant.py b/vnpy/trader/language/english/constant.py index 852cfc56..fe4d046f 100644 --- a/vnpy/trader/language/english/constant.py +++ b/vnpy/trader/language/english/constant.py @@ -82,15 +82,13 @@ EXCHANGE_FXCM = 'FXCM' # FXCM外汇做市商 EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所 EXCHANGE_HUOBI = 'HUOBI' # 火币比特币交易所 EXCHANGE_LBANK = 'LBANK' # LBANK比特币交易所 -EXCHANGE_KORBIT = 'KORBIT' # KORBIT韩国交易所 EXCHANGE_ZB = 'ZB' # 比特币中国比特币交易所 EXCHANGE_OKEX = 'OKEX' # OKEX比特币交易所 -EXCHANGE_ZAIF = "ZAIF" # ZAIF日本比特币交易所 -EXCHANGE_COINCHECK = "COINCHECK" # COINCHECK日本比特币交易所 EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所 EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所 EXCHANGE_FCOIN = 'FCOIN' # FCoin比特币交易所 +EXCHANGE_BIGONE = 'BIGONE' # BigOne比特币交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 From df89da1e6b5dab5446e859120799f0ae483435a0 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 2 Jul 2018 08:46:50 +0800 Subject: [PATCH 066/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9Eapi/vnfcoin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/fcoin/__init__.py | 1 + vnpy/api/fcoin/vnfcoin.py | 292 +++++++++++++++++++++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 vnpy/api/fcoin/__init__.py create mode 100644 vnpy/api/fcoin/vnfcoin.py diff --git a/vnpy/api/fcoin/__init__.py b/vnpy/api/fcoin/__init__.py new file mode 100644 index 00000000..ac8a89bc --- /dev/null +++ b/vnpy/api/fcoin/__init__.py @@ -0,0 +1 @@ +from .vnfcoin import FcoinRestApi, FcoinWebsocketApi \ No newline at end of file diff --git a/vnpy/api/fcoin/vnfcoin.py b/vnpy/api/fcoin/vnfcoin.py new file mode 100644 index 00000000..84b24651 --- /dev/null +++ b/vnpy/api/fcoin/vnfcoin.py @@ -0,0 +1,292 @@ +# encoding: UTF-8 + +import hashlib +import hmac +import json +import ssl +import traceback +import base64 + +from queue import Queue, Empty +from multiprocessing.dummy import Pool +from time import time +from urlparse import urlparse +from copy import copy +from urllib import urlencode +from threading import Thread + +import requests +import websocket + + +REST_HOST = 'https://api.fcoin.com/v2' +WEBSOCKET_HOST = 'wss://api.fcoin.com/v2/ws' + + + + +######################################################################## +class FcoinRestApi(object): + """REST API""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.apiKey = '' + self.apiSecret = '' + + self.active = False + self.reqid = 0 + self.queue = Queue() + self.pool = None + self.sessionDict = {} # 会话对象字典 + + #---------------------------------------------------------------------- + def init(self, apiKey, apiSecret): + """初始化""" + self.apiKey = str(apiKey) + self.apiSecret = str(apiSecret) + + #---------------------------------------------------------------------- + def start(self, n=10): + """启动""" + if self.active: + return + + self.active = True + self.pool = Pool(n) + self.pool.map_async(self.run, range(n)) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.active = False + + if self.pool: + self.pool.close() + self.pool.join() + + #---------------------------------------------------------------------- + def addReq(self, method, path, callback, params=None, postdict=None): + """添加请求""" + self.reqid += 1 + req = (method, path, callback, params, postdict, self.reqid) + self.queue.put(req) + return self.reqid + + #---------------------------------------------------------------------- + def processReq(self, req, i): + """处理请求""" + method, path, callback, params, postdict, reqid = req + url = REST_HOST + path + timestamp = str(int(time()) * 1000) + + header = {} + header['FC-ACCESS-TIMESTAMP'] = timestamp + header['FC-ACCESS-KEY'] = self.apiKey + header['FC-ACCESS-SIGNATURE'] = self.generateSignature(method, url, timestamp, params, postdict) + + try: + # 使用长连接的session,比短连接的耗时缩短80% + session = self.sessionDict[i] + resp = session.request(method, url, headers=header, params=params, json=postdict) + #resp = requests.request(method, url, headers=header, params=params, data=postdict) + + #if method != 'GET': + #print '-' * 30 + #print 'method', method + #print 'url', url + #print 'header', header + #print 'params', params + #print 'postdict', postdict + + code = resp.status_code + d = resp.json() + + if code == 200: + callback(d, reqid) + else: + self.onError(code, d) + except Exception as e: + self.onError(type(e), e.message) + + #---------------------------------------------------------------------- + def run(self, i): + """连续运行""" + self.sessionDict[i] = requests.Session() + + while self.active: + try: + req = self.queue.get(timeout=1) + self.processReq(req, i) + except Empty: + pass + + #---------------------------------------------------------------------- + def generateSignature(self, method, path, timestamp, params=None, postdict=None): + """生成签名""" + # 对params在HTTP报文路径中,以请求字段方式序列化 + if params: + query = urlencode(sorted(params.items())) + path = path + '?' + query + + if postdict: + post = urlencode(sorted(postdict.items())) + else: + post = '' + + msg = method + path + timestamp + post + msg = base64.b64encode(msg) + + signature = hmac.new(self.apiSecret, msg, digestmod=hashlib.sha1).digest() + signature = base64.b64encode(signature) + + return signature + + #---------------------------------------------------------------------- + def onError(self, code, error): + """错误回调""" + print 'on error' + print code, error + + #---------------------------------------------------------------------- + def onData(self, data, reqid): + """通用回调""" + print 'on data' + print data, reqid + + +######################################################################## +class FcoinWebsocketApi(object): + """Websocket API""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.ws = None + self.thread = None + self.active = False + + #---------------------------------------------------------------------- + def start(self): + """启动""" + self.ws = websocket.create_connection(WEBSOCKET_HOST, + sslopt={'cert_reqs': ssl.CERT_NONE}) + + self.active = True + self.thread = Thread(target=self.run) + self.thread.start() + + self.onConnect() + + #---------------------------------------------------------------------- + def reconnect(self): + """重连""" + self.ws = websocket.create_connection(WEBSOCKET_HOST, + sslopt={'cert_reqs': ssl.CERT_NONE}) + + self.onConnect() + + #---------------------------------------------------------------------- + def run(self): + """运行""" + while self.active: + try: + stream = self.ws.recv() + data = json.loads(stream) + self.onData(data) + except: + msg = traceback.format_exc() + self.onError(msg) + self.reconnect() + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.active = False + + if self.thread: + self.thread.join() + + #---------------------------------------------------------------------- + def onConnect(self): + """连接回调""" + print 'connected' + + #---------------------------------------------------------------------- + def onData(self, data): + """数据回调""" + print '-' * 30 + l = data.keys() + l.sort() + for k in l: + print k, data[k] + + #---------------------------------------------------------------------- + def onError(self, msg): + """错误回调""" + print msg + + #---------------------------------------------------------------------- + def sendReq(self, req): + """发出请求""" + self.ws.send(json.dumps(req)) + + + + + +if __name__ == '__main__': + from datetime import datetime + from time import sleep + + API_KEY = '88893f839fbd49f4b5fcb03e7c15c015' + API_SECRET = 'ef383295cf4e4c128e6d18d7e9564b12' + + # REST测试 + rest = FcoinRestApi() + rest.init(API_KEY, API_SECRET) + rest.start(3) + + #rest.addReq('GET', '/accounts/balance', rest.onData) + + # 查委托 + #states = ['submitted', 'partial_filled', 'partial_canceled', + #'filled', 'canceled', 'pending_cancel'] + #req = { + #'symbol': 'ethusdt', + #'start': datetime.now().strftime('%Y%m%d'), + #'states': 'submitted', + #'limit': 500 + #} + + #for i in range(10): + #rest.addReq('GET', '/orders', rest.onData, params=req) + #sleep(2) + + req = { + 'symbol': 'ethusdt', + 'side': 'buy', + 'type': 'limit', + 'price': 300, + 'amount': 0.01 + } + rest.addReq('POST', '/orders', rest.onData, postdict=req) + #sleep(1) + #rest.addReq('POST', '/orders', rest.onData, params=req) + + ## WS测试 + #ws = FcoinWebsocketApi() + #ws.start() + + #req = { + #'cmd': 'sub', + #'args': ['depth.L20.btcusdt'], + #'id': 1 + #} + + #ws.sendReq(req) + + raw_input() + + \ No newline at end of file From 9ac6dc46428b021cd246ce685a29a542d1267abd Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 2 Jul 2018 08:47:06 +0800 Subject: [PATCH 067/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9Eapi/vnbigone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/bigone/__init__.py | 1 + vnpy/api/bigone/vnbigone.py | 155 ++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 vnpy/api/bigone/__init__.py create mode 100644 vnpy/api/bigone/vnbigone.py diff --git a/vnpy/api/bigone/__init__.py b/vnpy/api/bigone/__init__.py new file mode 100644 index 00000000..7c63904a --- /dev/null +++ b/vnpy/api/bigone/__init__.py @@ -0,0 +1 @@ +from .vnbigone import BigoneRestApi \ No newline at end of file diff --git a/vnpy/api/bigone/vnbigone.py b/vnpy/api/bigone/vnbigone.py new file mode 100644 index 00000000..425868ce --- /dev/null +++ b/vnpy/api/bigone/vnbigone.py @@ -0,0 +1,155 @@ +# encoding: UTF-8 + +import hashlib +import hmac +import json +import ssl +import traceback +import base64 + +from queue import Queue, Empty +from multiprocessing.dummy import Pool +from time import time +from urlparse import urlparse +from copy import copy +from urllib import urlencode +from threading import Thread + +import requests +from jwt import PyJWS + + +REST_HOST = 'https://big.one/api/v2/' + + + + +######################################################################## +class BigoneRestApi(object): + """REST API""" + jws = PyJWS() + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.apiKey = '' + self.apiSecret = '' + + self.active = False + self.reqid = 0 + self.queue = Queue() + self.pool = None + self.sessionDict = {} # 会话对象字典 + + #---------------------------------------------------------------------- + def init(self, apiKey, apiSecret): + """初始化""" + self.apiKey = str(apiKey) + self.apiSecret = str(apiSecret) + + #---------------------------------------------------------------------- + def start(self, n=10): + """启动""" + if self.active: + return + + self.active = True + self.pool = Pool(n) + self.pool.map_async(self.run, range(n)) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.active = False + + if self.pool: + self.pool.close() + self.pool.join() + + #---------------------------------------------------------------------- + def addReq(self, method, path, callback, params=None, postdict=None): + """添加请求""" + self.reqid += 1 + req = (method, path, callback, params, postdict, self.reqid) + self.queue.put(req) + return self.reqid + + #---------------------------------------------------------------------- + def processReq(self, req, i): + """处理请求""" + method, path, callback, params, postdict, reqid = req + url = REST_HOST + path + + header = {} + header['Authorization'] = 'Bearer ' + self.generateSignature() + + try: + # 使用长连接的session,比短连接的耗时缩短20% + session = self.sessionDict[i] + resp = session.request(method, url, headers=header, params=params, json=postdict) + #resp = requests.request(method, url, headers=header, params=params, data=postdict) + + code = resp.status_code + d = resp.json() + + if code == 200: + callback(d, reqid) + else: + self.onError(code, d) + except Exception as e: + self.onError(type(e), e.message) + + #---------------------------------------------------------------------- + def run(self, i): + """连续运行""" + self.sessionDict[i] = requests.Session() + + while self.active: + try: + req = self.queue.get(timeout=1) + self.processReq(req, i) + except Empty: + pass + + #---------------------------------------------------------------------- + def generateSignature(self): + """生成签名""" + payload = '{"type":"OpenAPI","sub":"%s","nonce":%s}' %(self.apiKey, time()*1000000000) + signature = self.jws.encode(payload, self.apiSecret) + return signature + + #---------------------------------------------------------------------- + def onError(self, code, error): + """错误回调""" + print 'on error' + print code, error + + #---------------------------------------------------------------------- + def onData(self, data, reqid): + """通用回调""" + print 'on data' + print data, reqid + + + + +if __name__ == '__main__': + from datetime import datetime + from time import sleep + + API_KEY = 'c9c61d5e-6a4b-42c5-9d9d-e0656feb2c94' + API_SECRET = '806E0FAEFCD2FF8CBD325A55D77B3E018BB7A9B94419EA4F5B2B3F71F5B188CB' + + # REST测试 + rest = BigoneRestApi() + rest.init(API_KEY, API_SECRET) + rest.start(1) + + #rest.addReq('GET', '/markets/EOS-BTC/depth', rest.onData) + + rest.addReq('GET', '/viewer/orders', rest.onData) + + + raw_input() + + \ No newline at end of file From e968c3e5835abec585ed323e539eeebb396b9361 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 2 Jul 2018 08:47:34 +0800 Subject: [PATCH 068/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9EbigoneGateway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/gateway/bigoneGateway/__init__.py | 10 + .../gateway/bigoneGateway/bigoneGateway.py | 514 ++++++++++++++++++ 2 files changed, 524 insertions(+) create mode 100644 vnpy/trader/gateway/bigoneGateway/__init__.py create mode 100644 vnpy/trader/gateway/bigoneGateway/bigoneGateway.py diff --git a/vnpy/trader/gateway/bigoneGateway/__init__.py b/vnpy/trader/gateway/bigoneGateway/__init__.py new file mode 100644 index 00000000..4796cae6 --- /dev/null +++ b/vnpy/trader/gateway/bigoneGateway/__init__.py @@ -0,0 +1,10 @@ +# encoding: UTF-8 + +from vnpy.trader import vtConstant +from .bigoneGateway import BigoneGateway + +gatewayClass = BigoneGateway +gatewayName = 'BIGONE' +gatewayDisplayName = 'BIGONE' +gatewayType = vtConstant.GATEWAYTYPE_BTC +gatewayQryEnabled = True \ No newline at end of file diff --git a/vnpy/trader/gateway/bigoneGateway/bigoneGateway.py b/vnpy/trader/gateway/bigoneGateway/bigoneGateway.py new file mode 100644 index 00000000..c3368309 --- /dev/null +++ b/vnpy/trader/gateway/bigoneGateway/bigoneGateway.py @@ -0,0 +1,514 @@ +# encoding: UTF-8 + +''' +vnpy.api.bigone的gateway接入 +''' + +import os +import json +import time +import traceback +from datetime import datetime, timedelta +from copy import copy +from math import pow + +from vnpy.api.bigone import BigoneRestApi +from vnpy.trader.vtGateway import * +from vnpy.trader.vtFunction import getJsonPath, getTempPath + + + +# 委托状态类型映射 +statusMapReverse = {} +statusMapReverse['PENDING'] = STATUS_NOTTRADED +statusMapReverse['FILLED'] = STATUS_ALLTRADED +statusMapReverse['CANCELED'] = STATUS_CANCELLED + +# 方向映射 +directionMap = {} +directionMap[DIRECTION_LONG] = 'BID' +directionMap[DIRECTION_SHORT] = 'ASK' +directionMapReverse = {v:k for k,v in directionMap.items()} + + +######################################################################## +class BigoneGateway(VtGateway): + """Bigone接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine, gatewayName=''): + """Constructor""" + super(BigoneGateway, self).__init__(eventEngine, gatewayName) + + self.restApi = RestApi(self) + + self.qryEnabled = False # 是否要启动循环查询 + + self.fileName = self.gatewayName + '_connect.json' + self.filePath = getJsonPath(self.fileName, __file__) + + #---------------------------------------------------------------------- + def connect(self): + """连接""" + try: + f = file(self.filePath) + except IOError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'读取连接配置出错,请检查' + self.onLog(log) + return + + # 解析json文件 + setting = json.load(f) + try: + apiKey = str(setting['apiKey']) + apiSecret = str(setting['apiSecret']) + symbols = setting['symbols'] + except KeyError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'连接配置缺少字段,请检查' + self.onLog(log) + return + + # 创建行情和交易接口对象 + self.restApi.connect(apiKey, apiSecret, symbols) + + # 初始化并启动查询 + self.initQuery() + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """发单""" + return self.restApi.sendOrder(orderReq) + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """撤单""" + self.restApi.cancelOrder(cancelOrderReq) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.restApi.close() + + #---------------------------------------------------------------------- + def initQuery(self): + """初始化连续查询""" + if self.qryEnabled: + # 需要循环的查询函数列表 + self.qryFunctionList = [self.restApi.qryTickers, + self.restApi.qryDepth, + self.restApi.qryPosition, + self.restApi.qryOrder] + + self.qryCount = 0 # 查询触发倒计时 + self.qryTrigger = 1 # 查询触发点 + self.qryNextFunction = 0 # 上次运行的查询函数索引 + + self.startQuery() + + #---------------------------------------------------------------------- + def query(self, event): + """注册到事件处理引擎上的查询函数""" + self.qryCount += 1 + + if self.qryCount > self.qryTrigger: + # 清空倒计时 + self.qryCount = 0 + + # 执行查询函数 + function = self.qryFunctionList[self.qryNextFunction] + function() + + # 计算下次查询函数的索引,如果超过了列表长度,则重新设为0 + self.qryNextFunction += 1 + if self.qryNextFunction == len(self.qryFunctionList): + self.qryNextFunction = 0 + + #---------------------------------------------------------------------- + def startQuery(self): + """启动连续查询""" + self.eventEngine.register(EVENT_TIMER, self.query) + + #---------------------------------------------------------------------- + def setQryEnabled(self, qryEnabled): + """设置是否要启动循环查询""" + self.qryEnabled = qryEnabled + + +######################################################################## +class RestApi(BigoneRestApi): + """REST API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(RestApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.localID = 0 + self.tradeID = 0 + + self.orderDict = {} # sysID:order + self.localSysDict = {} # localID:sysID + self.reqOrderDict = {} # reqID:order + self.cancelDict = {} # localID:req + + self.tickDict = {} + + #---------------------------------------------------------------------- + def connect(self, apiKey, apiSecret, symbols): + """连接服务器""" + self.init(apiKey, apiSecret) + self.start() + + self.symbols = symbols + self.writeLog(u'REST API启动成功') + + self.qryContract() + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """""" + #orderReq.price = 300.0 + #orderReq.volume = 0.01 + + self.localID += 1 + orderID = str(self.localID) + vtOrderID = '.'.join([self.gatewayName, orderID]) + + req = { + 'market_id': orderReq.symbol, + 'side': directionMap[orderReq.direction], + 'price': str(orderReq.price), + 'amount': str(orderReq.volume) + } + + reqid = self.addReq('POST', '/viewer/orders', self.onSendOrder, postdict=req) + + # 缓存委托数据对象 + order = VtOrderData() + order.gatewayName = self.gatewayName + order.symbol = orderReq.symbol + order.exchange = EXCHANGE_BIGONE + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + order.orderID = orderID + order.vtOrderID = vtOrderID + order.price = orderReq.price + order.totalVolume = orderReq.volume + order.direction = orderReq.direction + order.status = STATUS_UNKNOWN + + self.reqOrderDict[reqid] = order + + return vtOrderID + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """""" + localID = cancelOrderReq.orderID + + if localID in self.localSysDict: + sysID = self.localSysDict[localID] + path = '/viewer/orders/%s/cancel' %sysID + self.addReq('POST', path, self.onCancelOrder) + else: + self.cancelDict[localID] = cancelOrderReq + + #---------------------------------------------------------------------- + def qryContract(self): + """""" + self.addReq('GET', '/markets', self.onQryContract) + + #---------------------------------------------------------------------- + def qryTickers(self): + """""" + self.addReq('GET', '/tickers', self.onQryTickers) + + #---------------------------------------------------------------------- + def qryDepth(self): + """""" + for symbol in self.symbols: + path = '/markets/%s/depth' %symbol + self.addReq('GET', path, self.onQryDepth) + + #---------------------------------------------------------------------- + def qryOrder(self): + """""" + #for symbol in self.symbols: + #req = { + #'market_id': symbol, + #'last': 100 + #} + #self.addReq('GET', '/viewer/orders', self.onQryOrder, params=req) + + req = { + #'market_id': symbol, + 'last': 100 + } + self.addReq('GET', '/viewer/orders', self.onQryOrder, params=req) + + #---------------------------------------------------------------------- + def qryPosition(self): + """""" + self.addReq('GET', '/viewer/accounts', self.onQryPosition) + + #---------------------------------------------------------------------- + def onSendOrder(self, data, reqid): + """""" + if self.checkError(u'委托', data): + return + + d = data['data'] + + order = self.reqOrderDict[reqid] + localID = order.orderID + sysID = d['id'] + + self.localSysDict[localID] = sysID + self.orderDict[sysID] = order + + self.gateway.onOrder(order) + + # 发出等待的撤单委托 + if localID in self.cancelDict: + req = self.cancelDict[localID] + self.cancelOrder(req) + del self.cancelDict[localID] + + #---------------------------------------------------------------------- + def onCancelOrder(self, data, reqid): + """""" + if self.checkError(u'撤单', data): + return + + #---------------------------------------------------------------------- + def onError(self, code, error): + """""" + msg = u'发生异常,错误代码:%s,错误信息:%s' %(code, error) + self.writeLog(msg) + + #---------------------------------------------------------------------- + def onQryOrder(self, data, reqid): + """""" + if self.checkError(u'查询委托', data): + return + + for node in data['data']['edges']: + orderUpdated = False + tradeUpdated = False + d = node['node'] + + # 获取委托对象 + sysID = d['id'] + if sysID in self.orderDict: + order = self.orderDict[sysID] + else: + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.symbol = d['market_id'] + order.exchange = EXCHANGE_BIGONE + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + self.localID += 1 + localID = str(self.localID) + self.localSysDict[localID] = sysID + + order.orderID = localID + order.vtOrderID = '.'.join([order.gatewayName, order.orderID]) + + order.direction = directionMapReverse[d['side']] + order.price = float(d['price']) + order.totalVolume = float(d['amount']) + + self.orderDict[sysID] = order + orderUpdated = True + + # 检查是否委托有变化 + newTradedVolume = float(d['filled_amount']) + newStatus = statusMapReverse[d['state']] + + if newTradedVolume != float(order.tradedVolume) or newStatus != order.status: + orderUpdated = True + + if newTradedVolume != float(order.tradedVolume): + tradeUpdated = True + newVolume = newTradedVolume - order.tradedVolume + + order.tradedVolume = newTradedVolume + order.status = newStatus + + # 若有更新才推送 + if orderUpdated: + self.gateway.onOrder(order) + + if tradeUpdated: + # 推送成交 + trade = VtTradeData() + trade.gatewayName = order.gatewayName + + trade.symbol = order.symbol + trade.vtSymbol = order.vtSymbol + + trade.orderID = order.orderID + trade.vtOrderID = order.vtOrderID + + self.tradeID += 1 + trade.tradeID = str(self.tradeID) + trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID]) + + trade.direction = order.direction + trade.price = order.price + trade.volume = newTradedVolume + trade.tradeTime = datetime.now().strftime('%H:%M:%S') + + self.gateway.onTrade(trade) + + #---------------------------------------------------------------------- + def onQryPosition(self, data, reqid): + """""" + if self.checkError(u'查询持仓', data): + return + + for d in data['data']: + pos = VtPositionData() + pos.gatewayName = self.gatewayName + + pos.symbol = d['asset_id'] + pos.exchange = EXCHANGE_BIGONE + pos.vtSymbol = '.'.join([pos.symbol, pos.exchange]) + pos.direction = DIRECTION_NET + pos.vtPositionName = '.'.join([pos.vtSymbol, pos.direction]) + pos.position = float(d['balance']) + pos.frozen = float(d['locked_balance']) + + self.gateway.onPosition(pos) + + #---------------------------------------------------------------------- + def onQryContract(self, data, reqid): + """""" + if self.checkError(u'查询合约', data): + return + + for d in data['data']: + contract = VtContractData() + contract.gatewayName = self.gatewayName + + contract.symbol = d['name'] + contract.exchange = EXCHANGE_BIGONE + contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) + contract.name = contract.vtSymbol + contract.productClass = PRODUCT_SPOT + contract.priceTick = pow(10, -int(d['quoteScale'])) + contract.size = 1 + + self.gateway.onContract(contract) + + self.writeLog(u'合约信息查询完成') + + #---------------------------------------------------------------------- + def onQryTickers(self, data, reqid): + """""" + if self.checkError(u'查询行情', data): + return + + dt = datetime.now() + date = dt.strftime('%Y%m%d') + time = dt.strftime('%H:%M:%S') + + for d in data['data']: + symbol = str(d['market_id']) + tick = self.getTick(symbol) + + tick.openPrice = float(d['open']) + #tick.highPrice = float(d['high']) + #tick.lowPrice = float(d['low']) + tick.lastPrice = float(d['close']) + #tick.volume = float(d['volume']) + tick.datetime = datetime + tick.date = data + tick.time = time + + # 只有订阅了深度行情才推送 + if tick.bidPrice1: + self.gateway.onTick(tick) + + #---------------------------------------------------------------------- + def onQryDepth(self, data, reqid): + """""" + if self.checkError(u'查询深度', data): + return + + d = data['data'] + symbol = d['market_id'] + + tick = self.getTick(symbol) + + for n, bid in enumerate(d['bids'][:5]): + tick.__setattr__('bidPrice%s' %(n+1), float(bid['price'])) + tick.__setattr__('bidVolume%s' %(n+1), float(bid['amount'])) + + for n, ask in enumerate(d['asks'][:5]): + tick.__setattr__('askPrice%s' %(n+1), float(ask['price'])) + tick.__setattr__('askVolume%s' %(n+1), float(ask['amount'])) + + tick.datetime = datetime.now() + tick.date = tick.datetime.strftime('%Y%m%d') + tick.time = tick.datetime.strftime('%H:%M:%S') + + if tick.lastPrice: + self.gateway.onTick(tick) + + #---------------------------------------------------------------------- + def getTick(self, symbol): + """""" + tick = self.tickDict.get(symbol, None) + + if not tick: + tick = VtTickData() + tick.gatewayName = self.gatewayName + tick.symbol = symbol + tick.exchange = EXCHANGE_BIGONE + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + self.tickDict[symbol] = tick + + return tick + + #---------------------------------------------------------------------- + def checkError(self, name, data): + """""" + error = data.get('errors', None) + if not error: + return False + + msg = str(error) + self.writeLog(u'%s触发错误:%s' %(name, msg)) + return True + +#---------------------------------------------------------------------- +def printDict(d): + """""" + print '-' * 30 + l = d.keys() + l.sort() + for k in l: + print k, d[k] + \ No newline at end of file From 69310cf4201da0823d464dd000078d58eeadbf40 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 2 Jul 2018 08:47:44 +0800 Subject: [PATCH 069/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9EfcoinGateway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/gateway/fcoinGateway/__init__.py | 10 + .../gateway/fcoinGateway/fcoinGateway.py | 582 ++++++++++++++++++ 2 files changed, 592 insertions(+) create mode 100644 vnpy/trader/gateway/fcoinGateway/__init__.py create mode 100644 vnpy/trader/gateway/fcoinGateway/fcoinGateway.py diff --git a/vnpy/trader/gateway/fcoinGateway/__init__.py b/vnpy/trader/gateway/fcoinGateway/__init__.py new file mode 100644 index 00000000..2c6a4ee6 --- /dev/null +++ b/vnpy/trader/gateway/fcoinGateway/__init__.py @@ -0,0 +1,10 @@ +# encoding: UTF-8 + +from vnpy.trader import vtConstant +from .fcoinGateway import FcoinGateay + +gatewayClass = FcoinGateay +gatewayName = 'FCOIN' +gatewayDisplayName = 'FCOIN' +gatewayType = vtConstant.GATEWAYTYPE_BTC +gatewayQryEnabled = True \ No newline at end of file diff --git a/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py b/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py new file mode 100644 index 00000000..0832ab78 --- /dev/null +++ b/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py @@ -0,0 +1,582 @@ +# encoding: UTF-8 + +''' +vnpy.api.fcoin的gateway接入 +''' + +import os +import json +import time +import traceback +from datetime import datetime, timedelta +from copy import copy +from math import pow + +from vnpy.api.fcoin import FcoinRestApi, FcoinWebsocketApi +from vnpy.trader.vtGateway import * +from vnpy.trader.vtFunction import getJsonPath, getTempPath + +# 委托状态类型映射 +statusMapReverse = {} +statusMapReverse['submitted'] = STATUS_NOTTRADED +statusMapReverse['partial_filled'] = STATUS_PARTTRADED +statusMapReverse['partial_canceled'] = STATUS_CANCELLED +statusMapReverse['filled'] = STATUS_ALLTRADED +statusMapReverse['canceled'] = STATUS_CANCELLED +statusMapReverse['pending_cancel'] = STATUS_UNKNOWN + +# 方向映射 +directionMap = {} +directionMap[DIRECTION_LONG] = 'buy' +directionMap[DIRECTION_SHORT] = 'sell' +directionMapReverse = {v:k for k,v in directionMap.items()} + +# 价格类型映射 +priceTypeMap = {} +priceTypeMap[PRICETYPE_LIMITPRICE] = 'limit' +priceTypeMap[PRICETYPE_MARKETPRICE] = 'market' + + + +######################################################################## +class FcoinGateay(VtGateway): + """FCOIN接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine, gatewayName=''): + """Constructor""" + super(FcoinGateay, self).__init__(eventEngine, gatewayName) + + self.restApi = RestApi(self) + self.wsApi = WebsocketApi(self) + + self.qryEnabled = False # 是否要启动循环查询 + + self.fileName = self.gatewayName + '_connect.json' + self.filePath = getJsonPath(self.fileName, __file__) + + #---------------------------------------------------------------------- + def connect(self): + """连接""" + try: + f = file(self.filePath) + except IOError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'读取连接配置出错,请检查' + self.onLog(log) + return + + # 解析json文件 + setting = json.load(f) + try: + apiKey = str(setting['apiKey']) + apiSecret = str(setting['apiSecret']) + symbols = setting['symbols'] + except KeyError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'连接配置缺少字段,请检查' + self.onLog(log) + return + + # 创建行情和交易接口对象 + self.restApi.connect(apiKey, apiSecret, symbols) + self.wsApi.connect(apiKey, apiSecret, symbols) + + # 初始化并启动查询 + self.initQuery() + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """发单""" + return self.restApi.sendOrder(orderReq) + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """撤单""" + self.restApi.cancelOrder(cancelOrderReq) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.restApi.close() + self.wsApi.close() + + #---------------------------------------------------------------------- + def initQuery(self): + """初始化连续查询""" + if self.qryEnabled: + # 需要循环的查询函数列表 + self.qryFunctionList = [self.restApi.qryPosition, + self.restApi.qryOrderSubmitted, + self.restApi.qryOrderPartialFilled, + self.restApi.qryOrderCanceled, + self.restApi.qryOrderFilled, + self.restApi.qryOrderPartialCanceled] + + self.qryCount = 0 # 查询触发倒计时 + self.qryTrigger = 3 # 查询触发点 + 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 RestApi(FcoinRestApi): + """REST API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(RestApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.localID = 0 + self.tradeID = 0 + + self.orderDict = {} # sysID:order + self.localSysDict = {} # localID:sysID + self.reqOrderDict = {} # reqID:order + self.cancelDict = {} # localID:req + + #---------------------------------------------------------------------- + def connect(self, apiKey, apiSecret, symbols): + """连接服务器""" + self.init(apiKey, apiSecret) + self.start() + + self.symbols = symbols + self.writeLog(u'REST API启动成功') + + self.qryContract() + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """""" + #orderReq.price = 300.0 + #orderReq.volume = 0.01 + + self.localID += 1 + orderID = str(self.localID) + vtOrderID = '.'.join([self.gatewayName, orderID]) + + req = { + 'symbol': orderReq.symbol, + 'side': directionMap[orderReq.direction], + 'type': priceTypeMap[orderReq.priceType], + 'price': orderReq.price, + 'amount': orderReq.volume + } + + reqid = self.addReq('POST', '/orders', self.onSendOrder, postdict=req) + + # 缓存委托数据对象 + order = VtOrderData() + order.gatewayName = self.gatewayName + order.symbol = orderReq.symbol + order.exchange = EXCHANGE_FCOIN + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + order.orderID = orderID + order.vtOrderID = vtOrderID + order.price = orderReq.price + order.totalVolume = orderReq.volume + order.direction = orderReq.direction + order.status = STATUS_UNKNOWN + + self.reqOrderDict[reqid] = order + + return vtOrderID + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """""" + localID = cancelOrderReq.orderID + + if localID in self.localSysDict: + sysID = self.localSysDict[localID] + path = '/orders/%s/submit-cancel' %sysID + self.addReq('POST', path, self.onCancelOrder) + else: + self.cancelDict[localID] = cancelOrderReq + + #---------------------------------------------------------------------- + def qryContract(self): + """""" + self.addReq('GET', '/public/symbols', self.onQryContract) + + #---------------------------------------------------------------------- + def qryOrder(self, state): + """""" + for symbol in self.symbols: + req = { + 'symbol': symbol, + 'states': state, + 'limit': 50 + } + self.addReq('GET', '/orders', self.onQryOrder, params=req) + + #---------------------------------------------------------------------- + def qryOrderSubmitted(self): + """""" + self.qryOrder('submitted') + + #---------------------------------------------------------------------- + def qryOrderPartialFilled(self): + """""" + self.qryOrder('partial_filled') + + #---------------------------------------------------------------------- + def qryOrderPartialCanceled(self): + """""" + self.qryOrder('partial_canceled') + + #---------------------------------------------------------------------- + def qryOrderFilled(self): + """""" + self.qryOrder('filled') + + #---------------------------------------------------------------------- + def qryOrderCanceled(self): + """""" + self.qryOrder('canceled') + + #---------------------------------------------------------------------- + def qryPosition(self): + """""" + self.addReq('GET', '/accounts/balance', self.onQryPosition) + + #---------------------------------------------------------------------- + def onSendOrder(self, data, reqid): + """""" + if 'msg' in data: + self.writeLog(data['msg']) + return + + if 'data' in data: + order = self.reqOrderDict[reqid] + localID = order.orderID + sysID = data['data'] + + self.localSysDict[localID] = sysID + self.orderDict[sysID] = order + + self.gateway.onOrder(order) + + # 发出等待的撤单委托 + if localID in self.cancelDict: + req = self.cancelDict[localID] + self.cancelOrder(req) + del self.cancelDict[localID] + + #---------------------------------------------------------------------- + def onCancelOrder(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onError(self, code, error): + """""" + msg = u'发生异常,错误代码:%s,错误信息:%s' %(code, error) + self.writeLog(msg) + + #---------------------------------------------------------------------- + def onQryOrder(self, data, reqid): + """""" + data['data'].reverse() + + for d in data['data']: + orderUpdated = False + tradeUpdated = False + + # 获取委托对象 + sysID = d['id'] + if sysID in self.orderDict: + order = self.orderDict[sysID] + else: + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.symbol = d['symbol'] + order.exchange = EXCHANGE_FCOIN + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + self.localID += 1 + localID = str(self.localID) + self.localSysDict[localID] = sysID + + order.orderID = localID + order.vtOrderID = '.'.join([order.gatewayName, order.orderID]) + + order.direction = directionMapReverse[d['side']] + order.price = float(d['price']) + order.totalVolume = float(d['amount']) + + dt = datetime.fromtimestamp(d['created_at']/1000) + order.orderTime = dt.strftime('%H:%M:%S') + + self.orderDict[sysID] = order + orderUpdated = True + + # 检查是否委托有变化 + newTradedVolume = float(d['filled_amount']) + newStatus = statusMapReverse[d['state']] + + if newTradedVolume != float(order.tradedVolume) or newStatus != order.status: + orderUpdated = True + + if newTradedVolume != float(order.tradedVolume): + tradeUpdated = True + newVolume = newTradedVolume - order.tradedVolume + + order.tradedVolume = newTradedVolume + order.status = newStatus + + # 若有更新才推送 + if orderUpdated: + self.gateway.onOrder(order) + + if tradeUpdated: + # 推送成交 + trade = VtTradeData() + trade.gatewayName = order.gatewayName + + trade.symbol = order.symbol + trade.vtSymbol = order.vtSymbol + + trade.orderID = order.orderID + trade.vtOrderID = order.vtOrderID + + self.tradeID += 1 + trade.tradeID = str(self.tradeID) + trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID]) + + trade.direction = order.direction + trade.price = order.price + trade.volume = newTradedVolume + trade.tradeTime = datetime.now().strftime('%H:%M:%S') + + self.gateway.onTrade(trade) + + #---------------------------------------------------------------------- + def onQryPosition(self, data, reqid): + """""" + for d in data['data']: + pos = VtPositionData() + pos.gatewayName = self.gatewayName + + pos.symbol = d['currency'] + pos.exchange = EXCHANGE_FCOIN + pos.vtSymbol = '.'.join([pos.symbol, pos.exchange]) + pos.direction = DIRECTION_NET + pos.vtPositionName = '.'.join([pos.vtSymbol, pos.direction]) + pos.position = d['balance'] + pos.frozen = d['frozen'] + + self.gateway.onPosition(pos) + + #---------------------------------------------------------------------- + def onQryContract(self, data, reqid): + """""" + for d in data['data']: + contract = VtContractData() + contract.gatewayName = self.gatewayName + + contract.symbol = d['name'] + contract.exchange = EXCHANGE_FCOIN + contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) + contract.name = contract.vtSymbol + contract.productClass = PRODUCT_SPOT + contract.priceTick = pow(10, -int(d['price_decimal'])) + contract.size = 1 + + self.gateway.onContract(contract) + + self.writeLog(u'合约信息查询完成') + + +######################################################################## +class WebsocketApi(FcoinWebsocketApi): + """""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(WebsocketApi, self).__init__() + + self.gateway = gateway + self.gatewayName = gateway.gatewayName + + self.apiKey = '' + self.apiSecret = '' + self.symbols = [] + + self.tickDict = {} + + #---------------------------------------------------------------------- + def connect(self, apiKey, apiSecret, symbols): + """""" + self.apiKey = apiKey + self.apiSecret = apiSecret + self.symbols = symbols + + self.start() + + #---------------------------------------------------------------------- + def onConnect(self): + """连接回调""" + self.writeLog(u'Websocket API连接成功') + + #---------------------------------------------------------------------- + def subscribe(self): + """""" + l = [] + for symbol in self.symbols: + l.append('ticker.' + symbol) + l.append('depth.L20.' + symbol) + + tick = VtTickData() + tick.gatewayName = self.gatewayName + tick.symbol = symbol + tick.exchange = EXCHANGE_FCOIN + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + self.tickDict[symbol] = tick + + req = { + 'cmd': 'sub', + 'args': l, + 'id': 1 + } + self.sendReq(req) + + #---------------------------------------------------------------------- + def onData(self, data): + """数据回调""" + type_ = data['type'] + if 'hello' in type_: + self.subscribe() + elif 'ticker' in type_: + self.onTick(data) + elif 'depth' in type_: + self.onDepth(data) + + #---------------------------------------------------------------------- + def onError(self, msg): + """错误回调""" + self.writeLog(msg) + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def onTick(self, d): + """""" + symbol = d['type'].split('.')[-1] + tick = self.tickDict[symbol] + + ticker = d['ticker'] + tick.openPrice = ticker[6] + tick.highPrice = ticker[7] + tick.lowPrice = ticker[8] + tick.lastPrice = ticker[0] + tick.volume = ticker[9] + + self.gateway.onTick(copy(tick)) + + #---------------------------------------------------------------------- + def onDepth(self, d): + """""" + symbol = d['type'].split('.')[-1] + tick = self.tickDict[symbol] + + bids = d['bids'] + asks = d['asks'] + + tick.bidPrice1 = bids[0] + tick.bidPrice2 = bids[2] + tick.bidPrice3 = bids[4] + tick.bidPrice4 = bids[6] + tick.bidPrice5 = bids[8] + + tick.askPrice1 = asks[0] + tick.askPrice2 = asks[2] + tick.askPrice3 = asks[4] + tick.askPrice4 = asks[6] + tick.askPrice5 = asks[8] + + tick.bidVolume1 = bids[1] + tick.bidVolume2 = bids[3] + tick.bidVolume3 = bids[5] + tick.bidVolume4 = bids[7] + tick.bidVolume5 = bids[9] + + tick.askVolume1 = asks[1] + tick.askVolume2 = asks[3] + tick.askVolume3 = asks[5] + tick.askVolume4 = asks[7] + tick.askVolume5 = asks[9] + + tick.datetime = datetime.fromtimestamp(d['ts']/1000) + tick.date = tick.datetime.strftime('%Y%m%d') + tick.time = tick.datetime.strftime('%H:%M:%S') + + self.gateway.onTick(copy(tick)) + + +#---------------------------------------------------------------------- +def printDict(d): + """""" + print '-' * 30 + l = d.keys() + l.sort() + for k in l: + print k, d[k] + \ No newline at end of file From a5e972ce5a3b24f9f990c4c21f3c92b0150551cb Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 2 Jul 2018 17:52:16 +0800 Subject: [PATCH 070/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9E=E5=88=B7?= =?UTF-8?q?=E5=8D=95=E7=AE=97=E6=B3=95StAlgo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/CryptoTrader/BIGONE_connect.json | 5 + examples/CryptoTrader/FCOIN_connect.json | 5 + examples/CryptoTrader/run.py | 7 +- vnpy/trader/app/algoTrading/algoEngine.py | 15 +- vnpy/trader/app/algoTrading/stAlgo.py | 170 +++++++++++++++++++ vnpy/trader/app/algoTrading/uiAlgoManager.py | 2 + vnpy/trader/app/riskManager/RM_setting.json | 2 +- 7 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 examples/CryptoTrader/BIGONE_connect.json create mode 100644 examples/CryptoTrader/FCOIN_connect.json create mode 100644 vnpy/trader/app/algoTrading/stAlgo.py diff --git a/examples/CryptoTrader/BIGONE_connect.json b/examples/CryptoTrader/BIGONE_connect.json new file mode 100644 index 00000000..87aab6bd --- /dev/null +++ b/examples/CryptoTrader/BIGONE_connect.json @@ -0,0 +1,5 @@ +{ + "apiKey": "", + "apiSecret": "", + "symbols": ["BTC-USDT", "ETH-USDT", "EOS-USDT"] +} \ No newline at end of file diff --git a/examples/CryptoTrader/FCOIN_connect.json b/examples/CryptoTrader/FCOIN_connect.json new file mode 100644 index 00000000..00d51e2a --- /dev/null +++ b/examples/CryptoTrader/FCOIN_connect.json @@ -0,0 +1,5 @@ +{ + "apiKey": "", + "apiSecret": "", + "symbols": ["ethusdt"] +} \ No newline at end of file diff --git a/examples/CryptoTrader/run.py b/examples/CryptoTrader/run.py index 9088f66e..80ce604d 100644 --- a/examples/CryptoTrader/run.py +++ b/examples/CryptoTrader/run.py @@ -22,7 +22,8 @@ from vnpy.trader.uiMainWindow import MainWindow # 加载底层接口 from vnpy.trader.gateway import (huobiGateway, okexGateway, binanceGateway, bitfinexGateway, - bitmexGateway) + bitmexGateway, fcoinGateway, + bigoneGateway) # 加载上层应用 from vnpy.trader.app import (riskManager, algoTrading) @@ -41,12 +42,14 @@ def main(): me = MainEngine(ee) # 添加交易接口 + me.addGateway(bigoneGateway) + me.addGateway(fcoinGateway) me.addGateway(bitmexGateway) me.addGateway(huobiGateway) me.addGateway(okexGateway) me.addGateway(binanceGateway) me.addGateway(bitfinexGateway) - + # 添加上层应用 me.addApp(riskManager) me.addApp(algoTrading) diff --git a/vnpy/trader/app/algoTrading/algoEngine.py b/vnpy/trader/app/algoTrading/algoEngine.py index f1da8037..68406569 100644 --- a/vnpy/trader/app/algoTrading/algoEngine.py +++ b/vnpy/trader/app/algoTrading/algoEngine.py @@ -16,6 +16,7 @@ from vnpy.trader.vtObject import VtSubscribeReq, VtOrderReq, VtCancelOrderReq, V from .twapAlgo import TwapAlgo from .dmaAlgo import DmaAlgo from .stopAlgo import StopAlgo +from .stAlgo import StAlgo EVENT_ALGO_LOG = 'eAlgoLog' # 算法日志事件 EVENT_ALGO_PARAM = 'eAlgoParam' # 算法参数事件 @@ -32,7 +33,8 @@ HISTORY_COLLECTION_NAME = 'AlgoHistory' # 算法历史集合名 ALGO_DICT = { TwapAlgo.templateName: TwapAlgo, DmaAlgo.templateName: DmaAlgo, - StopAlgo.templateName: StopAlgo + StopAlgo.templateName: StopAlgo, + StAlgo.templateName: StAlgo } @@ -274,6 +276,17 @@ class AlgoEngine(object): return tick + #---------------------------------------------------------------------- + def getContract(self, algo, vtSymbol): + """查询合约""" + contract = self.mainEngine.getContract(vtSymbol) + if not contract: + self.writeLog(u'%s查询合约失败,找不到报价:%s' %(algo.algoName, vtSymbol)) + return + + return contract + + #---------------------------------------------------------------------- def saveAlgoSetting(self, algoSetting): """保存算法配置""" diff --git a/vnpy/trader/app/algoTrading/stAlgo.py b/vnpy/trader/app/algoTrading/stAlgo.py new file mode 100644 index 00000000..9d0d6893 --- /dev/null +++ b/vnpy/trader/app/algoTrading/stAlgo.py @@ -0,0 +1,170 @@ +# encoding: UTF-8 + +from __future__ import division +from collections import OrderedDict + +from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, + OFFSET_OPEN, OFFSET_CLOSE) +from vnpy.trader.uiQt import QtWidgets + +from .algoTemplate import AlgoTemplate +from .uiAlgoWidget import AlgoWidget, QtWidgets + + + +######################################################################## +class StAlgo(AlgoTemplate): + """自成交算法(self trade),用于刷成交量""" + + templateName = 'ST' + + #---------------------------------------------------------------------- + def __init__(self, engine, setting, algoName): + """Constructor""" + super(StAlgo, self).__init__(engine, setting, algoName) + + # 参数,强制类型转换,保证从CSV加载的配置正确 + self.vtSymbol = str(setting['vtSymbol']) # 合约代码 + self.orderVolume = float(setting['orderVolume']) # 委托数量 + self.interval = int(setting['interval']) # 运行间隔 + self.minTickSpread = int(setting['minTickSpread']) # 最小价差 + + self.count = 0 # 定时计数 + self.tradedVolume = 0 # 总成交数量 + + self.subscribe(self.vtSymbol) + self.paramEvent() + self.varEvent() + + #---------------------------------------------------------------------- + def onTick(self, tick): + """""" + pass + + #---------------------------------------------------------------------- + def onTrade(self, trade): + """""" + self.tradedVolume += trade.volume + self.varEvent() + + #---------------------------------------------------------------------- + def onOrder(self, order): + """""" + pass + + #---------------------------------------------------------------------- + def onTimer(self): + """""" + self.count += 1 + if self.count == self.interval: + self.count = 0 + + # 全撤委托 + self.cancelAll() + + # 获取行情 + tick = self.getTick(self.vtSymbol) + if not tick: + return + + contract = self.getContract(self.vtSymbol) + if not contract: + return + + tickSpread = (tick.askPrice1 - tick.bidPrice1) / contract.priceTick + if tickSpread < self.minTickSpread: + self.writeLog(u'当前价差为%s个Tick,小于算法设置%s,不执行刷单' %(tickSpread, self.minTickSpread)) + return + + midPrice = tick.bidPrice1 + contract.priceTick * int(tickSpread/2) + + self.buy(self.vtSymbol, midPrice, self.orderVolume) + self.sell(self.vtSymbol, midPrice, self.orderVolume) + + self.writeLog(u'发出刷单买卖委托,价格:%s,数量:%s' %(midPrice, self.orderVolume)) + + self.varEvent() + + #---------------------------------------------------------------------- + def onStop(self): + """""" + self.writeLog(u'停止算法') + self.varEvent() + + #---------------------------------------------------------------------- + def varEvent(self): + """更新变量""" + d = OrderedDict() + d[u'算法状态'] = self.active + d[u'成交数量'] = self.tradedVolume + d[u'定时计数'] = self.count + d['active'] = self.active + self.putVarEvent(d) + + #---------------------------------------------------------------------- + def paramEvent(self): + """更新参数""" + d = OrderedDict() + d[u'代码'] = self.vtSymbol + d[u'单次委托数量'] = self.orderVolume + d[u'执行间隔'] = self.interval + d[u'最小价差Tick'] = self.minTickSpread + self.putParamEvent(d) + + +######################################################################## +class StWidget(AlgoWidget): + """""" + + #---------------------------------------------------------------------- + def __init__(self, algoEngine, parent=None): + """Constructor""" + super(StWidget, self).__init__(algoEngine, parent) + + self.templateName = StAlgo.templateName + + #---------------------------------------------------------------------- + def initAlgoLayout(self): + """""" + self.lineSymbol = QtWidgets.QLineEdit() + + self.spinVolume = QtWidgets.QDoubleSpinBox() + self.spinVolume.setMinimum(0) + self.spinVolume.setMaximum(1000000000) + self.spinVolume.setDecimals(6) + + self.spinInterval = QtWidgets.QSpinBox() + self.spinInterval.setMinimum(20) + self.spinInterval.setMaximum(3600) + + self.spinMinTickSpread = QtWidgets.QSpinBox() + self.spinMinTickSpread.setMinimum(0) + self.spinMinTickSpread.setMaximum(1000) + + Label = QtWidgets.QLabel + + grid = QtWidgets.QGridLayout() + grid.addWidget(Label(u'代码'), 0, 0) + grid.addWidget(self.lineSymbol, 0, 1) + grid.addWidget(Label(u'单次刷单量'), 1, 0) + grid.addWidget(self.spinVolume, 1, 1) + grid.addWidget(Label(u'执行间隔'), 2, 0) + grid.addWidget(self.spinInterval, 2, 1) + grid.addWidget(Label(u'最小价差Tick'), 3, 0) + grid.addWidget(self.spinMinTickSpread, 3, 1) + + return grid + + #---------------------------------------------------------------------- + def getAlgoSetting(self): + """""" + setting = OrderedDict() + setting['templateName'] = StAlgo.templateName + setting['vtSymbol'] = str(self.lineSymbol.text()) + setting['orderVolume'] = float(self.spinVolume.value()) + setting['interval'] = int(self.spinInterval.value()) + setting['minTickSpread'] = int(self.spinMinTickSpread.value()) + + return setting + + \ No newline at end of file diff --git a/vnpy/trader/app/algoTrading/uiAlgoManager.py b/vnpy/trader/app/algoTrading/uiAlgoManager.py index 59aec40c..62f656de 100644 --- a/vnpy/trader/app/algoTrading/uiAlgoManager.py +++ b/vnpy/trader/app/algoTrading/uiAlgoManager.py @@ -15,6 +15,7 @@ from .algoEngine import (EVENT_ALGO_LOG, EVENT_ALGO_PARAM, from .twapAlgo import TwapWidget from .dmaAlgo import DmaWidget from .stopAlgo import StopWidget +from .stAlgo import StWidget ######################################################################## @@ -393,6 +394,7 @@ class AlgoManager(QtWidgets.QWidget): self.addAlgoWidget(TwapWidget) self.addAlgoWidget(DmaWidget) self.addAlgoWidget(StopWidget) + self.addAlgoWidget(StWidget) self.algoEngine.loadAlgoSetting() # 界面初始化后,再加载算法配置 diff --git a/vnpy/trader/app/riskManager/RM_setting.json b/vnpy/trader/app/riskManager/RM_setting.json index 6dc35464..d34f89d0 100644 --- a/vnpy/trader/app/riskManager/RM_setting.json +++ b/vnpy/trader/app/riskManager/RM_setting.json @@ -5,6 +5,6 @@ "workingOrderLimit": 20, "tradeLimit": 1000, "orderSizeLimit": 100, - "active": true, + "active": false, "orderFlowLimit": 50 } \ No newline at end of file From 5d046d360c893e6d3abcaf922104873d606d5ea6 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Thu, 5 Jul 2018 14:03:04 +0800 Subject: [PATCH 071/135] =?UTF-8?q?[Add]=E9=87=8D=E6=96=B0=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0Lbank=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/CryptoTrader/LBANK_connect.json | 5 + examples/CryptoTrader/run.py | 6 +- vnpy/api/bigone/vnbigone.py | 2 +- vnpy/api/lbank/__init__.py | 2 +- vnpy/api/lbank/test.py | 103 +- vnpy/api/lbank/vnlbank.py | 397 +++----- vnpy/trader/gateway/fcoinGateway/__init__.py | 4 +- .../gateway/fcoinGateway/fcoinGateway.py | 4 +- .../gateway/lbankGateway/LBANK_connect.json | 7 +- .../gateway/lbankGateway/lbankGateway.py | 958 ++++++++++-------- 10 files changed, 758 insertions(+), 730 deletions(-) create mode 100644 examples/CryptoTrader/LBANK_connect.json diff --git a/examples/CryptoTrader/LBANK_connect.json b/examples/CryptoTrader/LBANK_connect.json new file mode 100644 index 00000000..59286a19 --- /dev/null +++ b/examples/CryptoTrader/LBANK_connect.json @@ -0,0 +1,5 @@ +{ + "apiKey": "", + "secretKey": "", + "symbols": ["eth_usdt", "sc_btc", "btc_usdt"] +} diff --git a/examples/CryptoTrader/run.py b/examples/CryptoTrader/run.py index 80ce604d..23868fa1 100644 --- a/examples/CryptoTrader/run.py +++ b/examples/CryptoTrader/run.py @@ -23,10 +23,10 @@ from vnpy.trader.uiMainWindow import MainWindow from vnpy.trader.gateway import (huobiGateway, okexGateway, binanceGateway, bitfinexGateway, bitmexGateway, fcoinGateway, - bigoneGateway) + bigoneGateway, lbankGateway) # 加载上层应用 -from vnpy.trader.app import (riskManager, algoTrading) +from vnpy.trader.app import (algoTrading) #---------------------------------------------------------------------- @@ -42,6 +42,7 @@ def main(): me = MainEngine(ee) # 添加交易接口 + me.addGateway(lbankGateway) me.addGateway(bigoneGateway) me.addGateway(fcoinGateway) me.addGateway(bitmexGateway) @@ -51,7 +52,6 @@ def main(): me.addGateway(bitfinexGateway) # 添加上层应用 - me.addApp(riskManager) me.addApp(algoTrading) # 创建主窗口 diff --git a/vnpy/api/bigone/vnbigone.py b/vnpy/api/bigone/vnbigone.py index 425868ce..cf82b685 100644 --- a/vnpy/api/bigone/vnbigone.py +++ b/vnpy/api/bigone/vnbigone.py @@ -95,7 +95,7 @@ class BigoneRestApi(object): if code == 200: callback(d, reqid) else: - self.onError(code, d) + self.onError(code, str(d)) except Exception as e: self.onError(type(e), e.message) diff --git a/vnpy/api/lbank/__init__.py b/vnpy/api/lbank/__init__.py index 2e68766f..080efd11 100644 --- a/vnpy/api/lbank/__init__.py +++ b/vnpy/api/lbank/__init__.py @@ -1,4 +1,4 @@ # encoding: UTF-8 from __future__ import absolute_import -from .vnlbank import LbankApi \ No newline at end of file +from .vnlbank import LbankRestApi, LbankWebsocketApi \ No newline at end of file diff --git a/vnpy/api/lbank/test.py b/vnpy/api/lbank/test.py index 463830e1..5391f15d 100644 --- a/vnpy/api/lbank/test.py +++ b/vnpy/api/lbank/test.py @@ -1,50 +1,67 @@ -# encoding: utf-8 - -from __future__ import absolute_import -from time import time, sleep +# encoding: UTF-8 from six.moves import input +from time import time -from .vnlbank import LbankApi +from vnlbank import LbankRestApi, LbankWebsocketApi + +API_KEY = '132a36ce-ad1c-409a-b48c-09b7877ae49b' +SECRET_KEY = '319320BF875297E7F4050E1195B880E8' + + +#---------------------------------------------------------------------- +def restTest(): + """""" + # 创建API对象并初始化 + api = LbankRestApi() + api.init(API_KEY, SECRET_KEY) + api.start(1) + + # 测试 + #api.addReq('GET', '/currencyPairs.do', {}, api.onData) + #api.addReq('GET', '/accuracy.do', {}, api.onData) + + #api.addReq('GET', '/ticker.do', {'symbol': 'eth_btc'}, api.onData) + #api.addReq('GET', '/depth.do', {'symbol': 'eth_btc', 'size': '5'}, api.onData) + + #api.addReq('post', '/user_info.do', {}, api.onData) + + req = { + 'symbol': 'sc_btc', + 'current_page': '1', + 'page_length': '50' + } + api.addReq('POST', '/orders_info_no_deal.do', req, api.onData) + + # 阻塞 + input() + + +#---------------------------------------------------------------------- +def wsTest(): + """""" + ws = LbankWebsocketApi() + ws.start() + + channels = [ + 'lh_sub_spot_eth_btc_depth_20', + 'lh_sub_spot_eth_btc_trades', + 'lh_sub_spot_eth_btc_ticker' + ] + + for channel in channels: + req = { + 'event': 'addChannel', + 'channel': channel + } + ws.sendReq(req) + + + # 阻塞 + input() if __name__ == '__main__': - apiKey = '' - secretKey = '' + restTest() - # 创建API对象并初始化 - api = LbankApi() - api.DEBUG = True - api.init(apiKey, secretKey, 2) - - # 查询行情 - api.getTicker('btc_cny') - - # 查询深度 - api.getDepth('btc_cny', '60', '1') - - # 查询历史成交 - #api.getTrades('btc_cny', '1', str(int(time()))) - - # 查询K线 - #t = int(time()) - #sleep(300) - #api.getKline('btc_cny', '20', 'minute1', str(t)) - - # 查询账户 - #api.getUserInfo() - - # 发送委托 - #api.createOrder('btc_cny', 'sell', '8000', '0.001') - - # 撤单 - #api.cancelOrder('btc_cny', '725bd2da-73aa-419f-8090-f68488074e8f') - - # 查询委托 - #api.getOrdersInfo('btc_cny', '725bd2da-73aa-419f-8090-f68488074e8f') - - # 查询委托历史 - #api.getOrdersInfoHistory('btc_cny', '0', '1', '100') - - # 阻塞 - input() + #wsTest() \ No newline at end of file diff --git a/vnpy/api/lbank/vnlbank.py b/vnpy/api/lbank/vnlbank.py index d0f9158a..1d04ecf6 100644 --- a/vnpy/api/lbank/vnlbank.py +++ b/vnpy/api/lbank/vnlbank.py @@ -3,306 +3,223 @@ from __future__ import print_function import urllib import hashlib +import ssl +import json +import traceback import requests from Queue import Queue, Empty from threading import Thread -from time import sleep +from multiprocessing.dummy import Pool +from time import time + +import websocket -API_ROOT ="https://api.lbank.info/v1/" - -FUNCTION_TICKER = ('ticker.do', 'get') -FUNCTION_DEPTH = ('depth.do', 'get') -FUNCTION_TRADES = ('trades.do', 'get') -FUNCTION_KLINE = ('kline.do', 'get') - -FUNCTION_USERINFO = ('user_info.do', 'post') -FUNCTION_CREATEORDER = ('create_order.do', 'post') -FUNCTION_CANCELORDER = ('cancel_order.do', 'post') -FUNCTION_ORDERSINFO = ('orders_info.do', 'post') -FUNCTION_ORDERSINFOHISTORY = ('orders_info_history.do', 'post') +REST_HOST = "https://api.lbank.info/v1" +WEBSOCKET_HOST = 'ws://api.lbank.info/ws' -#---------------------------------------------------------------------- -def signature(params, secretKey): - """生成签名""" - params = sorted(params.iteritems(), key=lambda d:d[0], reverse=False) - params.append(('secret_key', secretKey)) - message = urllib.urlencode(params) - - m = hashlib.md5() - m.update(message) - m.digest() - - sig=m.hexdigest() - return sig - ######################################################################## -class LbankApi(object): +class LbankRestApi(object): """""" - DEBUG = True - + #---------------------------------------------------------------------- def __init__(self): """Constructor""" self.apiKey = '' self.secretKey = '' - self.interval = 1 # 每次请求的间隔等待 self.active = False # API工作状态 self.reqID = 0 # 请求编号 - self.reqQueue = Queue() # 请求队列 - self.reqThread = Thread(target=self.processQueue) # 请求处理线程 - + self.queue = Queue() # 请求队列 + self.pool = None # 线程池 + self.sessionDict = {} # 连接池 + #---------------------------------------------------------------------- - def init(self, apiKey, secretKey, interval): + def init(self, apiKey, secretKey): """初始化""" self.apiKey = apiKey self.secretKey = secretKey - self.interval = interval - - self.active = True - self.reqThread.start() #---------------------------------------------------------------------- - def exit(self): + def start(self, n=10): + """""" + if self.active: + return + + self.active = True + self.pool = Pool(n) + self.pool.map_async(self.run, range(n)) + + #---------------------------------------------------------------------- + def close(self): """退出""" self.active = False - if self.reqThread.isAlive(): - self.reqThread.join() + if self.pool: + self.pool.close() + self.pool.join() #---------------------------------------------------------------------- - def processRequest(self, req): + def processReq(self, req, i): """处理请求""" # 读取方法和参数 - api, method = req['function'] - params = req['params'] - url = API_ROOT + api + method, path, params, callback, reqID = req + url = REST_HOST + path # 在参数中增加必须的字段 params['api_key'] = self.apiKey - - # 添加签名 - sign = signature(params, self.secretKey) - params['sign'] = sign + params['sign'] = self.generateSignature(params) # 发送请求 payload = urllib.urlencode(params) - - r = requests.request(method, url, params=payload) - if r.status_code == 200: - data = r.json() - return data - else: - return None + + try: + # 使用会话重用技术,请求延时降低80% + session = self.sessionDict[i] + resp = session.request(method, url, params=payload) + #resp = requests.request(method, url, params=payload) + + code = resp.status_code + d = resp.json() + + if code == 200: + callback(d, reqID) + else: + self.onError(code, str(d)) + + except Exception as e: + self.onError(type(e), e.message) #---------------------------------------------------------------------- - def processQueue(self): - """处理请求队列中的请求""" + def run(self, i): + """连续运行""" + self.sessionDict[i] = requests.Session() + while self.active: try: - req = self.reqQueue.get(block=True, timeout=1) # 获取请求的阻塞为一秒 - callback = req['callback'] - reqID = req['reqID'] - - data = self.processRequest(req) - - # 请求失败 - if data is None: - error = u'请求失败' - self.onError(error, req, reqID) - elif 'error_code' in data: - error = u'请求出错,错误代码:%s' % data['error_code'] - self.onError(error, req, reqID) - # 请求成功 - else: - if self.DEBUG: - print(callback.__name__) - callback(data, req, reqID) - - # 流控等待 - sleep(self.interval) - + req = self.queue.get(block=True, timeout=1) # 获取请求的阻塞为一秒 + self.processReq(req, i) except Empty: pass #---------------------------------------------------------------------- - def sendRequest(self, function, params, callback): + def addReq(self, method, path, params, callback): """发送请求""" # 请求编号加1 self.reqID += 1 # 生成请求字典并放入队列中 - req = {} - req['function'] = function - req['params'] = params - req['callback'] = callback - req['reqID'] = self.reqID - self.reqQueue.put(req) + req = (method, path, params, callback, self.reqID) + self.queue.put(req) # 返回请求编号 return self.reqID - + #---------------------------------------------------------------------- - def onError(self, error, req, reqID): + def generateSignature(self, params): + """生成签名""" + params = sorted(params.iteritems(), key=lambda d:d[0], reverse=False) + params.append(('secret_key', self.secretKey)) + message = urllib.urlencode(params) + + m = hashlib.md5() + m.update(message) + m.digest() + + sig = m.hexdigest() + return sig + + #---------------------------------------------------------------------- + def onError(self, code, msg): """错误推送""" - print(error, req, reqID) + print(code, msg) - ############################################### - # 行情接口 - ############################################### + #---------------------------------------------------------------------- + def onData(self, data, reqID): + """""" + print(data, reqID) + + +######################################################################## +class LbankWebsocketApi(object): + """Websocket API""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.ws = None + self.thread = None + self.active = False #---------------------------------------------------------------------- - def getTicker(self, symbol): - """查询行情""" - function = FUNCTION_TICKER - params = {'symbol': symbol} - callback = self.onGetTicker - return self.sendRequest(function, params, callback) - - # ---------------------------------------------------------------------- - def getDepth(self, symbol, size, merge): - """查询深度""" - function = FUNCTION_DEPTH - params = { - 'symbol': symbol, - 'size': size, - 'mege': merge - } - callback = self.onGetDepth - return self.sendRequest(function, params, callback) - - # ---------------------------------------------------------------------- - def getTrades(self, symbol, size, time): - """查询历史成交""" - function = FUNCTION_TRADES - params = { - 'symbol': symbol, - 'size': size, - 'time': time - } - callback = self.onGetTrades - return self.sendRequest(function, params, callback) - - # ---------------------------------------------------------------------- - def getKline(self, symbol, size, type_, time): - """查询K线""" - function = FUNCTION_KLINE - params = { - 'symbol': symbol, - 'size': size, - 'type': type_, - 'time': time - } - callback = self.onGetKline - return self.sendRequest(function, params, callback) - + def start(self): + """启动""" + self.ws = websocket.create_connection(WEBSOCKET_HOST, + sslopt={'cert_reqs': ssl.CERT_NONE}) + + self.active = True + self.thread = Thread(target=self.run) + self.thread.start() + + self.onConnect() + #---------------------------------------------------------------------- - def onGetTicker(self, data, req, reqID): - """查询行情回调""" - print(data, reqID) + def reconnect(self): + """重连""" + self.ws = websocket.create_connection(WEBSOCKET_HOST, + sslopt={'cert_reqs': ssl.CERT_NONE}) + + self.onConnect() + + #---------------------------------------------------------------------- + def run(self): + """运行""" + while self.active: + try: + stream = self.ws.recv() + data = json.loads(stream) + self.onData(data) + except: + msg = traceback.format_exc() + print(msg) + self.onError(msg) + self.reconnect() + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.active = False + + if self.thread: + self.ws.shutdown() + self.thread.join() + + #---------------------------------------------------------------------- + def onConnect(self): + """连接回调""" + print('connected') + + #---------------------------------------------------------------------- + def onData(self, data): + """数据回调""" + print('-' * 30) + l = data.keys() + l.sort() + for k in l: + print(k, data[k]) + + #---------------------------------------------------------------------- + def onError(self, msg): + """错误回调""" + print(msg) + + #---------------------------------------------------------------------- + def sendReq(self, req): + """发出请求""" + self.ws.send(json.dumps(req)) - # ---------------------------------------------------------------------- - def onGetDepth(self, data, req, reqID): - """查询深度回调""" - print(data, reqID) - - # ---------------------------------------------------------------------- - def onGetTrades(self, data, req, reqID): - """查询历史成交""" - print(data, reqID) - - # ---------------------------------------------------------------------- - def onGetKline(self, data, req, reqID): - """查询K线回报""" - print(data, reqID) - - ############################################### - # 交易接口 - ############################################### - - # ---------------------------------------------------------------------- - def getUserInfo(self): - """查询账户信息""" - function = FUNCTION_USERINFO - params = {} - callback = self.onGetUserInfo - return self.sendRequest(function, params, callback) - - # ---------------------------------------------------------------------- - def createOrder(self, symbol, type_, price, amount): - """发送委托""" - function = FUNCTION_CREATEORDER - params = { - 'symbol': symbol, - 'type': type_, - 'price': price, - 'amount': amount - } - callback = self.onCreateOrder - return self.sendRequest(function, params, callback) - - # ---------------------------------------------------------------------- - def cancelOrder(self, symbol, orderId): - """撤单""" - function = FUNCTION_CANCELORDER - params = { - 'symbol': symbol, - 'order_id': orderId - } - callback = self.onCancelOrder - return self.sendRequest(function, params, callback) - - # ---------------------------------------------------------------------- - def getOrdersInfo(self, symbol, orderId): - """查询委托""" - function = FUNCTION_ORDERSINFO - params = { - 'symbol': symbol, - 'order_id': orderId - } - callback = self.onGetOrdersInfo - return self.sendRequest(function, params, callback) - - # ---------------------------------------------------------------------- - def getOrdersInfoHistory(self, symbol, status, currentPage, pageLength): - """撤单""" - function = FUNCTION_ORDERSINFOHISTORY - params = { - 'symbol': symbol, - 'status': status, - 'current_page': currentPage, - 'page_length': pageLength - } - callback = self.onGetOrdersInfoHistory - return self.sendRequest(function, params, callback) - - # ---------------------------------------------------------------------- - def onGetUserInfo(self, data, req, reqID): - """查询账户信息""" - print(data, reqID) - - # ---------------------------------------------------------------------- - def onCreateOrder(self, data, req, reqID): - """委托回报""" - print(data, reqID) - - # ---------------------------------------------------------------------- - def onCancelOrder(self, data, req, reqID): - """撤单回报""" - print(data, reqID) - - # ---------------------------------------------------------------------- - def onGetOrdersInfo(self, data, req, reqID): - """查询委托回报""" - print(data, reqID) - - # ---------------------------------------------------------------------- - def onGetOrdersInfoHistory(self, data, req, reqID): - """撤单回报""" - print(data, reqID) diff --git a/vnpy/trader/gateway/fcoinGateway/__init__.py b/vnpy/trader/gateway/fcoinGateway/__init__.py index 2c6a4ee6..bd76f422 100644 --- a/vnpy/trader/gateway/fcoinGateway/__init__.py +++ b/vnpy/trader/gateway/fcoinGateway/__init__.py @@ -1,9 +1,9 @@ # encoding: UTF-8 from vnpy.trader import vtConstant -from .fcoinGateway import FcoinGateay +from .fcoinGateway import FcoinGateway -gatewayClass = FcoinGateay +gatewayClass = FcoinGateway gatewayName = 'FCOIN' gatewayDisplayName = 'FCOIN' gatewayType = vtConstant.GATEWAYTYPE_BTC diff --git a/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py b/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py index 0832ab78..637b49e6 100644 --- a/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py +++ b/vnpy/trader/gateway/fcoinGateway/fcoinGateway.py @@ -39,13 +39,13 @@ priceTypeMap[PRICETYPE_MARKETPRICE] = 'market' ######################################################################## -class FcoinGateay(VtGateway): +class FcoinGateway(VtGateway): """FCOIN接口""" #---------------------------------------------------------------------- def __init__(self, eventEngine, gatewayName=''): """Constructor""" - super(FcoinGateay, self).__init__(eventEngine, gatewayName) + super(FcoinGateway, self).__init__(eventEngine, gatewayName) self.restApi = RestApi(self) self.wsApi = WebsocketApi(self) diff --git a/vnpy/trader/gateway/lbankGateway/LBANK_connect.json b/vnpy/trader/gateway/lbankGateway/LBANK_connect.json index 5df1ee29..59286a19 100644 --- a/vnpy/trader/gateway/lbankGateway/LBANK_connect.json +++ b/vnpy/trader/gateway/lbankGateway/LBANK_connect.json @@ -1,6 +1,5 @@ { - "apiKey": "请在链行官网申请", - "secretKey": "请在链行官网申请", - "interval": 1, - "debug": false + "apiKey": "", + "secretKey": "", + "symbols": ["eth_usdt", "sc_btc", "btc_usdt"] } diff --git a/vnpy/trader/gateway/lbankGateway/lbankGateway.py b/vnpy/trader/gateway/lbankGateway/lbankGateway.py index 3cade80e..744eec64 100644 --- a/vnpy/trader/gateway/lbankGateway/lbankGateway.py +++ b/vnpy/trader/gateway/lbankGateway/lbankGateway.py @@ -1,60 +1,54 @@ # encoding: UTF-8 ''' -vn.lhang的gateway接入 +vnpy.api.lhang的gateway接入 ''' -from __future__ import print_function +from __future__ import print_function import os import json from datetime import datetime from time import sleep +from copy import copy -from vnpy.api.lbank import LbankApi +from vnpy.api.lbank import LbankRestApi, LbankWebsocketApi from vnpy.trader.vtGateway import * from vnpy.trader.vtFunction import getJsonPath -SYMBOL_BTCCNY = 'BTCCNY' -SYMBOL_ZECCNY = 'ZECCNY' +directionMap = {} +directionMap[DIRECTION_LONG] = 'buy' +directionMap[DIRECTION_SHORT] = 'sell' +directionMapReverse = {v:k for k,v in directionMap.items()} -SYMBOL_MAP = {} -SYMBOL_MAP['btc_cny'] = SYMBOL_BTCCNY -SYMBOL_MAP['zec_cny'] = SYMBOL_ZECCNY -SYMBOL_MAP_REVERSE = {v: k for k, v in SYMBOL_MAP.items()} - - -DIRECTION_MAP = {} -DIRECTION_MAP['buy'] = DIRECTION_LONG -DIRECTION_MAP['sell'] = DIRECTION_SHORT - -STATUS_MAP = {} -STATUS_MAP[0] = STATUS_NOTTRADED -STATUS_MAP[1] = STATUS_PARTTRADED -STATUS_MAP[2] = STATUS_ALLTRADED -STATUS_MAP[4] = STATUS_UNKNOWN -STATUS_MAP[-1] = STATUS_CANCELLED +statusMapReverse = {} +statusMapReverse[0] = STATUS_NOTTRADED +statusMapReverse[1] = STATUS_PARTTRADED +statusMapReverse[2] = STATUS_ALLTRADED +statusMapReverse[4] = STATUS_UNKNOWN +statusMapReverse[-1] = STATUS_CANCELLED ######################################################################## class LbankGateway(VtGateway): - """LBANK接口""" + """FCOIN接口""" #---------------------------------------------------------------------- - def __init__(self, eventEngine, gatewayName='LBANK'): + def __init__(self, eventEngine, gatewayName=''): """Constructor""" super(LbankGateway, self).__init__(eventEngine, gatewayName) + + self.restApi = RestApi(self) - self.api = LbankApi(self) - + self.qryEnabled = False # 是否要启动循环查询 + self.fileName = self.gatewayName + '_connect.json' - self.filePath = getJsonPath(self.fileName, __file__) - + self.filePath = getJsonPath(self.fileName, __file__) + #---------------------------------------------------------------------- def connect(self): """连接""" - # 载入json文件 try: f = file(self.filePath) except IOError: @@ -63,466 +57,562 @@ class LbankGateway(VtGateway): log.logContent = u'读取连接配置出错,请检查' self.onLog(log) return - + # 解析json文件 setting = json.load(f) try: - accessKey = str(setting['apiKey']) + apiKey = str(setting['apiKey']) secretKey = str(setting['secretKey']) - interval = setting['interval'] - debug = setting['debug'] + symbols = setting['symbols'] except KeyError: log = VtLogData() log.gatewayName = self.gatewayName log.logContent = u'连接配置缺少字段,请检查' self.onLog(log) - return - - # 初始化接口 - self.api.connect(accessKey, secretKey, interval, debug) - self.writeLog(u'接口初始化成功') - - # 启动查询 + return + + # 创建行情和交易接口对象 + self.restApi.connect(apiKey, secretKey, symbols) + + # 初始化并启动查询 self.initQuery() - self.startQuery() + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """发单""" + return self.restApi.sendOrder(orderReq) + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """撤单""" + self.restApi.cancelOrder(cancelOrderReq) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.restApi.close() + #---------------------------------------------------------------------- + def initQuery(self): + """初始化连续查询""" + if self.qryEnabled: + # 需要循环的查询函数列表 + self.qryFunctionList = [self.restApi.qryAccount, + self.restApi.qryWorkingOrder, + self.restApi.qryCompletedOrder, + self.restApi.qryMarketData] + + self.qryCount = 0 # 查询触发倒计时 + self.qryTrigger = 1 # 查询触发点 + self.qryNextFunction = 0 # 上次运行的查询函数索引 + + self.startQuery() + + #---------------------------------------------------------------------- + def query(self, event): + """注册到事件处理引擎上的查询函数""" + self.qryCount += 1 + + if self.qryCount > self.qryTrigger: + # 清空倒计时 + self.qryCount = 0 + + # 执行查询函数 + function = self.qryFunctionList[self.qryNextFunction] + function() + + # 计算下次查询函数的索引,如果超过了列表长度,则重新设为0 + self.qryNextFunction += 1 + if self.qryNextFunction == len(self.qryFunctionList): + self.qryNextFunction = 0 + + #---------------------------------------------------------------------- + def startQuery(self): + """启动连续查询""" + self.eventEngine.register(EVENT_TIMER, self.query) + + #---------------------------------------------------------------------- + def setQryEnabled(self, qryEnabled): + """设置是否要启动循环查询""" + self.qryEnabled = qryEnabled + + +######################################################################## +class RestApi(LbankRestApi): + """REST API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(RestApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.localID = 0 + self.tradeID = 0 + + self.orderDict = {} # sysID:order + self.localSysDict = {} # localID:sysID + self.reqOrderDict = {} # reqID:order + self.cancelDict = {} # localID:req + + self.tickDict = {} # symbol:tick + self.reqSymbolDict = {} + + #---------------------------------------------------------------------- + def connect(self, apiKey, apiSecret, symbols): + """连接服务器""" + self.init(apiKey, apiSecret) + self.start() + + self.symbols = symbols + for symbol in symbols: + tick = VtTickData() + tick.gatewayName = self.gatewayName + tick.symbol = symbol + tick.exchange = EXCHANGE_LBANK + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + self.tickDict[symbol] = tick + + self.writeLog(u'REST API启动成功') + self.qryContract() + #---------------------------------------------------------------------- def writeLog(self, content): """发出日志""" log = VtLogData() log.gatewayName = self.gatewayName log.logContent = content - self.onLog(log) + self.gateway.onLog(log) - #---------------------------------------------------------------------- - def subscribe(self, subscribeReq): - """订阅行情,自动订阅全部行情,无需实现""" - pass - #---------------------------------------------------------------------- def sendOrder(self, orderReq): - """发单""" - self.api.sendOrder(orderReq) + """""" + self.localID += 1 + orderID = str(self.localID) + vtOrderID = '.'.join([self.gatewayName, orderID]) + req = { + 'symbol': orderReq.symbol, + 'type': directionMap[orderReq.direction], + 'price': orderReq.price, + 'amount': orderReq.volume + } + + reqid = self.addReq('POST', '/create_order.do', req, self.onSendOrder) + + # 缓存委托数据对象 + order = VtOrderData() + order.gatewayName = self.gatewayName + order.symbol = orderReq.symbol + order.exchange = EXCHANGE_LBANK + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + order.orderID = orderID + order.vtOrderID = vtOrderID + order.price = orderReq.price + order.totalVolume = orderReq.volume + order.direction = orderReq.direction + order.status = STATUS_UNKNOWN + + self.reqOrderDict[reqid] = order + + return vtOrderID + #---------------------------------------------------------------------- def cancelOrder(self, cancelOrderReq): - """撤单""" - self.api.cancel(cancelOrderReq) + """""" + localID = cancelOrderReq.orderID + if localID in self.localSysDict: + sysID = self.localSysDict[localID] + order = self.orderDict[sysID] + req = { + 'symbol': order.symbol, + 'order_id': sysID + } + self.addReq('POST', '/cancel_order.do', req, self.onCancelOrder) + + else: + self.cancelDict[localID] = cancelOrderReq + + #---------------------------------------------------------------------- + def qryContract(self): + """""" + self.addReq('GET', '/accuracy.do', {}, self.onQryContract) + + #---------------------------------------------------------------------- + def qryCompletedOrder(self): + """""" + for symbol in self.symbols: + req = { + 'symbol': symbol, + 'current_page': '1', + 'page_length': '100' + } + self.addReq('POST', '/orders_info_history.do', req, self.onQryOrder) + + #---------------------------------------------------------------------- + def qryWorkingOrder(self): + """""" + for symbol in self.symbols: + req = { + 'symbol': symbol, + 'current_page': '1', + 'page_length': '100' + } + self.addReq('POST', '/orders_info_no_deal.do', req, self.onQryOrder) + #---------------------------------------------------------------------- def qryAccount(self): - """查询账户资金""" - pass - - #---------------------------------------------------------------------- - def qryPosition(self): - """查询持仓""" - pass - - #---------------------------------------------------------------------- - def close(self): - """关闭""" - self.api.exit() - - #---------------------------------------------------------------------- - def initQuery(self): - """初始化连续查询""" - if self.qryEnabled: - self.qryFunctionList = [self.api.queryPrice, - self.api.queryWorkingOrders, - self.api.queryAccount] - self.startQuery() + """""" + self.addReq('POST', '/user_info.do', {}, self.onQryAccount) #---------------------------------------------------------------------- - def query(self, event): - """注册到事件处理引擎上的查询函数""" - for function in self.qryFunctionList: - function() + def qryDepth(self): + """""" + for symbol in self.symbols: + req = { + 'symbol': symbol, + 'size': '5' + } + i = self.addReq('GET', '/depth.do', req, self.onQryDepth) + self.reqSymbolDict[i] = symbol + + #---------------------------------------------------------------------- + def qryTicker(self): + """""" + for symbol in self.symbols: + req = {'symbol': symbol} + i = self.addReq('GET', '/ticker.do', req, self.onQryTicker) + self.reqSymbolDict[i] = symbol + + #---------------------------------------------------------------------- + def qryMarketData(self): + """""" + self.qryDepth() + self.qryTicker() + + #---------------------------------------------------------------------- + def onSendOrder(self, data, reqid): + """""" + order = self.reqOrderDict[reqid] + localID = order.orderID + sysID = data['order_id'] + + self.localSysDict[localID] = sysID + self.orderDict[sysID] = order + + self.gateway.onOrder(order) + + # 发出等待的撤单委托 + if localID in self.cancelDict: + req = self.cancelDict[localID] + self.cancelOrder(req) + del self.cancelDict[localID] + + #---------------------------------------------------------------------- + def onCancelOrder(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onError(self, code, error): + """""" + msg = u'发生异常,错误代码:%s,错误信息:%s' %(code, error) + self.writeLog(msg) + + #---------------------------------------------------------------------- + def onQryOrder(self, data, reqid): + """""" + if 'orders' not in data: + return + + if not isinstance(data['orders'], list): + return + + data['orders'].reverse() + + for d in data['orders']: + orderUpdated = False + tradeUpdated = False + + # 获取委托对象 + sysID = d['order_id'] + if sysID in self.orderDict: + order = self.orderDict[sysID] + else: + order = VtOrderData() + order.gatewayName = self.gatewayName + order.symbol = d['symbol'] + order.exchange = EXCHANGE_LBANK + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + self.localID += 1 + localID = str(self.localID) + self.localSysDict[localID] = sysID + + order.orderID = localID + order.vtOrderID = '.'.join([order.gatewayName, order.orderID]) + + order.direction = directionMapReverse[d['type']] + order.price = float(d['price']) + order.totalVolume = float(d['amount']) + + dt = datetime.fromtimestamp(d['create_time']/1000) + order.orderTime = dt.strftime('%H:%M:%S') + + self.orderDict[sysID] = order + orderUpdated = True + + # 检查是否委托有变化 + newTradedVolume = float(d['deal_amount']) + newStatus = statusMapReverse[d['status']] + + if newTradedVolume != float(order.tradedVolume) or newStatus != order.status: + orderUpdated = True + + if newTradedVolume != float(order.tradedVolume): + tradeUpdated = True + newVolume = newTradedVolume - order.tradedVolume + + order.tradedVolume = newTradedVolume + order.status = newStatus + + # 若有更新才推送 + if orderUpdated: + self.gateway.onOrder(order) + + if tradeUpdated: + # 推送成交 + trade = VtTradeData() + trade.gatewayName = order.gatewayName + + trade.symbol = order.symbol + trade.vtSymbol = order.vtSymbol + + trade.orderID = order.orderID + trade.vtOrderID = order.vtOrderID + + self.tradeID += 1 + trade.tradeID = str(self.tradeID) + trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID]) + + trade.direction = order.direction + trade.price = order.price + trade.volume = newTradedVolume + trade.tradeTime = datetime.now().strftime('%H:%M:%S') + + self.gateway.onTrade(trade) + #---------------------------------------------------------------------- - def startQuery(self): - """启动连续查询""" - self.eventEngine.register(EVENT_TIMER, self.query) + def onQryAccount(self, data, reqid): + """""" + info = data['info'] + asset = info['asset'] + free = info['free'] + freeze = info['freeze'] + + for currency in asset.keys(): + account = VtAccountData() + account.gatewayName = self.gatewayName + + account.accountID = currency + account.vtAccountID = '.'.join([self.gatewayName, account.accountID]) + account.balance = float(asset[currency]) + account.available = float(free[currency]) + + self.gateway.onAccount(account) #---------------------------------------------------------------------- - def setQryEnabled(self, qryEnabled): - """设置是否要启动循环查询""" - self.qryEnabled = qryEnabled + def onQryContract(self, data, reqid): + """""" + for d in data: + contract = VtContractData() + contract.gatewayName = self.gatewayName + + contract.symbol = str(d['symbol']) + contract.exchange = EXCHANGE_LBANK + contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) + contract.name = contract.vtSymbol + contract.productClass = PRODUCT_SPOT + contract.priceTick = pow(10, -int(d['priceAccuracy'])) + contract.size = 1 + + self.gateway.onContract(contract) + + self.writeLog(u'合约信息查询完成') + #---------------------------------------------------------------------- + def onQryTicker(self, data, reqid): + """""" + ticker = data['ticker'] + + symbol = self.reqSymbolDict.pop(reqid) + tick = self.tickDict[symbol] + + tick.highPrice = float(ticker['high']) + tick.lowPrice = float(ticker['low']) + tick.lastPrice = float(ticker['latest']) + tick.volume = float(ticker['vol']) + + tick.datetime = datetime.fromtimestamp(int(data['timestamp']/1000)) + tick.date = tick.datetime.strftime('%Y%m%d') + tick.time = tick.datetime.strftime('%H:%M:%S') + + if tick.bidPrice1: + self.gateway.onTick(copy(tick)) + + #---------------------------------------------------------------------- + def onQryDepth(self, data, reqid): + """""" + symbol = self.reqSymbolDict.pop(reqid) + tick = self.tickDict[symbol] + + bids = data['bids'] + asks = data['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] + + if tick.lastPrice: + self.gateway.onTick(copy(tick)) + ######################################################################## -class LbankApi(LbankApi): +class WebsocketApi(LbankWebsocketApi): """""" #---------------------------------------------------------------------- def __init__(self, gateway): """Constructor""" - super(LbankApi, self).__init__() + super(WebsocketApi, self).__init__() self.gateway = gateway self.gatewayName = gateway.gatewayName - self.interval = 1 - - 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.tradeID = 0 - - self.tickDict = {} # key:symbol, value:tick - + self.symbols = [] + self.channelTickDict = {} + #---------------------------------------------------------------------- - def onError(self, error, req, reqID): - """错误推送""" - err = VtErrorData() - err.gatewayName = self.gatewayName - err.errorMsg = str(error) - err.errorTime = datetime.now().strftime('%H:%M:%S.%f')[:-3] - self.gateway.onError(err) - + def connect(self, symbols): + """""" + self.symbols = symbols + self.start() + #---------------------------------------------------------------------- - def onGetTicker(self, data, req, reqID): - """查询行情回调""" - ticker = data['ticker'] - params = req['params'] - symbol = SYMBOL_MAP[params['symbol']] - - if symbol not in self.tickDict: + def onConnect(self): + """连接回调""" + self.writeLog(u'Websocket API连接成功') + self.subscribe() + + #---------------------------------------------------------------------- + def subscribe(self): + """""" + for symbol in self.symbols: tick = VtTickData() tick.gatewayName = self.gatewayName - tick.symbol = symbol tick.exchange = EXCHANGE_LBANK tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) - self.tickDict[symbol] = tick - else: - tick = self.tickDict[symbol] - - tick.highPrice = float(ticker['high']) - tick.lowPrice = float(ticker['low']) - tick.lastPrice = float(ticker['latest']) - tick.openPrice = tick.lastPrice - float(ticker['change']) - tick.volume = ticker['vol'] - - # ---------------------------------------------------------------------- - def onGetDepth(self, data, req, reqID): - """查询深度回调""" - params = req['params'] - symbol = SYMBOL_MAP[params['symbol']] - if symbol not in self.tickDict: - tick = VtTickData() - tick.gatewayName = self.gatewayName - - tick.symbol = symbol - tick.exchange = EXCHANGE_LBANK - tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) - self.tickDict[symbol] = tick - else: - tick = self.tickDict[symbol] - - tick.bidPrice1, tick.bidVolume1 = data['bids'][0] - tick.bidPrice2, tick.bidVolume2 = data['bids'][1] - tick.bidPrice3, tick.bidVolume3 = data['bids'][2] - tick.bidPrice4, tick.bidVolume4 = data['bids'][3] - tick.bidPrice5, tick.bidVolume5 = data['bids'][4] - - tick.askPrice1, tick.askVolume1 = data['asks'][0] - tick.askPrice2, tick.askVolume2 = data['asks'][1] - tick.askPrice3, tick.askVolume3 = data['asks'][2] - tick.askPrice4, tick.askVolume4 = data['asks'][3] - tick.askPrice5, tick.askVolume5 = data['asks'][4] - - now = datetime.now() - tick.time = now.strftime('%H:%M:%S.%f')[:-3] - tick.date = now.strftime('%Y%m%d') - - self.gateway.onTick(tick) - - # ---------------------------------------------------------------------- - def onGetTrades(self, data, req, reqID): - """查询历史成交""" - print(data, reqID) - - # ---------------------------------------------------------------------- - def onGetKline(self, data, req, reqID): - print(data, reqID) - - # ---------------------------------------------------------------------- - def onGetUserInfo(self, data, req, reqID): - """查询K线回报""" - d = data['info'] - account = VtAccountData() - account.gatewayName = self.gatewayName - account.accountID = self.gatewayName - account.vtAccountID = '.'.join([account.accountID, self.gatewayName]) - account.balance = d['asset']['net'] - self.gateway.onAccount(account) - - # 推送持仓数据 - posCny = VtPositionData() - posCny.gatewayName = self.gatewayName - posCny.symbol = 'CNY' - posCny.exchange = EXCHANGE_LBANK - posCny.vtSymbol = '.'.join([posCny.symbol, posCny.exchange]) - posCny.vtPositionName = posCny.vtSymbol - posCny.frozen = d['freeze']['cny'] - posCny.position = posCny.frozen + d['free']['cny'] - self.gateway.onPosition(posCny) - - posBtc = VtPositionData() - posBtc.gatewayName = self.gatewayName - posBtc.symbol = 'BTC' - posBtc.exchange = EXCHANGE_LBANK - posBtc.vtSymbol = '.'.join([posBtc.symbol, posBtc.exchange]) - posBtc.vtPositionName = posBtc.vtSymbol - posBtc.frozen = d['freeze']['btc'] - posBtc.position = posBtc.frozen + d['free']['btc'] - self.gateway.onPosition(posBtc) - - posZec = VtPositionData() - posZec.gatewayName = self.gatewayName - posZec.symbol = 'ZEC' - posZec.exchange = EXCHANGE_LBANK - posZec.vtSymbol = '.'.join([posZec.symbol, posZec.exchange]) - posZec.vtPositionName = posZec.vtSymbol - posZec.frozen = d['freeze']['zec'] - posZec.position = posZec.frozen + d['free']['zec'] - self.gateway.onPosition(posZec) - - # 查询历史委托 - self.queryOrders() - - # ---------------------------------------------------------------------- - def onCreateOrder(self, data, req, reqID): - """发单回调""" - 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['result'] == 'success': - order.status = STATUS_NOTTRADED - self.gateway.onOrder(order) - - # ---------------------------------------------------------------------- - def onCancelOrder(self, data, req, reqID): - """撤单回调""" - if data['result'] == 'success': - systemID = req['params']['id'] - localID = self.systemLocalDict[systemID] - - order = self.workingOrderDict[localID] - order.status = STATUS_CANCELLED - - del self.workingOrderDict[localID] - self.gateway.onOrder(order) - - # ---------------------------------------------------------------------- - def onGetOrdersInfo(self, data, req, reqID): - """查询委托回报""" - if 'orders' in data: - for d in data['orders']: - systemID = d['order_id'] - localID = self.systemLocalDict[systemID] - order = self.workingOrderDict.get(localID, None) - if not order: - return - - # 记录最新成交的金额 - newTradeVolume = float(d['deal_amount']) - order.tradedVolume - if newTradeVolume: - 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.volume = newTradeVolume - trade.price = d['avg_price'] - trade.direction = order.direction - trade.offset = order.offset - trade.exchange = order.exchange - trade.tradeTime = datetime.now().strftime('%H:%M:%S.%f')[:-3] - - self.gateway.onTrade(trade) - - # 更新委托状态 - order.tradedVolume = float(d['deal_amount']) - order.status = STATUS_MAP.get(d['status'], STATUS_UNKNOWN) - - if newTradeVolume: - self.gateway.onOrder(order) - - if order.status == STATUS_ALLTRADED or order.status == STATUS_CANCELLED: - del self.workingOrderDict[order.orderID] - - # ---------------------------------------------------------------------- - def onGetOrdersInfoHistory(self, data, req, reqID): - """撤单回报""" - if 'orders' in data: - for d in data['orders']: - order = VtOrderData() - order.gatewayName = self.gatewayName - - order.symbol = SYMBOL_MAP[data['symbol']] - order.exchange = EXCHANGE_LBANK - order.vtSymbol = '.'.join([order.symbol, order.exchange]) - - systemID = d['order_id'] - self.localID += 1 - localID = str(self.localID) - self.systemLocalDict[systemID] = localID - self.localSystemDict[localID] = systemID - order.orderID = localID - order.vtOrderID = '.'.join([order.orderID, order.gatewayName]) - - order.totalVolume = float(d['amount']) - order.tradedVolume = float(d['deal_amount']) - order.price = float(d['price']) - order.direction = DIRECTION_MAP[d['type']] - order.offset = OFFSET_NONE - order.orderTime = datetime.fromtimestamp(d['create_time'], '%H:%M:%S') - - # 委托状态 - if order.tradedVolume == 0: - order.status = STATUS_NOTTRADED - else: - order.status = STATUS_PARTTRADED - - # 缓存病推送 - self.workingOrderDict[localID] = order - self.gateway.onOrder(order) - - #---------------------------------------------------------------------- - def connect(self, apiKey, secretKey, interval, debug): - """初始化""" - self.interval = interval - self.DEBUG = debug - - self.init(apiKey, secretKey, self.interval) - - # 推送合约信息 - contract = VtContractData() - contract.gatewayName = self.gatewayName - contract.symbol = SYMBOL_BTCCNY - contract.exchange = EXCHANGE_LBANK - contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) - contract.name = u'人民币现货BTC' - contract.size = 1 - contract.priceTick = 0.01 - contract.productClass = PRODUCT_SPOT - self.gateway.onContract(contract) - - contract = VtContractData() - contract.gatewayName = self.gatewayName - contract.symbol = SYMBOL_ZECCNY - contract.exchange = EXCHANGE_LBANK - contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) - contract.name = u'人民币现货ZEC' - contract.size = 1 - contract.priceTick = 0.01 - contract.productClass = PRODUCT_SPOT - self.gateway.onContract(contract) - - #---------------------------------------------------------------------- - def sendOrder(self, req): - """发单""" - # 检查是否填入了价格,禁止市价委托 - if req.priceType != PRICETYPE_LIMITPRICE: - err = VtErrorData() - err.gatewayName = self.gatewayName - err.errorMsg = u'LBANK接口仅支持限价单' - err.errorTime = datetime.now().strftime('%H:%M:%S.%f')[:-3] - self.gateway.onError(err) - return None - - # 发送限价委托 - s = SYMBOL_MAP_REVERSE[req.symbol] - - if req.direction == DIRECTION_LONG: - type_ = 'buy' - else: - type_ = 'sell' - - reqID = self.createOrder(s, type_, req.price, 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_LBANK - order.vtSymbol = '.'.join([order.symbol, order.exchange]) - - order.orderID = localID - order.vtOrderID = '.'.join([order.orderID, order.gatewayName]) - - order.direction = req.direction - order.offset = OFFSET_UNKNOWN - order.price = req.price - order.volume = req.volume - order.orderTime = datetime.now().strftime('%H:%M:%S.%f')[:-3] - order.status = STATUS_UNKNOWN - - self.workingOrderDict[localID] = order - self.gateway.onOrder(order) - - # 返回委托号 - return order.vtOrderID + + channelDepth = 'lh_sub_spot_%s_depth_20' %symbol + #channelTrades = 'lh_sub_spot_%s_ticker' %symbol + self.channelTickDict[channelDepth] = tick + # self.channelTickDict[channelTrades] = tick + + for channel in [channelDepth]: #, channelTrades]: + req = { + 'event': 'addChannel', + 'channel': channel + } + self.sendReq(req) #---------------------------------------------------------------------- - def cancel(self, req): - """撤单""" - localID = req.orderID - if localID in self.localSystemDict: - systemID = self.localSystemDict[localID] - s = SYMBOL_MAP_REVERSE[req.symbol] - self.cancelOrder(s, systemID) - else: - self.cancelDict[localID] = req + def onData(self, data): + """数据回调""" + channel = data['channel'] + print(data) + if 'trades' in channel: + self.onTick(data) + elif 'depth' in channel: + self.onDepth(data) + + #---------------------------------------------------------------------- + def onError(self, msg): + """错误回调""" + self.writeLog(msg) + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def onTick(self, data): + """""" + if 'data' not in data: + return + + channel = data['channel'] + tick = self.channelTickDict[channel] + + fill = data['data'][0] + tick.lastPrice = float(fill[0]) + tick.datetime = datetime.fromtimestamp(int(fill[2])/1000) + tick.date = tick.datetime.strftime('%Y%m%d') + tick.time = tick.datetime.strftime('%H:%M:%S') + + self.gateway.onTick(copy(tick)) #---------------------------------------------------------------------- - def queryOrders(self): - """查询委托""" - for s in SYMBOL_MAP.keys(): - self.getOrdersInfoHistory(s, '0', '1', '200') + def onDepth(self, data): + """""" + if 'data' not in data: + return + + channel = data['channel'] + tick = self.channelTickDict[channel] - #---------------------------------------------------------------------- - def queryWorkingOrders(self): - """查询活动委托""" - for localID, order in self.workingOrderDict.items(): - if localID in self.localSystemDict: - systemID = self.localSystemDict[localID] - s = SYMBOL_MAP_REVERSE[order.symbol] - self.getOrdersInfo(s, systemID) + d = data['data'] + bids = d['bids'] + asks = d['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.lastPrice = (tick.askPrice1 + tick.askPrice2) / 2 + + tick.datetime = datetime.fromtimestamp(d['timestamp']/1000) + tick.date = tick.datetime.strftime('%Y%m%d') + tick.time = tick.datetime.strftime('%H:%M:%S') + + self.gateway.onTick(copy(tick)) - #---------------------------------------------------------------------- - def queryPrice(self): - """查询行情""" - for s in SYMBOL_MAP.keys(): - self.getTicker(s) - self.getDepth(s, 5, 0) - - #---------------------------------------------------------------------- - def queryAccount(self): - """查询资金和资产""" - self.getUserInfo() \ No newline at end of file From 1c0a2cf758d2df40d11aa7b8e2b425c4e187f7ae Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 7 Jul 2018 17:58:22 +0800 Subject: [PATCH 072/135] =?UTF-8?q?[Fix]=E4=BF=AE=E5=A4=8DBitfinex?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=9A=84=E4=B8=8B=E5=8D=95=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E5=A7=94=E6=89=98=E5=8F=B7=E5=AD=97=E6=AE=B5=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py index 1e622ad4..3dfc320f 100644 --- a/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py +++ b/vnpy/trader/gateway/bitfinexGateway/bitfinexGateway.py @@ -255,7 +255,7 @@ class GatewayApi(BitfinexApi): amount = -orderReq.volume o = { - 'CID': orderId, + 'cid': orderId, 'type': priceTypeMap[orderReq.priceType], 'symbol': 't' + orderReq.symbol, 'amount': str(amount), From 8e988caf5c24f28a0d82e314ddd99844a7545165 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Mon, 9 Jul 2018 10:40:17 +0800 Subject: [PATCH 073/135] =?UTF-8?q?[Del]=E7=A7=BB=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E5=85=B3=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/bigone/vnbigone.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vnpy/api/bigone/vnbigone.py b/vnpy/api/bigone/vnbigone.py index cf82b685..8a8bb8ab 100644 --- a/vnpy/api/bigone/vnbigone.py +++ b/vnpy/api/bigone/vnbigone.py @@ -137,8 +137,8 @@ if __name__ == '__main__': from datetime import datetime from time import sleep - API_KEY = 'c9c61d5e-6a4b-42c5-9d9d-e0656feb2c94' - API_SECRET = '806E0FAEFCD2FF8CBD325A55D77B3E018BB7A9B94419EA4F5B2B3F71F5B188CB' + API_KEY = '' + API_SECRET = '' # REST测试 rest = BigoneRestApi() From 1dd5eaceaebf9b6ea602bc628f3dfeafa98e74be Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 10 Jul 2018 17:13:38 +0800 Subject: [PATCH 074/135] =?UTF-8?q?[Mod]=E4=BF=AE=E6=94=B9=E4=B8=8B?= =?UTF-8?q?=E5=8D=95=E7=BB=84=E4=BB=B6=E7=9A=84=E4=BB=B7=E6=A0=BC=E5=92=8C?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E8=BE=93=E5=85=A5=E4=BD=BF=E7=94=A8QLineEdit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/uiBasicWidget.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/vnpy/trader/uiBasicWidget.py b/vnpy/trader/uiBasicWidget.py index 138e9ccf..2446f017 100644 --- a/vnpy/trader/uiBasicWidget.py +++ b/vnpy/trader/uiBasicWidget.py @@ -727,7 +727,7 @@ class TradingWidget(QtWidgets.QFrame): def initUi(self): """初始化界面""" self.setWindowTitle(vtText.TRADING) - self.setMaximumWidth(400) + self.setFixedWidth(500) self.setFrameShape(self.Box) # 设置边框 self.setLineWidth(1) @@ -754,14 +754,14 @@ class TradingWidget(QtWidgets.QFrame): self.comboOffset = QtWidgets.QComboBox() self.comboOffset.addItems(self.offsetList) - self.spinPrice = QtWidgets.QDoubleSpinBox() - self.spinPrice.setDecimals(globalSetting.get('maxDecimal', 4)) - self.spinPrice.setMinimum(0) - self.spinPrice.setMaximum(1000000) + validator = QtGui.QDoubleValidator() + validator.setBottom(0) - self.spinVolume = QtWidgets.QSpinBox() - self.spinVolume.setMinimum(0) - self.spinVolume.setMaximum(1000000) + self.linePrice = QtWidgets.QLineEdit() + self.linePrice.setValidator(validator) + + self.lineVolume = QtWidgets.QLineEdit() + self.lineVolume.setValidator(validator) self.comboPriceType = QtWidgets.QComboBox() self.comboPriceType.addItems(self.priceTypeList) @@ -796,8 +796,8 @@ class TradingWidget(QtWidgets.QFrame): gridleft.addWidget(self.comboDirection, 2, 1, 1, -1) gridleft.addWidget(self.comboOffset, 3, 1, 1, -1) gridleft.addWidget(self.checkFixed, 4, 1) - gridleft.addWidget(self.spinPrice, 4, 2) - gridleft.addWidget(self.spinVolume, 5, 1, 1, -1) + gridleft.addWidget(self.linePrice, 4, 2) + gridleft.addWidget(self.lineVolume, 5, 1, 1, -1) gridleft.addWidget(self.comboPriceType, 6, 1, 1, -1) gridleft.addWidget(self.comboExchange, 7, 1, 1, -1) gridleft.addWidget(self.comboCurrency, 8, 1, 1, -1) @@ -882,6 +882,8 @@ class TradingWidget(QtWidgets.QFrame): gridRight.addWidget(self.labelBidVolume3, 8, 2) gridRight.addWidget(self.labelBidVolume4, 9, 2) gridRight.addWidget(self.labelBidVolume5, 10, 2) + + self.labelBidVolume5.setFixedWidth(100) # 发单按钮 buttonSendOrder = QtWidgets.QPushButton(vtText.SEND_ORDER) @@ -934,8 +936,8 @@ class TradingWidget(QtWidgets.QFrame): exchange = contract.exchange # 保证有交易所代码 # 清空价格数量 - self.spinPrice.setValue(0) - self.spinVolume.setValue(0) + self.linePrice.clear() + self.lineVolume.clear() # 清空行情显示 self.labelBidPrice1.setText('') @@ -984,7 +986,7 @@ class TradingWidget(QtWidgets.QFrame): return if not self.checkFixed.isChecked(): - self.spinPrice.setValue(tick.lastPrice) + self.linePrice.setText(str(tick.lastPrice)) self.labelBidPrice1.setText(str(tick.bidPrice1)) self.labelAskPrice1.setText(str(tick.askPrice1)) @@ -1053,8 +1055,8 @@ class TradingWidget(QtWidgets.QFrame): req.symbol = symbol req.exchange = exchange req.vtSymbol = vtSymbol - req.price = self.spinPrice.value() - req.volume = self.spinVolume.value() + req.price = float(self.linePrice.text()) + req.volume = float(self.lineVolume.text()) req.direction = unicode(self.comboDirection.currentText()) req.priceType = unicode(self.comboPriceType.currentText()) req.offset = unicode(self.comboOffset.currentText()) From e8282a135de68efe61e6a2d70111359d8409768f Mon Sep 17 00:00:00 2001 From: Mark <99972538@qq.com> Date: Wed, 11 Jul 2018 15:33:04 +0800 Subject: [PATCH 075/135] =?UTF-8?q?=E6=97=B6=E9=97=B4=E5=9B=9E=E9=80=801?= =?UTF-8?q?=E5=88=86=E9=92=9F=EF=BC=8C0=EF=BC=9A00=E6=97=B6=E8=B7=A8?= =?UTF-8?q?=E6=97=A5=E5=9B=9E=E9=80=80=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0:00时,人家日期时新的一天,而回退到23:59没有回退日期 --- examples/QuantosDataService/dataService.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/QuantosDataService/dataService.py b/examples/QuantosDataService/dataService.py index 730cb2e2..89f6726d 100644 --- a/examples/QuantosDataService/dataService.py +++ b/examples/QuantosDataService/dataService.py @@ -62,7 +62,6 @@ def generateVtBar(row): bar.low = row['low'] bar.close = row['close'] bar.volume = row['volume'] - bar.date = str(row['date']) bar.time = str(row['time']).rjust(6, '0') @@ -70,6 +69,19 @@ def generateVtBar(row): hour=bar.time[0:2] minute=bar.time[2:4] sec=bar.time[4:6] + + # ------------------------------add by yzl :start + # print(row.date, type(row.date), row.time, type(row.time))# add by yzl to show the date type and value + # 20180328 < type'long' > 93400 < type'long' > + # 最佳改进方法: 构建一个datetime,然后滞后一分钟,不能简单0:00:00处理,日期减一,弊端:处理量太大 + # 改进2:找出 0:00,此时日期回退一天 + if int(hour) == 0 and int(minute) == 0: + temp_date = datetime(int(bar.date[:4]), int(bar.date[4:6]), int(bar.date[6:])).date() + temp_date = temp_date - timedelta(days=1) + bar.date = temp_date.strftime("%Y%m%d") + + # -------------------------------add by yzl :end + if minute=="00": minute="59" @@ -81,6 +93,8 @@ def generateVtBar(row): else: minute=str(int(minute)-1).rjust(2,'0') bar.time=hour+minute+sec + + bar.datetime = datetime.strptime(' '.join([bar.date, bar.time]), '%Y%m%d %H%M%S') From bb2f45432f65940f0e0c938cd834c66431262a60 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Wed, 11 Jul 2018 22:07:14 +0800 Subject: [PATCH 076/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9ECoinbase?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/CryptoTrader/COINBASE_connect.json | 7 + examples/CryptoTrader/run.py | 4 +- vnpy/api/coinbase/__init__.py | 1 + vnpy/api/coinbase/vncoinbase.py | 298 ++++++++ vnpy/trader/gateway/bitmexGateway/__init__.py | 4 +- .../gateway/bitmexGateway/bitmexGateway.py | 4 +- .../coinbaseGateway/COINBASE_connect.json | 7 + .../gateway/coinbaseGateway/__init__.py | 10 + .../coinbaseGateway/coinbaseGateway.py | 664 ++++++++++++++++++ vnpy/trader/language/chinese/constant.py | 1 + vnpy/trader/language/english/constant.py | 1 + 11 files changed, 996 insertions(+), 5 deletions(-) create mode 100644 examples/CryptoTrader/COINBASE_connect.json create mode 100644 vnpy/api/coinbase/__init__.py create mode 100644 vnpy/api/coinbase/vncoinbase.py create mode 100644 vnpy/trader/gateway/coinbaseGateway/COINBASE_connect.json create mode 100644 vnpy/trader/gateway/coinbaseGateway/__init__.py create mode 100644 vnpy/trader/gateway/coinbaseGateway/coinbaseGateway.py diff --git a/examples/CryptoTrader/COINBASE_connect.json b/examples/CryptoTrader/COINBASE_connect.json new file mode 100644 index 00000000..9926112b --- /dev/null +++ b/examples/CryptoTrader/COINBASE_connect.json @@ -0,0 +1,7 @@ +{ + "apiKey": "", + "secretKey": "", + "passphrase": "", + "sessionCount": 10, + "symbols": ["ETH-USD"] +} \ No newline at end of file diff --git a/examples/CryptoTrader/run.py b/examples/CryptoTrader/run.py index 23868fa1..6f77a38e 100644 --- a/examples/CryptoTrader/run.py +++ b/examples/CryptoTrader/run.py @@ -23,7 +23,8 @@ from vnpy.trader.uiMainWindow import MainWindow from vnpy.trader.gateway import (huobiGateway, okexGateway, binanceGateway, bitfinexGateway, bitmexGateway, fcoinGateway, - bigoneGateway, lbankGateway) + bigoneGateway, lbankGateway, + coinbaseGateway) # 加载上层应用 from vnpy.trader.app import (algoTrading) @@ -42,6 +43,7 @@ def main(): me = MainEngine(ee) # 添加交易接口 + me.addGateway(coinbaseGateway) me.addGateway(lbankGateway) me.addGateway(bigoneGateway) me.addGateway(fcoinGateway) diff --git a/vnpy/api/coinbase/__init__.py b/vnpy/api/coinbase/__init__.py new file mode 100644 index 00000000..282a1dc0 --- /dev/null +++ b/vnpy/api/coinbase/__init__.py @@ -0,0 +1 @@ +from .vncoinbase import CoinbaseRestApi, CoinbaseWebsocketApi \ No newline at end of file diff --git a/vnpy/api/coinbase/vncoinbase.py b/vnpy/api/coinbase/vncoinbase.py new file mode 100644 index 00000000..7efad269 --- /dev/null +++ b/vnpy/api/coinbase/vncoinbase.py @@ -0,0 +1,298 @@ +# encoding: UTF-8 + +from __future__ import print_function + +import hashlib +import hmac +import base64 +import json +import ssl +import traceback + +from queue import Queue, Empty +from multiprocessing.dummy import Pool +from time import time +from urlparse import urlparse +from copy import copy +from urllib import urlencode +from threading import Thread + +from six.moves import input + +import requests +import websocket + + +REST_HOST = 'https://api-public.sandbox.pro.coinbase.com' +WEBSOCKET_HOST = 'wss://ws-feed-public.sandbox.pro.coinbase.com' + + + +######################################################################## +class CoinbaseRestApi(object): + """REST API""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.apiKey = '' + self.secretKey = '' + self.passphrase = '' + self.hmacKey = '' + + self.active = False + self.reqid = 0 + self.queue = Queue() + self.pool = None + self.sessionDict = {} # 会话对象字典 + + self.header = { + 'Content-Type': 'Application/JSON' + } + + #---------------------------------------------------------------------- + def init(self, apiKey, secretKey, passphrase): + """初始化""" + self.apiKey = apiKey + self.secretKey = secretKey + self.passphrase = passphrase + + self.hmacKey = base64.b64decode(self.secretKey) + + #---------------------------------------------------------------------- + def start(self, n=10): + """启动""" + if self.active: + return + + self.active = True + self.pool = Pool(n) + self.pool.map_async(self.run, range(n)) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.active = False + + if self.pool: + self.pool.close() + self.pool.join() + + #---------------------------------------------------------------------- + def addReq(self, method, path, callback, params=None, postdict=None): + """添加请求""" + self.reqid += 1 + req = (method, path, callback, params, postdict, self.reqid) + self.queue.put(req) + return self.reqid + + #---------------------------------------------------------------------- + def processReq(self, req, i): + """处理请求""" + method, path, callback, params, postdict, reqid = req + url = REST_HOST + path + timestamp = str(time()) + + if postdict: + rq = requests.Request(url=url, data=json.dumps(postdict)) + else: + rq = requests.Request(url=url) + p = rq.prepare() + + header = copy(self.header) + header['CB-ACCESS-KEY'] = self.apiKey + header['CB-ACCESS-PASSPHRASE'] = self.passphrase + header['CB-ACCESS-TIMESTAMP'] = timestamp + header['CB-ACCESS-SIGN'] = self.generateSignature(method, path, timestamp, params, body=p.body) + + # 使用长连接的session,比短连接的耗时缩短80% + session = self.sessionDict[i] + if postdict: + resp = session.request(method, url, headers=header, params=params, data=json.dumps(postdict)) + else: + resp = session.request(method, url, headers=header, params=params) + + #resp = requests.request(method, url, headers=header, params=params, data=postdict) + + code = resp.status_code + d = resp.json() + + if code == 200: + callback(d, reqid) + else: + self.onError(code, d) + + #---------------------------------------------------------------------- + def run(self, i): + """连续运行""" + self.sessionDict[i] = requests.Session() + + while self.active: + try: + req = self.queue.get(timeout=1) + self.processReq(req, i) + except Empty: + pass + + #---------------------------------------------------------------------- + def generateSignature(self, method, path, timestamp, params=None, body=None): + """生成签名""" + # 对params在HTTP报文路径中,以请求字段方式序列化 + if params: + query = urlencode(sorted(params.items())) + path = path + '?' + query + + if body is None: + body = '' + + msg = timestamp + method + path + body + msg = msg.encode('ascii') + signature = hmac.new(self.hmacKey, msg, hashlib.sha256) + signature64 = base64.b64encode(signature.digest()).decode('utf-8') + return signature64 + + #---------------------------------------------------------------------- + def onError(self, code, error): + """错误回调""" + print('on error') + print(code, error) + + #---------------------------------------------------------------------- + def onData(self, data, reqid): + """通用回调""" + print('on data') + print(data, reqid) + + +######################################################################## +class CoinbaseWebsocketApi(object): + """Websocket API""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.ws = None + self.thread = None + self.active = False + + #---------------------------------------------------------------------- + def start(self): + """启动""" + self.ws = websocket.create_connection(WEBSOCKET_HOST, + sslopt={'cert_reqs': ssl.CERT_NONE}) + + self.active = True + self.thread = Thread(target=self.run) + self.thread.start() + + self.onConnect() + + #---------------------------------------------------------------------- + def reconnect(self): + """重连""" + self.ws = websocket.create_connection(WEBSOCKET_HOST, + sslopt={'cert_reqs': ssl.CERT_NONE}) + + self.onConnect() + + #---------------------------------------------------------------------- + def run(self): + """运行""" + while self.active: + stream = self.ws.recv() + data = json.loads(stream) + self.onData(data) + + #try: + #stream = self.ws.recv() + #data = json.loads(stream) + #self.onData(data) + #except: + #msg = traceback.format_exc() + #self.onError(msg) + #self.reconnect() + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.active = False + + if self.thread: + self.thread.join() + + #---------------------------------------------------------------------- + def onConnect(self): + """连接回调""" + print('connected') + + #---------------------------------------------------------------------- + def onData(self, data): + """数据回调""" + print('-' * 30) + l = data.keys() + l.sort() + for k in l: + print(k, data[k]) + + #---------------------------------------------------------------------- + def onError(self, msg): + """错误回调""" + print(msg) + + #---------------------------------------------------------------------- + def sendReq(self, req): + """发出请求""" + self.ws.send(json.dumps(req)) + + + + + +if __name__ == '__main__': + API_KEY = '2982e190ce2785b862c36f7748ec6864' + API_SECRET = 'sUXjm5HZKA+Dru9+dtekGF6DlfQnHvbQCs+DaTuOTSBFR+vvMIiWkpPTwHcfZwNapSRpFhjNerrb111hojazIA==' + PASSPHRASE = 'vnpytesting' + + # REST测试 + rest = CoinbaseRestApi() + rest.init(API_KEY, API_SECRET, PASSPHRASE) + rest.start(1) + + #data = { + #'symbol': 'XBTUSD' + #} + #rest.addReq('POST', '/position/isolate', rest.onData, postdict=data) + + rest.addReq('GET', '/orders', rest.onData, {'status': 'all'}) + + ## WEBSOCKET测试 + #ws = CoinbaseWebsocketApi() + #ws.start() + + #req = { + #'type': 'subscribe', + #"product_ids": [ + #"ETH-USD" + #], + #"channels": ['level2'] + #} + #ws.sendReq(req) + + #expires = int(time()) + #method = 'GET' + #path = '/realtime' + #msg = method + path + str(expires) + #signature = hmac.new(API_SECRET, msg, digestmod=hashlib.sha256).hexdigest() + + #req = { + #'op': 'authKey', + #'args': [API_KEY, expires, signature] + #} + + #ws.sendReq(req) + + #req = {"op": "subscribe", "args": ['order', 'execution', 'position', 'margin']} + #req = {"op": "subscribe", "args": ['instrument']} + #ws.sendReq(req) + + input() diff --git a/vnpy/trader/gateway/bitmexGateway/__init__.py b/vnpy/trader/gateway/bitmexGateway/__init__.py index 4c149c8f..acf7d3a6 100644 --- a/vnpy/trader/gateway/bitmexGateway/__init__.py +++ b/vnpy/trader/gateway/bitmexGateway/__init__.py @@ -1,9 +1,9 @@ # encoding: UTF-8 from vnpy.trader import vtConstant -from .bitmexGateway import BitmexGateay +from .bitmexGateway import BitmexGateway -gatewayClass = BitmexGateay +gatewayClass = BitmexGateway gatewayName = 'BITMEX' gatewayDisplayName = 'BITMEX' gatewayType = vtConstant.GATEWAYTYPE_BTC diff --git a/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py b/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py index b8803692..a0c87eb6 100644 --- a/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py +++ b/vnpy/trader/gateway/bitmexGateway/bitmexGateway.py @@ -41,13 +41,13 @@ priceTypeMap[PRICETYPE_MARKETPRICE] = 'Market' ######################################################################## -class BitmexGateay(VtGateway): +class BitmexGateway(VtGateway): """Bitfinex接口""" #---------------------------------------------------------------------- def __init__(self, eventEngine, gatewayName=''): """Constructor""" - super(BitmexGateay, self).__init__(eventEngine, gatewayName) + super(BitmexGateway, self).__init__(eventEngine, gatewayName) self.restApi = RestApi(self) self.wsApi = WebsocketApi(self) diff --git a/vnpy/trader/gateway/coinbaseGateway/COINBASE_connect.json b/vnpy/trader/gateway/coinbaseGateway/COINBASE_connect.json new file mode 100644 index 00000000..9926112b --- /dev/null +++ b/vnpy/trader/gateway/coinbaseGateway/COINBASE_connect.json @@ -0,0 +1,7 @@ +{ + "apiKey": "", + "secretKey": "", + "passphrase": "", + "sessionCount": 10, + "symbols": ["ETH-USD"] +} \ No newline at end of file diff --git a/vnpy/trader/gateway/coinbaseGateway/__init__.py b/vnpy/trader/gateway/coinbaseGateway/__init__.py new file mode 100644 index 00000000..757613d2 --- /dev/null +++ b/vnpy/trader/gateway/coinbaseGateway/__init__.py @@ -0,0 +1,10 @@ +# encoding: UTF-8 + +from vnpy.trader import vtConstant +from .coinbaseGateway import CoinbaseGateway + +gatewayClass = CoinbaseGateway +gatewayName = 'COINBASE' +gatewayDisplayName = 'COINBASE' +gatewayType = vtConstant.GATEWAYTYPE_BTC +gatewayQryEnabled = True \ No newline at end of file diff --git a/vnpy/trader/gateway/coinbaseGateway/coinbaseGateway.py b/vnpy/trader/gateway/coinbaseGateway/coinbaseGateway.py new file mode 100644 index 00000000..f46ecaf9 --- /dev/null +++ b/vnpy/trader/gateway/coinbaseGateway/coinbaseGateway.py @@ -0,0 +1,664 @@ +# encoding: UTF-8 + +''' +vnpy.api.bitmex的gateway接入 +''' +from __future__ import print_function + +import os +import json +import hashlib +import hmac +import time +import traceback +import base64 +import uuid +from datetime import datetime, timedelta +from copy import copy + +from vnpy.api.coinbase import CoinbaseRestApi, CoinbaseWebsocketApi +from vnpy.trader.vtGateway import * +from vnpy.trader.vtFunction import getJsonPath, getTempPath + +# 方向映射 +directionMap = {} +directionMap[DIRECTION_LONG] = 'buy' +directionMap[DIRECTION_SHORT] = 'sell' +directionMapReverse = {v:k for k,v in directionMap.items()} + +# 价格类型映射 +priceTypeMap = {} +priceTypeMap[PRICETYPE_LIMITPRICE] = 'limit' +priceTypeMap[PRICETYPE_MARKETPRICE] = 'market' + +# 数据缓存字典 +cancelDict = {} # orderID:req +orderDict = {} # sysID:order +orderSysDict = {} # orderID:sysID + + +######################################################################## +class CoinbaseGateway(VtGateway): + """Bitfinex接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine, gatewayName=''): + """Constructor""" + super(CoinbaseGateway, self).__init__(eventEngine, gatewayName) + + self.restApi = RestApi(self) + self.wsApi = WebsocketApi(self) + + self.qryEnabled = False # 是否要启动循环查询 + + self.fileName = self.gatewayName + '_connect.json' + self.filePath = getJsonPath(self.fileName, __file__) + + #---------------------------------------------------------------------- + def connect(self): + """连接""" + try: + f = file(self.filePath) + except IOError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'读取连接配置出错,请检查' + self.onLog(log) + return + + # 解析json文件 + setting = json.load(f) + try: + apiKey = str(setting['apiKey']) + secretKey = str(setting['secretKey']) + passphrase = str(setting['passphrase']) + sessionCount = int(setting['sessionCount']) + symbols = setting['symbols'] + except KeyError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'连接配置缺少字段,请检查' + self.onLog(log) + return + + # 创建行情和交易接口对象 + self.restApi.connect(apiKey, secretKey, passphrase, sessionCount) + self.wsApi.connect(apiKey, secretKey, passphrase, symbols) + + # 初始化并启动查询 + self.initQuery() + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """发单""" + return self.restApi.sendOrder(orderReq) + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """撤单""" + self.restApi.cancelOrder(cancelOrderReq) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.restApi.close() + self.wsApi.close() + + #---------------------------------------------------------------------- + def initQuery(self): + """初始化连续查询""" + if self.qryEnabled: + # 需要循环的查询函数列表 + self.qryFunctionList = [self.restApi.qryAccount] + + self.qryCount = 0 # 查询触发倒计时 + self.qryTrigger = 1 # 查询触发点 + self.qryNextFunction = 0 # 上次运行的查询函数索引 + + self.startQuery() + + #---------------------------------------------------------------------- + def query(self, event): + """注册到事件处理引擎上的查询函数""" + self.qryCount += 1 + + if self.qryCount > self.qryTrigger: + # 清空倒计时 + self.qryCount = 0 + + # 执行查询函数 + function = self.qryFunctionList[self.qryNextFunction] + function() + + # 计算下次查询函数的索引,如果超过了列表长度,则重新设为0 + self.qryNextFunction += 1 + if self.qryNextFunction == len(self.qryFunctionList): + self.qryNextFunction = 0 + + #---------------------------------------------------------------------- + def startQuery(self): + """启动连续查询""" + self.eventEngine.register(EVENT_TIMER, self.query) + + #---------------------------------------------------------------------- + def setQryEnabled(self, qryEnabled): + """设置是否要启动循环查询""" + self.qryEnabled = qryEnabled + + +######################################################################## +class RestApi(CoinbaseRestApi): + """REST API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(RestApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.orderSysDict = {} + self.sysOrderDict = {} + self.cancelDict = {} + + #---------------------------------------------------------------------- + def connect(self, apiKey, secretKey, passphrase, sessionCount): + """连接服务器""" + self.init(apiKey, secretKey, passphrase) + self.start(sessionCount) + + self.writeLog(u'REST API启动成功') + + self.qryContract() + self.qryOrder() + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """""" + orderId = uuid.uuid1() + vtOrderID = '.'.join([self.gatewayName, str(orderId)]) + + req = { + 'product_id': orderReq.symbol, + 'side': directionMap[orderReq.direction], + 'price': str(orderReq.price), + 'size': str(orderReq.volume), + 'client_oid': str(orderId), + 'type': priceTypeMap[orderReq.priceType] + } + self.addReq('POST', '/orders', self.onSendOrder, postdict=req) + + return vtOrderID + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """""" + orderID = cancelOrderReq.orderID + if orderID not in orderSysDict: + cancelDict[orderID] = cancelOrderReq + return + + sysID = orderSysDict[orderID] + path = '/orders/%s' %sysID + self.addReq('DELETE', path, self.onCancelOrder) + + #---------------------------------------------------------------------- + def qryContract(self): + """""" + self.addReq('GET', '/products', self.onQryContract) + + #---------------------------------------------------------------------- + def qryAccount(self): + """""" + self.addReq('GET', '/accounts', self.onQryAccount) + + #---------------------------------------------------------------------- + def qryOrder(self): + """""" + req = {'status': 'all'} + self.addReq('GET', '/orders', self.onQryOrder, params=req) + + #---------------------------------------------------------------------- + def onSendOrder(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onCancelOrder(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onError(self, code, error): + """""" + e = VtErrorData() + e.gatewayName = self.gatewayName + e.errorID = code + e.errorMsg = error + self.gateway.onError(e) + + #---------------------------------------------------------------------- + def onQryContract(self, data, reqid): + """""" + for d in data: + contract = VtContractData() + contract.gatewayName = self.gatewayName + + contract.symbol = d['id'] + contract.exchange = EXCHANGE_COINBASE + contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) + contract.name = contract.vtSymbol + + contract.size = 1 + contract.priceTick = float(d['quote_increment']) + contract.productClass = PRODUCT_SPOT + + self.gateway.onContract(contract) + + self.writeLog(u'合约信息查询成功') + + #---------------------------------------------------------------------- + def onQryAccount(self, data, reqid): + """""" + for d in data: + account = VtAccountData() + account.gatewayName = self.gatewayName + + account.accountID = d['currency'] + account.vtAccountID = '.'.join([self.gatewayName, account.accountID]) + + account.balance = float(d['balance']) + account.available = float(d['available']) + + self.gateway.onAccount(account) + + #---------------------------------------------------------------------- + def onQryOrder(self, data, reqid): + """""" + for d in data: + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.orderID = d['id'] + order.vtOrderID = '.'.join([self.gatewayName, order.orderID]) + + order.symbol = d['product_id'] + order.exchange = EXCHANGE_COINBASE + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + order.direction = directionMapReverse[d['side']] + if 'price' in d: + order.price = float(d['price']) + order.totalVolume = float(d['size']) + order.tradedVolume = float(d['filled_size']) + + date, time = d['created_at'].split('T') + time = time.replace('Z', '') + order.orderTime = time + + if d['status'] == 'open': + if not order.tradedVolume: + order.status = STATUS_NOTTRADED + else: + order.status = STATUS_PARTTRADED + else: + if order.tradedVolume == order.totalVolume: + order.status = STATUS_ALLTRADED + else: + order.status = STATUS_CANCELLED + + self.gateway.onOrder(order) + + orderDict[order.orderID] = order + orderSysDict[order.orderID] = order.orderID + + self.writeLog(u'委托信息查询成功') + + +######################################################################## +class WebsocketApi(CoinbaseWebsocketApi): + """""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(WebsocketApi, self).__init__() + + self.gateway = gateway + self.gatewayName = gateway.gatewayName + + self.apiKey = '' + self.secretKey = '' + self.passphrase = '' + + self.callbackDict = { + 'ticker': self.onTicker, + 'snapshot': self.onSnapshot, + 'l2update': self.onL2update, + 'received': self.onOrderReceived, + 'open': self.onOrderOpen, + 'done': self.onOrderDone, + 'match': self.onMatch + } + + self.tickDict = {} + self.orderDict = {} + self.tradeSet = set() + + self.bidDict = {} + self.askDict = {} + + #---------------------------------------------------------------------- + def connect(self, apiKey, secretKey, passphrase, symbols): + """""" + self.apiKey = apiKey + self.secretKey = secretKey + self.passphrase = passphrase + self.symbols = symbols + + for symbol in symbols: + tick = VtTickData() + tick.gatewayName = self.gatewayName + tick.symbol = symbol + tick.exchange = EXCHANGE_COINBASE + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + self.tickDict[symbol] = tick + + self.start() + + #---------------------------------------------------------------------- + def onConnect(self): + """连接回调""" + self.writeLog(u'Websocket API连接成功') + + self.subscribe() + + #---------------------------------------------------------------------- + def onData(self, data): + """数据回调""" + if 'type' in data: + cb = self.callbackDict.get(data['type'], None) + if cb: + cb(data) + else: + self.writeLog(str(data)) + + #---------------------------------------------------------------------- + def onError(self, msg): + """错误回调""" + self.writeLog(msg) + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def authenticate(self): + """""" + timestamp = str(time.time()) + method = 'GET' + path = '/users/self/verify' + msg = timestamp + method + path + msg = msg.encode('ascii') + hmacKey = base64.b64decode(self.secretKey) + signature = hmac.new(hmacKey, msg, hashlib.sha256) + signature64 = base64.b64encode(signature.digest()).decode('utf-8').rstrip('\n') + + d = { + 'key': self.apiKey, + 'passphrase': self.passphrase, + 'timestamp': timestamp, + 'signature': signature64 + } + return d + + #---------------------------------------------------------------------- + def subscribe(self): + """""" + req = { + 'type': 'subscribe', + 'product_ids': self.symbols, + 'channels': ['ticker', 'level2', 'user'] + } + + d = self.authenticate() + req.update(d) + self.sendReq(req) + + #---------------------------------------------------------------------- + def onTicker(self, d): + """""" + symbol = d['product_id'] + + tick = self.tickDict.get(symbol, None) + if not tick: + return + + tick.openPrice = float(d['open_24h']) + tick.highPrice = float(d['high_24h']) + tick.lowPrice = float(d['low_24h']) + tick.lastPrice = float(d['price']) + tick.volume = float(d['volume_24h']) + + tick.datetime = datetime.now() + tick.date = tick.datetime.strftime('%Y%m%d') + tick.time = tick.datetime.strftime('%H:%M:%S') + + self.gateway.onTick(copy(tick)) + + #---------------------------------------------------------------------- + def onSnapshot(self, d): + """""" + symbol = d['product_id'] + tick = self.tickDict.get(symbol, None) + if not tick: + return + + bid = self.bidDict.setdefault(symbol, {}) + ask = self.askDict.setdefault(symbol, {}) + + for price, amount in d['bids']: + bid[float(price)] = float(amount) + + for price, amount in d['asks']: + ask[float(price)] = float(amount) + + self.generateTick(symbol) + + #---------------------------------------------------------------------- + def generateTick(self, symbol): + """""" + tick = self.tickDict[symbol] + bid = self.bidDict[symbol] + ask = self.askDict[symbol] + + + bidPriceList = bid.keys() + tick.bidPrice1 = bidPriceList[0] + tick.bidPrice2 = bidPriceList[1] + tick.bidPrice3 = bidPriceList[2] + tick.bidPrice4 = bidPriceList[3] + tick.bidPrice5 = bidPriceList[4] + + tick.bidVolume1 = bid[tick.bidPrice1] + tick.bidVolume2 = bid[tick.bidPrice2] + tick.bidVolume3 = bid[tick.bidPrice3] + tick.bidVolume4 = bid[tick.bidPrice4] + tick.bidVolume5 = bid[tick.bidPrice5] + + askPriceList = ask.keys() + askPriceList.sort() + + tick.askPrice1 = askPriceList[0] + tick.askPrice2 = askPriceList[1] + tick.askPrice3 = askPriceList[2] + tick.askPrice4 = askPriceList[3] + tick.askPrice5 = askPriceList[4] + + tick.askVolume1 = ask[tick.askPrice1] + tick.askVolume2 = ask[tick.askPrice2] + tick.askVolume3 = ask[tick.askPrice3] + tick.askVolume4 = ask[tick.askPrice4] + tick.askVolume5 = ask[tick.askPrice5] + + tick.datetime = datetime.now() + tick.date = tick.datetime.strftime('%Y%m%d') + tick.time = tick.datetime.strftime('%H:%M:%S') + + self.gateway.onTick(copy(tick)) + + #---------------------------------------------------------------------- + def onL2update(self, d): + """""" + symbol = d['product_id'] + tick = self.tickDict.get(symbol, None) + if not tick: + return + + bid = self.bidDict.setdefault(symbol, {}) + ask = self.askDict.setdefault(symbol, {}) + + for direction, price, amount in d['changes']: + price = float(price) + amount = float(amount) + + if direction == 'buy': + if amount: + bid[price] = amount + elif price in bid: + del bid[price] + else: + if amount: + ask[price] = amount + elif price in ask: + del ask[price] + + self.generateTick(symbol) + + #---------------------------------------------------------------------- + def onMatch(self, d): + """""" + trade = VtTradeData() + trade.gatewayName = self.gatewayName + + trade.symbol = d['product_id'] + trade.exchange = EXCHANGE_COINBASE + trade.vtSymbol = '.'.join([trade.symbol, trade.exchange]) + + if d['maker_order_id'] in orderDict: + order = orderDict[d['maker_order_id']] + else: + order = orderDict[d['taker_order_id']] + + trade.orderID = order.orderID + trade.vtOrderID = order.vtOrderID + + trade.tradeID = str(d['trade_id']) + trade.vtTradeID = '.'.join([trade.gatewayName, trade.tradeID]) + + trade.direction = order.direction + trade.price = float(d['price']) + trade.volume = float(d['size']) + + date, time = d['time'].split('T') + time = time.replace('Z', '') + trade.tradeTime = time + + self.gateway.onTrade(trade) + + #---------------------------------------------------------------------- + def onOrderReceived(self, d): + """""" + sysID = d['order_id'] + orderID = d['client_oid'] + + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.orderID = orderID + order.vtOrderID = '.'.join([self.gatewayName, order.orderID]) + + order.symbol = d['product_id'] + order.exchange = EXCHANGE_COINBASE + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + order.direction = directionMapReverse[d['side']] + if 'price' in d: + order.price = float(d['price']) + order.totalVolume = float(d['size']) + + date, time = d['time'].split('T') + time = time.replace('Z', '') + order.orderTime = time + + order.status = STATUS_NOTTRADED + + self.gateway.onOrder(order) + + # 缓存委托 + orderDict[sysID] = order + orderSysDict[orderID] = sysID + + # 执行待撤单 + if orderID in cancelDict: + req = cancelDict.pop(orderID) + self.gateway.cancelOrder(req) + + #---------------------------------------------------------------------- + def onOrderOpen(self, d): + """""" + order = orderDict.get(d['order_id'], None) + if not order: + return + + order.tradedVolume = order.totalVolume - float(d['remaining_size']) + if order.tradedVolume: + order.status = STATUS_PARTTRADED + self.gateway.onOrder(order) + + #---------------------------------------------------------------------- + def onOrderDone(self, d): + """""" + #print('done') + #print(d) + order = orderDict.get(d['order_id'], None) + if not order: + return + + order.tradedVolume = order.totalVolume - float(d['remaining_size']) + + if order.tradedVolume == order.totalVolume: + order.status = STATUS_ALLTRADED + else: + order.status = STATUS_CANCELLED + + self.gateway.onOrder(order) + + +#---------------------------------------------------------------------- +def printDict(d): + """""" + print('-' * 30) + l = d.keys() + l.sort() + for k in l: + print(k, d[k]) + \ No newline at end of file diff --git a/vnpy/trader/language/chinese/constant.py b/vnpy/trader/language/chinese/constant.py index 37417925..3069e785 100644 --- a/vnpy/trader/language/chinese/constant.py +++ b/vnpy/trader/language/chinese/constant.py @@ -93,6 +93,7 @@ EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所 EXCHANGE_FCOIN = 'FCOIN' # FCoin比特币交易所 EXCHANGE_BIGONE = 'BIGONE' # BigOne比特币交易所 +EXCHANGE_COINBASE = 'COINBASE' # Coinbase交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 diff --git a/vnpy/trader/language/english/constant.py b/vnpy/trader/language/english/constant.py index fe4d046f..7fa779d5 100644 --- a/vnpy/trader/language/english/constant.py +++ b/vnpy/trader/language/english/constant.py @@ -89,6 +89,7 @@ EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所 EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所 EXCHANGE_FCOIN = 'FCOIN' # FCoin比特币交易所 EXCHANGE_BIGONE = 'BIGONE' # BigOne比特币交易所 +EXCHANGE_COINBASE = 'COINBASE' # Coinbase交易所 # 货币类型 CURRENCY_USD = 'USD' # 美元 From 1e98c96a4ab2f33c41698d700ada2106b67eee2f Mon Sep 17 00:00:00 2001 From: nanoric Date: Thu, 12 Jul 2018 08:50:41 -0400 Subject: [PATCH 077/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9EBithumb=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/api/bithumb/__init__.py | 1 + vnpy/api/bithumb/vnbithumb.py | 165 ++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 vnpy/api/bithumb/__init__.py create mode 100644 vnpy/api/bithumb/vnbithumb.py diff --git a/vnpy/api/bithumb/__init__.py b/vnpy/api/bithumb/__init__.py new file mode 100644 index 00000000..c45d2fc5 --- /dev/null +++ b/vnpy/api/bithumb/__init__.py @@ -0,0 +1 @@ +from .vnbithumb import BithumbRestApi \ No newline at end of file diff --git a/vnpy/api/bithumb/vnbithumb.py b/vnpy/api/bithumb/vnbithumb.py new file mode 100644 index 00000000..4eee827b --- /dev/null +++ b/vnpy/api/bithumb/vnbithumb.py @@ -0,0 +1,165 @@ +# encoding: UTF-8 +import base64 +import hashlib +import hmac +import urllib +from multiprocessing.dummy import Pool +from time import time + +import requests +from queue import Queue, Empty + +REST_HOST = 'https://api.bithumb.com' + + +######################################################################## +class BithumbRestApi(object): + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.apiKey = '' + self.apiSecret = '' + + self.active = False + self.reqid = 0 + self.queue = Queue() + self.pool = None # type: Pool + self.sessionDict = {} + + #---------------------------------------------------------------------- + def init(self, apiKey, apiSecret): + """初始化""" + self.apiKey = str(apiKey) + self.apiSecret = str(apiSecret) + + #---------------------------------------------------------------------- + def start(self, n=10): + """启动""" + if self.active: + return + + self.active = True + self.pool = Pool(n) + self.pool.map_async(self.run, range(n)) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.active = False + + if self.pool: + self.pool.close() + self.pool.join() + + #---------------------------------------------------------------------- + def addReq(self, method, path, callback, params=None, postdict=None): + """添加请求""" + self.reqid += 1 + req = (method, path, callback, params, postdict, self.reqid) + self.queue.put(req) + return self.reqid + + def processReq(self, req, i): + """处理请求""" + method, path, callback, params, postdict, reqid = req + url = REST_HOST + path + + body = '' + header = {} + + # 如果调用的是需要签名的API,则加上签名 + # 不是以/public/开头的API都需要签名 + if path[:8] != '/public/': + nonce = BithumbRestApi.generateNonce() + + # 如果有参数,使用urlencode编码参数 + body = urllib.urlencode(postdict) if postdict else '' + + # 加上签名 + header = { + 'Api-Key': self.apiKey, + 'Api-Sign': self.generateSignature(path, body, nonce), + 'Api-Nonce': nonce, + 'Content-Type': 'application/x-www-form-urlencoded' + } + + try: + # 使用长连接的session,比短连接的耗时缩短20% + session = self.sessionDict[i] + resp = session.request(method, url, headers=header, params=params, data=body) + + code = resp.status_code + d = resp.json() + + if code == 200: + callback(d, reqid) + else: + self.onError(code, str(d)) + except Exception as e: + self.onError(type(e), e.message) + + #---------------------------------------------------------------------- + def run(self, i): + """连续运行""" + self.sessionDict[i] = requests.Session() + + while self.active: + try: + req = self.queue.get(timeout=1) + self.processReq(req, i) + except Empty: + pass + + #---------------------------------------------------------------------- + def generateSignature(self, path, body, nonce): + """生成签名""" + # 要签名的数据包括path,body和nonce,用\x00隔开 + data = path + chr(0) + body + chr(0) + nonce + + # 签名的核心方法:hmac-sha512 + # 签名流程:base64(hex(hmac-sha512(要签名的数据))) + return base64.b64encode(hmac.new(bytes(self.apiSecret), data, hashlib.sha512).hexdigest()) + + #---------------------------------------------------------------------- + def onError(self, code, error): + """错误回调""" + print('on error') + print(code, error) + + #---------------------------------------------------------------------- + def onData(self, data, reqid): + """通用回调""" + print('on data') + print(data, reqid) + + #---------------------------------------------------------------------- + @staticmethod # 静态函数:不依赖于self的函数 + def generateNonce(): + """生成时间戳""" + return str(int(time() * 1000)) + + +if __name__ == '__main__': + API_KEY = '0c2f5621ac18d26d51ce640b25eb44f9' + API_SECRET = '62bb8b4e263476f443f8d3dbf0aad6bc' + + rest = BithumbRestApi() + rest.init(apiKey=API_KEY, apiSecret=API_SECRET) + rest.start(1) + + + def on_btc_tick(jsonObj, reqid): + print('on_btc_tick : \n{}'.format(jsonObj)) + pass + + + def on_account_info(jsonObj, reqid): + print('on_account_info : \n{}'.format(jsonObj)) + pass + + + rest.addReq('POST', '/public/ticker/BTC', on_btc_tick) + rest.addReq('POST', '/info/account', on_account_info, postdict={'currency': 'BTC'}) + + raw_input() From 7fea0f862a3092f48348527040b60a313cc64145 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Fri, 13 Jul 2018 19:58:12 +0800 Subject: [PATCH 078/135] =?UTF-8?q?[Add]=E6=96=B0=E5=A2=9ECCXT=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/CryptoTrader/CCXT_connect.json | 6 + examples/CryptoTrader/run.py | 3 +- vnpy/trader/gateway/ccxtGateway/__init__.py | 10 + .../trader/gateway/ccxtGateway/ccxtGateway.py | 540 ++++++++++++++++++ 4 files changed, 558 insertions(+), 1 deletion(-) create mode 100644 examples/CryptoTrader/CCXT_connect.json create mode 100644 vnpy/trader/gateway/ccxtGateway/__init__.py create mode 100644 vnpy/trader/gateway/ccxtGateway/ccxtGateway.py diff --git a/examples/CryptoTrader/CCXT_connect.json b/examples/CryptoTrader/CCXT_connect.json new file mode 100644 index 00000000..cb367091 --- /dev/null +++ b/examples/CryptoTrader/CCXT_connect.json @@ -0,0 +1,6 @@ +{ + "exchange": "huobipro", + "apiKey": "", + "apiSecret": "", + "symbols": ["THETA/BTC", "BTC/USDT", "ETH/USDT"] +} \ No newline at end of file diff --git a/examples/CryptoTrader/run.py b/examples/CryptoTrader/run.py index 6f77a38e..09cac3df 100644 --- a/examples/CryptoTrader/run.py +++ b/examples/CryptoTrader/run.py @@ -24,7 +24,7 @@ from vnpy.trader.gateway import (huobiGateway, okexGateway, binanceGateway, bitfinexGateway, bitmexGateway, fcoinGateway, bigoneGateway, lbankGateway, - coinbaseGateway) + coinbaseGateway, ccxtGateway) # 加载上层应用 from vnpy.trader.app import (algoTrading) @@ -43,6 +43,7 @@ def main(): me = MainEngine(ee) # 添加交易接口 + me.addGateway(ccxtGateway) me.addGateway(coinbaseGateway) me.addGateway(lbankGateway) me.addGateway(bigoneGateway) diff --git a/vnpy/trader/gateway/ccxtGateway/__init__.py b/vnpy/trader/gateway/ccxtGateway/__init__.py new file mode 100644 index 00000000..f494e6e4 --- /dev/null +++ b/vnpy/trader/gateway/ccxtGateway/__init__.py @@ -0,0 +1,10 @@ +# encoding: UTF-8 + +from vnpy.trader import vtConstant +from .ccxtGateway import CcxtGateway + +gatewayClass = CcxtGateway +gatewayName = 'CCXT' +gatewayDisplayName = 'CCXT' +gatewayType = vtConstant.GATEWAYTYPE_BTC +gatewayQryEnabled = True \ No newline at end of file diff --git a/vnpy/trader/gateway/ccxtGateway/ccxtGateway.py b/vnpy/trader/gateway/ccxtGateway/ccxtGateway.py new file mode 100644 index 00000000..2df0e484 --- /dev/null +++ b/vnpy/trader/gateway/ccxtGateway/ccxtGateway.py @@ -0,0 +1,540 @@ +# encoding: UTF-8 + +''' +ccxt的gateway接入 +''' + +import os +import json +import time +import traceback +from queue import Queue, Empty +from multiprocessing.dummy import Pool +from datetime import datetime, timedelta +from copy import copy +from math import pow + +import ccxt + +from vnpy.trader.vtGateway import * +from vnpy.trader.vtFunction import getJsonPath, getTempPath + + + +# 委托状态类型映射 +statusMapReverse = {} +statusMapReverse['open'] = STATUS_NOTTRADED +statusMapReverse['closed'] = STATUS_ALLTRADED +statusMapReverse['canceled'] = STATUS_CANCELLED + +# 方向映射 +directionMap = {} +directionMap[DIRECTION_LONG] = 'buy' +directionMap[DIRECTION_SHORT] = 'sell' +directionMapReverse = {v:k for k,v in directionMap.items()} + +# 类型映射 +priceTypeMap = {} +priceTypeMap[PRICETYPE_MARKETPRICE] = 'market' +priceTypeMap[PRICETYPE_LIMITPRICE] = 'limit' + + +######################################################################## +class CcxtGateway(VtGateway): + """CCXT接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine, gatewayName=''): + """Constructor""" + super(CcxtGateway, self).__init__(eventEngine, gatewayName) + + self.api = CcxtApi(self) + + self.qryEnabled = False # 是否要启动循环查询 + + self.fileName = self.gatewayName + '_connect.json' + self.filePath = getJsonPath(self.fileName, __file__) + + #---------------------------------------------------------------------- + def connect(self): + """连接""" + try: + f = file(self.filePath) + except IOError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'读取连接配置出错,请检查' + self.onLog(log) + return + + # 解析json文件 + setting = json.load(f) + try: + exchange = str(setting['exchange']) + apiKey = str(setting['apiKey']) + apiSecret = str(setting['apiSecret']) + symbols = setting['symbols'] + except KeyError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'连接配置缺少字段,请检查' + self.onLog(log) + return + + # 创建行情和交易接口对象 + self.api.connect(exchange, apiKey, apiSecret, symbols) + + # 初始化并启动查询 + self.initQuery() + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """发单""" + return self.api.sendOrder(orderReq) + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """撤单""" + self.api.cancelOrder(cancelOrderReq) + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + self.api.close() + + #---------------------------------------------------------------------- + def initQuery(self): + """初始化连续查询""" + if self.qryEnabled: + # 需要循环的查询函数列表 + self.qryFunctionList = [self.api.qryMarketData, + self.api.qryAccount, + self.api.qryOrder] + + self.qryCount = 0 # 查询触发倒计时 + self.qryTrigger = 1 # 查询触发点 + self.qryNextFunction = 0 # 上次运行的查询函数索引 + + self.startQuery() + + #---------------------------------------------------------------------- + def query(self, event): + """注册到事件处理引擎上的查询函数""" + self.qryCount += 1 + + if self.qryCount > self.qryTrigger: + # 清空倒计时 + self.qryCount = 0 + + # 执行查询函数 + function = self.qryFunctionList[self.qryNextFunction] + function() + + # 计算下次查询函数的索引,如果超过了列表长度,则重新设为0 + self.qryNextFunction += 1 + if self.qryNextFunction == len(self.qryFunctionList): + self.qryNextFunction = 0 + + #---------------------------------------------------------------------- + def startQuery(self): + """启动连续查询""" + self.eventEngine.register(EVENT_TIMER, self.query) + + #---------------------------------------------------------------------- + def setQryEnabled(self, qryEnabled): + """设置是否要启动循环查询""" + self.qryEnabled = qryEnabled + + +######################################################################## +class CcxtApi(object): + """REST API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway): + """Constructor""" + super(CcxtApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.api = None + self.active = False + self.queue = Queue() + self.pool = None + + self.reqID = 0 + self.localID = 0 + self.tradeID = 0 + + self.orderDict = {} # sysID:order + self.localSysDict = {} # localID:sysID + self.reqOrderDict = {} # reqID:order + self.cancelDict = {} # localID:req + + self.depthQryDict = {} # reqID:symbol + + self.tickDict = {} + + #---------------------------------------------------------------------- + def run(self, i): + """""" + while self.active: + try: + req = self.queue.get(timeout=1) + self.processReq(req) + except Empty: + pass + + #---------------------------------------------------------------------- + def addReq(self, func, args, callback): + """""" + self.reqID += 1 + req = (func, args, callback, self.reqID) + self.queue.put(req) + return self.reqID + + #---------------------------------------------------------------------- + def processReq(self, req): + """""" + try: + func, args, callback, reqID = req + data = func(*args) + callback(data, reqID) + except: + code = 'api' + msg = traceback.format_exc() + self.onError(code, msg) + + #---------------------------------------------------------------------- + def connect(self, exchange, apiKey, apiSecret, symbols): + """连接服务器""" + self.exchange = exchange.upper() + self.symbols = symbols + + # 初始化CCXT对象 + config = { + 'apiKey': apiKey, + 'secret': apiSecret + } + apiClass = ccxt.__getattribute__(exchange) + self.api = apiClass(config) + #self.api = ccxt.huobipro() + + # 启动线程池 + n = 10 + self.active = True + self.pool = Pool(n) + self.pool.map_async(self.run, range(n)) + + self.writeLog(u'CCXT API(%s)启动成功' %exchange) + + # 初始化查询 + self.qryContract() + + #---------------------------------------------------------------------- + def close(self): + """""" + self.active = False + if self.pool: + self.pool.close() + self.pool.join() + + #---------------------------------------------------------------------- + def writeLog(self, content): + """发出日志""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = content + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def sendOrder(self, orderReq): + """""" + self.localID += 1 + orderID = str(self.localID) + vtOrderID = '.'.join([self.gatewayName, orderID]) + + req = [ + orderReq.symbol, + priceTypeMap[orderReq.priceType], + directionMap[orderReq.direction], + orderReq.volume, + orderReq.price + ] + + func = self.api.createOrder + reqid = self.addReq(func, req, self.onSendOrder) + + # 缓存委托数据对象 + order = VtOrderData() + order.gatewayName = self.gatewayName + order.symbol = orderReq.symbol + order.exchange = self.exchange + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + order.orderID = orderID + order.vtOrderID = vtOrderID + order.price = orderReq.price + order.totalVolume = orderReq.volume + order.direction = orderReq.direction + order.status = STATUS_UNKNOWN + + self.reqOrderDict[reqid] = order + + return vtOrderID + + #---------------------------------------------------------------------- + def cancelOrder(self, cancelOrderReq): + """""" + localID = cancelOrderReq.orderID + + if localID in self.localSysDict: + sysID = self.localSysDict[localID] + func = self.api.cancelOrder + reqid = self.addReq(func, [sysID], self.onCancelOrder) + else: + self.cancelDict[localID] = cancelOrderReq + + #---------------------------------------------------------------------- + def qryContract(self): + """""" + func = self.api.fetchMarkets + self.addReq(func, [], self.onQryContract) + + #---------------------------------------------------------------------- + def qryTicker(self): + """""" + for symbol in self.symbols: + func = self.api.fetchTicker + self.addReq(func, [symbol], self.onQryTicker) + + #---------------------------------------------------------------------- + def qryDepth(self): + """""" + for symbol in self.symbols: + func = self.api.fetchOrderBook + i = self.addReq(func, [symbol], self.onQryDepth) + self.depthQryDict[i] = symbol + + #---------------------------------------------------------------------- + def qryOrder(self): + """""" + for symbol in self.symbols: + func = self.api.fetchOrders + i = self.addReq(func, [symbol], self.onQryOrder) + + #---------------------------------------------------------------------- + def qryAccount(self): + """""" + func = self.api.fetchBalance + self.addReq(func, [], self.onQryAccount) + + #---------------------------------------------------------------------- + def onSendOrder(self, data, reqid): + """""" + order = self.reqOrderDict[reqid] + localID = order.orderID + sysID = data['id'] + + self.localSysDict[localID] = sysID + self.orderDict[sysID] = order + + self.gateway.onOrder(order) + + # 发出等待的撤单委托 + if localID in self.cancelDict: + req = self.cancelDict[localID] + self.cancelOrder(req) + del self.cancelDict[localID] + + #---------------------------------------------------------------------- + def onCancelOrder(self, data, reqid): + """""" + pass + + #---------------------------------------------------------------------- + def onError(self, code, error): + """""" + msg = u'发生异常,错误代码:%s,错误信息:%s' %(code, error) + self.writeLog(msg) + + #---------------------------------------------------------------------- + def onQryOrder(self, data, reqid): + """""" + for d in data: + orderUpdated = False + tradeUpdated = False + + # 获取委托对象 + sysID = d['id'] + if sysID in self.orderDict: + order = self.orderDict[sysID] + else: + order = VtOrderData() + order.gatewayName = self.gatewayName + + order.symbol = d['symbol'] + order.exchange = EXCHANGE_BIGONE + order.vtSymbol = '.'.join([order.symbol, order.exchange]) + + self.localID += 1 + localID = str(self.localID) + self.localSysDict[localID] = sysID + + order.orderID = localID + order.vtOrderID = '.'.join([order.gatewayName, order.orderID]) + + order.direction = directionMapReverse[d['side']] + order.price = float(d['price']) + order.totalVolume = float(d['amount']) + + dt = datetime.fromtimestamp(d['timestamp']/1000) + order.orderTime = dt.strftime('%H:%M:%S') + + self.orderDict[sysID] = order + orderUpdated = True + + # 检查是否委托有变化 + newTradedVolume = float(d['filled']) + newStatus = statusMapReverse[d['status']] + + if newTradedVolume != float(order.tradedVolume) or newStatus != order.status: + orderUpdated = True + + if newTradedVolume != float(order.tradedVolume): + tradeUpdated = True + newVolume = newTradedVolume - order.tradedVolume + + order.tradedVolume = newTradedVolume + order.status = newStatus + + # 若有更新才推送 + if orderUpdated: + self.gateway.onOrder(order) + + if tradeUpdated: + # 推送成交 + trade = VtTradeData() + trade.gatewayName = order.gatewayName + + trade.symbol = order.symbol + trade.vtSymbol = order.vtSymbol + + trade.orderID = order.orderID + trade.vtOrderID = order.vtOrderID + + self.tradeID += 1 + trade.tradeID = str(self.tradeID) + trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID]) + + trade.direction = order.direction + trade.price = order.price + trade.volume = newTradedVolume + trade.tradeTime = datetime.now().strftime('%H:%M:%S') + + self.gateway.onTrade(trade) + + #---------------------------------------------------------------------- + def onQryAccount(self, data, reqid): + """""" + total = data['total'] + used = data['used'] + free = data['free'] + + for currency in total.keys(): + account = VtAccountData() + account.gatewayName = self.gatewayName + + account.accountID = currency + account.vtAccountID = '.'.join([self.gatewayName, account.accountID]) + + account.balance = total[currency] + account.available = free[currency] + + self.gateway.onAccount(account) + + #---------------------------------------------------------------------- + def onQryContract(self, data, reqid): + """""" + for d in data: + contract = VtContractData() + contract.gatewayName = self.gatewayName + + contract.symbol = d['symbol'] + contract.exchange = self.exchange + contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) + contract.name = contract.vtSymbol + contract.productClass = PRODUCT_SPOT + contract.priceTick = pow(10, -int(d['precision']['price'])) + contract.size = 1 + + self.gateway.onContract(contract) + + self.writeLog(u'合约信息查询完成') + + #---------------------------------------------------------------------- + def onQryTicker(self, data, reqid): + """""" + symbol = data['symbol'] + tick = self.getTick(symbol) + + tick.openPrice = float(data['open']) + tick.highPrice = float(data['high']) + tick.lowPrice = float(data['low']) + tick.lastPrice = float(data['close']) + tick.volume = float(data['quoteVolume']) + tick.datetime = datetime.fromtimestamp(data['timestamp']/1000) + tick.date = tick.datetime.strftime('%Y%m%d') + tick.time = tick.datetime.strftime('%H:%M:%S') + + # 只有订阅了深度行情才推送 + if tick.bidPrice1: + self.gateway.onTick(tick) + + #---------------------------------------------------------------------- + def onQryDepth(self, data, reqid): + """""" + symbol = self.depthQryDict.pop(reqid) + tick = self.getTick(symbol) + + for n, bid in enumerate(data['bids'][:5]): + tick.__setattr__('bidPrice%s' %(n+1), float(bid[0])) + tick.__setattr__('bidVolume%s' %(n+1), float(bid[1])) + + for n, ask in enumerate(data['asks'][:5]): + tick.__setattr__('askPrice%s' %(n+1), float(ask[0])) + tick.__setattr__('askVolume%s' %(n+1), float(ask[1])) + + tick.datetime = datetime.fromtimestamp(data['timestamp']/1000) + tick.date = tick.datetime.strftime('%Y%m%d') + tick.time = tick.datetime.strftime('%H:%M:%S') + + if tick.lastPrice: + self.gateway.onTick(tick) + + #---------------------------------------------------------------------- + def getTick(self, symbol): + """""" + tick = self.tickDict.get(symbol, None) + + if not tick: + tick = VtTickData() + tick.gatewayName = self.gatewayName + tick.symbol = symbol + tick.exchange = self.exchange + tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) + self.tickDict[symbol] = tick + + return tick + + #---------------------------------------------------------------------- + def qryMarketData(self): + """""" + self.qryDepth() + self.qryTicker() + \ No newline at end of file From c998cf9e7a0aaecdc0caefd8db644fc27b44ddc0 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 17 Jul 2018 14:05:01 +0800 Subject: [PATCH 079/135] =?UTF-8?q?[Fix]=E4=BF=AE=E6=94=B9=E5=A7=94?= =?UTF-8?q?=E6=89=98=E6=95=B0=E9=87=8F=E4=B8=BAfloat=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=8E=A5=E5=8F=A3=E5=A7=94=E6=89=98=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/uiBasicWidget.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/vnpy/trader/uiBasicWidget.py b/vnpy/trader/uiBasicWidget.py index 2446f017..d3e9dbab 100644 --- a/vnpy/trader/uiBasicWidget.py +++ b/vnpy/trader/uiBasicWidget.py @@ -1050,13 +1050,30 @@ class TradingWidget(QtWidgets.QFrame): gatewayName = contract.gatewayName exchange = contract.exchange # 保证有交易所代码 vtSymbol = contract.vtSymbol - + + # 获取价格 + priceText = self.linePrice.text() + if not priceText: + return + price = float(priceText) + + # 获取数量 + volumeText = self.lineVolume.text() + if not volumeText: + return + + if '.' in volumeText: + volume = float(volumeText) + else: + volume = int(volumeText) + + # 委托 req = VtOrderReq() req.symbol = symbol req.exchange = exchange req.vtSymbol = vtSymbol - req.price = float(self.linePrice.text()) - req.volume = float(self.lineVolume.text()) + req.price = price + req.volume = volume req.direction = unicode(self.comboDirection.currentText()) req.priceType = unicode(self.comboPriceType.currentText()) req.offset = unicode(self.comboOffset.currentText()) From 65f671608927031bb10f016727da96815ea80375 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 17 Jul 2018 16:27:50 +0800 Subject: [PATCH 080/135] =?UTF-8?q?[Del]=E7=A7=BB=E9=99=A4QuantOS=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=86=85=E5=AE=B9=EF=BC=88=E6=9A=82=E6=97=B6=E6=94=BE?= =?UTF-8?q?=E5=88=B0beta=E7=9B=AE=E5=BD=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {examples => beta/quantos}/JaqsService/CTP_connect.json | 0 {examples => beta/quantos}/JaqsService/JS_setting.json | 0 {examples => beta/quantos}/JaqsService/VT_setting.json | 0 {examples => beta/quantos}/JaqsService/run.py | 0 {examples => beta/quantos}/JaqsService/runUI.py | 0 {examples => beta/quantos}/QuantosDataService/README.md | 0 {examples => beta/quantos}/QuantosDataService/config.json | 0 {examples => beta/quantos}/QuantosDataService/dataService.py | 0 {examples => beta/quantos}/QuantosDataService/downloadData.py | 0 {examples => beta/quantos}/QuantosDataService/runService.py | 0 .../gateway => beta/quantos}/tkproGateway/DataApi/LICENSE | 0 .../gateway => beta/quantos}/tkproGateway/DataApi/README.md | 0 .../gateway => beta/quantos}/tkproGateway/DataApi/__init__.py | 0 .../gateway => beta/quantos}/tkproGateway/DataApi/data_api.py | 0 .../gateway => beta/quantos}/tkproGateway/DataApi/jrpc_py.py | 0 .../gateway => beta/quantos}/tkproGateway/DataApi/utils.py | 0 .../gateway => beta/quantos}/tkproGateway/TKPRO_connect.json | 0 .../gateway => beta/quantos}/tkproGateway/TradeApi/LICENSE | 0 .../gateway => beta/quantos}/tkproGateway/TradeApi/README.md | 0 .../quantos}/tkproGateway/TradeApi/__init__.py | 0 .../gateway => beta/quantos}/tkproGateway/TradeApi/jrpc_py.py | 0 .../quantos}/tkproGateway/TradeApi/trade_api.py | 0 .../gateway => beta/quantos}/tkproGateway/TradeApi/utils.py | 0 .../trader/gateway => beta/quantos}/tkproGateway/__init__.py | 0 .../gateway => beta/quantos}/tkproGateway/tkproGateway.py | 0 examples/README.md | 4 +--- install.bat | 2 -- install.sh | 1 - 28 files changed, 1 insertion(+), 6 deletions(-) rename {examples => beta/quantos}/JaqsService/CTP_connect.json (100%) rename {examples => beta/quantos}/JaqsService/JS_setting.json (100%) rename {examples => beta/quantos}/JaqsService/VT_setting.json (100%) rename {examples => beta/quantos}/JaqsService/run.py (100%) rename {examples => beta/quantos}/JaqsService/runUI.py (100%) rename {examples => beta/quantos}/QuantosDataService/README.md (100%) rename {examples => beta/quantos}/QuantosDataService/config.json (100%) rename {examples => beta/quantos}/QuantosDataService/dataService.py (100%) rename {examples => beta/quantos}/QuantosDataService/downloadData.py (100%) rename {examples => beta/quantos}/QuantosDataService/runService.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/DataApi/LICENSE (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/DataApi/README.md (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/DataApi/__init__.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/DataApi/data_api.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/DataApi/jrpc_py.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/DataApi/utils.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/TKPRO_connect.json (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/TradeApi/LICENSE (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/TradeApi/README.md (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/TradeApi/__init__.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/TradeApi/jrpc_py.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/TradeApi/trade_api.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/TradeApi/utils.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/__init__.py (100%) rename {vnpy/trader/gateway => beta/quantos}/tkproGateway/tkproGateway.py (100%) diff --git a/examples/JaqsService/CTP_connect.json b/beta/quantos/JaqsService/CTP_connect.json similarity index 100% rename from examples/JaqsService/CTP_connect.json rename to beta/quantos/JaqsService/CTP_connect.json diff --git a/examples/JaqsService/JS_setting.json b/beta/quantos/JaqsService/JS_setting.json similarity index 100% rename from examples/JaqsService/JS_setting.json rename to beta/quantos/JaqsService/JS_setting.json diff --git a/examples/JaqsService/VT_setting.json b/beta/quantos/JaqsService/VT_setting.json similarity index 100% rename from examples/JaqsService/VT_setting.json rename to beta/quantos/JaqsService/VT_setting.json diff --git a/examples/JaqsService/run.py b/beta/quantos/JaqsService/run.py similarity index 100% rename from examples/JaqsService/run.py rename to beta/quantos/JaqsService/run.py diff --git a/examples/JaqsService/runUI.py b/beta/quantos/JaqsService/runUI.py similarity index 100% rename from examples/JaqsService/runUI.py rename to beta/quantos/JaqsService/runUI.py diff --git a/examples/QuantosDataService/README.md b/beta/quantos/QuantosDataService/README.md similarity index 100% rename from examples/QuantosDataService/README.md rename to beta/quantos/QuantosDataService/README.md diff --git a/examples/QuantosDataService/config.json b/beta/quantos/QuantosDataService/config.json similarity index 100% rename from examples/QuantosDataService/config.json rename to beta/quantos/QuantosDataService/config.json diff --git a/examples/QuantosDataService/dataService.py b/beta/quantos/QuantosDataService/dataService.py similarity index 100% rename from examples/QuantosDataService/dataService.py rename to beta/quantos/QuantosDataService/dataService.py diff --git a/examples/QuantosDataService/downloadData.py b/beta/quantos/QuantosDataService/downloadData.py similarity index 100% rename from examples/QuantosDataService/downloadData.py rename to beta/quantos/QuantosDataService/downloadData.py diff --git a/examples/QuantosDataService/runService.py b/beta/quantos/QuantosDataService/runService.py similarity index 100% rename from examples/QuantosDataService/runService.py rename to beta/quantos/QuantosDataService/runService.py diff --git a/vnpy/trader/gateway/tkproGateway/DataApi/LICENSE b/beta/quantos/tkproGateway/DataApi/LICENSE similarity index 100% rename from vnpy/trader/gateway/tkproGateway/DataApi/LICENSE rename to beta/quantos/tkproGateway/DataApi/LICENSE diff --git a/vnpy/trader/gateway/tkproGateway/DataApi/README.md b/beta/quantos/tkproGateway/DataApi/README.md similarity index 100% rename from vnpy/trader/gateway/tkproGateway/DataApi/README.md rename to beta/quantos/tkproGateway/DataApi/README.md diff --git a/vnpy/trader/gateway/tkproGateway/DataApi/__init__.py b/beta/quantos/tkproGateway/DataApi/__init__.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/DataApi/__init__.py rename to beta/quantos/tkproGateway/DataApi/__init__.py diff --git a/vnpy/trader/gateway/tkproGateway/DataApi/data_api.py b/beta/quantos/tkproGateway/DataApi/data_api.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/DataApi/data_api.py rename to beta/quantos/tkproGateway/DataApi/data_api.py diff --git a/vnpy/trader/gateway/tkproGateway/DataApi/jrpc_py.py b/beta/quantos/tkproGateway/DataApi/jrpc_py.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/DataApi/jrpc_py.py rename to beta/quantos/tkproGateway/DataApi/jrpc_py.py diff --git a/vnpy/trader/gateway/tkproGateway/DataApi/utils.py b/beta/quantos/tkproGateway/DataApi/utils.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/DataApi/utils.py rename to beta/quantos/tkproGateway/DataApi/utils.py diff --git a/vnpy/trader/gateway/tkproGateway/TKPRO_connect.json b/beta/quantos/tkproGateway/TKPRO_connect.json similarity index 100% rename from vnpy/trader/gateway/tkproGateway/TKPRO_connect.json rename to beta/quantos/tkproGateway/TKPRO_connect.json diff --git a/vnpy/trader/gateway/tkproGateway/TradeApi/LICENSE b/beta/quantos/tkproGateway/TradeApi/LICENSE similarity index 100% rename from vnpy/trader/gateway/tkproGateway/TradeApi/LICENSE rename to beta/quantos/tkproGateway/TradeApi/LICENSE diff --git a/vnpy/trader/gateway/tkproGateway/TradeApi/README.md b/beta/quantos/tkproGateway/TradeApi/README.md similarity index 100% rename from vnpy/trader/gateway/tkproGateway/TradeApi/README.md rename to beta/quantos/tkproGateway/TradeApi/README.md diff --git a/vnpy/trader/gateway/tkproGateway/TradeApi/__init__.py b/beta/quantos/tkproGateway/TradeApi/__init__.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/TradeApi/__init__.py rename to beta/quantos/tkproGateway/TradeApi/__init__.py diff --git a/vnpy/trader/gateway/tkproGateway/TradeApi/jrpc_py.py b/beta/quantos/tkproGateway/TradeApi/jrpc_py.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/TradeApi/jrpc_py.py rename to beta/quantos/tkproGateway/TradeApi/jrpc_py.py diff --git a/vnpy/trader/gateway/tkproGateway/TradeApi/trade_api.py b/beta/quantos/tkproGateway/TradeApi/trade_api.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/TradeApi/trade_api.py rename to beta/quantos/tkproGateway/TradeApi/trade_api.py diff --git a/vnpy/trader/gateway/tkproGateway/TradeApi/utils.py b/beta/quantos/tkproGateway/TradeApi/utils.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/TradeApi/utils.py rename to beta/quantos/tkproGateway/TradeApi/utils.py diff --git a/vnpy/trader/gateway/tkproGateway/__init__.py b/beta/quantos/tkproGateway/__init__.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/__init__.py rename to beta/quantos/tkproGateway/__init__.py diff --git a/vnpy/trader/gateway/tkproGateway/tkproGateway.py b/beta/quantos/tkproGateway/tkproGateway.py similarity index 100% rename from vnpy/trader/gateway/tkproGateway/tkproGateway.py rename to beta/quantos/tkproGateway/tkproGateway.py diff --git a/examples/README.md b/examples/README.md index 0739e3f2..ef046ab8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -22,6 +22,4 @@ * TushareDataService:TuShare历史行情服务(A股) -* FutuDataService:富途证券历史行情服务(美股、港股) - -* QuantosDataService: quantOS历史行情服务(A股、期货) \ No newline at end of file +* FutuDataService:富途证券历史行情服务(美股、港股) \ No newline at end of file diff --git a/install.bat b/install.bat index f8af121f..564150f0 100644 --- a/install.bat +++ b/install.bat @@ -6,7 +6,5 @@ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/f conda config --set show_channel_urls yes conda install -c quantopian ta-lib=0.4.9 -y -::conda install -c conda-forge python-snappy -y - :: Install vn.py python setup.py install \ No newline at end of file diff --git a/install.sh b/install.sh index e4337eab..27fd9235 100755 --- a/install.sh +++ b/install.sh @@ -24,7 +24,6 @@ pip install -r requirements.txt conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --set show_channel_urls yes conda install -c quantopian ta-lib=0.4.9 -#conda install -c conda-forge python-snappy #Install vn.py python setup.py install From aced9cfbd2b4a3078d6017be5e37a36962a0b20e Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 17 Jul 2018 16:39:17 +0800 Subject: [PATCH 081/135] [FIX]Close #936 --- vnpy/trader/app/algoTrading/stopAlgo.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vnpy/trader/app/algoTrading/stopAlgo.py b/vnpy/trader/app/algoTrading/stopAlgo.py index a08d09d7..d9bf2ecc 100644 --- a/vnpy/trader/app/algoTrading/stopAlgo.py +++ b/vnpy/trader/app/algoTrading/stopAlgo.py @@ -3,6 +3,8 @@ from __future__ import division from collections import OrderedDict +from six import text_type + from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT, OFFSET_OPEN, OFFSET_CLOSE) from vnpy.trader.uiQt import QtWidgets @@ -10,7 +12,9 @@ from vnpy.trader.uiQt import QtWidgets from .algoTemplate import AlgoTemplate from .uiAlgoWidget import AlgoWidget, QtWidgets -from six import text_type + + +STATUS_FINISHED = set([STATUS_ALLTRADED, STATUS_CANCELLED, STATUS_REJECTED]) ######################################################################## From a835a7ab2cd52677a6b250043d3d1d6db31a297b Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 17 Jul 2018 16:40:56 +0800 Subject: [PATCH 082/135] [Fix]Close #928 --- vnpy/trader/app/algoTrading/stopAlgo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnpy/trader/app/algoTrading/stopAlgo.py b/vnpy/trader/app/algoTrading/stopAlgo.py index d9bf2ecc..cb711121 100644 --- a/vnpy/trader/app/algoTrading/stopAlgo.py +++ b/vnpy/trader/app/algoTrading/stopAlgo.py @@ -53,7 +53,7 @@ class StopAlgo(AlgoTemplate): # 如果到达止损位,才触发委托 if (self.direction == DIRECTION_LONG and - tick.lastPrice >= self.price): + tick.lastPrice >= self.stopPrice): # 计算超价委托价格 price = self.stopPrice + self.priceAdd From 06ceccc3c3191d59b384950e29ab490f7bf05319 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 17 Jul 2018 16:41:52 +0800 Subject: [PATCH 083/135] [Fix]Close #927 --- vnpy/trader/app/algoTrading/dmaAlgo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnpy/trader/app/algoTrading/dmaAlgo.py b/vnpy/trader/app/algoTrading/dmaAlgo.py index 2f0736f2..54281d7d 100644 --- a/vnpy/trader/app/algoTrading/dmaAlgo.py +++ b/vnpy/trader/app/algoTrading/dmaAlgo.py @@ -55,7 +55,7 @@ class DmaAlgo(AlgoTemplate): else: func = self.sell - self.vtOrderID = func(self.vtSymbol, self.price, self.volume, + self.vtOrderID = func(self.vtSymbol, self.price, self.totalVolume, self.priceType, self.offset) # 更新变量 From 9cb8b2be433f1371696c9b28e5c8c66ad187b48d Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Tue, 17 Jul 2018 17:20:05 +0800 Subject: [PATCH 084/135] =?UTF-8?q?[Del]=E7=A7=BB=E9=99=A4vnpy/api/ib?= =?UTF-8?q?=E4=B8=8B=E7=9A=84build=E7=BC=96=E8=AF=91=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=20close=20#917?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 14 +- vnpy/api/ib/build/CMakeCache.txt | 393 ------------- .../CMakeFiles/3.5.1/CMakeCXXCompiler.cmake | 68 --- .../3.5.1/CMakeDetermineCompilerABI_CXX.bin | Bin 8656 -> 0 bytes .../build/CMakeFiles/3.5.1/CMakeSystem.cmake | 15 - .../CompilerIdCXX/CMakeCXXCompilerId.cpp | 533 ------------------ .../CMakeFiles/3.5.1/CompilerIdCXX/a.out | Bin 8808 -> 0 bytes .../CMakeDirectoryInformation.cmake | 16 - vnpy/api/ib/build/CMakeFiles/CMakeError.log | 55 -- vnpy/api/ib/build/CMakeFiles/CMakeOutput.log | 384 ------------- .../CheckFunctionExists.cxx | 26 - vnpy/api/ib/build/CMakeFiles/Makefile.cmake | 108 ---- vnpy/api/ib/build/CMakeFiles/Makefile2 | 108 ---- .../ib/build/CMakeFiles/TargetDirectories.txt | 3 - .../api/ib/build/CMakeFiles/cmake.check_cache | 1 - .../api/ib/build/CMakeFiles/feature_tests.bin | Bin 12696 -> 0 bytes .../api/ib/build/CMakeFiles/feature_tests.cxx | 405 ------------- vnpy/api/ib/build/CMakeFiles/progress.marks | 1 - .../CMakeFiles/vnib.dir/CXX.includecache | 198 ------- .../CMakeFiles/vnib.dir/DependInfo.cmake | 29 - .../ib/build/CMakeFiles/vnib.dir/build.make | 122 ---- .../CMakeFiles/vnib.dir/cmake_clean.cmake | 10 - .../build/CMakeFiles/vnib.dir/depend.internal | 30 - .../ib/build/CMakeFiles/vnib.dir/depend.make | 30 - .../ib/build/CMakeFiles/vnib.dir/flags.make | 10 - .../api/ib/build/CMakeFiles/vnib.dir/link.txt | 1 - .../build/CMakeFiles/vnib.dir/progress.make | 3 - .../CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o | Bin 4919576 -> 0 bytes vnpy/api/ib/build/Makefile | 178 ------ vnpy/api/ib/build/cmake_install.cmake | 44 -- vnpy/api/ib/build/lib/vnib.so | Bin 3047352 -> 0 bytes 31 files changed, 6 insertions(+), 2779 deletions(-) delete mode 100644 vnpy/api/ib/build/CMakeCache.txt delete mode 100644 vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeCXXCompiler.cmake delete mode 100644 vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeDetermineCompilerABI_CXX.bin delete mode 100644 vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeSystem.cmake delete mode 100644 vnpy/api/ib/build/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp delete mode 100644 vnpy/api/ib/build/CMakeFiles/3.5.1/CompilerIdCXX/a.out delete mode 100644 vnpy/api/ib/build/CMakeFiles/CMakeDirectoryInformation.cmake delete mode 100644 vnpy/api/ib/build/CMakeFiles/CMakeError.log delete mode 100644 vnpy/api/ib/build/CMakeFiles/CMakeOutput.log delete mode 100644 vnpy/api/ib/build/CMakeFiles/CheckLibraryExists/CheckFunctionExists.cxx delete mode 100644 vnpy/api/ib/build/CMakeFiles/Makefile.cmake delete mode 100644 vnpy/api/ib/build/CMakeFiles/Makefile2 delete mode 100644 vnpy/api/ib/build/CMakeFiles/TargetDirectories.txt delete mode 100644 vnpy/api/ib/build/CMakeFiles/cmake.check_cache delete mode 100644 vnpy/api/ib/build/CMakeFiles/feature_tests.bin delete mode 100644 vnpy/api/ib/build/CMakeFiles/feature_tests.cxx delete mode 100644 vnpy/api/ib/build/CMakeFiles/progress.marks delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/CXX.includecache delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/DependInfo.cmake delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/build.make delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/cmake_clean.cmake delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/depend.internal delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/depend.make delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/flags.make delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/link.txt delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/progress.make delete mode 100644 vnpy/api/ib/build/CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o delete mode 100644 vnpy/api/ib/build/Makefile delete mode 100644 vnpy/api/ib/build/cmake_install.cmake delete mode 100644 vnpy/api/ib/build/lib/vnib.so diff --git a/.gitignore b/.gitignore index a218a899..02df4f28 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ Release/ *.exp *.pdb *.cd +*.o +*.out # Python编译文件 *.pyc @@ -28,8 +30,6 @@ Release/ # 本地持久化文件 *.vn - - # 其他文件 *.dump *.vssettings @@ -41,16 +41,14 @@ Release/ *.temp *.vt *.log +*.bak -vn.ctp/build/* -vn.lts/build/* +# 目录 .idea .vscode .python-version .gitignore -vn.trader/ctaAlgo/data/* -vn.trader/build/* -vn.trader/dist/* -*.bak + + diff --git a/vnpy/api/ib/build/CMakeCache.txt b/vnpy/api/ib/build/CMakeCache.txt deleted file mode 100644 index 9f6254c3..00000000 --- a/vnpy/api/ib/build/CMakeCache.txt +++ /dev/null @@ -1,393 +0,0 @@ -# This is the CMakeCache file. -# For build in directory: /home/vnpy/桌面/new/vn.ib/build -# It was generated by CMake: /usr/bin/cmake -# You can edit this file to change values found and used by cmake. -# If you do not want to change any of the values, simply exit the editor. -# If you do want to change a value, simply edit, save, and exit the editor. -# The syntax for the file is as follows: -# KEY:TYPE=VALUE -# KEY is the name of a variable in the cache. -# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. -# VALUE is the current value for the KEY. - -######################## -# EXTERNAL cache entries -######################## - -//The threading library used by boost-thread -BOOST_THREAD_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpthread.so - -//build ib -BUILD_IB:BOOL=ON - -//Boost atomic library (debug) -Boost_ATOMIC_LIBRARY_DEBUG:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_atomic.so - -//Boost atomic library (release) -Boost_ATOMIC_LIBRARY_RELEASE:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_atomic.so - -//Boost chrono library (debug) -Boost_CHRONO_LIBRARY_DEBUG:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_chrono.so - -//Boost chrono library (release) -Boost_CHRONO_LIBRARY_RELEASE:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_chrono.so - -//Boost date_time library (debug) -Boost_DATE_TIME_LIBRARY_DEBUG:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_date_time.so - -//Boost date_time library (release) -Boost_DATE_TIME_LIBRARY_RELEASE:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_date_time.so - -//The directory containing a CMake configuration file for Boost. -Boost_DIR:PATH=Boost_DIR-NOTFOUND - -//Path to a file. -Boost_INCLUDE_DIR:PATH=/usr/include - -//Boost library directory DEBUG -Boost_LIBRARY_DIR_DEBUG:PATH=/usr/lib/x86_64-linux-gnu - -//Boost library directory RELEASE -Boost_LIBRARY_DIR_RELEASE:PATH=/usr/lib/x86_64-linux-gnu - -//Boost python library (debug) -Boost_PYTHON_LIBRARY_DEBUG:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_python.so - -//Boost python library (release) -Boost_PYTHON_LIBRARY_RELEASE:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_python.so - -//Boost system library (debug) -Boost_SYSTEM_LIBRARY_DEBUG:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_system.so - -//Boost system library (release) -Boost_SYSTEM_LIBRARY_RELEASE:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_system.so - -//Boost thread library (debug) -Boost_THREAD_LIBRARY_DEBUG:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_thread.so - -//Boost thread library (release) -Boost_THREAD_LIBRARY_RELEASE:FILEPATH=/usr/lib/x86_64-linux-gnu/libboost_thread.so - -//Path to a program. -CMAKE_AR:FILEPATH=/usr/bin/ar - -//Choose the type of build, options are: None(CMAKE_CXX_FLAGS or -// CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel. -CMAKE_BUILD_TYPE:STRING= - -//Enable/Disable color output during build. -CMAKE_COLOR_MAKEFILE:BOOL=ON - -//CXX compiler -CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++ - -//Flags used by the compiler during all build types. -CMAKE_CXX_FLAGS:STRING= - -//Flags used by the compiler during debug builds. -CMAKE_CXX_FLAGS_DEBUG:STRING=-g - -//Flags used by the compiler during release builds for minimum -// size. -CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG - -//Flags used by the compiler during release builds. -CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG - -//Flags used by the compiler during release builds with debug info. -CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG - -//Flags used by the linker. -CMAKE_EXE_LINKER_FLAGS:STRING= - -//Flags used by the linker during debug builds. -CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING= - -//Flags used by the linker during release minsize builds. -CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING= - -//Flags used by the linker during release builds. -CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING= - -//Flags used by the linker during Release with Debug Info builds. -CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING= - -//Enable/Disable output of compile commands during generation. -CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=OFF - -//Install path prefix, prepended onto install directories. -CMAKE_INSTALL_PREFIX:PATH=/usr/local - -//Path to a program. -CMAKE_LINKER:FILEPATH=/usr/bin/ld - -//Path to a program. -CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/make - -//Flags used by the linker during the creation of modules. -CMAKE_MODULE_LINKER_FLAGS:STRING= - -//Flags used by the linker during debug builds. -CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING= - -//Flags used by the linker during release minsize builds. -CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING= - -//Flags used by the linker during release builds. -CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING= - -//Flags used by the linker during Release with Debug Info builds. -CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING= - -//Path to a program. -CMAKE_NM:FILEPATH=/usr/bin/nm - -//Path to a program. -CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy - -//Path to a program. -CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump - -//Value Computed by CMake -CMAKE_PROJECT_NAME:STATIC=vn_ib_api - -//Path to a program. -CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib - -//Flags used by the linker during the creation of dll's. -CMAKE_SHARED_LINKER_FLAGS:STRING= - -//Flags used by the linker during debug builds. -CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING= - -//Flags used by the linker during release minsize builds. -CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING= - -//Flags used by the linker during release builds. -CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING= - -//Flags used by the linker during Release with Debug Info builds. -CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING= - -//If set, runtime paths are not added when installing shared libraries, -// but are added when building. -CMAKE_SKIP_INSTALL_RPATH:BOOL=NO - -//If set, runtime paths are not added when using shared libraries. -CMAKE_SKIP_RPATH:BOOL=NO - -//Flags used by the linker during the creation of static libraries. -CMAKE_STATIC_LINKER_FLAGS:STRING= - -//Flags used by the linker during debug builds. -CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING= - -//Flags used by the linker during release minsize builds. -CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING= - -//Flags used by the linker during release builds. -CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING= - -//Flags used by the linker during Release with Debug Info builds. -CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING= - -//Path to a program. -CMAKE_STRIP:FILEPATH=/usr/bin/strip - -//If this value is on, makefiles will be generated without the -// .SILENT directive, and all commands will be echoed to the console -// during the make. This is useful for debugging only. With Visual -// Studio IDE projects all commands are done without /nologo. -CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE - -//Path to a library. -IBAPI_LIBRARY:FILEPATH=/home/vnpy/桌面/new/vn.ib/ibapi/linux/build/lib/twsapi.so - -//Path to a library. -PYTHON_LIBRARY:FILEPATH=/home/vnpy/anaconda2/lib/libpython2.7.so - -//comiple 64bits -USE_64BITS:BOOL=ON - -//Value Computed by CMake -vn_ib_api_BINARY_DIR:STATIC=/home/vnpy/桌面/new/vn.ib/build - -//Value Computed by CMake -vn_ib_api_SOURCE_DIR:STATIC=/home/vnpy/桌面/new/vn.ib - -//Dependencies for the target -vnib_LIB_DEPENDS:STATIC=general;/home/vnpy/桌面/new/vn.ib/ibapi/linux/build/lib/twsapi.so;general;/usr/lib/x86_64-linux-gnu/libboost_python.so;general;/usr/lib/x86_64-linux-gnu/libboost_thread.so;general;/usr/lib/x86_64-linux-gnu/libboost_date_time.so;general;/usr/lib/x86_64-linux-gnu/libboost_system.so;general;/usr/lib/x86_64-linux-gnu/libboost_chrono.so;general;/usr/lib/x86_64-linux-gnu/libboost_atomic.so;general;/usr/lib/x86_64-linux-gnu/libpthread.so;general;/home/vnpy/anaconda2/lib/libpython2.7.so; - - -######################## -# INTERNAL cache entries -######################## - -//ADVANCED property for variable: Boost_ATOMIC_LIBRARY_DEBUG -Boost_ATOMIC_LIBRARY_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_ATOMIC_LIBRARY_RELEASE -Boost_ATOMIC_LIBRARY_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_CHRONO_LIBRARY_DEBUG -Boost_CHRONO_LIBRARY_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_CHRONO_LIBRARY_RELEASE -Boost_CHRONO_LIBRARY_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_DATE_TIME_LIBRARY_DEBUG -Boost_DATE_TIME_LIBRARY_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_DATE_TIME_LIBRARY_RELEASE -Boost_DATE_TIME_LIBRARY_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_DIR -Boost_DIR-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_INCLUDE_DIR -Boost_INCLUDE_DIR-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_LIBRARY_DIR_DEBUG -Boost_LIBRARY_DIR_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_LIBRARY_DIR_RELEASE -Boost_LIBRARY_DIR_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_PYTHON_LIBRARY_DEBUG -Boost_PYTHON_LIBRARY_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_PYTHON_LIBRARY_RELEASE -Boost_PYTHON_LIBRARY_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_SYSTEM_LIBRARY_DEBUG -Boost_SYSTEM_LIBRARY_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_SYSTEM_LIBRARY_RELEASE -Boost_SYSTEM_LIBRARY_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_THREAD_LIBRARY_DEBUG -Boost_THREAD_LIBRARY_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: Boost_THREAD_LIBRARY_RELEASE -Boost_THREAD_LIBRARY_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_AR -CMAKE_AR-ADVANCED:INTERNAL=1 -//This is the directory where this CMakeCache.txt was created -CMAKE_CACHEFILE_DIR:INTERNAL=/home/vnpy/桌面/new/vn.ib/build -//Major version of cmake used to create the current loaded cache -CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 -//Minor version of cmake used to create the current loaded cache -CMAKE_CACHE_MINOR_VERSION:INTERNAL=5 -//Patch version of cmake used to create the current loaded cache -CMAKE_CACHE_PATCH_VERSION:INTERNAL=1 -//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE -CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1 -//Path to CMake executable. -CMAKE_COMMAND:INTERNAL=/usr/bin/cmake -//Path to cpack program executable. -CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack -//Path to ctest program executable. -CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest -//ADVANCED property for variable: CMAKE_CXX_COMPILER -CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_CXX_FLAGS -CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG -CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL -CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE -CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO -CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//Executable file format -CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF -//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS -CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG -CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL -CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE -CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO -CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS -CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1 -//Name of external makefile project generator. -CMAKE_EXTRA_GENERATOR:INTERNAL= -//Name of generator. -CMAKE_GENERATOR:INTERNAL=Unix Makefiles -//Name of generator platform. -CMAKE_GENERATOR_PLATFORM:INTERNAL= -//Name of generator toolset. -CMAKE_GENERATOR_TOOLSET:INTERNAL= -//Have symbol pthread_create -CMAKE_HAVE_LIBC_CREATE:INTERNAL= -//Have library pthreads -CMAKE_HAVE_PTHREADS_CREATE:INTERNAL= -//Have library pthread -CMAKE_HAVE_PTHREAD_CREATE:INTERNAL=1 -//Have include pthread.h -CMAKE_HAVE_PTHREAD_H:INTERNAL=1 -//Source directory with the top level CMakeLists.txt file for this -// project -CMAKE_HOME_DIRECTORY:INTERNAL=/home/vnpy/桌面/new/vn.ib -//Install .so files without execute permission. -CMAKE_INSTALL_SO_NO_EXE:INTERNAL=1 -//ADVANCED property for variable: CMAKE_LINKER -CMAKE_LINKER-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_MAKE_PROGRAM -CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS -CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG -CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL -CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE -CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO -CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_NM -CMAKE_NM-ADVANCED:INTERNAL=1 -//number of local generators -CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1 -//ADVANCED property for variable: CMAKE_OBJCOPY -CMAKE_OBJCOPY-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_OBJDUMP -CMAKE_OBJDUMP-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_RANLIB -CMAKE_RANLIB-ADVANCED:INTERNAL=1 -//Path to CMake installation. -CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.5 -//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS -CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG -CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL -CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE -CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO -CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH -CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_SKIP_RPATH -CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS -CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG -CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL -CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE -CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO -CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 -//ADVANCED property for variable: CMAKE_STRIP -CMAKE_STRIP-ADVANCED:INTERNAL=1 -//uname command -CMAKE_UNAME:INTERNAL=/bin/uname -//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE -CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1 -//Details about finding Threads -FIND_PACKAGE_MESSAGE_DETAILS_Threads:INTERNAL=[TRUE][v()] -//Components requested for this build tree. -_Boost_COMPONENTS_SEARCHED:INTERNAL=atomic;chrono;date_time;python;system;thread -//Last used Boost_INCLUDE_DIR value. -_Boost_INCLUDE_DIR_LAST:INTERNAL=/usr/include -//Last used Boost_LIBRARY_DIR_DEBUG value. -_Boost_LIBRARY_DIR_DEBUG_LAST:INTERNAL=/usr/lib/x86_64-linux-gnu -//Last used Boost_LIBRARY_DIR_RELEASE value. -_Boost_LIBRARY_DIR_RELEASE_LAST:INTERNAL=/usr/lib/x86_64-linux-gnu -//Last used Boost_NAMESPACE value. -_Boost_NAMESPACE_LAST:INTERNAL=boost -//Last used Boost_USE_MULTITHREADED value. -_Boost_USE_MULTITHREADED_LAST:INTERNAL=ON - diff --git a/vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeCXXCompiler.cmake b/vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeCXXCompiler.cmake deleted file mode 100644 index 013ee929..00000000 --- a/vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeCXXCompiler.cmake +++ /dev/null @@ -1,68 +0,0 @@ -set(CMAKE_CXX_COMPILER "/usr/bin/c++") -set(CMAKE_CXX_COMPILER_ARG1 "") -set(CMAKE_CXX_COMPILER_ID "GNU") -set(CMAKE_CXX_COMPILER_VERSION "5.4.0") -set(CMAKE_CXX_COMPILER_WRAPPER "") -set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "98") -set(CMAKE_CXX_COMPILE_FEATURES "cxx_template_template_parameters;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") -set(CMAKE_CXX98_COMPILE_FEATURES "cxx_template_template_parameters") -set(CMAKE_CXX11_COMPILE_FEATURES "cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") -set(CMAKE_CXX14_COMPILE_FEATURES "cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") - -set(CMAKE_CXX_PLATFORM_ID "Linux") -set(CMAKE_CXX_SIMULATE_ID "") -set(CMAKE_CXX_SIMULATE_VERSION "") - -set(CMAKE_AR "/usr/bin/ar") -set(CMAKE_RANLIB "/usr/bin/ranlib") -set(CMAKE_LINKER "/usr/bin/ld") -set(CMAKE_COMPILER_IS_GNUCXX 1) -set(CMAKE_CXX_COMPILER_LOADED 1) -set(CMAKE_CXX_COMPILER_WORKS TRUE) -set(CMAKE_CXX_ABI_COMPILED TRUE) -set(CMAKE_COMPILER_IS_MINGW ) -set(CMAKE_COMPILER_IS_CYGWIN ) -if(CMAKE_COMPILER_IS_CYGWIN) - set(CYGWIN 1) - set(UNIX 1) -endif() - -set(CMAKE_CXX_COMPILER_ENV_VAR "CXX") - -if(CMAKE_COMPILER_IS_MINGW) - set(MINGW 1) -endif() -set(CMAKE_CXX_COMPILER_ID_RUN 1) -set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC) -set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;mm;CPP) -set(CMAKE_CXX_LINKER_PREFERENCE 30) -set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1) - -# Save compiler ABI information. -set(CMAKE_CXX_SIZEOF_DATA_PTR "8") -set(CMAKE_CXX_COMPILER_ABI "ELF") -set(CMAKE_CXX_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") - -if(CMAKE_CXX_SIZEOF_DATA_PTR) - set(CMAKE_SIZEOF_VOID_P "${CMAKE_CXX_SIZEOF_DATA_PTR}") -endif() - -if(CMAKE_CXX_COMPILER_ABI) - set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_CXX_COMPILER_ABI}") -endif() - -if(CMAKE_CXX_LIBRARY_ARCHITECTURE) - set(CMAKE_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") -endif() - -set(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX "") -if(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX) - set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_CXX_CL_SHOWINCLUDES_PREFIX}") -endif() - - - - -set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "stdc++;m;c") -set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-linux-gnu/5;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib") -set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "") diff --git a/vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeDetermineCompilerABI_CXX.bin b/vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeDetermineCompilerABI_CXX.bin deleted file mode 100644 index fa406403bf1df5c3585fbd95ade63c52706e28c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8656 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 Q?b<)T)bQ49ac*t@-`yIDZ~y=R diff --git a/vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeSystem.cmake b/vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeSystem.cmake deleted file mode 100644 index b40fa5e9..00000000 --- a/vnpy/api/ib/build/CMakeFiles/3.5.1/CMakeSystem.cmake +++ /dev/null @@ -1,15 +0,0 @@ -set(CMAKE_HOST_SYSTEM "Linux-4.4.0-47-generic") -set(CMAKE_HOST_SYSTEM_NAME "Linux") -set(CMAKE_HOST_SYSTEM_VERSION "4.4.0-47-generic") -set(CMAKE_HOST_SYSTEM_PROCESSOR "x86_64") - - - -set(CMAKE_SYSTEM "Linux-4.4.0-47-generic") -set(CMAKE_SYSTEM_NAME "Linux") -set(CMAKE_SYSTEM_VERSION "4.4.0-47-generic") -set(CMAKE_SYSTEM_PROCESSOR "x86_64") - -set(CMAKE_CROSSCOMPILING "FALSE") - -set(CMAKE_SYSTEM_LOADED 1) diff --git a/vnpy/api/ib/build/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp b/vnpy/api/ib/build/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp deleted file mode 100644 index e6d85363..00000000 --- a/vnpy/api/ib/build/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/* This source file must have a .cpp extension so that all C++ compilers - recognize the extension without flags. Borland does not know .cxx for - example. */ -#ifndef __cplusplus -# error "A C compiler has been selected for C++." -#endif - - -/* Version number components: V=Version, R=Revision, P=Patch - Version date components: YYYY=Year, MM=Month, DD=Day */ - -#if defined(__COMO__) -# define COMPILER_ID "Comeau" - /* __COMO_VERSION__ = VRR */ -# define COMPILER_VERSION_MAJOR DEC(__COMO_VERSION__ / 100) -# define COMPILER_VERSION_MINOR DEC(__COMO_VERSION__ % 100) - -#elif defined(__INTEL_COMPILER) || defined(__ICC) -# define COMPILER_ID "Intel" -# if defined(_MSC_VER) -# define SIMULATE_ID "MSVC" -# endif - /* __INTEL_COMPILER = VRP */ -# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100) -# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10) -# if defined(__INTEL_COMPILER_UPDATE) -# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE) -# else -# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10) -# endif -# if defined(__INTEL_COMPILER_BUILD_DATE) - /* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */ -# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE) -# endif -# if defined(_MSC_VER) - /* _MSC_VER = VVRR */ -# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) -# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) -# endif - -#elif defined(__PATHCC__) -# define COMPILER_ID "PathScale" -# define COMPILER_VERSION_MAJOR DEC(__PATHCC__) -# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__) -# if defined(__PATHCC_PATCHLEVEL__) -# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__) -# endif - -#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__) -# define COMPILER_ID "Embarcadero" -# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF) -# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF) -# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF) - -#elif defined(__BORLANDC__) -# define COMPILER_ID "Borland" - /* __BORLANDC__ = 0xVRR */ -# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8) -# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF) - -#elif defined(__WATCOMC__) && __WATCOMC__ < 1200 -# define COMPILER_ID "Watcom" - /* __WATCOMC__ = VVRR */ -# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100) -# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) -# if (__WATCOMC__ % 10) > 0 -# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) -# endif - -#elif defined(__WATCOMC__) -# define COMPILER_ID "OpenWatcom" - /* __WATCOMC__ = VVRP + 1100 */ -# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100) -# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) -# if (__WATCOMC__ % 10) > 0 -# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) -# endif - -#elif defined(__SUNPRO_CC) -# define COMPILER_ID "SunPro" -# if __SUNPRO_CC >= 0x5100 - /* __SUNPRO_CC = 0xVRRP */ -# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>12) -# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xFF) -# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF) -# else - /* __SUNPRO_CC = 0xVRP */ -# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>8) -# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xF) -# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF) -# endif - -#elif defined(__HP_aCC) -# define COMPILER_ID "HP" - /* __HP_aCC = VVRRPP */ -# define COMPILER_VERSION_MAJOR DEC(__HP_aCC/10000) -# define COMPILER_VERSION_MINOR DEC(__HP_aCC/100 % 100) -# define COMPILER_VERSION_PATCH DEC(__HP_aCC % 100) - -#elif defined(__DECCXX) -# define COMPILER_ID "Compaq" - /* __DECCXX_VER = VVRRTPPPP */ -# define COMPILER_VERSION_MAJOR DEC(__DECCXX_VER/10000000) -# define COMPILER_VERSION_MINOR DEC(__DECCXX_VER/100000 % 100) -# define COMPILER_VERSION_PATCH DEC(__DECCXX_VER % 10000) - -#elif defined(__IBMCPP__) && defined(__COMPILER_VER__) -# define COMPILER_ID "zOS" - /* __IBMCPP__ = VRP */ -# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) -# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) -# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) - -#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ >= 800 -# define COMPILER_ID "XL" - /* __IBMCPP__ = VRP */ -# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) -# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) -# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) - -#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ < 800 -# define COMPILER_ID "VisualAge" - /* __IBMCPP__ = VRP */ -# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) -# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) -# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) - -#elif defined(__PGI) -# define COMPILER_ID "PGI" -# define COMPILER_VERSION_MAJOR DEC(__PGIC__) -# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__) -# if defined(__PGIC_PATCHLEVEL__) -# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__) -# endif - -#elif defined(_CRAYC) -# define COMPILER_ID "Cray" -# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR) -# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR) - -#elif defined(__TI_COMPILER_VERSION__) -# define COMPILER_ID "TI" - /* __TI_COMPILER_VERSION__ = VVVRRRPPP */ -# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000) -# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000) -# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000) - -#elif defined(__FUJITSU) || defined(__FCC_VERSION) || defined(__fcc_version) -# define COMPILER_ID "Fujitsu" - -#elif defined(__SCO_VERSION__) -# define COMPILER_ID "SCO" - -#elif defined(__clang__) && defined(__apple_build_version__) -# define COMPILER_ID "AppleClang" -# if defined(_MSC_VER) -# define SIMULATE_ID "MSVC" -# endif -# define COMPILER_VERSION_MAJOR DEC(__clang_major__) -# define COMPILER_VERSION_MINOR DEC(__clang_minor__) -# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) -# if defined(_MSC_VER) - /* _MSC_VER = VVRR */ -# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) -# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) -# endif -# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__) - -#elif defined(__clang__) -# define COMPILER_ID "Clang" -# if defined(_MSC_VER) -# define SIMULATE_ID "MSVC" -# endif -# define COMPILER_VERSION_MAJOR DEC(__clang_major__) -# define COMPILER_VERSION_MINOR DEC(__clang_minor__) -# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) -# if defined(_MSC_VER) - /* _MSC_VER = VVRR */ -# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) -# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) -# endif - -#elif defined(__GNUC__) -# define COMPILER_ID "GNU" -# define COMPILER_VERSION_MAJOR DEC(__GNUC__) -# if defined(__GNUC_MINOR__) -# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__) -# endif -# if defined(__GNUC_PATCHLEVEL__) -# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__) -# endif - -#elif defined(_MSC_VER) -# define COMPILER_ID "MSVC" - /* _MSC_VER = VVRR */ -# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100) -# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100) -# if defined(_MSC_FULL_VER) -# if _MSC_VER >= 1400 - /* _MSC_FULL_VER = VVRRPPPPP */ -# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000) -# else - /* _MSC_FULL_VER = VVRRPPPP */ -# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000) -# endif -# endif -# if defined(_MSC_BUILD) -# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD) -# endif - -#elif defined(__VISUALDSPVERSION__) || defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__) -# define COMPILER_ID "ADSP" -#if defined(__VISUALDSPVERSION__) - /* __VISUALDSPVERSION__ = 0xVVRRPP00 */ -# define COMPILER_VERSION_MAJOR HEX(__VISUALDSPVERSION__>>24) -# define COMPILER_VERSION_MINOR HEX(__VISUALDSPVERSION__>>16 & 0xFF) -# define COMPILER_VERSION_PATCH HEX(__VISUALDSPVERSION__>>8 & 0xFF) -#endif - -#elif defined(__IAR_SYSTEMS_ICC__ ) || defined(__IAR_SYSTEMS_ICC) -# define COMPILER_ID "IAR" - -#elif defined(__ARMCC_VERSION) -# define COMPILER_ID "ARMCC" -#if __ARMCC_VERSION >= 1000000 - /* __ARMCC_VERSION = VRRPPPP */ - # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000) - # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100) - # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) -#else - /* __ARMCC_VERSION = VRPPPP */ - # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000) - # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10) - # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) -#endif - - -#elif defined(_SGI_COMPILER_VERSION) || defined(_COMPILER_VERSION) -# define COMPILER_ID "MIPSpro" -# if defined(_SGI_COMPILER_VERSION) - /* _SGI_COMPILER_VERSION = VRP */ -# define COMPILER_VERSION_MAJOR DEC(_SGI_COMPILER_VERSION/100) -# define COMPILER_VERSION_MINOR DEC(_SGI_COMPILER_VERSION/10 % 10) -# define COMPILER_VERSION_PATCH DEC(_SGI_COMPILER_VERSION % 10) -# else - /* _COMPILER_VERSION = VRP */ -# define COMPILER_VERSION_MAJOR DEC(_COMPILER_VERSION/100) -# define COMPILER_VERSION_MINOR DEC(_COMPILER_VERSION/10 % 10) -# define COMPILER_VERSION_PATCH DEC(_COMPILER_VERSION % 10) -# endif - - -/* These compilers are either not known or too old to define an - identification macro. Try to identify the platform and guess that - it is the native compiler. */ -#elif defined(__sgi) -# define COMPILER_ID "MIPSpro" - -#elif defined(__hpux) || defined(__hpua) -# define COMPILER_ID "HP" - -#else /* unknown compiler */ -# define COMPILER_ID "" -#endif - -/* Construct the string literal in pieces to prevent the source from - getting matched. Store it in a pointer rather than an array - because some compilers will just produce instructions to fill the - array rather than assigning a pointer to a static array. */ -char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]"; -#ifdef SIMULATE_ID -char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; -#endif - -#ifdef __QNXNTO__ -char const* qnxnto = "INFO" ":" "qnxnto[]"; -#endif - -#if defined(__CRAYXE) || defined(__CRAYXC) -char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]"; -#endif - -#define STRINGIFY_HELPER(X) #X -#define STRINGIFY(X) STRINGIFY_HELPER(X) - -/* Identify known platforms by name. */ -#if defined(__linux) || defined(__linux__) || defined(linux) -# define PLATFORM_ID "Linux" - -#elif defined(__CYGWIN__) -# define PLATFORM_ID "Cygwin" - -#elif defined(__MINGW32__) -# define PLATFORM_ID "MinGW" - -#elif defined(__APPLE__) -# define PLATFORM_ID "Darwin" - -#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) -# define PLATFORM_ID "Windows" - -#elif defined(__FreeBSD__) || defined(__FreeBSD) -# define PLATFORM_ID "FreeBSD" - -#elif defined(__NetBSD__) || defined(__NetBSD) -# define PLATFORM_ID "NetBSD" - -#elif defined(__OpenBSD__) || defined(__OPENBSD) -# define PLATFORM_ID "OpenBSD" - -#elif defined(__sun) || defined(sun) -# define PLATFORM_ID "SunOS" - -#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__) -# define PLATFORM_ID "AIX" - -#elif defined(__sgi) || defined(__sgi__) || defined(_SGI) -# define PLATFORM_ID "IRIX" - -#elif defined(__hpux) || defined(__hpux__) -# define PLATFORM_ID "HP-UX" - -#elif defined(__HAIKU__) -# define PLATFORM_ID "Haiku" - -#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS) -# define PLATFORM_ID "BeOS" - -#elif defined(__QNX__) || defined(__QNXNTO__) -# define PLATFORM_ID "QNX" - -#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__) -# define PLATFORM_ID "Tru64" - -#elif defined(__riscos) || defined(__riscos__) -# define PLATFORM_ID "RISCos" - -#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__) -# define PLATFORM_ID "SINIX" - -#elif defined(__UNIX_SV__) -# define PLATFORM_ID "UNIX_SV" - -#elif defined(__bsdos__) -# define PLATFORM_ID "BSDOS" - -#elif defined(_MPRAS) || defined(MPRAS) -# define PLATFORM_ID "MP-RAS" - -#elif defined(__osf) || defined(__osf__) -# define PLATFORM_ID "OSF1" - -#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv) -# define PLATFORM_ID "SCO_SV" - -#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX) -# define PLATFORM_ID "ULTRIX" - -#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX) -# define PLATFORM_ID "Xenix" - -#elif defined(__WATCOMC__) -# if defined(__LINUX__) -# define PLATFORM_ID "Linux" - -# elif defined(__DOS__) -# define PLATFORM_ID "DOS" - -# elif defined(__OS2__) -# define PLATFORM_ID "OS2" - -# elif defined(__WINDOWS__) -# define PLATFORM_ID "Windows3x" - -# else /* unknown platform */ -# define PLATFORM_ID "" -# endif - -#else /* unknown platform */ -# define PLATFORM_ID "" - -#endif - -/* For windows compilers MSVC and Intel we can determine - the architecture of the compiler being used. This is because - the compilers do not have flags that can change the architecture, - but rather depend on which compiler is being used -*/ -#if defined(_WIN32) && defined(_MSC_VER) -# if defined(_M_IA64) -# define ARCHITECTURE_ID "IA64" - -# elif defined(_M_X64) || defined(_M_AMD64) -# define ARCHITECTURE_ID "x64" - -# elif defined(_M_IX86) -# define ARCHITECTURE_ID "X86" - -# elif defined(_M_ARM) -# if _M_ARM == 4 -# define ARCHITECTURE_ID "ARMV4I" -# elif _M_ARM == 5 -# define ARCHITECTURE_ID "ARMV5I" -# else -# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM) -# endif - -# elif defined(_M_MIPS) -# define ARCHITECTURE_ID "MIPS" - -# elif defined(_M_SH) -# define ARCHITECTURE_ID "SHx" - -# else /* unknown architecture */ -# define ARCHITECTURE_ID "" -# endif - -#elif defined(__WATCOMC__) -# if defined(_M_I86) -# define ARCHITECTURE_ID "I86" - -# elif defined(_M_IX86) -# define ARCHITECTURE_ID "X86" - -# else /* unknown architecture */ -# define ARCHITECTURE_ID "" -# endif - -#else -# define ARCHITECTURE_ID "" -#endif - -/* Convert integer to decimal digit literals. */ -#define DEC(n) \ - ('0' + (((n) / 10000000)%10)), \ - ('0' + (((n) / 1000000)%10)), \ - ('0' + (((n) / 100000)%10)), \ - ('0' + (((n) / 10000)%10)), \ - ('0' + (((n) / 1000)%10)), \ - ('0' + (((n) / 100)%10)), \ - ('0' + (((n) / 10)%10)), \ - ('0' + ((n) % 10)) - -/* Convert integer to hex digit literals. */ -#define HEX(n) \ - ('0' + ((n)>>28 & 0xF)), \ - ('0' + ((n)>>24 & 0xF)), \ - ('0' + ((n)>>20 & 0xF)), \ - ('0' + ((n)>>16 & 0xF)), \ - ('0' + ((n)>>12 & 0xF)), \ - ('0' + ((n)>>8 & 0xF)), \ - ('0' + ((n)>>4 & 0xF)), \ - ('0' + ((n) & 0xF)) - -/* Construct a string literal encoding the version number components. */ -#ifdef COMPILER_VERSION_MAJOR -char const info_version[] = { - 'I', 'N', 'F', 'O', ':', - 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[', - COMPILER_VERSION_MAJOR, -# ifdef COMPILER_VERSION_MINOR - '.', COMPILER_VERSION_MINOR, -# ifdef COMPILER_VERSION_PATCH - '.', COMPILER_VERSION_PATCH, -# ifdef COMPILER_VERSION_TWEAK - '.', COMPILER_VERSION_TWEAK, -# endif -# endif -# endif - ']','\0'}; -#endif - -/* Construct a string literal encoding the version number components. */ -#ifdef SIMULATE_VERSION_MAJOR -char const info_simulate_version[] = { - 'I', 'N', 'F', 'O', ':', - 's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[', - SIMULATE_VERSION_MAJOR, -# ifdef SIMULATE_VERSION_MINOR - '.', SIMULATE_VERSION_MINOR, -# ifdef SIMULATE_VERSION_PATCH - '.', SIMULATE_VERSION_PATCH, -# ifdef SIMULATE_VERSION_TWEAK - '.', SIMULATE_VERSION_TWEAK, -# endif -# endif -# endif - ']','\0'}; -#endif - -/* Construct the string literal in pieces to prevent the source from - getting matched. Store it in a pointer rather than an array - because some compilers will just produce instructions to fill the - array rather than assigning a pointer to a static array. */ -char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]"; -char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]"; - - - - -const char* info_language_dialect_default = "INFO" ":" "dialect_default[" -#if __cplusplus >= 201402L - "14" -#elif __cplusplus >= 201103L - "11" -#else - "98" -#endif -"]"; - -/*--------------------------------------------------------------------------*/ - -int main(int argc, char* argv[]) -{ - int require = 0; - require += info_compiler[argc]; - require += info_platform[argc]; -#ifdef COMPILER_VERSION_MAJOR - require += info_version[argc]; -#endif -#ifdef SIMULATE_ID - require += info_simulate[argc]; -#endif -#ifdef SIMULATE_VERSION_MAJOR - require += info_simulate_version[argc]; -#endif -#if defined(__CRAYXE) || defined(__CRAYXC) - require += info_cray[argc]; -#endif - require += info_language_dialect_default[argc]; - (void)argv; - return require; -} diff --git a/vnpy/api/ib/build/CMakeFiles/3.5.1/CompilerIdCXX/a.out b/vnpy/api/ib/build/CMakeFiles/3.5.1/CompilerIdCXX/a.out deleted file mode 100644 index b9caff71e899f65626b048e3bc12275df8f8e08f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8808 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 z - -int main(int argc, char** argv) -{ - (void)argv; -#ifndef pthread_create - return ((int*)(&pthread_create))[argc]; -#else - (void)argc; - return 0; -#endif -} - -Determining if the function pthread_create exists in the pthreads failed with the following output: -Change Dir: /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp - -Run Build Command:"/usr/bin/make" "cmTC_704c9/fast" -/usr/bin/make -f CMakeFiles/cmTC_704c9.dir/build.make CMakeFiles/cmTC_704c9.dir/build -make[1]: Entering directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' -Building CXX object CMakeFiles/cmTC_704c9.dir/CheckFunctionExists.cxx.o -/usr/bin/c++ -fPIC -std=c++11 -DCHECK_FUNCTION_EXISTS=pthread_create -o CMakeFiles/cmTC_704c9.dir/CheckFunctionExists.cxx.o -c /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CheckLibraryExists/CheckFunctionExists.cxx -Linking CXX executable cmTC_704c9 -/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_704c9.dir/link.txt --verbose=1 -/usr/bin/c++ -fPIC -std=c++11 -DCHECK_FUNCTION_EXISTS=pthread_create CMakeFiles/cmTC_704c9.dir/CheckFunctionExists.cxx.o -o cmTC_704c9 -rdynamic -lpthreads -/usr/bin/ld: 找不到 -lpthreads -collect2: error: ld returned 1 exit status -CMakeFiles/cmTC_704c9.dir/build.make:97: recipe for target 'cmTC_704c9' failed -make[1]: *** [cmTC_704c9] Error 1 -make[1]: Leaving directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' -Makefile:126: recipe for target 'cmTC_704c9/fast' failed -make: *** [cmTC_704c9/fast] Error 2 - - diff --git a/vnpy/api/ib/build/CMakeFiles/CMakeOutput.log b/vnpy/api/ib/build/CMakeFiles/CMakeOutput.log deleted file mode 100644 index 6eb2c134..00000000 --- a/vnpy/api/ib/build/CMakeFiles/CMakeOutput.log +++ /dev/null @@ -1,384 +0,0 @@ -The system is: Linux - 4.4.0-47-generic - x86_64 -Compiling the CXX compiler identification source file "CMakeCXXCompilerId.cpp" succeeded. -Compiler: /usr/bin/c++ -Build flags: -Id flags: - -The output was: -0 - - -Compilation of the CXX compiler identification source "CMakeCXXCompilerId.cpp" produced "a.out" - -The CXX compiler identification is GNU, found in "/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/3.5.1/CompilerIdCXX/a.out" - -Determining if the CXX compiler works passed with the following output: -Change Dir: /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp - -Run Build Command:"/usr/bin/make" "cmTC_37c25/fast" -/usr/bin/make -f CMakeFiles/cmTC_37c25.dir/build.make CMakeFiles/cmTC_37c25.dir/build -make[1]: Entering directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' -Building CXX object CMakeFiles/cmTC_37c25.dir/testCXXCompiler.cxx.o -/usr/bin/c++ -o CMakeFiles/cmTC_37c25.dir/testCXXCompiler.cxx.o -c /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp/testCXXCompiler.cxx -Linking CXX executable cmTC_37c25 -/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_37c25.dir/link.txt --verbose=1 -/usr/bin/c++ CMakeFiles/cmTC_37c25.dir/testCXXCompiler.cxx.o -o cmTC_37c25 -rdynamic -make[1]: Leaving directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' - - -Detecting CXX compiler ABI info compiled with the following output: -Change Dir: /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp - -Run Build Command:"/usr/bin/make" "cmTC_40a3c/fast" -/usr/bin/make -f CMakeFiles/cmTC_40a3c.dir/build.make CMakeFiles/cmTC_40a3c.dir/build -make[1]: Entering directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' -Building CXX object CMakeFiles/cmTC_40a3c.dir/CMakeCXXCompilerABI.cpp.o -/usr/bin/c++ -o CMakeFiles/cmTC_40a3c.dir/CMakeCXXCompilerABI.cpp.o -c /usr/share/cmake-3.5/Modules/CMakeCXXCompilerABI.cpp -Linking CXX executable cmTC_40a3c -/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_40a3c.dir/link.txt --verbose=1 -/usr/bin/c++ -v CMakeFiles/cmTC_40a3c.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_40a3c -rdynamic -Using built-in specs. -COLLECT_GCC=/usr/bin/c++ -COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -Target: x86_64-linux-gnu -Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.4' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu -Thread model: posix -gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) -COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/ -LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/ -COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_40a3c' '-rdynamic' '-shared-libgcc' '-mtune=generic' '-march=x86-64' - /usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/cctb0mbm.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o cmTC_40a3c /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. CMakeFiles/cmTC_40a3c.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o -make[1]: Leaving directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' - - -Parsed CXX implicit link information from above output: - link line regex: [^( *|.*[/\])(ld|([^/\]+-)?ld|collect2)[^/\]*( |$)] - ignore line: [Change Dir: /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp] - ignore line: [] - ignore line: [Run Build Command:"/usr/bin/make" "cmTC_40a3c/fast"] - ignore line: [/usr/bin/make -f CMakeFiles/cmTC_40a3c.dir/build.make CMakeFiles/cmTC_40a3c.dir/build] - ignore line: [make[1]: Entering directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp'] - ignore line: [Building CXX object CMakeFiles/cmTC_40a3c.dir/CMakeCXXCompilerABI.cpp.o] - ignore line: [/usr/bin/c++ -o CMakeFiles/cmTC_40a3c.dir/CMakeCXXCompilerABI.cpp.o -c /usr/share/cmake-3.5/Modules/CMakeCXXCompilerABI.cpp] - ignore line: [Linking CXX executable cmTC_40a3c] - ignore line: [/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_40a3c.dir/link.txt --verbose=1] - ignore line: [/usr/bin/c++ -v CMakeFiles/cmTC_40a3c.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_40a3c -rdynamic ] - ignore line: [Using built-in specs.] - ignore line: [COLLECT_GCC=/usr/bin/c++] - ignore line: [COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper] - ignore line: [Target: x86_64-linux-gnu] - ignore line: [Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.4' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu] - ignore line: [Thread model: posix] - ignore line: [gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ] - ignore line: [COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/] - ignore line: [LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/] - ignore line: [COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_40a3c' '-rdynamic' '-shared-libgcc' '-mtune=generic' '-march=x86-64'] - link line: [ /usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/cctb0mbm.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o cmTC_40a3c /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. CMakeFiles/cmTC_40a3c.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o] - arg [/usr/lib/gcc/x86_64-linux-gnu/5/collect2] ==> ignore - arg [-plugin] ==> ignore - arg [/usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so] ==> ignore - arg [-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper] ==> ignore - arg [-plugin-opt=-fresolution=/tmp/cctb0mbm.res] ==> ignore - arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore - arg [-plugin-opt=-pass-through=-lgcc] ==> ignore - arg [-plugin-opt=-pass-through=-lc] ==> ignore - arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore - arg [-plugin-opt=-pass-through=-lgcc] ==> ignore - arg [--sysroot=/] ==> ignore - arg [--build-id] ==> ignore - arg [--eh-frame-hdr] ==> ignore - arg [-m] ==> ignore - arg [elf_x86_64] ==> ignore - arg [--hash-style=gnu] ==> ignore - arg [--as-needed] ==> ignore - arg [-export-dynamic] ==> ignore - arg [-dynamic-linker] ==> ignore - arg [/lib64/ld-linux-x86-64.so.2] ==> ignore - arg [-zrelro] ==> ignore - arg [-o] ==> ignore - arg [cmTC_40a3c] ==> ignore - arg [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o] ==> ignore - arg [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o] ==> ignore - arg [/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o] ==> ignore - arg [-L/usr/lib/gcc/x86_64-linux-gnu/5] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/5] - arg [-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu] - arg [-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib] - arg [-L/lib/x86_64-linux-gnu] ==> dir [/lib/x86_64-linux-gnu] - arg [-L/lib/../lib] ==> dir [/lib/../lib] - arg [-L/usr/lib/x86_64-linux-gnu] ==> dir [/usr/lib/x86_64-linux-gnu] - arg [-L/usr/lib/../lib] ==> dir [/usr/lib/../lib] - arg [-L/usr/lib/gcc/x86_64-linux-gnu/5/../../..] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../..] - arg [CMakeFiles/cmTC_40a3c.dir/CMakeCXXCompilerABI.cpp.o] ==> ignore - arg [-lstdc++] ==> lib [stdc++] - arg [-lm] ==> lib [m] - arg [-lgcc_s] ==> lib [gcc_s] - arg [-lgcc] ==> lib [gcc] - arg [-lc] ==> lib [c] - arg [-lgcc_s] ==> lib [gcc_s] - arg [-lgcc] ==> lib [gcc] - arg [/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o] ==> ignore - arg [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o] ==> ignore - remove lib [gcc_s] - remove lib [gcc] - remove lib [gcc_s] - remove lib [gcc] - collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/5] ==> [/usr/lib/gcc/x86_64-linux-gnu/5] - collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu] - collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib] ==> [/usr/lib] - collapse library dir [/lib/x86_64-linux-gnu] ==> [/lib/x86_64-linux-gnu] - collapse library dir [/lib/../lib] ==> [/lib] - collapse library dir [/usr/lib/x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu] - collapse library dir [/usr/lib/../lib] ==> [/usr/lib] - collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../..] ==> [/usr/lib] - implicit libs: [stdc++;m;c] - implicit dirs: [/usr/lib/gcc/x86_64-linux-gnu/5;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib] - implicit fwks: [] - - - - -Detecting CXX [-std=c++14] compiler features compiled with the following output: -Change Dir: /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp - -Run Build Command:"/usr/bin/make" "cmTC_ce700/fast" -/usr/bin/make -f CMakeFiles/cmTC_ce700.dir/build.make CMakeFiles/cmTC_ce700.dir/build -make[1]: Entering directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' -Building CXX object CMakeFiles/cmTC_ce700.dir/feature_tests.cxx.o -/usr/bin/c++ -std=c++14 -o CMakeFiles/cmTC_ce700.dir/feature_tests.cxx.o -c /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/feature_tests.cxx -Linking CXX executable cmTC_ce700 -/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_ce700.dir/link.txt --verbose=1 -/usr/bin/c++ CMakeFiles/cmTC_ce700.dir/feature_tests.cxx.o -o cmTC_ce700 -rdynamic -make[1]: Leaving directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' - - - Feature record: CXX_FEATURE:1cxx_aggregate_default_initializers - Feature record: CXX_FEATURE:1cxx_alias_templates - Feature record: CXX_FEATURE:1cxx_alignas - Feature record: CXX_FEATURE:1cxx_alignof - Feature record: CXX_FEATURE:1cxx_attributes - Feature record: CXX_FEATURE:1cxx_attribute_deprecated - Feature record: CXX_FEATURE:1cxx_auto_type - Feature record: CXX_FEATURE:1cxx_binary_literals - Feature record: CXX_FEATURE:1cxx_constexpr - Feature record: CXX_FEATURE:1cxx_contextual_conversions - Feature record: CXX_FEATURE:1cxx_decltype - Feature record: CXX_FEATURE:1cxx_decltype_auto - Feature record: CXX_FEATURE:1cxx_decltype_incomplete_return_types - Feature record: CXX_FEATURE:1cxx_default_function_template_args - Feature record: CXX_FEATURE:1cxx_defaulted_functions - Feature record: CXX_FEATURE:1cxx_defaulted_move_initializers - Feature record: CXX_FEATURE:1cxx_delegating_constructors - Feature record: CXX_FEATURE:1cxx_deleted_functions - Feature record: CXX_FEATURE:1cxx_digit_separators - Feature record: CXX_FEATURE:1cxx_enum_forward_declarations - Feature record: CXX_FEATURE:1cxx_explicit_conversions - Feature record: CXX_FEATURE:1cxx_extended_friend_declarations - Feature record: CXX_FEATURE:1cxx_extern_templates - Feature record: CXX_FEATURE:1cxx_final - Feature record: CXX_FEATURE:1cxx_func_identifier - Feature record: CXX_FEATURE:1cxx_generalized_initializers - Feature record: CXX_FEATURE:1cxx_generic_lambdas - Feature record: CXX_FEATURE:1cxx_inheriting_constructors - Feature record: CXX_FEATURE:1cxx_inline_namespaces - Feature record: CXX_FEATURE:1cxx_lambdas - Feature record: CXX_FEATURE:1cxx_lambda_init_captures - Feature record: CXX_FEATURE:1cxx_local_type_template_args - Feature record: CXX_FEATURE:1cxx_long_long_type - Feature record: CXX_FEATURE:1cxx_noexcept - Feature record: CXX_FEATURE:1cxx_nonstatic_member_init - Feature record: CXX_FEATURE:1cxx_nullptr - Feature record: CXX_FEATURE:1cxx_override - Feature record: CXX_FEATURE:1cxx_range_for - Feature record: CXX_FEATURE:1cxx_raw_string_literals - Feature record: CXX_FEATURE:1cxx_reference_qualified_functions - Feature record: CXX_FEATURE:1cxx_relaxed_constexpr - Feature record: CXX_FEATURE:1cxx_return_type_deduction - Feature record: CXX_FEATURE:1cxx_right_angle_brackets - Feature record: CXX_FEATURE:1cxx_rvalue_references - Feature record: CXX_FEATURE:1cxx_sizeof_member - Feature record: CXX_FEATURE:1cxx_static_assert - Feature record: CXX_FEATURE:1cxx_strong_enums - Feature record: CXX_FEATURE:1cxx_template_template_parameters - Feature record: CXX_FEATURE:1cxx_thread_local - Feature record: CXX_FEATURE:1cxx_trailing_return_types - Feature record: CXX_FEATURE:1cxx_unicode_literals - Feature record: CXX_FEATURE:1cxx_uniform_initialization - Feature record: CXX_FEATURE:1cxx_unrestricted_unions - Feature record: CXX_FEATURE:1cxx_user_literals - Feature record: CXX_FEATURE:1cxx_variable_templates - Feature record: CXX_FEATURE:1cxx_variadic_macros - Feature record: CXX_FEATURE:1cxx_variadic_templates - - -Detecting CXX [-std=c++11] compiler features compiled with the following output: -Change Dir: /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp - -Run Build Command:"/usr/bin/make" "cmTC_1d72c/fast" -/usr/bin/make -f CMakeFiles/cmTC_1d72c.dir/build.make CMakeFiles/cmTC_1d72c.dir/build -make[1]: Entering directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' -Building CXX object CMakeFiles/cmTC_1d72c.dir/feature_tests.cxx.o -/usr/bin/c++ -std=c++11 -o CMakeFiles/cmTC_1d72c.dir/feature_tests.cxx.o -c /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/feature_tests.cxx -Linking CXX executable cmTC_1d72c -/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_1d72c.dir/link.txt --verbose=1 -/usr/bin/c++ CMakeFiles/cmTC_1d72c.dir/feature_tests.cxx.o -o cmTC_1d72c -rdynamic -make[1]: Leaving directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' - - - Feature record: CXX_FEATURE:0cxx_aggregate_default_initializers - Feature record: CXX_FEATURE:1cxx_alias_templates - Feature record: CXX_FEATURE:1cxx_alignas - Feature record: CXX_FEATURE:1cxx_alignof - Feature record: CXX_FEATURE:1cxx_attributes - Feature record: CXX_FEATURE:0cxx_attribute_deprecated - Feature record: CXX_FEATURE:1cxx_auto_type - Feature record: CXX_FEATURE:0cxx_binary_literals - Feature record: CXX_FEATURE:1cxx_constexpr - Feature record: CXX_FEATURE:0cxx_contextual_conversions - Feature record: CXX_FEATURE:1cxx_decltype - Feature record: CXX_FEATURE:0cxx_decltype_auto - Feature record: CXX_FEATURE:1cxx_decltype_incomplete_return_types - Feature record: CXX_FEATURE:1cxx_default_function_template_args - Feature record: CXX_FEATURE:1cxx_defaulted_functions - Feature record: CXX_FEATURE:1cxx_defaulted_move_initializers - Feature record: CXX_FEATURE:1cxx_delegating_constructors - Feature record: CXX_FEATURE:1cxx_deleted_functions - Feature record: CXX_FEATURE:0cxx_digit_separators - Feature record: CXX_FEATURE:1cxx_enum_forward_declarations - Feature record: CXX_FEATURE:1cxx_explicit_conversions - Feature record: CXX_FEATURE:1cxx_extended_friend_declarations - Feature record: CXX_FEATURE:1cxx_extern_templates - Feature record: CXX_FEATURE:1cxx_final - Feature record: CXX_FEATURE:1cxx_func_identifier - Feature record: CXX_FEATURE:1cxx_generalized_initializers - Feature record: CXX_FEATURE:0cxx_generic_lambdas - Feature record: CXX_FEATURE:1cxx_inheriting_constructors - Feature record: CXX_FEATURE:1cxx_inline_namespaces - Feature record: CXX_FEATURE:1cxx_lambdas - Feature record: CXX_FEATURE:0cxx_lambda_init_captures - Feature record: CXX_FEATURE:1cxx_local_type_template_args - Feature record: CXX_FEATURE:1cxx_long_long_type - Feature record: CXX_FEATURE:1cxx_noexcept - Feature record: CXX_FEATURE:1cxx_nonstatic_member_init - Feature record: CXX_FEATURE:1cxx_nullptr - Feature record: CXX_FEATURE:1cxx_override - Feature record: CXX_FEATURE:1cxx_range_for - Feature record: CXX_FEATURE:1cxx_raw_string_literals - Feature record: CXX_FEATURE:1cxx_reference_qualified_functions - Feature record: CXX_FEATURE:0cxx_relaxed_constexpr - Feature record: CXX_FEATURE:0cxx_return_type_deduction - Feature record: CXX_FEATURE:1cxx_right_angle_brackets - Feature record: CXX_FEATURE:1cxx_rvalue_references - Feature record: CXX_FEATURE:1cxx_sizeof_member - Feature record: CXX_FEATURE:1cxx_static_assert - Feature record: CXX_FEATURE:1cxx_strong_enums - Feature record: CXX_FEATURE:1cxx_template_template_parameters - Feature record: CXX_FEATURE:1cxx_thread_local - Feature record: CXX_FEATURE:1cxx_trailing_return_types - Feature record: CXX_FEATURE:1cxx_unicode_literals - Feature record: CXX_FEATURE:1cxx_uniform_initialization - Feature record: CXX_FEATURE:1cxx_unrestricted_unions - Feature record: CXX_FEATURE:1cxx_user_literals - Feature record: CXX_FEATURE:0cxx_variable_templates - Feature record: CXX_FEATURE:1cxx_variadic_macros - Feature record: CXX_FEATURE:1cxx_variadic_templates - - -Detecting CXX [-std=c++98] compiler features compiled with the following output: -Change Dir: /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp - -Run Build Command:"/usr/bin/make" "cmTC_ee307/fast" -/usr/bin/make -f CMakeFiles/cmTC_ee307.dir/build.make CMakeFiles/cmTC_ee307.dir/build -make[1]: Entering directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' -Building CXX object CMakeFiles/cmTC_ee307.dir/feature_tests.cxx.o -/usr/bin/c++ -std=c++98 -o CMakeFiles/cmTC_ee307.dir/feature_tests.cxx.o -c /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/feature_tests.cxx -Linking CXX executable cmTC_ee307 -/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_ee307.dir/link.txt --verbose=1 -/usr/bin/c++ CMakeFiles/cmTC_ee307.dir/feature_tests.cxx.o -o cmTC_ee307 -rdynamic -make[1]: Leaving directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' - - - Feature record: CXX_FEATURE:0cxx_aggregate_default_initializers - Feature record: CXX_FEATURE:0cxx_alias_templates - Feature record: CXX_FEATURE:0cxx_alignas - Feature record: CXX_FEATURE:0cxx_alignof - Feature record: CXX_FEATURE:0cxx_attributes - Feature record: CXX_FEATURE:0cxx_attribute_deprecated - Feature record: CXX_FEATURE:0cxx_auto_type - Feature record: CXX_FEATURE:0cxx_binary_literals - Feature record: CXX_FEATURE:0cxx_constexpr - Feature record: CXX_FEATURE:0cxx_contextual_conversions - Feature record: CXX_FEATURE:0cxx_decltype - Feature record: CXX_FEATURE:0cxx_decltype_auto - Feature record: CXX_FEATURE:0cxx_decltype_incomplete_return_types - Feature record: CXX_FEATURE:0cxx_default_function_template_args - Feature record: CXX_FEATURE:0cxx_defaulted_functions - Feature record: CXX_FEATURE:0cxx_defaulted_move_initializers - Feature record: CXX_FEATURE:0cxx_delegating_constructors - Feature record: CXX_FEATURE:0cxx_deleted_functions - Feature record: CXX_FEATURE:0cxx_digit_separators - Feature record: CXX_FEATURE:0cxx_enum_forward_declarations - Feature record: CXX_FEATURE:0cxx_explicit_conversions - Feature record: CXX_FEATURE:0cxx_extended_friend_declarations - Feature record: CXX_FEATURE:0cxx_extern_templates - Feature record: CXX_FEATURE:0cxx_final - Feature record: CXX_FEATURE:0cxx_func_identifier - Feature record: CXX_FEATURE:0cxx_generalized_initializers - Feature record: CXX_FEATURE:0cxx_generic_lambdas - Feature record: CXX_FEATURE:0cxx_inheriting_constructors - Feature record: CXX_FEATURE:0cxx_inline_namespaces - Feature record: CXX_FEATURE:0cxx_lambdas - Feature record: CXX_FEATURE:0cxx_lambda_init_captures - Feature record: CXX_FEATURE:0cxx_local_type_template_args - Feature record: CXX_FEATURE:0cxx_long_long_type - Feature record: CXX_FEATURE:0cxx_noexcept - Feature record: CXX_FEATURE:0cxx_nonstatic_member_init - Feature record: CXX_FEATURE:0cxx_nullptr - Feature record: CXX_FEATURE:0cxx_override - Feature record: CXX_FEATURE:0cxx_range_for - Feature record: CXX_FEATURE:0cxx_raw_string_literals - Feature record: CXX_FEATURE:0cxx_reference_qualified_functions - Feature record: CXX_FEATURE:0cxx_relaxed_constexpr - Feature record: CXX_FEATURE:0cxx_return_type_deduction - Feature record: CXX_FEATURE:0cxx_right_angle_brackets - Feature record: CXX_FEATURE:0cxx_rvalue_references - Feature record: CXX_FEATURE:0cxx_sizeof_member - Feature record: CXX_FEATURE:0cxx_static_assert - Feature record: CXX_FEATURE:0cxx_strong_enums - Feature record: CXX_FEATURE:1cxx_template_template_parameters - Feature record: CXX_FEATURE:0cxx_thread_local - Feature record: CXX_FEATURE:0cxx_trailing_return_types - Feature record: CXX_FEATURE:0cxx_unicode_literals - Feature record: CXX_FEATURE:0cxx_uniform_initialization - Feature record: CXX_FEATURE:0cxx_unrestricted_unions - Feature record: CXX_FEATURE:0cxx_user_literals - Feature record: CXX_FEATURE:0cxx_variable_templates - Feature record: CXX_FEATURE:0cxx_variadic_macros - Feature record: CXX_FEATURE:0cxx_variadic_templates -Determining if the include file pthread.h exists passed with the following output: -Change Dir: /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp - -Run Build Command:"/usr/bin/make" "cmTC_e6946/fast" -/usr/bin/make -f CMakeFiles/cmTC_e6946.dir/build.make CMakeFiles/cmTC_e6946.dir/build -make[1]: Entering directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' -Building CXX object CMakeFiles/cmTC_e6946.dir/CheckIncludeFile.cxx.o -/usr/bin/c++ -fPIC -std=c++11 -o CMakeFiles/cmTC_e6946.dir/CheckIncludeFile.cxx.o -c /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp/CheckIncludeFile.cxx -Linking CXX executable cmTC_e6946 -/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_e6946.dir/link.txt --verbose=1 -/usr/bin/c++ -fPIC -std=c++11 CMakeFiles/cmTC_e6946.dir/CheckIncludeFile.cxx.o -o cmTC_e6946 -rdynamic -make[1]: Leaving directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' - - -Determining if the function pthread_create exists in the pthread passed with the following output: -Change Dir: /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp - -Run Build Command:"/usr/bin/make" "cmTC_239ea/fast" -/usr/bin/make -f CMakeFiles/cmTC_239ea.dir/build.make CMakeFiles/cmTC_239ea.dir/build -make[1]: Entering directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' -Building CXX object CMakeFiles/cmTC_239ea.dir/CheckFunctionExists.cxx.o -/usr/bin/c++ -fPIC -std=c++11 -DCHECK_FUNCTION_EXISTS=pthread_create -o CMakeFiles/cmTC_239ea.dir/CheckFunctionExists.cxx.o -c /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CheckLibraryExists/CheckFunctionExists.cxx -Linking CXX executable cmTC_239ea -/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_239ea.dir/link.txt --verbose=1 -/usr/bin/c++ -fPIC -std=c++11 -DCHECK_FUNCTION_EXISTS=pthread_create CMakeFiles/cmTC_239ea.dir/CheckFunctionExists.cxx.o -o cmTC_239ea -rdynamic -lpthread -make[1]: Leaving directory '/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/CMakeTmp' - - diff --git a/vnpy/api/ib/build/CMakeFiles/CheckLibraryExists/CheckFunctionExists.cxx b/vnpy/api/ib/build/CMakeFiles/CheckLibraryExists/CheckFunctionExists.cxx deleted file mode 100644 index fd29618c..00000000 --- a/vnpy/api/ib/build/CMakeFiles/CheckLibraryExists/CheckFunctionExists.cxx +++ /dev/null @@ -1,26 +0,0 @@ -#ifdef CHECK_FUNCTION_EXISTS - -#ifdef __cplusplus -extern "C" -#endif -char CHECK_FUNCTION_EXISTS(); -#ifdef __CLASSIC_C__ -int main(){ - int ac; - char*av[]; -#else -int main(int ac, char*av[]){ -#endif - CHECK_FUNCTION_EXISTS(); - if(ac > 1000) - { - return *av[0]; - } - return 0; -} - -#else /* CHECK_FUNCTION_EXISTS */ - -# error "CHECK_FUNCTION_EXISTS has to specify the function" - -#endif /* CHECK_FUNCTION_EXISTS */ diff --git a/vnpy/api/ib/build/CMakeFiles/Makefile.cmake b/vnpy/api/ib/build/CMakeFiles/Makefile.cmake deleted file mode 100644 index 3d95def3..00000000 --- a/vnpy/api/ib/build/CMakeFiles/Makefile.cmake +++ /dev/null @@ -1,108 +0,0 @@ -# CMAKE generated file: DO NOT EDIT! -# Generated by "Unix Makefiles" Generator, CMake Version 3.5 - -# The generator used is: -set(CMAKE_DEPENDS_GENERATOR "Unix Makefiles") - -# The top level Makefile was generated from the following files: -set(CMAKE_MAKEFILE_DEPENDS - "CMakeCache.txt" - "../CMakeLists.txt" - "CMakeFiles/3.5.1/CMakeCXXCompiler.cmake" - "CMakeFiles/3.5.1/CMakeSystem.cmake" - "CMakeFiles/CheckLibraryExists/CheckFunctionExists.cxx" - "CMakeFiles/feature_tests.cxx" - "/usr/share/cmake-3.5/Modules/CMakeCXXCompiler.cmake.in" - "/usr/share/cmake-3.5/Modules/CMakeCXXCompilerABI.cpp" - "/usr/share/cmake-3.5/Modules/CMakeCXXInformation.cmake" - "/usr/share/cmake-3.5/Modules/CMakeCommonLanguageInclude.cmake" - "/usr/share/cmake-3.5/Modules/CMakeCompilerIdDetection.cmake" - "/usr/share/cmake-3.5/Modules/CMakeConfigurableFile.in" - "/usr/share/cmake-3.5/Modules/CMakeDetermineCXXCompiler.cmake" - "/usr/share/cmake-3.5/Modules/CMakeDetermineCompileFeatures.cmake" - "/usr/share/cmake-3.5/Modules/CMakeDetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/CMakeDetermineCompilerABI.cmake" - "/usr/share/cmake-3.5/Modules/CMakeDetermineCompilerId.cmake" - "/usr/share/cmake-3.5/Modules/CMakeDetermineSystem.cmake" - "/usr/share/cmake-3.5/Modules/CMakeFindBinUtils.cmake" - "/usr/share/cmake-3.5/Modules/CMakeFindDependencyMacro.cmake" - "/usr/share/cmake-3.5/Modules/CMakeGenericSystem.cmake" - "/usr/share/cmake-3.5/Modules/CMakeLanguageInformation.cmake" - "/usr/share/cmake-3.5/Modules/CMakeParseArguments.cmake" - "/usr/share/cmake-3.5/Modules/CMakeParseImplicitLinkInfo.cmake" - "/usr/share/cmake-3.5/Modules/CMakeSystem.cmake.in" - "/usr/share/cmake-3.5/Modules/CMakeSystemSpecificInformation.cmake" - "/usr/share/cmake-3.5/Modules/CMakeSystemSpecificInitialize.cmake" - "/usr/share/cmake-3.5/Modules/CMakeTestCXXCompiler.cmake" - "/usr/share/cmake-3.5/Modules/CMakeTestCompilerCommon.cmake" - "/usr/share/cmake-3.5/Modules/CMakeUnixFindMake.cmake" - "/usr/share/cmake-3.5/Modules/CheckFunctionExists.c" - "/usr/share/cmake-3.5/Modules/CheckIncludeFile.cxx.in" - "/usr/share/cmake-3.5/Modules/CheckIncludeFileCXX.cmake" - "/usr/share/cmake-3.5/Modules/CheckLibraryExists.cmake" - "/usr/share/cmake-3.5/Modules/CheckSymbolExists.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/ADSP-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/ARMCC-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/AppleClang-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Borland-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Clang-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Clang-DetermineCompilerInternal.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Comeau-CXX-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Compaq-CXX-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Cray-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Embarcadero-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Fujitsu-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/GHS-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/GNU-CXX-FeatureTests.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/GNU-CXX.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/GNU-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/GNU.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/HP-CXX-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/IAR-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/IBMCPP-CXX-DetermineVersionInternal.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Intel-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/MIPSpro-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/MSVC-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/OpenWatcom-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/PGI-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/PathScale-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/SCO-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/SunPro-CXX-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/TI-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/VisualAge-CXX-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/Watcom-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/XL-CXX-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/Compiler/zOS-CXX-DetermineCompiler.cmake" - "/usr/share/cmake-3.5/Modules/FindBoost.cmake" - "/usr/share/cmake-3.5/Modules/FindPackageHandleStandardArgs.cmake" - "/usr/share/cmake-3.5/Modules/FindPackageMessage.cmake" - "/usr/share/cmake-3.5/Modules/FindThreads.cmake" - "/usr/share/cmake-3.5/Modules/Internal/FeatureTesting.cmake" - "/usr/share/cmake-3.5/Modules/MultiArchCross.cmake" - "/usr/share/cmake-3.5/Modules/Platform/Linux-CXX.cmake" - "/usr/share/cmake-3.5/Modules/Platform/Linux-GNU-CXX.cmake" - "/usr/share/cmake-3.5/Modules/Platform/Linux-GNU.cmake" - "/usr/share/cmake-3.5/Modules/Platform/Linux.cmake" - "/usr/share/cmake-3.5/Modules/Platform/UnixPaths.cmake" - ) - -# The corresponding makefile is: -set(CMAKE_MAKEFILE_OUTPUTS - "Makefile" - "CMakeFiles/cmake.check_cache" - ) - -# Byproducts of CMake generate step: -set(CMAKE_MAKEFILE_PRODUCTS - "CMakeFiles/3.5.1/CMakeSystem.cmake" - "CMakeFiles/3.5.1/CMakeCXXCompiler.cmake" - "CMakeFiles/3.5.1/CMakeCXXCompiler.cmake" - "CMakeFiles/CheckLibraryExists/CheckFunctionExists.cxx" - "CMakeFiles/CheckLibraryExists/CheckFunctionExists.cxx" - "CMakeFiles/CMakeDirectoryInformation.cmake" - ) - -# Dependency information for all targets: -set(CMAKE_DEPEND_INFO_FILES - "CMakeFiles/vnib.dir/DependInfo.cmake" - ) diff --git a/vnpy/api/ib/build/CMakeFiles/Makefile2 b/vnpy/api/ib/build/CMakeFiles/Makefile2 deleted file mode 100644 index b21f6988..00000000 --- a/vnpy/api/ib/build/CMakeFiles/Makefile2 +++ /dev/null @@ -1,108 +0,0 @@ -# CMAKE generated file: DO NOT EDIT! -# Generated by "Unix Makefiles" Generator, CMake Version 3.5 - -# Default target executed when no arguments are given to make. -default_target: all - -.PHONY : default_target - -# The main recursive all target -all: - -.PHONY : all - -# The main recursive preinstall target -preinstall: - -.PHONY : preinstall - -#============================================================================= -# Special targets provided by cmake. - -# Disable implicit rules so canonical targets will work. -.SUFFIXES: - - -# Remove some rules from gmake that .SUFFIXES does not remove. -SUFFIXES = - -.SUFFIXES: .hpux_make_needs_suffix_list - - -# Suppress display of executed commands. -$(VERBOSE).SILENT: - - -# A target that is always out of date. -cmake_force: - -.PHONY : cmake_force - -#============================================================================= -# Set environment variables for the build. - -# The shell in which to execute make rules. -SHELL = /bin/sh - -# The CMake executable. -CMAKE_COMMAND = /usr/bin/cmake - -# The command to remove a file. -RM = /usr/bin/cmake -E remove -f - -# Escaping for special characters. -EQUALS = = - -# The top-level source directory on which CMake was run. -CMAKE_SOURCE_DIR = /home/vnpy/桌面/new/vn.ib - -# The top-level build directory on which CMake was run. -CMAKE_BINARY_DIR = /home/vnpy/桌面/new/vn.ib/build - -#============================================================================= -# Target rules for target CMakeFiles/vnib.dir - -# All Build rule for target. -CMakeFiles/vnib.dir/all: - $(MAKE) -f CMakeFiles/vnib.dir/build.make CMakeFiles/vnib.dir/depend - $(MAKE) -f CMakeFiles/vnib.dir/build.make CMakeFiles/vnib.dir/build - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --progress-dir=/home/vnpy/桌面/new/vn.ib/build/CMakeFiles --progress-num=1,2 "Built target vnib" -.PHONY : CMakeFiles/vnib.dir/all - -# Include target in all. -all: CMakeFiles/vnib.dir/all - -.PHONY : all - -# Build rule for subdir invocation for target. -CMakeFiles/vnib.dir/rule: cmake_check_build_system - $(CMAKE_COMMAND) -E cmake_progress_start /home/vnpy/桌面/new/vn.ib/build/CMakeFiles 2 - $(MAKE) -f CMakeFiles/Makefile2 CMakeFiles/vnib.dir/all - $(CMAKE_COMMAND) -E cmake_progress_start /home/vnpy/桌面/new/vn.ib/build/CMakeFiles 0 -.PHONY : CMakeFiles/vnib.dir/rule - -# Convenience name for target. -vnib: CMakeFiles/vnib.dir/rule - -.PHONY : vnib - -# clean rule for target. -CMakeFiles/vnib.dir/clean: - $(MAKE) -f CMakeFiles/vnib.dir/build.make CMakeFiles/vnib.dir/clean -.PHONY : CMakeFiles/vnib.dir/clean - -# clean rule for target. -clean: CMakeFiles/vnib.dir/clean - -.PHONY : clean - -#============================================================================= -# Special targets to cleanup operation of make. - -# Special rule to run CMake to check the build system integrity. -# No rule that depends on this can have commands that come from listfiles -# because they might be regenerated. -cmake_check_build_system: - $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 -.PHONY : cmake_check_build_system - diff --git a/vnpy/api/ib/build/CMakeFiles/TargetDirectories.txt b/vnpy/api/ib/build/CMakeFiles/TargetDirectories.txt deleted file mode 100644 index 91621b39..00000000 --- a/vnpy/api/ib/build/CMakeFiles/TargetDirectories.txt +++ /dev/null @@ -1,3 +0,0 @@ -/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/edit_cache.dir -/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/rebuild_cache.dir -/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/vnib.dir diff --git a/vnpy/api/ib/build/CMakeFiles/cmake.check_cache b/vnpy/api/ib/build/CMakeFiles/cmake.check_cache deleted file mode 100644 index 3dccd731..00000000 --- a/vnpy/api/ib/build/CMakeFiles/cmake.check_cache +++ /dev/null @@ -1 +0,0 @@ -# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/vnpy/api/ib/build/CMakeFiles/feature_tests.bin b/vnpy/api/ib/build/CMakeFiles/feature_tests.bin deleted file mode 100644 index 4c5b3641afc2613da6573f1e5b55094363efbb23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12696 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 lx#LqXu6Rg73>=2T26*WEX= 500 && __cplusplus >= 201402L -"1" -#else -"0" -#endif -"cxx_aggregate_default_initializers\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_alias_templates\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_alignas\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_alignof\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_attributes\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L -"1" -#else -"0" -#endif -"cxx_attribute_deprecated\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_auto_type\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L -"1" -#else -"0" -#endif -"cxx_binary_literals\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_constexpr\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L -"1" -#else -"0" -#endif -"cxx_contextual_conversions\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_decltype\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L -"1" -#else -"0" -#endif -"cxx_decltype_auto\n" -"CXX_FEATURE:" -#if ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_decltype_incomplete_return_types\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_default_function_template_args\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_defaulted_functions\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_defaulted_move_initializers\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_delegating_constructors\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_deleted_functions\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L -"1" -#else -"0" -#endif -"cxx_digit_separators\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_enum_forward_declarations\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_explicit_conversions\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_extended_friend_declarations\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_extern_templates\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_final\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_func_identifier\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_generalized_initializers\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L -"1" -#else -"0" -#endif -"cxx_generic_lambdas\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_inheriting_constructors\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_inline_namespaces\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_lambdas\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L -"1" -#else -"0" -#endif -"cxx_lambda_init_captures\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_local_type_template_args\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_long_long_type\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_noexcept\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_nonstatic_member_init\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_nullptr\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_override\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_range_for\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_raw_string_literals\n" -"CXX_FEATURE:" -#if ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_reference_qualified_functions\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L -"1" -#else -"0" -#endif -"cxx_relaxed_constexpr\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L -"1" -#else -"0" -#endif -"cxx_return_type_deduction\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_right_angle_brackets\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_rvalue_references\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_sizeof_member\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_static_assert\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_strong_enums\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && __cplusplus -"1" -#else -"0" -#endif -"cxx_template_template_parameters\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_thread_local\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_trailing_return_types\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_unicode_literals\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_uniform_initialization\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_unrestricted_unions\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L -"1" -#else -"0" -#endif -"cxx_user_literals\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L -"1" -#else -"0" -#endif -"cxx_variable_templates\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_variadic_macros\n" -"CXX_FEATURE:" -#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) -"1" -#else -"0" -#endif -"cxx_variadic_templates\n" - -}; - -int main(int argc, char** argv) { (void)argv; return features[argc]; } diff --git a/vnpy/api/ib/build/CMakeFiles/progress.marks b/vnpy/api/ib/build/CMakeFiles/progress.marks deleted file mode 100644 index 0cfbf088..00000000 --- a/vnpy/api/ib/build/CMakeFiles/progress.marks +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/CXX.includecache b/vnpy/api/ib/build/CMakeFiles/vnib.dir/CXX.includecache deleted file mode 100644 index 57162ad0..00000000 --- a/vnpy/api/ib/build/CMakeFiles/vnib.dir/CXX.includecache +++ /dev/null @@ -1,198 +0,0 @@ -#IncludeRegexLine: ^[ ]*#[ ]*(include|import)[ ]*[<"]([^">]+)([">]) - -#IncludeRegexScan: ^.*$ - -#IncludeRegexComplain: ^$ - -#IncludeRegexTransform: - -.././ibapi/linux/client/CommissionReport.h - -.././ibapi/linux/client/CommonDefs.h - -.././ibapi/linux/client/Contract.h -TagValue.h -.././ibapi/linux/client/TagValue.h - -.././ibapi/linux/client/EClient.h -memory -- -string -- -vector -- -iosfwd -- -CommonDefs.h -.././ibapi/linux/client/CommonDefs.h -TagValue.h -.././ibapi/linux/client/TagValue.h - -.././ibapi/linux/client/EClientMsgSink.h - -.././ibapi/linux/client/EClientSocket.h -EClient.h -.././ibapi/linux/client/EClient.h -EClientMsgSink.h -.././ibapi/linux/client/EClientMsgSink.h -ESocket.h -.././ibapi/linux/client/ESocket.h - -.././ibapi/linux/client/EDecoder.h -Contract.h -.././ibapi/linux/client/Contract.h - -.././ibapi/linux/client/EMutex.h -StdAfx.h -.././ibapi/linux/client/StdAfx.h - -.././ibapi/linux/client/EReader.h -StdAfx.h -.././ibapi/linux/client/StdAfx.h -EDecoder.h -.././ibapi/linux/client/EDecoder.h -EMutex.h -.././ibapi/linux/client/EMutex.h -EReaderOSSignal.h -.././ibapi/linux/client/EReaderOSSignal.h - -.././ibapi/linux/client/EReaderOSSignal.h -EReaderSignal.h -.././ibapi/linux/client/EReaderSignal.h -StdAfx.h -.././ibapi/linux/client/StdAfx.h -stdexcept -- - -.././ibapi/linux/client/EReaderSignal.h - -.././ibapi/linux/client/ESocket.h -ETransport.h -.././ibapi/linux/client/ETransport.h - -.././ibapi/linux/client/ETransport.h - -.././ibapi/linux/client/EWrapper.h -CommonDefs.h -.././ibapi/linux/client/CommonDefs.h -SoftDollarTier.h -.././ibapi/linux/client/SoftDollarTier.h -string -- -set -- - -.././ibapi/linux/client/Execution.h - -.././ibapi/linux/client/IExternalizable.h -ios -- - -.././ibapi/linux/client/Order.h -TagValue.h -.././ibapi/linux/client/TagValue.h -OrderCondition.h -.././ibapi/linux/client/OrderCondition.h -SoftDollarTier.h -.././ibapi/linux/client/SoftDollarTier.h -float.h -- -limits.h -- - -.././ibapi/linux/client/OrderCondition.h -IExternalizable.h -.././ibapi/linux/client/IExternalizable.h -shared_ptr.h -.././ibapi/linux/client/shared_ptr.h - -.././ibapi/linux/client/OrderState.h -Order.h -.././ibapi/linux/client/Order.h - -.././ibapi/linux/client/ScannerSubscription.h -float.h -- -limits.h -- - -.././ibapi/linux/client/SoftDollarTier.h - -.././ibapi/linux/client/StdAfx.h -WinSock2.h -- -Windows.h -- -unistd.h -- -pthread.h -- -string -- -deque -- -vector -- -algorithm -- - -.././ibapi/linux/client/TagValue.h -shared_ptr.h -.././ibapi/linux/client/shared_ptr.h -string -- -vector -- - -.././ibapi/linux/client/shared_ptr.h - -/home/vnpy/桌面/new/vn.ib/vnib/vnib/vnib.cpp -StdAfx.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/StdAfx.h -vnib.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/vnib.h -Contract.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/Contract.h -OrderState.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/OrderState.h -Execution.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/Execution.h -CommissionReport.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/CommissionReport.h -ScannerSubscription.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/ScannerSubscription.h -TagValue.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/TagValue.h -Order.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/Order.h - -/home/vnpy/桌面/new/vn.ib/vnib/vnib/vnib.h -StdAfx.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/StdAfx.h -string -- -boost/python/module.hpp -- -boost/python/def.hpp -- -boost/python/object.hpp -- -boost/python/register_ptr_to_python.hpp -- -boost/python/suite/indexing/vector_indexing_suite.hpp -- -boost/python.hpp -- -boost/thread.hpp -- -boost/bind.hpp -- -boost/shared_ptr.hpp -- -EWrapper.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/EWrapper.h -EClientSocket.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/EClientSocket.h -EReader.h -/home/vnpy/桌面/new/vn.ib/vnib/vnib/EReader.h - diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/DependInfo.cmake b/vnpy/api/ib/build/CMakeFiles/vnib.dir/DependInfo.cmake deleted file mode 100644 index 793abe4d..00000000 --- a/vnpy/api/ib/build/CMakeFiles/vnib.dir/DependInfo.cmake +++ /dev/null @@ -1,29 +0,0 @@ -# The set of languages for which implicit dependencies are needed: -set(CMAKE_DEPENDS_LANGUAGES - "CXX" - ) -# The set of files for implicit dependencies of each language: -set(CMAKE_DEPENDS_CHECK_CXX - "/home/vnpy/桌面/new/vn.ib/vnib/vnib/vnib.cpp" "/home/vnpy/桌面/new/vn.ib/build/CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o" - ) -set(CMAKE_CXX_COMPILER_ID "GNU") - -# Preprocessor definitions for this target. -set(CMAKE_TARGET_DEFINITIONS_CXX - "BUILD_IB" - "USE_64BITS" - ) - -# The include file search paths: -set(CMAKE_CXX_TARGET_INCLUDE_PATH - ".././ibapi/linux/client" - "../IB_PATH" - "/usr/include/python2.7" - ) - -# Targets to which this target links. -set(CMAKE_TARGET_LINKED_INFO_FILES - ) - -# Fortran module output directory. -set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/build.make b/vnpy/api/ib/build/CMakeFiles/vnib.dir/build.make deleted file mode 100644 index db4cfe94..00000000 --- a/vnpy/api/ib/build/CMakeFiles/vnib.dir/build.make +++ /dev/null @@ -1,122 +0,0 @@ -# CMAKE generated file: DO NOT EDIT! -# Generated by "Unix Makefiles" Generator, CMake Version 3.5 - -# Delete rule output on recipe failure. -.DELETE_ON_ERROR: - - -#============================================================================= -# Special targets provided by cmake. - -# Disable implicit rules so canonical targets will work. -.SUFFIXES: - - -# Remove some rules from gmake that .SUFFIXES does not remove. -SUFFIXES = - -.SUFFIXES: .hpux_make_needs_suffix_list - - -# Suppress display of executed commands. -$(VERBOSE).SILENT: - - -# A target that is always out of date. -cmake_force: - -.PHONY : cmake_force - -#============================================================================= -# Set environment variables for the build. - -# The shell in which to execute make rules. -SHELL = /bin/sh - -# The CMake executable. -CMAKE_COMMAND = /usr/bin/cmake - -# The command to remove a file. -RM = /usr/bin/cmake -E remove -f - -# Escaping for special characters. -EQUALS = = - -# The top-level source directory on which CMake was run. -CMAKE_SOURCE_DIR = /home/vnpy/桌面/new/vn.ib - -# The top-level build directory on which CMake was run. -CMAKE_BINARY_DIR = /home/vnpy/桌面/new/vn.ib/build - -# Include any dependencies generated for this target. -include CMakeFiles/vnib.dir/depend.make - -# Include the progress variables for this target. -include CMakeFiles/vnib.dir/progress.make - -# Include the compile flags for this target's objects. -include CMakeFiles/vnib.dir/flags.make - -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: CMakeFiles/vnib.dir/flags.make -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: ../vnib/vnib/vnib.cpp - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/vnpy/桌面/new/vn.ib/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o" - /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o -c /home/vnpy/桌面/new/vn.ib/vnib/vnib/vnib.cpp - -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.i: cmake_force - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.i" - /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/vnpy/桌面/new/vn.ib/vnib/vnib/vnib.cpp > CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.i - -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.s: cmake_force - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.s" - /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/vnpy/桌面/new/vn.ib/vnib/vnib/vnib.cpp -o CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.s - -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o.requires: - -.PHONY : CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o.requires - -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o.provides: CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o.requires - $(MAKE) -f CMakeFiles/vnib.dir/build.make CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o.provides.build -.PHONY : CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o.provides - -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o.provides.build: CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o - - -# Object files for target vnib -vnib_OBJECTS = \ -"CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o" - -# External object files for target vnib -vnib_EXTERNAL_OBJECTS = - -lib/vnib.so: CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o -lib/vnib.so: CMakeFiles/vnib.dir/build.make -lib/vnib.so: ../ibapi/linux/build/lib/twsapi.so -lib/vnib.so: /usr/lib/x86_64-linux-gnu/libboost_python.so -lib/vnib.so: /usr/lib/x86_64-linux-gnu/libboost_thread.so -lib/vnib.so: /usr/lib/x86_64-linux-gnu/libboost_date_time.so -lib/vnib.so: /usr/lib/x86_64-linux-gnu/libboost_system.so -lib/vnib.so: /usr/lib/x86_64-linux-gnu/libboost_chrono.so -lib/vnib.so: /usr/lib/x86_64-linux-gnu/libboost_atomic.so -lib/vnib.so: /usr/lib/x86_64-linux-gnu/libpthread.so -lib/vnib.so: /home/vnpy/anaconda2/lib/libpython2.7.so -lib/vnib.so: CMakeFiles/vnib.dir/link.txt - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/home/vnpy/桌面/new/vn.ib/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking CXX shared library lib/vnib.so" - $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/vnib.dir/link.txt --verbose=$(VERBOSE) - -# Rule to build all files generated by this target. -CMakeFiles/vnib.dir/build: lib/vnib.so - -.PHONY : CMakeFiles/vnib.dir/build - -CMakeFiles/vnib.dir/requires: CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o.requires - -.PHONY : CMakeFiles/vnib.dir/requires - -CMakeFiles/vnib.dir/clean: - $(CMAKE_COMMAND) -P CMakeFiles/vnib.dir/cmake_clean.cmake -.PHONY : CMakeFiles/vnib.dir/clean - -CMakeFiles/vnib.dir/depend: - cd /home/vnpy/桌面/new/vn.ib/build && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /home/vnpy/桌面/new/vn.ib /home/vnpy/桌面/new/vn.ib /home/vnpy/桌面/new/vn.ib/build /home/vnpy/桌面/new/vn.ib/build /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/vnib.dir/DependInfo.cmake --color=$(COLOR) -.PHONY : CMakeFiles/vnib.dir/depend - diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/cmake_clean.cmake b/vnpy/api/ib/build/CMakeFiles/vnib.dir/cmake_clean.cmake deleted file mode 100644 index 882648d8..00000000 --- a/vnpy/api/ib/build/CMakeFiles/vnib.dir/cmake_clean.cmake +++ /dev/null @@ -1,10 +0,0 @@ -file(REMOVE_RECURSE - "CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o" - "lib/vnib.pdb" - "lib/vnib.so" -) - -# Per-language clean rules from dependency scanning. -foreach(lang CXX) - include(CMakeFiles/vnib.dir/cmake_clean_${lang}.cmake OPTIONAL) -endforeach() diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/depend.internal b/vnpy/api/ib/build/CMakeFiles/vnib.dir/depend.internal deleted file mode 100644 index b4529621..00000000 --- a/vnpy/api/ib/build/CMakeFiles/vnib.dir/depend.internal +++ /dev/null @@ -1,30 +0,0 @@ -# CMAKE generated file: DO NOT EDIT! -# Generated by "Unix Makefiles" Generator, CMake Version 3.5 - -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o - .././ibapi/linux/client/CommissionReport.h - .././ibapi/linux/client/CommonDefs.h - .././ibapi/linux/client/Contract.h - .././ibapi/linux/client/EClient.h - .././ibapi/linux/client/EClientMsgSink.h - .././ibapi/linux/client/EClientSocket.h - .././ibapi/linux/client/EDecoder.h - .././ibapi/linux/client/EMutex.h - .././ibapi/linux/client/EReader.h - .././ibapi/linux/client/EReaderOSSignal.h - .././ibapi/linux/client/EReaderSignal.h - .././ibapi/linux/client/ESocket.h - .././ibapi/linux/client/ETransport.h - .././ibapi/linux/client/EWrapper.h - .././ibapi/linux/client/Execution.h - .././ibapi/linux/client/IExternalizable.h - .././ibapi/linux/client/Order.h - .././ibapi/linux/client/OrderCondition.h - .././ibapi/linux/client/OrderState.h - .././ibapi/linux/client/ScannerSubscription.h - .././ibapi/linux/client/SoftDollarTier.h - .././ibapi/linux/client/StdAfx.h - .././ibapi/linux/client/TagValue.h - .././ibapi/linux/client/shared_ptr.h - /home/vnpy/桌面/new/vn.ib/vnib/vnib/vnib.cpp - /home/vnpy/桌面/new/vn.ib/vnib/vnib/vnib.h diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/depend.make b/vnpy/api/ib/build/CMakeFiles/vnib.dir/depend.make deleted file mode 100644 index cb336b8a..00000000 --- a/vnpy/api/ib/build/CMakeFiles/vnib.dir/depend.make +++ /dev/null @@ -1,30 +0,0 @@ -# CMAKE generated file: DO NOT EDIT! -# Generated by "Unix Makefiles" Generator, CMake Version 3.5 - -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/CommissionReport.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/CommonDefs.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/Contract.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/EClient.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/EClientMsgSink.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/EClientSocket.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/EDecoder.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/EMutex.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/EReader.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/EReaderOSSignal.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/EReaderSignal.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/ESocket.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/ETransport.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/EWrapper.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/Execution.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/IExternalizable.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/Order.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/OrderCondition.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/OrderState.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/ScannerSubscription.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/SoftDollarTier.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/StdAfx.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/TagValue.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: .././ibapi/linux/client/shared_ptr.h -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: ../vnib/vnib/vnib.cpp -CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o: ../vnib/vnib/vnib.h - diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/flags.make b/vnpy/api/ib/build/CMakeFiles/vnib.dir/flags.make deleted file mode 100644 index bae3cb48..00000000 --- a/vnpy/api/ib/build/CMakeFiles/vnib.dir/flags.make +++ /dev/null @@ -1,10 +0,0 @@ -# CMAKE generated file: DO NOT EDIT! -# Generated by "Unix Makefiles" Generator, CMake Version 3.5 - -# compile CXX with /usr/bin/c++ -CXX_FLAGS = -fPIC -std=c++11 -O3 -DNDEBUG -fPIC - -CXX_DEFINES = -DBUILD_IB -DUSE_64BITS -Dvnib_EXPORTS - -CXX_INCLUDES = -I/home/vnpy/桌面/new/vn.ib/./ibapi/linux/client -I/home/vnpy/桌面/new/vn.ib/IB_PATH -I/usr/include/python2.7 - diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/link.txt b/vnpy/api/ib/build/CMakeFiles/vnib.dir/link.txt deleted file mode 100644 index 60fd0138..00000000 --- a/vnpy/api/ib/build/CMakeFiles/vnib.dir/link.txt +++ /dev/null @@ -1 +0,0 @@ -/usr/bin/c++ -fPIC -fPIC -std=c++11 -O3 -DNDEBUG -shared -Wl,-soname,vnib.so -o lib/vnib.so CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o ../ibapi/linux/build/lib/twsapi.so -lboost_python -lboost_thread -lboost_date_time -lboost_system -lboost_chrono -lboost_atomic -lpthread /home/vnpy/anaconda2/lib/libpython2.7.so -Wl,-rpath,/home/vnpy/桌面/new/vn.ib/ibapi/linux/build/lib:/home/vnpy/anaconda2/lib diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/progress.make b/vnpy/api/ib/build/CMakeFiles/vnib.dir/progress.make deleted file mode 100644 index abadeb0c..00000000 --- a/vnpy/api/ib/build/CMakeFiles/vnib.dir/progress.make +++ /dev/null @@ -1,3 +0,0 @@ -CMAKE_PROGRESS_1 = 1 -CMAKE_PROGRESS_2 = 2 - diff --git a/vnpy/api/ib/build/CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o b/vnpy/api/ib/build/CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o deleted file mode 100644 index 267c381aa5c4e964044e5fe883afecaea5e74769..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4919576 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Ν!@d7;7iwNzG}^8P?9p^O}pA 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

jt_`w#DuaEm#eOy@)(*O%dmHw%+r6h! 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

C63 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�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 z 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Πl59 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⪻4X`QY7%mwE@{)jB_Ne7O++8?TGNjn~EC-H4ZZ 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

&Hs2A+yNfN>p!Wzw+HS0HMo8&`gM8!wmN** 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. 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

4drT@IA()X_Y_m-ag*++(a=l>?~Z?McSSNflP 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 Tl5&@BPW?W={wDHMy8r(G_A+3y diff --git a/vnpy/api/ib/build/Makefile b/vnpy/api/ib/build/Makefile deleted file mode 100644 index fddffe1b..00000000 --- a/vnpy/api/ib/build/Makefile +++ /dev/null @@ -1,178 +0,0 @@ -# CMAKE generated file: DO NOT EDIT! -# Generated by "Unix Makefiles" Generator, CMake Version 3.5 - -# Default target executed when no arguments are given to make. -default_target: all - -.PHONY : default_target - -# Allow only one "make -f Makefile2" at a time, but pass parallelism. -.NOTPARALLEL: - - -#============================================================================= -# Special targets provided by cmake. - -# Disable implicit rules so canonical targets will work. -.SUFFIXES: - - -# Remove some rules from gmake that .SUFFIXES does not remove. -SUFFIXES = - -.SUFFIXES: .hpux_make_needs_suffix_list - - -# Suppress display of executed commands. -$(VERBOSE).SILENT: - - -# A target that is always out of date. -cmake_force: - -.PHONY : cmake_force - -#============================================================================= -# Set environment variables for the build. - -# The shell in which to execute make rules. -SHELL = /bin/sh - -# The CMake executable. -CMAKE_COMMAND = /usr/bin/cmake - -# The command to remove a file. -RM = /usr/bin/cmake -E remove -f - -# Escaping for special characters. -EQUALS = = - -# The top-level source directory on which CMake was run. -CMAKE_SOURCE_DIR = /home/vnpy/桌面/new/vn.ib - -# The top-level build directory on which CMake was run. -CMAKE_BINARY_DIR = /home/vnpy/桌面/new/vn.ib/build - -#============================================================================= -# Targets provided globally by CMake. - -# Special rule for the target edit_cache -edit_cache: - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." - /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. -.PHONY : edit_cache - -# Special rule for the target edit_cache -edit_cache/fast: edit_cache - -.PHONY : edit_cache/fast - -# Special rule for the target rebuild_cache -rebuild_cache: - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." - /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) -.PHONY : rebuild_cache - -# Special rule for the target rebuild_cache -rebuild_cache/fast: rebuild_cache - -.PHONY : rebuild_cache/fast - -# The main all target -all: cmake_check_build_system - $(CMAKE_COMMAND) -E cmake_progress_start /home/vnpy/桌面/new/vn.ib/build/CMakeFiles /home/vnpy/桌面/new/vn.ib/build/CMakeFiles/progress.marks - $(MAKE) -f CMakeFiles/Makefile2 all - $(CMAKE_COMMAND) -E cmake_progress_start /home/vnpy/桌面/new/vn.ib/build/CMakeFiles 0 -.PHONY : all - -# The main clean target -clean: - $(MAKE) -f CMakeFiles/Makefile2 clean -.PHONY : clean - -# The main clean target -clean/fast: clean - -.PHONY : clean/fast - -# Prepare targets for installation. -preinstall: all - $(MAKE) -f CMakeFiles/Makefile2 preinstall -.PHONY : preinstall - -# Prepare targets for installation. -preinstall/fast: - $(MAKE) -f CMakeFiles/Makefile2 preinstall -.PHONY : preinstall/fast - -# clear depends -depend: - $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 -.PHONY : depend - -#============================================================================= -# Target rules for targets named vnib - -# Build rule for target. -vnib: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 vnib -.PHONY : vnib - -# fast build rule for target. -vnib/fast: - $(MAKE) -f CMakeFiles/vnib.dir/build.make CMakeFiles/vnib.dir/build -.PHONY : vnib/fast - -vnib/vnib/vnib.o: vnib/vnib/vnib.cpp.o - -.PHONY : vnib/vnib/vnib.o - -# target to build an object file -vnib/vnib/vnib.cpp.o: - $(MAKE) -f CMakeFiles/vnib.dir/build.make CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.o -.PHONY : vnib/vnib/vnib.cpp.o - -vnib/vnib/vnib.i: vnib/vnib/vnib.cpp.i - -.PHONY : vnib/vnib/vnib.i - -# target to preprocess a source file -vnib/vnib/vnib.cpp.i: - $(MAKE) -f CMakeFiles/vnib.dir/build.make CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.i -.PHONY : vnib/vnib/vnib.cpp.i - -vnib/vnib/vnib.s: vnib/vnib/vnib.cpp.s - -.PHONY : vnib/vnib/vnib.s - -# target to generate assembly for a file -vnib/vnib/vnib.cpp.s: - $(MAKE) -f CMakeFiles/vnib.dir/build.make CMakeFiles/vnib.dir/vnib/vnib/vnib.cpp.s -.PHONY : vnib/vnib/vnib.cpp.s - -# Help Target -help: - @echo "The following are some of the valid targets for this Makefile:" - @echo "... all (the default if no target is provided)" - @echo "... clean" - @echo "... depend" - @echo "... edit_cache" - @echo "... rebuild_cache" - @echo "... vnib" - @echo "... vnib/vnib/vnib.o" - @echo "... vnib/vnib/vnib.i" - @echo "... vnib/vnib/vnib.s" -.PHONY : help - - - -#============================================================================= -# Special targets to cleanup operation of make. - -# Special rule to run CMake to check the build system integrity. -# No rule that depends on this can have commands that come from listfiles -# because they might be regenerated. -cmake_check_build_system: - $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 -.PHONY : cmake_check_build_system - diff --git a/vnpy/api/ib/build/cmake_install.cmake b/vnpy/api/ib/build/cmake_install.cmake deleted file mode 100644 index 2de8dd80..00000000 --- a/vnpy/api/ib/build/cmake_install.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# Install script for directory: /home/vnpy/桌面/new/vn.ib - -# Set the install prefix -if(NOT DEFINED CMAKE_INSTALL_PREFIX) - set(CMAKE_INSTALL_PREFIX "/usr/local") -endif() -string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") - -# Set the install configuration name. -if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) - if(BUILD_TYPE) - string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" - CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") - else() - set(CMAKE_INSTALL_CONFIG_NAME "Release") - endif() - message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") -endif() - -# Set the component getting installed. -if(NOT CMAKE_INSTALL_COMPONENT) - if(COMPONENT) - message(STATUS "Install component: \"${COMPONENT}\"") - set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") - else() - set(CMAKE_INSTALL_COMPONENT) - endif() -endif() - -# Install shared libraries without execute permission? -if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) - set(CMAKE_INSTALL_SO_NO_EXE "1") -endif() - -if(CMAKE_INSTALL_COMPONENT) - set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") -else() - set(CMAKE_INSTALL_MANIFEST "install_manifest.txt") -endif() - -string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT - "${CMAKE_INSTALL_MANIFEST_FILES}") -file(WRITE "/home/vnpy/桌面/new/vn.ib/build/${CMAKE_INSTALL_MANIFEST}" - "${CMAKE_INSTALL_MANIFEST_CONTENT}") diff --git a/vnpy/api/ib/build/lib/vnib.so b/vnpy/api/ib/build/lib/vnib.so deleted file mode 100644 index ead8808bee49c7c2fa8c31c359b0e1beb0f0ac9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3047352 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

3mXr6XaL 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

    _Itwz(~Mfi^o*ERPc$ zfw1?PzX0=mP83`zWx|SKP-l{rjItsVhSjL<62Y20EfXd5BK{{G*VxVZtkb<8BR`Ay zEQCpm32Qlr=|Qb4IIV>?AEq{=@XD!w8lTUL4ocX#b0(YZX0_eDc}u7nI1cx(q<)dl zr99niV|TyvSHWWd^I~uVH!TT4s9t#!+wp{?3R41{Wtsl=^Bo+9I&rzpK&X^prn$)T zKtA+k2QI426jt#vyF>R4svlTDoZWrCdu7|8@UK^p*j=kzc@vH~+TckyrGcA#i9~?# zDI-5j?UcS*4l>aI5TG07O}e=k9U7<8%G1r${*r z-NYi_e&@mD#q`bm6bdLiBIMK4U$lUBuo5N%^whHIoI+SV=f?5ALO5~{EW+y_s_I1j z_OREt!1*>B^va+@O3(SpO&N;cSIA>5XwdFc2Ovg@P? zHJD74AL`=+;YAV|GB(SH>9k2oc~3|xJ45y&;bIfB5G(Z)Agr6u5D2SoR?gbF6)wV- zBrB}{BdzJ4!%&75UB|?yQ%OoGFp{VAiE2EVO0`X&tRp6W$&8X*yRUVT zDY~B2z?cctgq>8B2|JmRE9^Eq7fWcj$h$L=a(QV7oK-$dpUI`drjJV6f$i;FOGN-X zc-D8VYhILO(KdFh8=hYejMn?dHBTqlsjhibX+a>YU`V7OS? ztxQ_k(?Qu#26)^iwI9e3;8V_tyYNHk8*%nf#e(pGm9el20@fTOic9w^G#q(*Xd1_! z5!TOzeOt+Ip!=_FvuJ1m1g=9LpPMzU)iqBvJfd0k&d>l9Z4z08@@rDfxX?;B`)wEIl;r`2^^u~K=Rf~a>c<)-cXW_qT~9Di-~x( z!62hWft9K>G})Jj|mI*Jfm|yI7ZgNo$Oku#ba(9Q__!6&K7VX4!D*6FQI*!SODV@~a=%(lGxcsl3i!uXo?l{pv!r zwqKPNdAV1W!I>XIf;$<#q}DJwhfWo^iWHMo*Na_t5DJgcp@P(~?q-~0ePLiVkqcM? z?9l-Ip1G3~_uhZm`*7Gk%g3=a0tJ?udI_9=o4boY-1~H)bV?`ZNi@ZqhN_jB!0oWm zMUwQE6;+T#;#aIFf`b4YEEnf6tQ^CbT;Pqja6kYLYO>E|BMs7nGi9TKD_S0co6z~& zU&Qa2O?*6t*3Da35y{;4m!*e0rped^IQ;iBNbU=+OZ!`_Ox3T@tWLZ4*jOQ@H(6Yz z>U;UUZRb2W23F`t;`n775Hq;ExYSUTx%B*`9a>HN@KwHXaWcm}V?6pqATN zrKt)2G@0L5Uo}e|NJVxPyFU9D+_syXX-zy76qf<7`M1!UrQhMs3N|VWl&ud40pw6v zs*Icol&-yqIhD;eHBlg|oRXBdwGU^Sw{Psb34QrDnfc2e`pIb!%x&HSdFg*jZ|{b- z3H45ZAUPm<>zvL$g#nz3UTjxzhjZ~vXgvEn{gsQ^JsceNBiJ0e6Mh~4o&InUJZ-$> zn(O@6&lACgF?q`1GIwz4@B?9SaqaHDz@zs(byFXIDn_MUCh)7L0?7UOWG}(ElVk~r?8-eTerdTcb zNgXb`S|ZiY@*b^*e%cX_n%NR|c%^){I7lanhSo|RGM&euwKi%PGKj>X`0QWdjpN@(d5j7HkZp<(b0sN{kpN$4a$_k|QfdxH?G z{+@&n1MA0tot&>O_9>(TN8Fc9@V{_#1N2WppnZlE!zm7Sss`q#=A(#9-xekjv)>=W zDVzy*{cs$94>mE+<|3XHM{`zqDX<&B1>{}~BVkchWnw5MGS`ctw)ucRQ3UUUfwXY< zdeZiWGjWnl0`VLY?C|oR!WlHMMVRgZI49hBrewW<<6?9)j*)Ra*o%z(r5EC`$|ZK! zuqATY+QIRs`)qP?)|ZrSNT?2MD~BiP5l}X2U~&alzI&mY-uwZ0ih@_XdI@e_Uj;sl z0jj%V<@`0+2QloG!lSY+AoyLpGa@}~bql#d<#1GTv>475AqcA%`Y{Xwu24Qyvf(%ykewI2c6 zs~jDH<*`pRrR9<4RwIelNO_~SLbSva+ag-x`?E;3d=1|&k&#Yno!U&lX&rvzB7Iq& z;HcN+zRtR2Qj$V`c9X5I8ML35y3e)JF?qSjmeD0QOr&c6ow`QC)PAKF9fVf?zgQn_(c(w2)m@245$V^o>!(ME4{@qPyfvTITo_VYqt`Eia z$-q;kG|-e}_sxqez~b0BDqh;fTiDm7S1;>(ty2EDB3jczwXn_x{HWTy++I{FYiJzI zD{DP+!YsF(lh)<7b$WMziV;2&jPUw-Ej7S!{0X}8E87hpUAP_$&u*#4ik3*N+R-!4 zj!N2>8tkYol4RzPqe{gb+9Ii)Rl{2^>#f>hV~|m+s@FLLD|(HXGuCTML@^bW6V+Np zWw(vu&R{+>r}j9rmx*~`v~z(xnAf3gf=g`R zCns>%iu>IU;S}o}&T)VhNdM$92OLMehf}E)*yhJ^u1ZYcJ&17sQF5`e1*ZGw*#Z;m z?5p?nv%$+4n9#cv#U|%Bwd!}{E|Nb?{k}2vv$1@7tgi#G33vgIJpwnZP{vSAr3jVOgzqPRV_c!eP$^A{*wR(TUk2iwcILwAhU9=lTIz(UjK((Bs&zJ>PuP34(L- z=v~90ODrk%VN~47k6(nnA-2o;r51y z3_TN1m-M@>Yd3g)3_Sg8@Vf`<0k#y}Z$%4K5Up}Ax^j9x21csl3 z5Qd*5*G1HDA{+VI@cY~l0eF9THu)7aG`+%n59!bLikress3m z!blhx!Sa0@!aNfE2>b}K3||)+i7<0Lxolawx={UnUT(kmPfc?8lugTQ_#H_(A7v&U z@VSh)i#UQQ4j<1~KYu#e4O!xEo#9xRq`61Fkm>GnLXGOGq#rE_|1uRmOb2=_WRLAm zXVA}-$i9$!_U%c!Qn}Qz?=$&oC34Xsq`8CvM{f{$8VEMCA&c#Gc^`NkLFG(E}k=K6BwZ(g30YsNlJ@J4~V&#dhcus(x- zzJeye|KtC`ZDS{Hp{}PVQM!1K(e^o4T2QMe-*NFCp0tDaiVY^3$!;d75^{x?G$ap3 zf|5H~N;7+KT*x_G;_wr%55q<|oIJy32hG^6dTJla^r!Tp!!48tEE6sOp6gWYsLaP7 z!Obt3RV=F}{e>MPreE{~dh`;UY;KggC8`O|JlZC^ zm*YPbCd^J_8J~5i(9?8oqMU1_#)qXh2N0qWLHj&DSbk4@o1lf2|nHZA#6WpS!S_wQGJ3j3K&!v2{IL zV4JEdE}P$<-RoF~c$pc+vwE%goHj2YWPUi9t_IsUJOpP0;sU~*ezgk-;c}JJeERD& zoy0env2g+q&uvGc;CH&*rZw{ac2%3FR za9CjicON*?zu}uD(fpZ+rdSG@rRPc^7_ehBQFF;PENYTq%dWXLdh)#bOA z6{c{H_buZ*MYsk!^B!trDAW8IPgvt}qSMX;fCeblIBK{UG&%iLqPPHH{ikr7qWbc3 zgZaVdocsR^XUQPofK(l6SN!Lr4arLhhRS8RfD`so8OE9ZqaVb=D^9l)$iN;Axw zBCGQ?P-_UcQF!!!YNRl*zU@Y9QLEaR*1iZJ{x1c`USwB&SVRq_9zst@F)Y} z%HE9;c)j2X5wwgoE@5Urloepi{q2?OK5brH-08S*VOo9k40~>DLjN6vH3@DYr9vO7 z{3hoVUX@*b;Jy5u{{f;(Bc--)zTTzxzMH?hFfz8^UG{CrAC-iwZ|Y9Mhu=*mVW|fs zlymL-I+J#53g~1m^_Y}GuYF;6Qg2Oy9kFZr(Il)8Eo z%Nhtd=ywK15aLIVphXNm8?Jd-yTRl9ka)GW(TlF9i=^u*jgm2BJ9lA20%{EI96LIa z1bdHL%JZ9a146UK0 zCKsd@QR2JvI|nl|C0EYwUQ=8A1e z!`nk>1B4`f5fbLxzA6pb53jj}SD{VljxX((Dd~Gf%!Mvb5?xN)Ru1TL@=Ku0$p9op zU2d6zIOm`{U>}~~f};)IqIJ-e<*5h&8*0Nq9rkBVP9C?>KVAgJ5G)dIl;+l@q(O%^ zb6OCMMzGZ;h}{i7xIYQ*RKgpqk_|p+RH^Rmv4Vf4M9bB0p%|N%Rj1#pC7&v`fpN$7?k(L@bm#kr=*2JFa;YdKsl)KszfAP8tbcQgUY}MBem$h5 zk7QNq(~jXUi{#)F4vfrs5J;{Gp@{{Hbc-0xI~DcZHHoTe=+ zNwW{6_ocRsKay>RQtV!A^efQYZ^sI72nJm$rq&HA_vyx$>^u-#aOn?ooSc8kj>KF; z-zRj2uuoNfkfY>V1P65TUrSG+-XEOwv!V7AuvH_%ol0Ts|e<~MKF_6eOL+WBzu`l zwlu0a&^u=wU=)ObP~OFdDLyzOoc4r0VR>b%{pwuh-Eq)AmOcD87v`ynE0yjtOA$Lc zRWLh8Ck0{UC3mpZt^9?}mCx2DS^hSCgjr|cWwE@aCkR~KTGB%st6QS8J&@};6?pwt z<+ZC7ET5Ox=Iys+ymC1#_;-~nt+2q-3fkpVSi#ismuykp3QkFMP(cANBnl|n)m*xEo7zj2 zom#;IKV^~HN}5DhZ;xDoU`ffM4y86NKMN!kTp4He(@YBw%$3DY;;r>8zda5G9*uo9 z%u40#N?P}TYR^ehKv}>kTWG9pT@K3@CB$MWdS$pvZy1ubxI2Z{nkZ$7O4P9 z$7zqri`)zLi%BR0=wlM`nEz=wC|LsDUsVwuA?~^mn*gr=R0UUO??!?x&L`sd*V==) zC4BwAs+is(zS@dTz}SDQ!mGm=s}%KXzo1@Z!G5&`*Eoz9xy%GqDOfI+&-9Bbh@b&g zdPzoG)f=JJ>OyP+wiGPZpt1mif>=vODA{>CLPwYe_$1O2kd)5vXthhiYJ>e+DX1i- zzK<&%6zx52ecCv1qAd%36tFsGzbJspXNrPqJ+h^xu(cQ3Zz(`a4#Mgy?BI+bQhBGj zyGfMNMq~n}jy3viQO|8@UnCMZ!bl{AjgeFYWc-gtw*B?uug!e${PyS_IQTEoxq=D!=$b*!u$faQaos&lBKnigB7r{(5>xj7DBg1s6eX1 zjwXw(YviYwlq<+Q+n-N%&wi_Ho!N?&2E(1OQVJ1}Y^A^^IkFydST)Jq;$kxkhcnKF z>4?cza;qVbS@R~}SwYy5Td2#hxD{4XlGoMQEVf@zgGxKEJrM0%%IUe~4VA0yStdoW&TvS35e$jE&D-j8?oyIF(bta*h@+d5u zg0(&rmmot2l+}WXh9DY;3#ZE3iH+t%n!b1xR!zY&vlZbvq!AH9Az;cuTl3m*%`g|a^&M{!v5C+ini$$q^Z#P%J$4T zZ7x%%<5Ri`v$7&UmKOC9W$Ag`9;G7yMHXXS!BF+=M!ks_J-O9uy5@Rp&+wA4d>~I{ ziVxvyorc%e&RyyP)mprsqA6|#Va-j|pk*h)k@sJBB{Kn5lKzXj zOsud8M-Hn*$8I56YZsFL0v6l_nYvAAacYp7KD4f+-iE7Xqu*Yy^+E;}pL3Fi3Xs;P zZ|o|k9}KI)8Y-mlYz=$14zPsabZiNq)oFbA*z*l(zxE$y)+tkqiK zo{lZ#_P8_Q2|!cmJ}z{)XP|)wm4TLPVN1x^GSodoMneq3;!6y8i^6$mTwjDIXw?^M zB7zXtKU}x!s^au5X+dJ26-4v7+Lf|d)EE`N$w&XY~{TXiJDK0Krh2DOD@EWAb9Qq_G{rsdBx-i9 zB0qkKHh4qZi|_8XVX%6G8Q3STbDsTV`Ii2$G0M)L$K570ySrK4JwQ$ew?kF>6$%B6 z{HI_K7c=>AA3bvCZkK=it2lYvRSdZST>kX9oz*_Uxd_&92Rlshb{}6ht2o$hlCj?p zi`z8ja!&T&J$(Ewj({_Fx_gViZ#Ovw&KbKFD!FSa830}<7Dp-;r*^762>HP6xeaUe zAz7;r$y%X&>qT&qN)GRVasV@ra7iynEoi=&{{ORgFMy3ys}$H>QWux9T_7r&^tGEnw+(G~$@(G)N|1*IQ6i!SR0x6+L?Ft;s0i@^R8WW@ z7z82sz=(no74J+QoqyAO-6lQL*LLr{+}&TNXU_S)@0^*;B$GOxS*EYULG9iAJTHK<*HNv@*74qA4J)kf_6=H--X+>2;>#aS zSX`=<>hftnq4a>Z;{LU-f#^EbJJ7RHtM9(9p4QTH#q}PJxlN#bTcViUDXs8C3a7nC&bU-P$+1O*~yz8`EW#K3%kHYWnZ2 z)8@@Y)1^)QbZMXDbSZwZkv=G`^2SXUcV+P#J6)EUHeDLIyKT%c{o#z|FC!?ANZ|sc zM@T-r&XHL*#rUo%#&=DnDyC<0Hq>XW4D^q?ZTAqUeRfwL&P0d6wZ@!&uRf>WtIcWc zchYd7fvS1@SJD|L$t#OhrZ2S% zv$%PMS38>=*xb=O`YNQgt=8v?CXA?7<>d)cO8vAPR43WX@`AnHnX0Yr#Th-p$!e=H zQ{DPZb!#)V`1YCB31_N)W~m+h6do(uPnzUR_2^&4EzQ*OCpW84R##5%OkGlZWHP>K zbz!1vFVX`&gZk>UofF2IQ(%IC&YpQXu9FMXbXHGO*TMwd(mUkUFRGNTqBTB2Ylm{y zOigxgVN%ZSNx7k8(~0_pUG0k4!clWdM@>G;#0cf=nUu)%!lZ0yQW`U)=2_GD7mQk* zmuTnC9qp^vuhkxbpz1ZvlF_dpZKOx~_4M*5xQo}+>iE80{rz{^H;orxp^bTMUb?s3 zKI97Y4|?^R3`<+Jn+w`~->v$XtG(*`U>T}^@Zgx< z*45m)>KEP3uJVhezV3h?OZ~ldT91|NZ1l}Qs@ki3`LLq)%2lssuw%ZcM^Tg2U#hHV z+;~8+qK-N@cC6|c>L?bEnkHAC(5HQGwDuAqsA$mRzsOus!&r0MhqP;7U7=8ObMse3cY{VG@ShOL^g8J(%zyHnk6 zty{Ot^h1D}9o0IL;!MiSy%?`YP25X!^<@e(_ciVeMAgn3D0D@;?jF}p`Fvp9VbE1N znhtDR-rL`Q>Xu;fOfLvr;D7}ul5n(bB8&iH2rshOEIAL^*6&&CW_ z(^un)gPQiL&zcksyLvHD(^=yfu${$JKlT*AD|1@*Yb>wRy5H&;7mv8pHtKPOqk8wo zr(+b=r2gt?f_J*9yz8{RdlA(Otx?$8i%z|W$}c^YoqNolp$qRVQZrF!QHmRSr-X4e z&0Q>m(f9n!zwp%iMue)jyX+VNsXnE~IY_?Qg(o?v7A+P!gwWMoGGc=eW#-A1QfM1iJWjQSI*YQ zheORJapAhPn#OBfZ(p}!_4=L;?JA#sX{@K~)T4U?+Q%|Vw+OTsv8A6K*WMo3)YsA5 zyySH4!r7Ysfzm%qTNw>#p9ARX8614y;HJUSkJheOU23nnIWVYQH|{HbN}%|&(Ko)9 zU-egB>n}cipd%RU87SVc&@ZuXXdQcPLEiAhb=vdg^(%iJoq?^*`a9rCpRXA6X_V^y zt|;%Xb@Up4t(^*6joss7ZSRADN$s?B6J)S?X|O-gr+tdX>+kQ?a?sj_iCANr)_x(g zLc8!+e200BXc;de%-&P;vOu49vq$^5M0qg9I95K*Q_iy7o$XD-rNYvv4_+I-e)YDl z;Vaso%JEgLx+acn;yq1zOxn8Gr(H_#8Yjh6tH_Vt8@^rfi!6IbiCiv;UOKSVV_p)5qE`fyiSF&8&$pWv%}QqmjH zKF?+xA?Hb%)x6vy*HriV;(MU=<$oSk)YO77Pbxv>1u40JitqEUe5_?2mCy`DZ@$#t z?&-y^2&DsQFw`+rOm0uu|zO6)&_*ed-An)x5a$6rl1oME#i+Gd#H_ zc+WtMr{hR3dFTAN=1jh7zUZ9zb@l8^&P0QlGnts;YqzSqsuNupnSBW~VeE4!@AbZl zs__xFxwDSeI{B(`Z_7Ah)*9{H&g}%R_-YMSIP0E;lektP*E!fZo!QziBIaFACqK-& zoz0rBbmm_+Cp*k_YsWkhee~6T?YmPeH+O`*9Ru1!KF7Z2qj}uNMi)DK1_t^EiVqF- zYM-$j>Ii6`I~{ZWQr9n4if-lT2T#YeRmGL9$L~UW?`Cg*UypIGt+EhLV$;!9SLmNb z9ldv_Und#UnP%zd+UM1l3~bRJlDfIaTMUnW*|^eb<$W;A27_9_w5{~VZ)%@c9n