# encoding: UTF-8

'''
Interactive Brokers的gateway接入，已经替换为vn.ib封装。

注意事项：
1. ib api只能获取和操作当前连接后下的单，并且每次重启程序后，之前下的单子收不到
2. ib api的成交也只会推送当前连接后的成交
3. ib api的持仓和账户更新可以订阅成主推模式，因此qryAccount和qryPosition就用不到了
4. 目前只支持股票和期货交易，ib api里期权合约的确定是基于Contract对象的多个字段，比较复杂暂时没做
5. 海外市场的交易规则和国内有很多细节上的不同，所以一些字段类型的映射可能不合理，如果发现问题欢迎指出
'''

import os
import json
import calendar
from datetime import datetime, timedelta
from copy import copy

from PyQt4 import QtGui, QtCore

from vnib import *
from vtGateway import *
from vtFunction import *
from ctaBase import *


# 以下为一些VT类型和CTP类型的映射字典
# 价格类型映射
priceTypeMap = {}
priceTypeMap[PRICETYPE_LIMITPRICE] = 'LMT'
priceTypeMap[PRICETYPE_MARKETPRICE] = 'MKT'
priceTypeMapReverse = {v: k for k, v in priceTypeMap.items()} 

# 方向类型映射
directionMap = {}
directionMap[DIRECTION_LONG] = 'BUY'
#directionMap[DIRECTION_SHORT] = 'SSHORT'   # SSHORT在IB系统中代表对股票的融券做空（而不是国内常见的卖出）
directionMap[DIRECTION_SHORT] = 'SELL'      # 出于和国内的统一性考虑，这里选择把IB里的SELL印射为vt的SHORT

directionMapReverse = {v: k for k, v in directionMap.items()}
directionMapReverse['BOT'] = DIRECTION_LONG
directionMapReverse['SLD'] = DIRECTION_SHORT

# 交易所类型映射
exchangeMap = {}
exchangeMap[EXCHANGE_SMART] = 'SMART'
exchangeMap[EXCHANGE_NYMEX] = 'NYMEX'
exchangeMap[EXCHANGE_GLOBEX] = 'GLOBEX'
exchangeMap[EXCHANGE_IDEALPRO] = 'IDEALPRO'
exchangeMap[EXCHANGE_HKEX] = 'HKEX'
exchangeMap[EXCHANGE_HKFE] = 'HKFE'
exchangeMapReverse = {v:k for k,v in exchangeMap.items()}

# 报单状态映射
orderStatusMap = {}
orderStatusMap[STATUS_NOTTRADED] = 'Submitted'
orderStatusMap[STATUS_ALLTRADED] = 'Filled'
orderStatusMap[STATUS_CANCELLED] = 'Cancelled'
orderStatusMapReverse = {v:k for k,v in orderStatusMap.items()}
orderStatusMapReverse['PendingSubmit'] = STATUS_UNKNOWN     # 这里未来视乎需求可以拓展vt订单的状态类型
orderStatusMapReverse['PendingCancel'] = STATUS_UNKNOWN
orderStatusMapReverse['PreSubmitted'] = STATUS_UNKNOWN
orderStatusMapReverse['Inactive'] = STATUS_UNKNOWN

# 合约类型映射
productClassMap = {}
productClassMap[PRODUCT_EQUITY] = 'STK'
productClassMap[PRODUCT_FUTURES] = 'FUT'
productClassMap[PRODUCT_OPTION] = 'OPT'
productClassMap[PRODUCT_FOREX] = 'CASH'
productClassMap[PRODUCT_INDEX] = 'IND'
productClassMapReverse = {v:k for k,v in productClassMap.items()}

# 期权类型映射
optionTypeMap = {}
optionTypeMap[OPTION_CALL] = 'CALL'
optionTypeMap[OPTION_PUT] = 'PUT'
optionTypeMap = {v:k for k,v in optionTypeMap.items()}

# 货币类型映射
currencyMap = {}
currencyMap[CURRENCY_USD] = 'USD'
currencyMap[CURRENCY_CNY] = 'CNY'
currencyMap[CURRENCY_HKD] = 'HKD'
currencyMap = {v:k for k,v in currencyMap.items()}

