[Mod]增加富途证券接口的成交重复推送过滤功能

This commit is contained in:
vn.py 2017-10-23 10:07:43 +08:00
parent 49a1dfdff1
commit 438948f8ac
3 changed files with 164 additions and 35 deletions

View File

@ -21,7 +21,7 @@ from vnpy.trader.gateway import (ctpGateway, oandaGateway, ibGateway,
if system == 'Windows': if system == 'Windows':
from vnpy.trader.gateway import (femasGateway, xspeedGateway, from vnpy.trader.gateway import (femasGateway, xspeedGateway,
futuGateway) futuGateway, secGateway)
if system == 'Linux': if system == 'Linux':
from vnpy.trader.gateway import xtpGateway from vnpy.trader.gateway import xtpGateway
@ -52,6 +52,7 @@ def main():
if system == 'Windows': if system == 'Windows':
me.addGateway(femasGateway) me.addGateway(femasGateway)
me.addGateway(xspeedGateway) me.addGateway(xspeedGateway)
me.addGateway(secGateway)
me.addGateway(futuGateway) me.addGateway(futuGateway)
if system == 'Linux': if system == 'Linux':

View File

@ -70,6 +70,7 @@ class FutuGateway(VtGateway):
self.filePath = getJsonPath(self.fileName, __file__) self.filePath = getJsonPath(self.fileName, __file__)
self.tickDict = {} self.tickDict = {}
self.tradeSet = set() # 保存成交编号的集合,防止重复推送
self.qryEnabled = True self.qryEnabled = True
self.qryThread = Thread(target=self.qryData) self.qryThread = Thread(target=self.qryData)
@ -477,13 +478,18 @@ class FutuGateway(VtGateway):
def processDeal(self, data): def processDeal(self, data):
"""处理成交推送""" """处理成交推送"""
for ix, row in data.iterrows(): for ix, row in data.iterrows():
tradeID = row['dealid']
if tradeID in self.tradeSet:
continue
self.tradeSet.add(tradeID)
trade = VtTradeData() trade = VtTradeData()
trade.gatewayName = self.gatewayName trade.gatewayName = self.gatewayName
trade.symbol = row['code'] trade.symbol = row['code']
trade.vtSymbol = trade.symbol trade.vtSymbol = trade.symbol
trade.tradeID = row['dealid'] trade.tradeID = tradeID
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID]) trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
trade.orderID = row['orderid'] trade.orderID = row['orderid']

View File

