diff --git a/tests/trader/run.py b/tests/trader/run.py index 77fdc24a..c5f0455b 100644 --- a/tests/trader/run.py +++ b/tests/trader/run.py @@ -7,7 +7,7 @@ from vnpy.trader.ui import MainWindow, create_qapp from vnpy.gateway.bitmex import BitmexGateway from vnpy.gateway.futu import FutuGateway from vnpy.gateway.ib import IbGateway -from vnpy.gateway.ctp import CtpGateway +#from vnpy.gateway.ctp import CtpGateway from vnpy.gateway.femas import FemasGateway from vnpy.gateway.tiger import TigerGateway from vnpy.gateway.oes import OesGateway @@ -30,7 +30,7 @@ def main(): event_engine = EventEngine() main_engine = MainEngine(event_engine) - main_engine.add_gateway(CtpGateway) + # main_engine.add_gateway(CtpGateway) main_engine.add_gateway(FemasGateway) main_engine.add_gateway(IbGateway) main_engine.add_gateway(FutuGateway) diff --git a/vnpy/api/femas/test/mdtest.py b/vnpy/api/femas/test/mdtest.py deleted file mode 100644 index b2c58c48..00000000 --- a/vnpy/api/femas/test/mdtest.py +++ /dev/null @@ -1,173 +0,0 @@ -# encoding: UTF-8 -# flake8: noqa - -from __future__ import print_function -import sys -from time import sleep - -from PyQt4 import QtGui - -from vnfemasmd import * - - -#---------------------------------------------------------------------- -def print_dict(d): - """按照键值打印一个字典""" - for key,value in d.items(): - print(key + ':' + str(value)) - - -#---------------------------------------------------------------------- -def simple_log(func): - """简单装饰器用于输出函数名""" - def wrapper(*args, **kw): - print("") - print(str(func.__name__)) - return func(*args, **kw) - return wrapper - - -######################################################################## -class TestMdApi(MdApi): - """测试用实例""" - - #---------------------------------------------------------------------- - def __init__(self): - """Constructor""" - super(TestMdApi, self).__init__() - - #---------------------------------------------------------------------- - @simple_log - def onFrontConnected(self): - """服务器连接""" - pass - - #---------------------------------------------------------------------- - @simple_log - def onFrontDisconnected(self, n): - """服务器断开""" - print(n) - - #---------------------------------------------------------------------- - @simple_log - def onHeartBeatWarning(self, n): - """心跳报警""" - print(n) - - #---------------------------------------------------------------------- - @simple_log - def onRspError(self, error, n, last): - """错误""" - print_dict(error) - - @simple_log - #---------------------------------------------------------------------- - def onRspUserLogin(self, data, error, n, last): - """登陆回报""" - print_dict(data) - print_dict(error) - - #---------------------------------------------------------------------- - @simple_log - def onRspUserLogout(self, data, error, n, last): - """登出回报""" - print_dict(data) - print_dict(error) - - #---------------------------------------------------------------------- - @simple_log - def onRspSubMarketData(self, data, error, n, last): - """订阅合约回报""" - print_dict(data) - print_dict(error) - - #---------------------------------------------------------------------- - @simple_log - def onRspUnSubMarketData(self, data, error, n, last): - """退订合约回报""" - print_dict(data) - print_dict(error) - - #---------------------------------------------------------------------- - @simple_log - def onRtnDepthMarketData(self, data): - """行情推送""" - print_dict(data) - - #---------------------------------------------------------------------- - @simple_log - def onRspQryTopic(self, data, error, n, last): - """订阅合约回报""" - print_dict(data) - print_dict(error) - - #---------------------------------------------------------------------- - @simple_log - def onRspSubscribeTopic(self, data, error, n, last): - """退订合约回报""" - print_dict(data) - print_dict(error) - - -#---------------------------------------------------------------------- -def main(): - """主测试函数,出现堵塞时可以考虑使用sleep""" - reqid = 0 - - # 创建Qt应用对象,用于事件循环 - app = QtGui.QApplication(sys.argv) - - # 创建API对象 - api = TestMdApi() - - # 在C++环境中创建MdApi对象,传入参数是希望用来保存.con文件的地址 - api.createFtdcMdApi('') - - # 注册前置机地址 - api.registerFront("tcp://118.126.16.229:17101") - - # 初始化api,连接前置机 - api.init() - sleep(0.5) - - # 登陆 - loginReq = {} # 创建一个空字典 - loginReq['UserID'] = '' # 参数作为字典键值的方式传入 - loginReq['Password'] = '' # 键名和C++中的结构体成员名对应 - loginReq['BrokerID'] = '' - reqid = reqid + 1 # 请求数必须保持唯一性 - i = api.reqUserLogin(loginReq, 1) - sleep(0.5) - - ## 登出,测试出错(无此功能) - #reqid = reqid + 1 - #i = api.reqUserLogout({}, 1) - #sleep(0.5) - - ## 安全退出,测试通过 - #i = api.exit() - - ## 获取交易日,目前输出为空 - #day = api.getTradingDay() - #print 'Trading Day is:' + str(day) - #sleep(0.5) - - ## 订阅合约,测试通过 - #i = api.subscribeMarketData('IF1505') - - ## 退订合约,测试通过 - #i = api.unSubscribeMarketData('IF1505') - - # 订阅询价,测试通过 - #i = api.subscribeForQuoteRsp('IO1504-C-3900') - - # 退订询价,测试通过 - #i = api.unSubscribeForQuoteRsp('IO1504-C-3900') - - # 连续运行,用于输出行情 - app.exec_() - - - -if __name__ == '__main__': - main() diff --git a/vnpy/api/femas/test/tdtest.py b/vnpy/api/femas/test/tdtest.py deleted file mode 100644 index ef21f0e1..00000000 --- a/vnpy/api/femas/test/tdtest.py +++ /dev/null @@ -1,166 +0,0 @@ -# encoding: UTF-8 -# flake8: noqa - -from __future__ import print_function -import sys -from time import sleep - -from PyQt4 import QtGui - -from vnfemastd import * - -#---------------------------------------------------------------------- -def print_dict(d): - """按照键值打印一个字典""" - for key,value in d.items(): - print(key + ':' + str(value)) - - -#---------------------------------------------------------------------- -def simple_log(func): - """简单装饰器用于输出函数名""" - def wrapper(*args, **kw): - print("") - print(str(func.__name__)) - return func(*args, **kw) - return wrapper - - -######################################################################## -class TestTdApi(TdApi): - """测试用实例""" - - #---------------------------------------------------------------------- - def __init__(self): - """Constructor""" - super(TestTdApi, self).__init__() - - #---------------------------------------------------------------------- - @simple_log - def onFrontConnected(self): - """服务器连接""" - pass - - #---------------------------------------------------------------------- - @simple_log - def onFrontDisconnected(self, n): - """服务器断开""" - print(n) - - #---------------------------------------------------------------------- - @simple_log - def onHeartBeatWarning(self, n): - """心跳报警""" - print(n) - - #---------------------------------------------------------------------- - @simple_log - def onRspError(self, error, n, last): - """错误""" - print_dict(error) - - #---------------------------------------------------------------------- - @simple_log - def onRspUserLogin(self, data, error, n, last): - """登陆回报""" - print_dict(data) - print_dict(error) - self.brokerID = data['BrokerID'] - self.userID = data['UserID'] - self.frontID = data['FrontID'] - self.sessionID = data['SessionID'] - - #---------------------------------------------------------------------- - @simple_log - def onRspUserLogout(self, data, error, n, last): - """登出回报""" - print_dict(data) - print_dict(error) - - #---------------------------------------------------------------------- - @simple_log - def onRspQrySettlementInfo(self, data, error, n, last): - """查询结算信息回报""" - print_dict(data) - print_dict(error) - - #---------------------------------------------------------------------- - @simple_log - def onRspSettlementInfoConfirm(self, data, error, n, last): - """确认结算信息回报""" - print_dict(data) - print_dict(error) - - #---------------------------------------------------------------------- - @simple_log - def onRspQryInstrument(self, data, error, n, last): - """查询合约回报""" - print_dict(data) - print_dict(error) - print(n) - print(last) - - -#---------------------------------------------------------------------- -def main(): - """主测试函数,出现堵塞时可以考虑使用sleep""" - reqid = 0 - - # 创建Qt应用对象,用于事件循环 - app = QtGui.QApplication(sys.argv) - - # 创建API对象,测试通过 - api = TestTdApi() - - # 在C++环境中创建MdApi对象,传入参数是希望用来保存.con文件的地址,测试通过 - api.createFtdcTraderApi('') - - # 设置数据流重传方式,测试通过 - api.subscribePrivateTopic(1) - api.subscribePublicTopic(1) - api.subscribeUserTopic(1) - - # 注册前置机地址,测试通过 - api.registerFront("tcp://118.126.16.229:17111") - - # 初始化api,连接前置机,测试通过 - api.init() - sleep(2) - - # 登陆,测试通过 - loginReq = {} # 创建一个空字典 - loginReq['UserID'] = '' # 参数作为字典键值的方式传入 - loginReq['Password'] = '' # 键名和C++中的结构体成员名对应 - loginReq['BrokerID'] = '' - reqid = reqid + 1 # 请求数必须保持唯一性 - i = api.reqUserLogin(loginReq, reqid) - sleep(0.5) - - ## 查询合约, 测试通过 - #reqid = reqid + 1 - #i = api.reqQryInstrument({}, reqid) - - ## 查询结算, 测试通过 - #req = {} - #req['BrokerID'] = api.brokerID - #req['InvestorID'] = api.userID - #reqid = reqid + 1 - #i = api.reqQrySettlementInfo(req, reqid) - #sleep(0.5) - - ## 确认结算, 测试通过 - #req = {} - #req['BrokerID'] = api.brokerID - #req['InvestorID'] = api.userID - #reqid = reqid + 1 - #i = api.reqSettlementInfoConfirm(req, reqid) - #sleep(0.5) - - - # 连续运行 - app.exec_() - - - -if __name__ == '__main__': - main() diff --git a/vnpy/api/femas/vnfemas/vnfemasmd/vnfemasmd.cpp b/vnpy/api/femas/vnfemas/vnfemasmd/vnfemasmd.cpp index 4ec0ddc9..c94bba42 100644 --- a/vnpy/api/femas/vnfemas/vnfemasmd/vnfemasmd.cpp +++ b/vnpy/api/femas/vnfemas/vnfemasmd/vnfemasmd.cpp @@ -717,7 +717,7 @@ public: PYBIND11_MODULE(vnfemasmd, m) { - class_ mdapi(m, "MdApi"); + class_ mdapi(m, "MdApi", module_local()); mdapi .def(init<>()) .def("createFtdcMdApi", &MdApi::createFtdcMdApi) diff --git a/vnpy/api/femas/vnfemas/vnfemasmd/vnfemasmd.vcxproj b/vnpy/api/femas/vnfemas/vnfemasmd/vnfemasmd.vcxproj index dea02d7d..734ee427 100644 --- a/vnpy/api/femas/vnfemas/vnfemasmd/vnfemasmd.vcxproj +++ b/vnpy/api/femas/vnfemas/vnfemasmd/vnfemasmd.vcxproj @@ -23,7 +23,7 @@ {F00054FF-282F-4826-848E-D58BFB9E9D9F} Win32Proj vnfemasmd - 10.0.17763.0 + 10.0.17134.0 vnfemasmd @@ -95,10 +95,10 @@ false - C:\Python37\include;$(SolutionDir);$(SolutionDir)..\include;$(SolutionDir)..\include\femas;$(IncludePath) + C:\Miniconda3\include;$(SolutionDir);$(SolutionDir)..\include;$(SolutionDir)..\include\femas;$(IncludePath) $(ReferencePath) .pyd - C:\Python37\libs;$(SolutionDir)..\libs;$(LibraryPath) + C:\Miniconda3\include\libs;$(SolutionDir)..\libs;$(LibraryPath) $(SolutionDir)..\ @@ -181,7 +181,7 @@ true true true - C:\ProgramData\VNConda\libs;C:\Users\Administrator\Documents\GitHub\vnpy\vnpy\api\femasctp\libs;%(AdditionalLibraryDirectories) + C:\Miniconda3\libs;$(SolutionDir)..\libs;%(AdditionalLibraryDirectories) USTPmduserapiAF.lib;USTPtraderapiAF.lib;%(AdditionalDependencies) diff --git a/vnpy/api/femas/vnfemas/vnfemastd/vnfemastd.cpp b/vnpy/api/femas/vnfemas/vnfemastd/vnfemastd.cpp index a071f8c1..1b911e4e 100644 --- a/vnpy/api/femas/vnfemas/vnfemastd/vnfemastd.cpp +++ b/vnpy/api/femas/vnfemas/vnfemastd/vnfemastd.cpp @@ -2451,14 +2451,11 @@ void TdApi::processRspQryInvestorAccount(Task *task) data["Margin"] = task_data->Margin; data["Premium"] = task_data->Premium; data["Risk"] = task_data->Risk; - print("processRspQryInvestorAccount"); - print(data["AccountID"]); delete task->task_data; } dict error; if (task->task_error) { - print("task->task_error"); CUstpFtdcRspInfoField *task_error = (CUstpFtdcRspInfoField*)task->task_error; error["ErrorID"] = task_error->ErrorID; error["ErrorMsg"] = toUtf(task_error->ErrorMsg); @@ -4237,7 +4234,7 @@ public: PYBIND11_MODULE(vnfemastd, m) { - class_ TdApi(m, "TdApi"); + class_ TdApi(m, "TdApi", module_local()); TdApi .def(init<>()) .def("createFtdcTraderApi", &TdApi::createFtdcTraderApi) diff --git a/vnpy/api/femas/vnfemas/vnfemastd/vnfemastd.vcxproj b/vnpy/api/femas/vnfemas/vnfemastd/vnfemastd.vcxproj index 8fc2e28a..77ab8b70 100644 --- a/vnpy/api/femas/vnfemas/vnfemastd/vnfemastd.vcxproj +++ b/vnpy/api/femas/vnfemas/vnfemastd/vnfemastd.vcxproj @@ -23,7 +23,7 @@ {016732E6-5789-4F7C-9A1C-C46A249080CF} Win32Proj vnfemastd - 10.0.17763.0 + 10.0.17134.0 vnfemastd @@ -95,8 +95,8 @@ false .pyd - C:\Python37\include;$(SolutionDir);$(SolutionDir)..\include;$(SolutionDir)..\include\femas;$(IncludePath) - C:\Python37\libs;$(SolutionDir)..\libs;$(LibraryPath) + C:\Miniconda3\include;$(SolutionDir);$(SolutionDir)..\include;$(SolutionDir)..\include\femas;$(IncludePath) + C:\Miniconda3\include\libs;$(SolutionDir)..\libs;$(LibraryPath) $(SolutionDir)..\ @@ -178,7 +178,7 @@ true true true - C:\ProgramData\VNConda\libs;C:\Users\Administrator\Documents\GitHub\vnpy\vnpy\api\femasctp\libs;%(AdditionalLibraryDirectories) + C:\Miniconda3\libs;$(SolutionDir)..\libs;%(AdditionalLibraryDirectories) USTPmduserapiAF.lib;USTPtraderapiAF.lib;%(AdditionalDependencies) diff --git a/vnpy/api/femas/vnfemasmd.pyd b/vnpy/api/femas/vnfemasmd.pyd index 42069546..4f9b2954 100644 Binary files a/vnpy/api/femas/vnfemasmd.pyd and b/vnpy/api/femas/vnfemasmd.pyd differ diff --git a/vnpy/api/femas/vnfemastd.pyd b/vnpy/api/femas/vnfemastd.pyd index 13222642..49d52c83 100644 Binary files a/vnpy/api/femas/vnfemastd.pyd and b/vnpy/api/femas/vnfemastd.pyd differ diff --git a/vnpy/gateway/femas/femas_gateway.py b/vnpy/gateway/femas/femas_gateway.py index 9db695e6..d0c22b15 100644 --- a/vnpy/gateway/femas/femas_gateway.py +++ b/vnpy/gateway/femas/femas_gateway.py @@ -30,7 +30,7 @@ from vnpy.api.femas import ( USTP_FTDC_TC_GFD, USTP_FTDC_TC_IOC, USTP_FTDC_VC_AV, - USTP_FTDC_VC_CV, + USTP_FTDC_VC_CV ) from vnpy.trader.constant import ( Direction, @@ -39,6 +39,7 @@ from vnpy.trader.constant import ( OptionType, OrderType, Status, + Product ) from vnpy.trader.event import EVENT_TIMER from vnpy.trader.gateway import BaseGateway @@ -86,6 +87,12 @@ OFFSET_VT2FEMAS = { } OFFSET_FEMAS2VT = {v: k for k, v in OFFSET_VT2FEMAS.items()} +# PRODUCT_CTP2VT = { +# THOST_FTDC_PC_Futures: Product.FUTURES, +# THOST_FTDC_PC_Options: Product.OPTION, +# THOST_FTDC_PC_Combination: Product.SPREAD +# } + DIRECTION_FEMAS2VT = {v: k for k, v in DIRECTION_VT2FEMAS.items()} DIRECTION_FEMAS2VT[USTP_FTDC_PD_Long] = Direction.LONG DIRECTION_FEMAS2VT[USTP_FTDC_PD_Short] = Direction.SHORT @@ -237,16 +244,15 @@ class FemasMdApi(MdApi): """ self.connect_status = False self.login_status = False - self.gateway.write_log(f"行情连接断开,原因{reason}") + self.gateway.write_log(f"行情服务器连接断开,原因{reason}") def onRspUserLogin(self, data: dict, error: dict, reqid: int, last: bool): """ Callback when user is logged in. """ - if self.gateway.if_error_write_error("行情登录失败", error): + if self.gateway.if_error_write_error("行情服务器登录失败", error): return self.login_status = True - self.gateway.write_log("onRspUserLogin行情服务器登录成功") self.gateway.write_log("行情服务器登录成功") for symbol in self.subscribed: @@ -373,7 +379,7 @@ class FemasTdApi(TdApi): def onFrontConnected(self): """""" self.connect_status = True - self.gateway.write_log("onFrontConnected交易连接成功") + self.gateway.write_log("交易服务器连接成功") self.login() @@ -381,7 +387,7 @@ class FemasTdApi(TdApi): """""" self.connect_status = False self.login_status = False - self.gateway.write_log(f"交易连接断开,原因{reason}") + self.gateway.write_log(f"交易服务器连接断开,原因{reason}") def onRspUserLogin(self, data: dict, error: dict, reqid: int, last: bool): """""" @@ -391,13 +397,13 @@ class FemasTdApi(TdApi): self.localID, int(data["MaxOrderLocalID"]) ) # 目前最大本地报单号 self.login_status = True - self.gateway.write_log("onRspUserLogin交易登录成功") + self.gateway.write_log("交易服务器登录成功") self.reqid += 1 self.reqQryInstrument({}, self.reqid) else: self.login_failed = True - self.gateway.if_error_write_error("交易登录失败", error) + self.gateway.if_error_write_error("交易服务器登录失败", error) def onRspOrderInsert(self, data: dict, error: dict, reqid: int, last: bool): """""" @@ -486,28 +492,41 @@ class FemasTdApi(TdApi): balance=data["PreBalance"], gateway_name=self.gateway_name, ) - # todo: check for error + self.gateway.on_account(account) def onRspQryInstrument(self, data: dict, error: dict, reqid: int, last: bool): """ Callback of instrument query. """ + # Femas gateway provides no ProductClass data, so need to determine + # product type using other logic. + option_type = OPTIONTYPE_FEMAS2VT.get(data["OptionsType"], None) + if option_type: + product = Product.OPTION + elif data["InstrumentID_2"]: + product = Product.SPREAD + else: + product = Product.FUTURES contract = ContractData( - product=data["ProductID"], symbol=data["InstrumentID"], exchange=EXCHANGE_FEMAS2VT[data["ExchangeID"]], name=data["InstrumentName"], size=data["VolumeMultiple"], pricetick=data["PriceTick"], - option_underlying=data["UnderlyingInstrID"], - option_type=OPTIONTYPE_FEMAS2VT.get(data["OptionsType"], None), - option_strike=data["StrikePrice"], - option_expiry=datetime.strptime(data["ExpireDate"], "%Y%m%d"), - gateway_name=self.gateway_name, + product=product, + gateway_name=self.gateway_name ) + if product == Product.OPTION: + contract.option_underlying = data["UnderlyingInstrID"] + contract.option_type = OPTIONTYPE_FEMAS2VT.get( + data["OptionsType"], None) + contract.option_strike = data["StrikePrice"] + contract.option_expiry = datetime.strptime( + data["ExpireDate"], "%Y%m%d") + self.gateway.on_contract(contract) symbol_exchange_map[contract.symbol] = contract.exchange @@ -522,7 +541,8 @@ class FemasTdApi(TdApi): Callback of order status update. """ # 更新最大报单编号 - self.localID = max(self.localID, int(data["UserOrderLocalID"])) # 检查并增加本地报单编号 + self.localID = max(self.localID, int( + data["UserOrderLocalID"])) # 检查并增加本地报单编号 symbol = data["InstrumentID"] exchange = symbol_exchange_map.get(symbol, "")