# Tick数据的Field和名称映射
tickFieldMap = {}
tickFieldMap[0] = 'bidVolume1'
tickFieldMap[1] = 'bidPrice1'
tickFieldMap[2] = 'askPrice1'
tickFieldMap[3] = 'askVolume1'
tickFieldMap[4] = 'lastPrice'
tickFieldMap[5] = 'lastVolume'
tickFieldMap[6] = 'highPrice'
tickFieldMap[7] = 'lowPrice'
tickFieldMap[8] = 'volume'
tickFieldMap[9] = 'preClosePrice'
tickFieldMap[14] = 'openPrice'
tickFieldMap[22] = 'openInterest'

# Account数据Key和名称的映射
accountKeyMap = {}
accountKeyMap['NetLiquidationByCurrency'] = 'balance'
accountKeyMap['NetLiquidation'] = 'balance'
accountKeyMap['UnrealizedPnL'] = 'positionProfit'
accountKeyMap['AvailableFunds'] = 'available'
accountKeyMap['MaintMarginReq'] = 'margin'


########################################################################
class IbGateway(VtGateway):
    """IB接口"""

    #----------------------------------------------------------------------
    def __init__(self, eventEngine, gatewayName='IB'):
        """Constructor"""
        super(IbGateway, self).__init__(eventEngine, gatewayName)
        
        self.host = EMPTY_STRING        # 连接地址
        self.port = EMPTY_INT           # 连接端口
        self.clientId = EMPTY_INT       # 用户编号
        self.accountCode = EMPTY_STRING # 账户编号
        
        self.autoConnect =True
        
        self.tickerId = 0               # 订阅行情时的代码编号    
        self.tickDict = {}              # tick快照字典，key为tickerId，value为VtTickData对象
        self.tickProductDict = {}       # tick对应的产品类型字典，key为tickerId，value为产品类型
        
        self.hisBarDict = {}            # bar快照字典，key为tickerId，value为VtBarData对象
        
        self.orderId  = 0               # 订单编号
        self.orderDict = {}             # 报单字典，key为orderId，value为VtOrderData对象
        
        self.accountDict = {}           # 账户字典
        
        self.contractDict = {}          # 合约字典
        
        self.subscribeReqDict = {}      # 用来保存订阅请求的字典 
        
        self.connected = False          # 连接状态
        
        self.api = IbWrapper(self)      # API接口

    #----------------------------------------------------------------------
    def connect(self):
        """连接"""
        # 载入json文件
        fileName = self.gatewayName + '_connect.json'
        path = os.path.abspath(os.path.dirname(__file__))
        fileName = os.path.join(path, fileName)
        
        try:
            f = file(fileName)
        except IOError:
            log = VtLogData()
            log.gatewayName = self.gatewayName
            log.logContent = u'读取连接配置出错，请检查'
            self.onLog(log)
            return
        
        # 解析json文件
        setting = json.load(f)
        try:
            self.host = str(setting['host'])
            self.port = int(setting['port'])
            self.clientId = int(setting['clientId'])
            self.accountCode = str(setting['accountCode'])
        except KeyError:
            log = VtLogData()
            log.gatewayName = self.gatewayName
            log.logContent = u'连接配置缺少字段，请检查'
            self.onLog(log)
            return            
        
        # 发起连接
        self.api.eConnect(self.host, self.port, self.clientId, False)
        
        # 查询服务器时间
        self.api.reqCurrentTime()
    
    #----------------------------------------------------------------------
    def subscribe(self, subscribeReq):
        """订阅行情"""
        # 如果尚未连接行情，则将订阅请求缓存下来后直接返回
        if not self.connected:
            self.subscribeReqDict[subscribeReq.symbol] = subscribeReq
            return
        
        contract = Contract()
        contract.localSymbol = str(subscribeReq.symbol)
        contract.exchange = exchangeMap.get(subscribeReq.exchange, '')
        contract.secType = productClassMap.get(subscribeReq.productClass, '')
        contract.currency = currencyMap.get(subscribeReq.currency, '')
        contract.expiry = subscribeReq.expiry
        contract.strike = subscribeReq.strikePrice
        contract.right = optionTypeMap.get(subscribeReq.optionType, '')

        # 获取合约详细信息
        self.tickerId += 1
        self.api.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.api.reqMktData(self.tickerId, contract, '', False, TagValueList())
        
        # 创建Tick对象并保存到字典中
        tick = VtTickData()
        tick.symbol = subscribeReq.symbol
        tick.exchange = subscribeReq.exchange
        tick.vtSymbol = '.'.join([tick.symbol, tick.exchange])
        tick.gatewayName = self.gatewayName
        self.tickDict[self.tickerId] = tick   
        self.tickProductDict[self.tickerId] = subscribeReq.productClass

    #----------------------------------------------------------------------
    def sendOrder(self, orderReq):
        """发单"""
        # 增加报单号1，最后再次进行查询
        # 这里双重设计的目的是为了防止某些情况下，连续发单时，nextOrderId的回调推送速度慢导致没有更新
        self.orderId += 1
        
        # 创建合约对象
        contract = Contract()
        contract.localSymbol = str(orderReq.symbol)
        contract.exchange = exchangeMap.get(orderReq.exchange, '')
        contract.secType = productClassMap.get(orderReq.productClass, '')
        contract.currency = currencyMap.get(orderReq.currency, '')
        contract.expiry = orderReq.expiry
        contract.strike = orderReq.strikePrice
        contract.right = optionTypeMap.get(orderReq.optionType, '')
        contract.lastTradeDateOrContractMonth = str(orderReq.lastTradeDateOrContractMonth)
        contract.multiplier = str(orderReq.multiplier)
        
        # 创建委托对象
        order = Order()
        order.orderId = self.orderId
        order.clientId = self.clientId
        order.action = directionMap.get(orderReq.direction, '')
        order.lmtPrice = orderReq.price
        order.totalQuantity = orderReq.volume
        order.orderType = priceTypeMap.get(orderReq.priceType, '')
        
        # 发送委托
        self.api.placeOrder(self.orderId, contract, order)
        
        # 查询下一个有效编号
        self.api.reqIds(1)
        
        # 返回委托编号
        vtOrderID = '.'.join([self.gatewayName, str(self.orderId)])
        return vtOrderID
    
    #----------------------------------------------------------------------
    def cancelOrder(self, cancelOrderReq):
        """撤单"""
        self.api.cancelOrder(int(cancelOrderReq.orderID))
    
    #----------------------------------------------------------------------
    def qryAccount(self):
        """查询账户资金"""
        log = VtLogData()
        log.gatewayName = self.gatewayName        
        log.logContent = u'IB接口账户信息提供主推更新，无需查询'
        self.onLog(log) 
    
    #----------------------------------------------------------------------
    def qryPosition(self):
        """查询持仓"""
        log = VtLogData()
        log.gatewayName = self.gatewayName        
        log.logContent = u'IB接口持仓信息提供主推更新，无需查询'
        self.onLog(log) 
    
    #----------------------------------------------------------------------
    def close(self):
        """关闭"""
        self.api.eDisconnect()

    def getHistoricalData(self, tickerId=None,  contractReq=None, endDateTimeStr=None, durationStr=None, barSizeSetting=12, whatToShow='MIDPOINT', useRTH=0      ):
        """
        # contract 要转换成 IB 的 contract
        # formatDate  返回柱的时间格式  1 日期时间格式 yyyymmdd hh:mm:ss   2 返回 timestamp  日线只能返回EST 时间格式
        # usrRTH 0 返回所有数据  1 只返回交易时段的数据
        # whatToShow 提取数据性质  TRADES  MIDPOINT BID ASK BID_ASK HISTORICAL_VOLATILITY
        # barSize 返回的K线长度 1 secs 5 secs 15 secs 30 secs 1 min 2 mins 3 mins 5 mins 15 mins 30 mins 1 hour 1day
            在常量文件中，已经定义一个柱体长度对应表。传入一个数值，自动转化为IB的周期字符串。
        # durationStr 请求的时间区域，S 秒  D 天  W 星期  M 月  Y  年， 默认使用秒
        # endDateTime yyyymmdd hh:mm:ss est 为了兼容TWS行情软件，一律使用est时区
        # contract  合约
        """
        # 创建请求历史数据的合约对象
        newcontract = Contract()
        newcontract.m_localSymbol = str(contractReq.symbol)
        newcontract.m_exchange = exchangeMap.get(contractReq.exchange, '')
        newcontract.m_secType = productClassMap.get(contractReq.productClass, '')
        newcontract.m_currency = currencyMap.get(contractReq.currency, '')
        newcontract.m_expiry = contractReq.expiry
        newcontract.m_strike = contractReq.strikePrice
        newcontract.m_right = optionTypeMap.get(contractReq.optionType, '')
        
        newcontract.localSymbol = newcontract.m_localSymbol
        newcontract.exchange = newcontract.m_exchange
        newcontract.secType = newcontract.m_secType
        newcontract.currency = newcontract.m_currency
        newcontract.expiry = newcontract.m_expiry
        newcontract.strike = newcontract.m_strike
        newcontract.right = newcontract.m_right        
    
        # 更新tickerId， 每次请求都不同
        if tickerId:
            self.tickerId =  tickerId       # 如果有 tickerId就使用传入进来的，否则使用默认的
        else:
            self.tickerId += 1
    
        # 创建新的 Bar对象并保存到字典中
        bar = VtBarData()
        bar.symbol = contractReq.symbol
        bar.exchange = contractReq.exchange
        bar.vtSymbol = '.'.join([bar.symbol, bar.exchange])
        bar.gatewayName = self.gatewayName
        bar.barsize = barSizeSetting            # 请求历史数据时传来的编号
        self.hisBarDict[self.tickerId] = bar    # 这里要使用自己的Bar对象，不能使用本身的 tickDict，否则它会推送到 tick 去。
    
        # 把barSizeSetting 转化为 IB 识别的字符串， 传递进来的是代号
        #barSizeStr = BARSIZE[barSizeSetting]
        barSizeStr = BARSIZE_DICT.get(barSizeSetting,'')
        if not barSizeStr:
            print  u'K线周期编号错误'
            log = VtLogData()
            log.gatewayName = self.gatewayName
            log.logContent = u'K线周期编号错误'
            self.onLog(log)
            return
        if endDateTimeStr is None:
            endDateTimeStr = toESTtime( returnStr=True )      #如果没有传入，就以当前时间。 把当前时间转化为EST时间，并返回 str 格式，否则返回 datetime类型
    
        formatDate = 2  # 返回的K线时间为 timeStamp, 如果日线则返回EST时间
    
        # 发出历史数据请求
        self.api.reqHistoricalData(self.tickerId, newcontract, endDateTimeStr, durationStr, barSizeStr, whatToShow, useRTH, formatDate, TagValueList())
        

    def onHistoricalData(self, bar):
        pass
    
    def onHistoricalDataDownloadFinished(self, reqId):
        pass
    