@ -202,7 +202,7 @@ class SecMdApi(MdApi):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def connect(self, accountID, password, address): def connect(self, accountID, password, address):
"""初始化连接""" """初始化连接"""
self.accountID = accountID # 账号 self.accountID = accountID # 账号
self.password = password # 密码 self.password = password # 密码
self.address = address # 服务器地址 self.address = address # 服务器地址
@ -213,35 +213,38 @@ class SecMdApi(MdApi):
# 初始化连接成功会调用onFrontConnected # 初始化连接成功会调用onFrontConnected
self.init(self.address) self.init(self.address)
# 若已经连接但尚未登录,则进行登录
else:
if not self.loginStatus:
self.login()
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def subscribe(self, subscribeReq): def subscribe(self, subscribeReq):
"""订阅合约""" """订阅合约"""
# 这里的设计是,如果尚未登录就调用了订阅方法
# 则先保存订阅请求,登录完成后会自动订阅
if self.loginStatus: if self.loginStatus:
self.reqID += 1 self.reqID += 1
symbol = str(exchangeMap[subscribeReq.exchange] + subscribeReq.symbol)
self.subscribeSOPMarketData(symbol, self.reqID) if len(subscribeReq.symbol) > 6:
symbol = str(exchangeMap[subscribeReq.exchange] + subscribeReq.symbol)
self.subscribeSOPMarketData(symbol, self.reqID)
else:
symbol = str(exchangeMap[subscribeReq.exchange] + subscribeReq.symbol)
self.subscribeStockMarketData(symbol, self.reqID)
self.subscribedSymbols.add(subscribeReq) self.subscribedSymbols.add(subscribeReq)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def login(self): def login(self):
"""登录""" """登录"""
# 如果填入了用户名密码等,则登录
if self.accountID and self.password: if self.accountID and self.password:
# 登录期权
self.reqID += 1 self.reqID += 1
req = {} req = {}
req['accountID'] = self.accountID req['accountID'] = self.accountID
req['password'] = self.password req['password'] = self.password
req['requestID'] = self.reqID req['requestID'] = self.reqID
self.reqSOPUserLogin(req) self.reqSOPUserLogin(req)
# 登录股票
self.reqID += 1
req['requestID'] = self.reqID
self.reqStockUserLogin(req)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def close(self): def close(self):
@ -292,8 +295,20 @@ class SecMdApi(MdApi):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspStockUserLogin(self, data, error): def onRspStockUserLogin(self, data, error):
"""""" """股票登录回报"""
pass # 如果登录成功,推送日志信息
if not error:
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'股票行情服务器登录完成'
self.gateway.onLog(log)
# 否则,推送错误信息
else:
err = VtErrorData()
err.gatewayName = self.gatewayName
err.errorID = error['errorID']
err.errorMsg = error['errorMsg'].decode('gbk')
self.gateway.onError(err)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspStockUserLogout(self, data, error): def onRspStockUserLogout(self, data, error):
@ -302,7 +317,7 @@ class SecMdApi(MdApi):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspSOPUserLogin(self, data, error): def onRspSOPUserLogin(self, data, error):
"""登陆回报""" """期权登录回报"""
# 如果登录成功,推送日志信息 # 如果登录成功,推送日志信息
if not error: if not error:
self.loginStatus = True self.loginStatus = True
@ -310,7 +325,7 @@ class SecMdApi(MdApi):
log = VtLogData() log = VtLogData()
log.gatewayName = self.gatewayName log.gatewayName = self.gatewayName
log.logContent = u'行情服务器登录完成' log.logContent = u'期权行情服务器登录完成'
self.gateway.onLog(log) self.gateway.onLog(log)
# 重新订阅之前订阅的合约 # 重新订阅之前订阅的合约
@ -342,7 +357,7 @@ class SecMdApi(MdApi):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspStockSubMarketData(self, data, error): def onRspStockSubMarketData(self, data, error):
"""""" """"""
pass pass
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspStockUnSubMarketData(self, data, error): def onRspStockUnSubMarketData(self, data, error):
@ -366,8 +381,52 @@ class SecMdApi(MdApi):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onStockMarketData(self, data): def onStockMarketData(self, data):
"""""" """股票行情推送"""
pass tick = VtTickData()
tick.gatewayName = self.gatewayName
tick.symbol = data['securityID']
tick.exchange = exchangeMapReverse.get(data['exchangeID'], u'未知')
tick.vtSymbol = '.'.join([tick.symbol, tick.exchange])
tick.lastPrice = data['latestPrice']
tick.volume = data['tradeQty']
tick.time = data['updateTime']
tick.date = data['tradingDay']
tick.openPrice = data['openPrice']
tick.highPrice = data['highestPrice']
tick.lowPrice = data['lowestPrice']
tick.preClosePrice = data['preClosePrice']
tick.upperLimit = data['upperLimitPrice']
tick.lowerLimit = data['lowerLimitPrice']
tick.bidPrice1 = data['bidPrice1']
tick.bidPrice2 = data['bidPrice2']
tick.bidPrice3 = data['bidPrice3']
tick.bidPrice4 = data['bidPrice4']
tick.bidPrice5 = data['bidPrice5']
tick.askPrice1 = data['askPrice1']
tick.askPrice2 = data['askPrice2']
tick.askPrice3 = data['askPrice3']
tick.askPrice4 = data['askPrice4']
tick.askPrice5 = data['askPrice5']
tick.bidVolume1 = data['bidQty1']
tick.bidVolume2 = data['bidQty2']
tick.bidVolume3 = data['bidQty3']
tick.bidVolume4 = data['bidQty4']
tick.bidVolume5 = data['bidQty5']
tick.askVolume1 = data['askQty1']
tick.askVolume2 = data['askQty2']
tick.askVolume3 = data['askQty3']
tick.askVolume4 = data['askQty4']
tick.askVolume5 = data['askQty5']
self.gateway.onTick(tick)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onSOPMarketData(self, data): def onSOPMarketData(self, data):
@ -452,7 +511,8 @@ class SecTdApi(TdApi):
self.localID = EMPTY_INT # 订单编号 self.localID = EMPTY_INT # 订单编号
self.connectionStatus = False # 连接状态 self.connectionStatus = False # 连接状态
self.loginStatus = False # 登录状态 self.optionLoginStatus = False # 期权登录状态
self.stockLoginStatus = False # 股票登录状态
self.accountID = EMPTY_STRING # 账号 self.accountID = EMPTY_STRING # 账号
self.password = EMPTY_STRING # 密码 self.password = EMPTY_STRING # 密码
@ -494,6 +554,10 @@ class SecTdApi(TdApi):
req['password'] = self.password req['password'] = self.password
req['requestID'] = self.reqID req['requestID'] = self.reqID
self.reqSOPUserLogin(req) self.reqSOPUserLogin(req)
self.reqID += 1
req['requestID'] = self.reqID
self.reqStockUserLogin(req)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def qryAccount(self): def qryAccount(self):
@ -504,6 +568,9 @@ class SecTdApi(TdApi):
req['accountID'] = self.accountID req['accountID'] = self.accountID
self.reqSOPQryCapitalAccountInfo(req) self.reqSOPQryCapitalAccountInfo(req)
self.reqID += 1
self.reqStockQryCapitalAccountInfo(req)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def qryPosition(self): def qryPosition(self):
"""查询持仓""" """查询持仓"""
@ -512,16 +579,28 @@ class SecTdApi(TdApi):
req['requestID'] = self.reqID req['requestID'] = self.reqID
req['accountID'] = self.accountID req['accountID'] = self.accountID
self.reqSOPQryPosition(req) self.reqSOPQryPosition(req)
self.reqID += 1
self.reqStockQryPosition(req)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def qryContracts(self): def qryOptionContracts(self):
"""查询合约""" """查询期权合约"""
# 查询期货合约代码
self.reqID += 1 self.reqID += 1
req = {} req = {}
req['requestID'] = self.reqID req['requestID'] = self.reqID
req['accountID'] = self.accountID req['accountID'] = self.accountID
self.reqSOPQryContactInfo(req) self.reqSOPQryContactInfo(req)
#----------------------------------------------------------------------
def qryStockContracts(self):
"""查询股票合约"""
self.reqID += 1
req = {}
req['exchangeID'] = 'SH'
req['requestID'] = self.reqID
req['accountID'] = self.accountID
i = self.reqStockQryStockStaticInfo(req)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def sendOrder(self, orderReq): def sendOrder(self, orderReq):
@ -632,8 +711,25 @@ class SecTdApi(TdApi):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspStockUserLogin(self, data, error): def onRspStockUserLogin(self, data, error):
"""""" """股票登录回报"""
pass # 如果登录成功,推送日志信息
if not error:
self.stockLoginStatus = True
self.gateway.tdConnected = True
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'股票交易服务器登录完成'
self.gateway.onLog(log)
self.qryStockContracts()
# 否则,推送错误信息
else:
err = VtErrorData()
err.gatewayName = self.gatewayName
err.errorID = error['errorID']
err.errorMsg = error['errorMsg'].decode('gbk')
self.gateway.onError(err)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspStockUserLogout(self, data, error): def onRspStockUserLogout(self, data, error):
@ -739,11 +835,37 @@ class SecTdApi(TdApi):
def onRspStockQryStockInfo(self, data, error, flag): def onRspStockQryStockInfo(self, data, error, flag):
"""""" """"""
pass pass
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspStockQryStockStaticInfo(self, data, error, flag): def onRspStockQryStockStaticInfo(self, data, error, flag):
"""""" """股票合约查询回报"""
pass if not data:
return
contract = VtContractData()
contract.gatewayName = self.gatewayName
contract.symbol = data['securityID']
contract.exchange = exchangeMapReverse.get(data['exchangeID'], EXCHANGE_UNKNOWN)
contract.vtSymbol = '.'.join([contract.symbol, contract.exchange])
contract.name = data['securityName'].decode('GBK')
# 合约数值
contract.size = data['tradeUnit']
contract.priceTick = 0.001 # 50ETF的最小价格变动
# 合约类型
contract.productClass = PRODUCT_EQUITY
# 推送
self.gateway.onContract(contract)
if flag:
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'股票交易合约信息获取完成'
self.gateway.onLog(log)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspStockQryTradeTime(self, data, error): def onRspStockQryTradeTime(self, data, error):
@ -767,18 +889,18 @@ class SecTdApi(TdApi):
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def onRspSOPUserLogin(self, data, error): def onRspSOPUserLogin(self, data, error):
"""登陆回报""" """期权登录回报"""
# 如果登录成功,推送日志信息 # 如果登录成功,推送日志信息
if not error: if not error:
self.loginStatus = True self.optionLoginStatus = True
self.gateway.tdConnected = True self.gateway.tdConnected = True
log = VtLogData() log = VtLogData()
log.gatewayName = self.gatewayName log.gatewayName = self.gatewayName
log.logContent = u'交易服务器登录完成' log.logContent = u'期权交易服务器登录完成'
self.gateway.onLog(log) self.gateway.onLog(log)
self.qryContracts() #self.qryOptionContracts()
# 否则,推送错误信息 # 否则,推送错误信息
else: else:
@ -956,7 +1078,7 @@ class SecTdApi(TdApi):
if flag: if flag:
log = VtLogData() log = VtLogData()
log.gatewayName = self.gatewayName log.gatewayName = self.gatewayName
log.logContent = u'交易合约信息获取完成' log.logContent = u'期权交易合约信息获取完成'
self.gateway.onLog(log) self.gateway.onLog(log)
#---------------------------------------------------------------------- #----------------------------------------------------------------------