From c4b7f5b6f2d69dc2747325516721bca02ac9e907 Mon Sep 17 00:00:00 2001 From: vnpy Date: Fri, 13 May 2016 20:59:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9IB=E6=8E=A5=E5=8F=A3=E4=B8=AD?= =?UTF-8?q?=E7=94=A8=E5=88=B0=E7=9A=84=E4=BB=A3=E7=A0=81=E4=B8=BAlocalSymb?= =?UTF-8?q?ol=EF=BC=8C=E4=BB=A5=E5=8F=8A=E4=B8=80=E4=BA=9B=E7=BB=86?= =?UTF-8?q?=E8=8A=82=E6=96=B9=E9=9D=A2=E7=9A=84bug=EF=BC=8C=E6=84=9F?= =?UTF-8?q?=E8=B0=A2Github=E8=B4=A1=E7=8C=AE=E8=80=85border?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vn.trader/ibGateway/IB_connect.json | 6 +- vn.trader/ibGateway/ibGateway.py | 124 +++++++++++++--------------- vn.trader/uiBasicWidget.py | 3 +- 3 files changed, 64 insertions(+), 69 deletions(-) diff --git a/vn.trader/ibGateway/IB_connect.json b/vn.trader/ibGateway/IB_connect.json index f911cc5b..71aa0cb4 100644 --- a/vn.trader/ibGateway/IB_connect.json +++ b/vn.trader/ibGateway/IB_connect.json @@ -1,5 +1,5 @@ { - "host": "localhost", - "port": 7496, - "clientId": 888 + "host": "localhost", + "port": 7496, + "clientId": 888 } \ No newline at end of file diff --git a/vn.trader/ibGateway/ibGateway.py b/vn.trader/ibGateway/ibGateway.py index e00cdfd6..f7ef6540 100644 --- a/vn.trader/ibGateway/ibGateway.py +++ b/vn.trader/ibGateway/ibGateway.py @@ -95,7 +95,7 @@ tickFieldMap[7] = 'lowPrice' tickFieldMap[8] = 'volume' tickFieldMap[9] = 'preClosePrice' tickFieldMap[14] = 'openPrice' -tickFieldMap[20] = 'openInterest' +tickFieldMap[22] = 'openInterest' # Account数据Key和名称的映射 accountKeyMap = {} @@ -121,12 +121,15 @@ class IbGateway(VtGateway): self.tickerId = 0 # 订阅行情时的代码编号 self.tickDict = {} # tick快照字典,key为tickerId,value为VtTickData对象 + self.tickProductDict = {} # tick对应的产品类型字典,key为tickerId,value为产品类型 self.orderId = 0 # 订单编号 self.orderDict = {} # 报单字典,key为orderId,value为VtOrderData对象 self.accountDict = {} # 账户字典 + self.contractDict = {} # 合约字典 + self.connected = False # 连接状态 self.wrapper = IbWrapper(self) # 回调接口 @@ -173,30 +176,31 @@ class IbGateway(VtGateway): #---------------------------------------------------------------------- def subscribe(self, subscribeReq): """订阅行情""" - # 订阅行情 - self.tickerId += 1 - contract = Contract() - contract.m_symbol = str(subscribeReq.symbol) + contract.m_localSymbol = str(subscribeReq.symbol) contract.m_exchange = exchangeMap.get(subscribeReq.exchange, '') contract.m_secType = productClassMap.get(subscribeReq.productClass, '') contract.m_currency = currencyMap.get(subscribeReq.currency, '') contract.m_expiry = subscribeReq.expiry contract.m_strike = subscribeReq.strikePrice contract.m_right = optionTypeMap.get(subscribeReq.optionType, '') - - # 考虑设计为针对期货用代码_到期日的方式来代替单纯的代码 - if contract.m_secType == 'FUT' and not subscribeReq.expiry: - # 期货 如果没有设置过期时间, 默认设置为下个月 - dt_obj = datetime.now() - days = calendar.monthrange(dt_obj.year, dt_obj.month)[1] - nextMonth = dt_obj + timedelta(days=(days - dt_obj.day + 1)) - contract.m_expiry = nextMonth.strftime('%Y%m') - self.connection.reqMktData(self.tickerId, contract, '', False) - # 获取合约详细信息 - self.connection.reqContractDetails(self.tickerId, contract) + self.tickerId += 1 + self.connection.reqContractDetails(self.tickerId, contract) + + # 创建合约对象并保存到字典中 + ct = VtContractData() + ct.gatewayName = self.gatewayName + ct.symbol = str(subscribeReq.symbol) + ct.exchange = subscribeReq.exchange + ct.vtSymbol = '.'.join([ct.symbol, ct.exchange]) + ct.productClass = subscribeReq.productClass + self.contractDict[ct.vtSymbol] = ct + + # 订阅行情 + self.tickerId += 1 + self.connection.reqMktData(self.tickerId, contract, '', False) # 创建Tick对象并保存到字典中 tick = VtTickData() @@ -204,9 +208,9 @@ class IbGateway(VtGateway): tick.exchange = subscribeReq.exchange tick.vtSymbol = '.'.join([tick.symbol, tick.exchange]) tick.gatewayName = self.gatewayName - tick.__setattr__('m_secType', productClassMap.get(subscribeReq.productClass, '')) - self.tickDict[self.tickerId] = tick - + self.tickDict[self.tickerId] = tick + self.tickProductDict[self.tickerId] = subscribeReq.productClass + #---------------------------------------------------------------------- def sendOrder(self, orderReq): """发单""" @@ -216,11 +220,10 @@ class IbGateway(VtGateway): # 创建合约对象 contract = Contract() - contract.m_symbol = str(orderReq.symbol) + contract.m_localSymbol = str(orderReq.symbol) contract.m_exchange = exchangeMap.get(orderReq.exchange, '') contract.m_secType = productClassMap.get(orderReq.productClass, '') contract.m_currency = currencyMap.get(orderReq.currency, '') - contract.m_expiry = orderReq.expiry contract.m_strike = orderReq.strikePrice contract.m_right = optionTypeMap.get(orderReq.optionType, '') @@ -229,7 +232,6 @@ class IbGateway(VtGateway): order = Order() order.m_orderId = self.orderId order.m_clientId = self.clientId - order.m_action = directionMap.get(orderReq.direction, '') order.m_lmtPrice = orderReq.price order.m_totalQuantity = orderReq.volume @@ -285,6 +287,8 @@ class IbWrapper(EWrapper): self.tickDict = gateway.tickDict # tick快照字典,key为tickerId,value为VtTickData对象 self.orderDict = gateway.orderDict # order字典 self.accountDict = gateway.accountDict # account字典 + self.contractDict = gateway.contractDict # contract字典 + self.tickProductDict = gateway.tickProductDict #---------------------------------------------------------------------- def tickPrice(self, tickerId, field, price, canAutoExecute): @@ -293,12 +297,13 @@ class IbWrapper(EWrapper): tick = self.tickDict[tickerId] key = tickFieldMap[field] tick.__setattr__(key, price) - - # 外汇单独设置时间, tickString 没有返回外汇时间 - if tick.m_secType == 'CASH': - dt_obj = datetime.now() - tick.time = dt_obj.strftime('%H:%M:%S.%f') - tick.date = dt_obj.strftime('%Y%m%d') + + if self.tickProductDict[tickerId] == PRODUCT_FOREX: + tick.lastPrice = (tick.bidPrice1 + tick.askPrice1) / 2 + + dt = datetime.now() + tick.time = dt.strftime('%H:%M:%S.%f') + tick.date = dt.strftime('%Y%m%d') # 行情数据更新 newtick = copy(tick) @@ -314,11 +319,9 @@ class IbWrapper(EWrapper): key = tickFieldMap[field] tick.__setattr__(key, size) - # 外汇单独设置时间, tickString 没有返回外汇时间 - if tick.m_secType == 'CASH': - dt_obj = datetime.now() - tick.time = dt_obj.strftime('%H:%M:%S.%f') - tick.date = dt_obj.strftime('%Y%m%d') + dt = datetime.now() + tick.time = dt.strftime('%H:%M:%S.%f') + tick.date = dt.strftime('%Y%m%d') # 行情数据更新 newtick = copy(tick) @@ -339,17 +342,11 @@ class IbWrapper(EWrapper): #---------------------------------------------------------------------- def tickString(self, tickerId, tickType, value): """行情推送,特殊字段相关""" - if tickType == 45: - dt_obj = datetime.fromtimestamp(int(value)) - - tick = self.tickDict[tickerId] - tick.time = dt_obj.strftime('%H:%M:%S.%f') - tick.date = dt_obj.strftime('%Y%m%d') - - # 这里使用copy的目的是为了保证推送到事件系统中的对象 - # 不会被当前的API线程修改,否则可能出现多线程数据同步错误 - newtick = copy(tick) - self.gateway.onTick(newtick) + # 参考了一些其他平台对于IB行情数据的开发建议后, + # 发现大部分都选择使用本地电脑时间戳而非IB推送的时间戳, + # 猜测原因可能是IB的行情质量一般(本身就是切片了的), + # 因此这里也选择使用电脑的本地时间 + pass #---------------------------------------------------------------------- def tickEFP(self, tickerId, tickType, basisPoints, formattedBasisPoints, impliedFuture, holdDays, futureExpiry, dividendImpact, dividendsToExpiry): @@ -387,7 +384,7 @@ class IbWrapper(EWrapper): od = VtOrderData() # od代表orderData od.orderID = orderId od.vtOrderID = '.'.join([self.gatewayName, orderId]) - od.symbol = contract.m_symbol + od.symbol = contract.m_localSymbol od.exchange = exchangeMapReverse.get(contract.m_exchange, '') od.vtSymbol = '.'.join([od.symbol, od.exchange]) od.gatewayName = self.gatewayName @@ -429,8 +426,8 @@ class IbWrapper(EWrapper): def updatePortfolio(self, contract, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accountName): """持仓更新推送""" pos = VtPositionData() - - pos.symbol = contract.m_symbol + + pos.symbol = contract.m_localSymbol pos.exchange = exchangeMapReverse.get(contract.m_exchange, contract.m_exchange) pos.vtSymbol = '.'.join([pos.symbol, pos.exchange]) pos.direction = DIRECTION_NET @@ -462,20 +459,19 @@ class IbWrapper(EWrapper): #---------------------------------------------------------------------- def contractDetails(self, reqId, contractDetails): """合约查询回报""" - contract = VtContractData() - contract.gatewayName = self.gatewayName - contract.symbol = contractDetails.m_summary.m_symbol - contract.exchange = contractDetails.m_summary.m_exchange + symbol = contractDetails.m_summary.m_localSymbol + exchange = exchangeMapReverse.get(contractDetails.m_summary.m_exchange, EXCHANGE_UNKNOWN) + vtSymbol = '.'.join([symbol, exchange]) + ct = self.contractDict.get(vtSymbol, None) + + if not ct: + return + + ct.name = contractDetails.m_longName.decode('UTF-8') + ct.priceTick = contractDetails.m_minTick - contract.vtSymbol = '.'.join([contract.symbol, contract.exchange]) - contract.name = contractDetails.m_summary.m_localSymbol.decode('UTF-8') - - # 合约类型 - contract.productClass = productClassMapReverse.get(contractDetails.m_summary.m_secType, - PRODUCT_UNKNOWN) - # 推送 - self.gateway.onContract(contract) + self.gateway.onContract(ct) #---------------------------------------------------------------------- def bondContractDetails(self, reqId, contractDetails): @@ -496,7 +492,7 @@ class IbWrapper(EWrapper): trade.tradeID = execution.m_execId trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID]) - trade.symbol = contract.m_symbol + trade.symbol = contract.m_localSymbol trade.exchange = exchangeMapReverse.get(contract.m_exchange, '') trade.vtSymbol = '.'.join([trade.symbol, trade.exchange]) @@ -566,8 +562,8 @@ class IbWrapper(EWrapper): #---------------------------------------------------------------------- def currentTime(self, time): """ generated source for method currentTime """ - dt_obj = datetime.fromtimestamp(time) - t = dt_obj.strftime("%Y-%m-%d %H:%M:%S.%f") + dt = datetime.fromtimestamp(time) + t = dt.strftime("%Y-%m-%d %H:%M:%S.%f") self.connectionStatus = True self.gateway.connected = True @@ -650,7 +646,7 @@ class IbWrapper(EWrapper): #---------------------------------------------------------------------- def connectionClosed(self): - """连接断开""" + """连接断开""" self.connectionStatus = False self.gateway.connected = False @@ -658,5 +654,3 @@ class IbWrapper(EWrapper): log.gatewayName = self.gatewayName log.logContent = (u'IB接口连接断开') self.gateway.onLog(log) - - diff --git a/vn.trader/uiBasicWidget.py b/vn.trader/uiBasicWidget.py index 7fe68f35..f60bb07f 100644 --- a/vn.trader/uiBasicWidget.py +++ b/vn.trader/uiBasicWidget.py @@ -619,7 +619,8 @@ class TradingWidget(QtGui.QFrame): signal = QtCore.pyqtSignal(type(Event())) directionList = [DIRECTION_LONG, - DIRECTION_SHORT] + DIRECTION_SHORT, + DIRECTION_SELL] offsetList = [OFFSET_OPEN, OFFSET_CLOSE,