########################################################################
class IbWrapper(IbApi):
    """IB回调接口的实现"""

    #----------------------------------------------------------------------
    def __init__(self, gateway):
        """Constructor"""
        super(IbWrapper, self).__init__()
        
        self.apiStatus = False               # 连接状态
        
        self.gateway = gateway                      # gateway对象
        self.gatewayName = gateway.gatewayName      # gateway对象名称
        
        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
        self.subscribeReqDict = gateway.subscribeReqDict
        self.hisBarDict = gateway.hisBarDict

    #----------------------------------------------------------------------
    def nextValidId(self, orderId):
        """"""
        self.gateway.orderId = orderId
        
    #----------------------------------------------------------------------
    def currentTime(self, time):
        """连接成功后推送当前时间"""
        dt = datetime.fromtimestamp(time)
        t = dt.strftime("%Y-%m-%d %H:%M:%S.%f")

        self.apiStatus = True
        self.gateway.connected = True
        
        log = VtLogData()
        log.gatewayName = self.gatewayName
        log.logContent = (u'IB接口连接成功，当前服务器时间 %s' %t)
        self.gateway.onLog(log) 
        
        for symbol, req in self.subscribeReqDict.items():
            del self.subscribeReqDict[symbol]
            self.gateway.subscribe(req)        
        
    #----------------------------------------------------------------------
    def connectAck(self):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def error(self, id_, errorCode, errorString):
        """错误推送"""
        err = VtErrorData()
        err.gatewayName = self.gatewayName
        err.errorID = errorCode
        err.errorMsg = errorString.decode('GBK')
        self.gateway.onError(err)
        
        '''
        if  errorCode==504 and   self.gateway.autoConnect == True:
            log = VtLogData()
            log.gatewayName = self.gatewayName
            log.logContent = (u'Not connected, 10秒后重新登录')
            self.gateway.onLog(log)
            thread = Thread(target=self.gateway.connect))
            thread.start()       
        '''
        
    #----------------------------------------------------------------------
    def accountSummary(self, reqId, account, tag, value, curency):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def accountSummaryEnd(self, reqId):
        """"""
        pass
         
    #----------------------------------------------------------------------
    def tickPrice(self, tickerId, field, price, canAutoExecute):
        """行情价格相关推送"""
        if field in tickFieldMap:
            # 对于股票、期货等行情，有新价格推送时仅更新tick缓存
            # 只有当发生成交后，tickString更新最新成交价时才推送新的tick
            # 即bid/ask的价格变动并不会触发新的tick推送
            tick = self.tickDict[tickerId]
            key = tickFieldMap[field]
            tick.__setattr__(key, price)
            
            # IB的外汇行情没有成交价和时间，通过本地计算生成，同时立即推送
            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)
                self.gateway.onTick(newtick)
        else:
            print field
        
    #----------------------------------------------------------------------
    def tickSize(self, tickerId, field, size):
        """行情数量相关推送"""
        if field in tickFieldMap:
            tick = self.tickDict[tickerId]
            key = tickFieldMap[field]
            tick.__setattr__(key, size)   
        else:
            print field
        
    #----------------------------------------------------------------------
    def tickOptionComputation(self, tickerId, tickType, impliedVol, delta, optPrice, pvDividend, gamma, vega, theta, undPrice):
        """"""
        pass

    #----------------------------------------------------------------------
    def tickGeneric(self, tickerId, tickType, value):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def tickString(self, tickerId, tickType, value):
        """行情补充信息相关推送"""
        # 如果是最新成交时间戳更新
        if tickType == '45':
            tick = self.tickDict[tickerId]

            dt = datetime.fromtimestamp(value)
            tick.time = dt.strftime('%H:%M:%S.%f')
            tick.date = dt.strftime('%Y%m%d')

            newtick = copy(tick)
            self.gateway.onTick(newtick)              
        
    #----------------------------------------------------------------------
    def tickEFP(self, tickerId, tickType, basisPoints, formattedBasisPoints, totalDividends, holdDays, futureLastTradeDate, dividendImpact, dividendsToLastTradeDate):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def orderStatus(self, orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld):
        """委托状态更新"""
        orderId = str(orderId)
        
        if orderId in self.orderDict:
            od = self.orderDict[orderId]
        else:
            od = VtOrderData()  # od代表orderData
            od.orderID = orderId
            od.vtOrderID = '.'.join([self.gatewayName, orderId])
            od.gatewayName = self.gatewayName
            self.orderDict[orderId] = od
        
        od.status = orderStatusMapReverse.get(status, STATUS_UNKNOWN)
        od.tradedVolume = filled
        
        newod = copy(od)
        self.gateway.onOrder(newod)
        
    #----------------------------------------------------------------------
    def openOrder(self, orderId, contract, order, orderState):
        """下达委托推送"""
        orderId = str(orderId)  # orderId是整数
        
        if orderId in self.orderDict:
            od = self.orderDict[orderId]
        else:
            od = VtOrderData()  # od代表orderData
            od.orderID = orderId
            od.vtOrderID = '.'.join([self.gatewayName, orderId])
            od.symbol = contract.localSymbol
            od.exchange = exchangeMapReverse.get(contract.exchange, '')
            od.vtSymbol = '.'.join([od.symbol, od.exchange])  
            od.gatewayName = self.gatewayName
            self.orderDict[orderId] = od
        
        od.direction = directionMapReverse.get(order.action, '')
        od.price = order.lmtPrice
        od.totalVolume = order.totalQuantity
        
        newod = copy(od)
        self.gateway.onOrder(newod)
        
    #----------------------------------------------------------------------
    def openOrderEnd(self):
        """"""
        pass
          
    #----------------------------------------------------------------------
    def winError(self, str_, lastError):
        """"""
        pass
 
    #----------------------------------------------------------------------
    def connectionClosed(self):
        """断线"""
        self.apiStatus = False
        self.gateway.connected = False
    
        log = VtLogData()
        log.gatewayName = self.gatewayName
        log.logContent = (u'IB接口连接断开')
        self.gateway.onLog(log) 
        
    #----------------------------------------------------------------------
    def updateAccountValue(self, key, val, currency, accountName):
        """更新账户数据"""
        # 仅逐个字段更新数据，这里对于没有currency的推送忽略
        if currency:
            name = '.'.join([accountName, currency])
            
            if name in self.accountDict:
                account = self.accountDict[name]
            else:
                account = VtAccountData()
                account.accountID = name
                account.vtAccountID = name
                account.gatewayName = self.gatewayName
                self.accountDict[name] = account
            
            if key in accountKeyMap:
                k = accountKeyMap[key]
                account.__setattr__(k, float(val))
        
    #----------------------------------------------------------------------
    def updatePortfolio(self, contract, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accountName):
        """持仓更新"""
        pos = VtPositionData()
    
        pos.symbol = contract.localSymbol
        pos.exchange = exchangeMapReverse.get(contract.exchange, contract.exchange)
        pos.vtSymbol = '.'.join([pos.symbol, pos.exchange])
        pos.direction = DIRECTION_NET
        pos.position = position
        pos.price = averageCost
        pos.vtPositionName = pos.vtSymbol
        pos.gatewayName = self.gatewayName
    
        self.gateway.onPosition(pos)
        
    #----------------------------------------------------------------------
    def updateAccountTime(self, timeStamp):
        """更新账户时间"""
        # 推送数据
        for account in self.accountDict.values():
            newaccount = copy(account)
            self.gateway.onAccount(newaccount)
        
    #----------------------------------------------------------------------
    def accountDownloadEnd(self, accountName):
        """"""
        pass
         
    #----------------------------------------------------------------------
    def contractDetails(self, reqId, contractDetails):
        """合约查询回报"""
        symbol = contractDetails.summary.localSymbol
        exchange = exchangeMapReverse.get(contractDetails.summary.exchange, EXCHANGE_UNKNOWN)
        vtSymbol = '.'.join([symbol, exchange])
        ct = self.contractDict.get(vtSymbol, None)
        
        if not ct:
            return
        
        ct.name = contractDetails.longName.decode('UTF-8')
        ct.priceTick = contractDetails.minTick        

        # 推送
        self.gateway.onContract(ct)
        
    #----------------------------------------------------------------------
    def bondContractDetails(self, reqId, contractDetails):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def contractDetailsEnd(self, reqId):
        """"""
        pass

    #----------------------------------------------------------------------
    def execDetails(self, reqId, contract, execution):
        """成交推送"""
        trade = VtTradeData()
        trade.gatewayName = self.gatewayName     
        trade.tradeID = execution.execId
        trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
    
        trade.symbol = contract.localSymbol
        trade.exchange = exchangeMapReverse.get(contract.exchange, '')
        trade.vtSymbol = '.'.join([trade.symbol, trade.exchange])  
    
        trade.orderID = str(execution.orderId)
        trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
        trade.direction = directionMapReverse.get(execution.side, '')
        trade.price = execution.price
        trade.volume = execution.shares
        trade.tradeTime = execution.time  
    
        self.gateway.onTrade(trade)
        
    #----------------------------------------------------------------------
    def execDetailsEnd(self, reqId):
        """"""
        pass

    #----------------------------------------------------------------------
    def updateMktDepth(self, id_, position, operation, side, price, size):
        """"""
        pass

    #----------------------------------------------------------------------
    def updateMktDepthL2(self, id_, position, marketMaker, operation, side, price, size):
        """"""
        pass

    #----------------------------------------------------------------------
    def updateNewsBulletin(self, msgId, msgType, newsMessage, originExch):
        """"""
        pass

    #----------------------------------------------------------------------
    def managedAccounts(self, accountsList):
        """推送管理账户的信息"""
        l = accountsList.split(',')
        
        # 请求账户数据主推更新
        for account in l:
            self.reqAccountUpdates(True, account)    
        
    #----------------------------------------------------------------------
    def receiveFA(self, pFaDataType, cxml):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def historicalData(self, reqId, date, open_, high, low, close, volume, barCount, WAP, hasGaps):
        """"""
        """
        reqId  请求的ID        date    日期时间        open        high        low        close
        volume  交易量        count   交易笔数        Wap     加权平均价        hasGaps 是否有间断
        """
        # 先判断 high > 0 才是传来的数据，否则不对。
        finishedFlag = u'finished'
        """行情推送（价格相关）"""
        if high > 0:
            bar = self.hisBarDict[reqId]
            bar.__setattr__('open', open)
            bar.__setattr__('high', high)
            bar.__setattr__('low', low)
            bar.__setattr__('close', close)
            bar.__setattr__('volume', volume)
    
            #dt = timeStampToESTtime(date)   #自己的小函数，把IB返回的timestamp转化为EST日期。
            dt = toESTtime(timestamp=date)  # 自己的小函数，把IB返回的timestamp转化为EST日期。
            bar.datetime = dt
            bar.time = dt.strftime('%H:%M:%S.%f')
            bar.date = dt.strftime('%Y%m%d')
            # 行情数据更新
            newbar = copy(bar)
            self.gateway.onHistoricalData(newbar)
        elif  finishedFlag in date :
            self.gateway.onHistoricalDataDownloadFinished(reqId)
            #print u'本次请求数据接收结束'
        else:
            return
        
    #----------------------------------------------------------------------
    def scannerParameters(self, xml):
        """"""
        pass
          
    #----------------------------------------------------------------------
    def scannerData(self, reqId, rank, contractDetails, distance, benchmark, projection, legsStr):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def scannerDataEnd(self, reqId):
        """"""
        pass

    #----------------------------------------------------------------------
    def realtimeBar(self, reqId, time, open_, high, low, close, volume, wap, count):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def fundamentalData(self, reqId, data):
        """"""
        pass
         
    #----------------------------------------------------------------------
    def deltaNeutralValidation(self, reqId, underComp):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def tickSnapshotEnd(self, reqId):
        """"""
        pass

    #----------------------------------------------------------------------
    def marketDataType(self, reqId, marketDataType):
        """"""
        pass
         
    #----------------------------------------------------------------------
    def commissionReport(self, commissionReport):
        """"""
        pass
          
    #----------------------------------------------------------------------
    def position(self, account, contract, position, avgCost):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def positionEnd(self):
        """"""
        pass 
        
    #----------------------------------------------------------------------
    def verifyMessageAPI(self, apiData):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def verifyCompleted(self, isSuccessful, errorText):
        """"""
        pass

    #----------------------------------------------------------------------
    def displayGroupList(self, reqId, groups):
        """"""
        pass
         
    #----------------------------------------------------------------------
    def displayGroupUpdated(self, reqId, contractInfo):
        """"""
        pass   
        
    #----------------------------------------------------------------------
    def verifyAndAuthMessageAPI(self, apiData, xyzChallange):
        """"""
        pass

    #----------------------------------------------------------------------
    def verifyAndAuthCompleted(self, isSuccessful, errorText):
        """"""
        pass
         
    #----------------------------------------------------------------------
    def positionMulti(self, reqId, account, modelCode, contract, pos, avgCost):
        """"""
        pass
         
    #----------------------------------------------------------------------
    def positionMultiEnd(self, reqId):	
        """"""
        pass
          
    #----------------------------------------------------------------------
    def accountUpdateMulti(self, reqId, account, modelCode, key, value, currency):
        """"""
        pass

    #----------------------------------------------------------------------
    def accountUpdateMultiEnd(self, reqId):	
        """"""
        pass

    #----------------------------------------------------------------------
    def securityDefinitionOptionalParameter(self, reqId, exchange, underlyingConId, tradingClass, multiplier, expirations, strikes):
        """"""
        pass

    #----------------------------------------------------------------------
    def securityDefinitionOptionalParameterEnd(self, reqId):
        """"""
        pass
    
    #----------------------------------------------------------------------
    def softDollarTiers(self, reqId, tiers):
        """"""
        pass

               