# encoding: UTF-8

'''
vn.ksgold的gateway接入

金仕达黄金接口在用户登录后，并不提供之前的Order和Trade数据主动推送，
而是需要用户自行查询，因此API里做了初始化后的查询设计。
'''

import os
import json
import time

from vnpy.api.ksgold import TdApi, defineDict
from vnpy.trader.vtGateway import *
from vnpy.trader.vtFunction import getJsonPath

# 以下类型映射参考的是原生API里的Constant.h

# 方向类型映射
directionMap = {}
directionMap[DIRECTION_LONG] = '0'
directionMap[DIRECTION_SHORT] = '1'
directionMapReverse = {v: k for k, v in directionMap.items()}

# 开平类型映射
offsetMap = {}
offsetMap[OFFSET_OPEN] = '0'
offsetMap[OFFSET_CLOSE] = '1'
offsetMapReverse = {v:k for k,v in offsetMap.items()}


########################################################################
class KsgoldGateway(VtGateway):
    """金仕达黄金接口"""

    #----------------------------------------------------------------------
    def __init__(self, eventEngine, gatewayName='KSGOLD'):
        """Constructor"""
        super(KsgoldGateway, self).__init__(eventEngine, gatewayName)

        self.tdApi = KsgoldTdApi(self)  # 黄金接口只有交易API
        self.tdConnected = False        # 交易API连接状态
        self.qryEnabled = False         # 是否要启动循环查询
        
        self.orderInited = False        # 委托初始化查询
        self.tradeInited = 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:
            accountID = str(setting['accountID'])
            password = str(setting['password'])
            address = str(setting['address'])
        except KeyError:
            log = VtLogData()
            log.gatewayName = self.gatewayName
            log.logContent = u'连接配置缺少字段，请检查'
            self.onLog(log)
            return            
        
        # 创建行情和交易接口对象
        self.tdApi.connect(accountID, password, address)
        
        # 初始化并启动查询
        self.initQuery()
    
    #----------------------------------------------------------------------
    def subscribe(self, subscribeReq):
        """订阅行情"""
        self.tdApi.subscribe(subscribeReq)
        
    #----------------------------------------------------------------------
    def sendOrder(self, orderReq):
        """发单"""
        return self.tdApi.sendOrder(orderReq)
        
    #----------------------------------------------------------------------
    def cancelOrder(self, cancelOrderReq):
        """撤单"""
        self.tdApi.cancelOrder(cancelOrderReq)
        
    #----------------------------------------------------------------------
    def qryAccount(self):
        """查询账户资金"""
        self.tdApi.qryAccount()
        
    #----------------------------------------------------------------------
    def qryPosition(self):
        """查询持仓"""
        self.tdApi.qryPosition()
        
    #----------------------------------------------------------------------
    def close(self):
        """关闭"""
        if self.tdConnected:
            self.tdApi.close()
        
    #----------------------------------------------------------------------
    def initQuery(self):
        """初始化连续查询"""
        # 需要循环的查询函数列表
        self.qryFunctionList = [self.qryAccount, self.qryPosition]
        
        self.qryCount = 0           # 查询触发倒计时
        self.qryTrigger = 2         # 查询触发点
        self.qryNextFunction = 0    # 上次运行的查询函数索引
    
    #----------------------------------------------------------------------
    def query(self, event):
        """注册到事件处理引擎上的查询函数"""
        self.qryCount += 1
        
        if self.qryCount > self.qryTrigger:
            # 清空倒计时
            self.qryCount = 0
            
            # 如果尚未完成委托查询则先查询委托
            if not self.orderInited:
                self.tdApi.getOrder()
            # 然后如果未完成成交查询则再查询成交
            elif not self.tradeInited:
                self.tdApi.getTrade()
            else:
                # 执行查询函数
                function = self.qryFunctionList[self.qryNextFunction]
                function()
                
                # 计算下次查询函数的索引，如果超过了列表长度，则重新设为0
                self.qryNextFunction += 1
                if self.qryNextFunction == len(self.qryFunctionList):
                    self.qryNextFunction = 0
    
    #----------------------------------------------------------------------
    def startQuery(self):
        """启动连续查询"""
        if self.qryEnabled:
            self.eventEngine.register(EVENT_TIMER, self.query)
    
    #----------------------------------------------------------------------
    def setQryEnabled(self, qryEnabled):
        """设置是否要启动循环查询"""
        self.qryEnabled = qryEnabled
    

########################################################################
class KsgoldTdApi(TdApi):
    """金仕达黄金交易API实现"""
    
    #----------------------------------------------------------------------
    def __init__(self, gateway):
        """API对象的初始化函数"""
        super(KsgoldTdApi, self).__init__()
        
        self.gateway = gateway                  # gateway对象
        self.gatewayName = gateway.gatewayName  # gateway对象名称
        
        self.reqID = EMPTY_INT              # 操作请求编号
        self.orderRef = EMPTY_INT           # 订单编号
        
        self.connectionStatus = False       # 连接状态
        self.loginStatus = False            # 登录状态
        
        self.accountID = EMPTY_STRING       # 账号
        self.password = EMPTY_STRING        # 密码
        self.memberID = EMPTY_STRING        # 会员代码（应该是银行）
        self.address = EMPTY_STRING         # 服务器地址
        
        self.seatID = EMPTY_STRING;         # 席位号
        self.tradeCode = EMPTY_STRING       # 交易编码
        
        self.subscribedSymbols = set()      # 已订阅合约代码    
        
        self.orderDict = {}                 # 委托字典，用于缓存委托对象，key为整数
        
        # 循环登录相关
        self.loginThread = None
        
    #----------------------------------------------------------------------
    def onFrontConnected(self):
        """服务器连接"""
        self.connectionStatus = True
        
        log = VtLogData()
        log.gatewayName = self.gatewayName
        log.logContent = u'交易服务器连接成功'
        self.gateway.onLog(log)
        
        self.login()
    
    #----------------------------------------------------------------------
    def onFrontDisconnected(self, n):
        """服务器断开"""
        self.connectionStatus = False
        self.loginStatus = False
        self.gateway.tdConnected = False
        
        log = VtLogData()
        log.gatewayName = self.gatewayName
        log.logContent = u'交易服务器连接断开'
        self.gateway.onLog(log)      
    
    #----------------------------------------------------------------------
    def onRspUserLogin(self, data, error, n, last):
        """登陆回报"""
        # 如果登录成功，推送日志信息
        if error['ErrorID'] == 0:
            self.seatID = str(data['SeatNo'])
            self.tradeCode = str(data['tradeCode'])
            
            self.loginStatus = True
            self.gateway.tdConnected = True
            
            log = VtLogData()
            log.gatewayName = self.gatewayName
            log.logContent = u'交易服务器登录完成'
            self.gateway.onLog(log)
            
            # 重新订阅之前订阅的合约
            for subscribeReq in self.subscribedSymbols:
                self.subscribe(subscribeReq)    
                            
            # 查询合约信息
            self.reqID += 1
            self.reqQryInstrument({}, self.reqID)
            
            # 启动查询
            self.gateway.startQuery()            

        # 否则，推送错误信息
        else:
            err = VtErrorData()
            err.gatewayName = self.gatewayName
            err.errorID = error['ErrorID']
            err.errorMsg = error['ErrorMsg'].decode('gbk')
            self.gateway.onError(err)
            
            if error['ErrorID'] == -8:
                self.login()
    
    #----------------------------------------------------------------------
    def onRspUserLogout(self, data, error, n, last):
        """登出回报"""
        # 如果登出成功，推送日志信息
        if error['ErrorID'] == 0:
            self.loginStatus = False
            self.gateway.tdConnected = False
            
            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 onNtyMktStatus(self, data, error, n, last):
        """更新市场状态"""
        pass
    
    #----------------------------------------------------------------------
    def onRspOrderInsert(self, data, error, n, last):
        """发单错误（柜台）"""
        if error['ErrorID']:
            err = VtErrorData()
            err.gatewayName = self.gatewayName
            err.errorID = error['ErrorID']
            err.errorMsg = error['ErrorMsg'].decode('gbk')
            self.gateway.onError(err)
    
    #----------------------------------------------------------------------
    def onRspOrderAction(self, data, error, n, last):
        """撤单错误（柜台）"""
        if error['ErrorID']:
            err = VtErrorData()
            err.gatewayName = self.gatewayName
            err.errorID = error['ErrorID']
            err.errorMsg = error['ErrorMsg'].decode('gbk')
            self.gateway.onError(err)
       
    #----------------------------------------------------------------------
    def onRspQryOrder(self, data, error, n, last):
        """"""
        if not self.gateway.orderInited:
            self.gateway.orderInited = True
            
            log = VtLogData()
            log.gatewayName = self.gatewayName
            log.logContent = u'委托查询完成'
            self.gateway.onLog(log)            
        
        # 更新最大报单编号
        newref = data['localOrderNo']
        self.orderRef = max(self.orderRef, int(newref))
        
        # 创建报单数据对象
        order = VtOrderData()
        order.gatewayName = self.gatewayName
        
        # 保存代码和报单号
        order.symbol = data['instID']
        order.exchange = EXCHANGE_SGE
        order.vtSymbol = order.symbol
        
        order.orderID = data['localOrderNo']
        
        # 方向
        if data['buyOrSell'] == '0':
            order.direction = DIRECTION_LONG
        elif data['buyOrSell'] == '1':
            order.direction = DIRECTION_SHORT
        else:
            order.direction = DIRECTION_UNKNOWN
            
        # 开平
        if data['offsetFlag'] == '0':
            order.offset = OFFSET_OPEN
        elif data['offsetFlag'] == '1':
            order.offset = OFFSET_CLOSE
        else:
            order.offset = OFFSET_UNKNOWN        
            
        # 状态
        if data['status'] == '5':
            order.status = STATUS_ALLTRADED
        elif data['status'] == '6':
            order.status = STATUS_PARTTRADED
        elif data['status'] == '3':
            order.status = STATUS_NOTTRADED
        elif data['status'] == '4' or data['status'] == '7':    # 全部撤销和部成部撤
            order.status = STATUS_CANCELLED
        else:
            order.status = STATUS_UNKNOWN
        
        # CTP的报单号一致性维护需要基于frontID, sessionID, orderID三个字段
        # 但在本接口设计中，已经考虑了CTP的OrderRef的自增性，避免重复
        # 唯一可能出现OrderRef重复的情况是多处登录并在非常接近的时间内（几乎同时发单）
        # 考虑到VtTrader的应用场景，认为以上情况不会构成问题
        order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
        
        # 价格、报单量等数值
        order.price = data['price']
        order.totalVolume = data['amount']
        order.tradedVolume = data['matchQty']
        order.orderTime = data['entrustTime']
        order.cancelTime = data['cancelTime']    
        
        # 推送
        self.gateway.onOrder(order)        
    
    #----------------------------------------------------------------------
    def onRspQryTrade(self, data, error, n, last):
        """"""
        if not self.gateway.tradeInited:
            self.gateway.tradeInited = True
            
            log = VtLogData()
            log.gatewayName = self.gatewayName
            log.logContent = u'成交查询完成'
            self.gateway.onLog(log)
        
        # 创建报单数据对象
        trade = VtTradeData()
        trade.gatewayName = self.gatewayName
        
        # 保存代码和报单号
        trade.symbol = data['instID']
        trade.exchange = EXCHANGE_SGE
        trade.vtSymbol = trade.symbol 
        
        trade.tradeID = data['matchNo']
        trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
        
        trade.orderID = data['localOrderNo']
        trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
        
        # 方向
        trade.direction = directionMapReverse.get(data['buyOrSell'], '')
            
        # 开平
        trade.offset = offsetMapReverse.get(data['offSetFlag'], '')
            
        # 价格、报单量等数值
        trade.price = data['price']
        trade.volume = data['volume']
        trade.tradeTime = data['matchTime']
        
        # 推送
        self.gateway.onTrade(trade)        
    
    #----------------------------------------------------------------------
    def onRspQryInvestorPosition(self, data, error, n, last):
        """持仓查询回报"""
        # 过滤一些空的数据
        if data['instID']:
            # 黄金API推送的持仓数据把多空仓放在了一条中，需要分离
            longPos = VtPositionData()
            longPos.gatewayName = self.gatewayName
            longPos.symbol = data['instID']
            longPos.vtSymbol = longPos.symbol
            longPos.direction = DIRECTION_LONG
            longPos.frozen = data['longPosiFrozen']
            longPos.position = data['longPosiVol']
            longPos.ydPosition = data['lastLong']
            longPos.price = data['longPosiAvgPrice']
            longPos.vtPositionName = '.'.join([longPos.vtSymbol, longPos.direction])
            self.gateway.onPosition(longPos)
            
            shortPos = VtPositionData()
            shortPos.gatewayName = self.gatewayName
            shortPos.symbol = data['instID']
            shortPos.vtSymbol = shortPos.symbol
            shortPos.direction = DIRECTION_SHORT
            shortPos.frozen = data['shortPosiFrozen']
            shortPos.position = data['shortPosiVol']
            shortPos.ydPosition = data['lastShort']
            shortPos.price = data['shortPosiAvgPrice']
            shortPos.vtPositionName = '.'.join([shortPos.vtSymbol, shortPos.direction])
            self.gateway.onPosition(shortPos)
    
    #----------------------------------------------------------------------
    def onRspQryTradingAccount(self, data, error, n, last):
        """资金账户查询回报"""
        account = VtAccountData()
        account.gatewayName = self.gatewayName
        
        # 账户代码
        account.accountID = 'sge_account'   # API不提供该字段，自行定义
        account.vtAccountID = '.'.join([self.gatewayName, account.accountID])
        
        # 数值相关
        account.available = data['availCap']
        account.commission = data['totalFee']
        account.margin = data['posiMargin']
        
        # 推送
        self.gateway.onAccount(account)
    
    #----------------------------------------------------------------------
    def onRspQryInstrument(self, data, error, n, last):
        """合约查询回报"""
        contract = VtContractData()
        contract.gatewayName = self.gatewayName

        contract.symbol = data['instID']
        contract.exchange = EXCHANGE_SGE    # 交易所只有金交所
        
        # 合约类型分为现货、延期、未知
        if data['marketID'] == '00':
            contract.productClass = PRODUCT_SPOT
        elif data['marketID'] == '10':
            contract.productClass = PRODUCT_DEFER
        else:
            contract.productclass = PRODUCT_UNKNOWN
        
        contract.vtSymbol = contract.symbol 
        contract.name = data['name'].decode('GBK')
        
        # 合约数值
        contract.size = data['unit']
        contract.priceTick = data['tick']
        
        # 推送，考虑到只对T+D类带杠杆的产品感兴趣，API里默认过滤了现货
        if data['marketID'] == '10':
            self.gateway.onContract(contract)
        
        if last:
            log = VtLogData()
            log.gatewayName = self.gatewayName
            log.logContent = u'交易合约信息获取完成'
            self.gateway.onLog(log)
    
    #----------------------------------------------------------------------  
    def onRtnDepthMarketData(self, data):
        """行情推送"""
        tick = VtTickData()
        tick.gatewayName = self.gatewayName
        
        tick.symbol = data['InstID']
        tick.vtSymbol = tick.symbol
        
        tick.lastPrice = data['Last']
        tick.volume = data['Volume']
        tick.openInterest = data['OpenInt']
        tick.time = data['QuoteTime']
        tick.date = data['QuoteDate']
        
        tick.openPrice = data['Open']
        tick.highPrice = data['High']
        tick.lowPrice = data['Low']
        tick.preClosePrice = data['PreClose']
        
        tick.upperLimit = data['highLimit']
        tick.lowerLimit = data['lowLimit']
        
        # CTP只有一档行情
        tick.bidPrice1 = data['Bid1']
        tick.bidPrice2 = data['Bid2']
        tick.bidPrice3 = data['Bid3']
        tick.bidPrice4 = data['Bid4']
        tick.bidPrice5 = data['Bid5']
        
        tick.askPrice1 = data['Ask1']
        tick.askPrice2 = data['Ask2']
        tick.askPrice3 = data['Ask3']
        tick.askPrice4 = data['Ask4']
        tick.askPrice5 = data['Ask5']        
        
        tick.bidVolume1 = data['BidLot1']
        tick.bidVolume2 = data['BidLot2']
        tick.bidVolume3 = data['BidLot3']
        tick.bidVolume4 = data['BidLot4']
        tick.bidVolume5 = data['BidLot5']
        
        tick.askVolume1 = data['AskLot1']
        tick.askVolume2 = data['AskLot2']
        tick.askVolume3 = data['AskLot3']
        tick.askVolume4 = data['AskLot4']
        tick.askVolume5 = data['AskLot5']
        
        self.gateway.onTick(tick)
        
    #----------------------------------------------------------------------
    def onRtnOrder(self, data):
        """报单回报"""
        # 更新最大报单编号
        newref = data['localOrderNo']
        self.orderRef = max(self.orderRef, int(newref))
        
        # 创建报单数据对象
        order = self.orderDict.get(int(newref), VtOrderData())
        order.gatewayName = self.gatewayName
        order.exchange = EXCHANGE_SGE
        order.orderID = data['localOrderNo']
        
        # 状态
        if data['status'] == '5':
            order.status = STATUS_ALLTRADED
        elif data['status'] == '6':
            order.status = STATUS_PARTTRADED
        elif data['status'] == '3':
            order.status = STATUS_NOTTRADED
        elif data['status'] == '4' or data['status'] == '7':    # 全部撤销和部成部撤
            order.status = STATUS_CANCELLED
        else:
            order.status = STATUS_UNKNOWN
        
        # 推送
        self.gateway.onOrder(order)
        
    #----------------------------------------------------------------------
    def onForceLogout(self, data):
        """强制登出推送"""
        self.gateway.onLog(u'被强制登出')
    
    #----------------------------------------------------------------------
    def onRtnTrade(self, data):
        """成交回报"""
        # 创建报单数据对象
        trade = VtTradeData()
        trade.gatewayName = self.gatewayName
        
        # 保存代码和报单号
        trade.symbol = data['instID']
        trade.exchange = EXCHANGE_SGE
        trade.vtSymbol = trade.symbol 
        
        trade.tradeID = data['matchNo']
        trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
        
        trade.orderID = data['localOrderNo']
        trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
        
        # 方向
        trade.direction = directionMapReverse.get(data['buyOrSell'], '')
            
        # 开平
        trade.offset = offsetMapReverse.get(data['offSetFlag'], '')
            
        # 价格、报单量等数值
        trade.price = data['price']
        trade.volume = data['volume']
        trade.tradeTime = data['matchTime']
        
        # 推送
        self.gateway.onTrade(trade)
        
        # 计算还原Order状态，并推送
        order = self.orderDict.get(int(trade.orderID), None)
        if order:
            order.tradedVolume = order.tradedVolume + trade.volume
            
            if order.tradedVolume == order.totalVolume:
                order.status = STATUS_ALLTRADED
            else:
                order.status = STATUS_PARTTRADED
                
        self.gateway.onOrder(order)
    
    #----------------------------------------------------------------------
    def onErrRtnOrderInsert(self, data, error):
        """发单错误回报（交易所）"""
        if error['ErrorID']:
            err = VtErrorData()
            err.gatewayName = self.gatewayName
            err.errorID = error['ErrorID']
            err.errorMsg = error['ErrorMsg'].decode('gbk')
            self.gateway.onError(err)
        
    #----------------------------------------------------------------------
    def onErrRtnOrderAction(self, data, error):
        """撤单错误回报（交易所）"""
        if error['ErrorID']:
            err = VtErrorData()
            err.gatewayName = self.gatewayName
            err.errorID = error['ErrorID']
            err.errorMsg = error['ErrorMsg'].decode('gbk')
            self.gateway.onError(err)

    #----------------------------------------------------------------------
    def onRspQryClientStorage(self, data, error, n, last):
        """"""
        pass
        
    #----------------------------------------------------------------------
    def onRspSubMarketData(self, data, error, n, last):
        """"""
        pass
    
    #----------------------------------------------------------------------
    def connect(self, accountID, password, address):
        """初始化连接"""
        self.accountID = accountID 
        self.password = password   
        self.address = address  
        
        # 如果尚未建立服务器连接，则进行连接
        if not self.connectionStatus:
            self.createGoldTraderApi()
            
            # 注册服务器地址
            self.registerFront(self.address)

            # 初始化连接，成功会调用onFrontConnected
            self.init(False)
            self.connectionStatus = True
            
            # 金仕达接口第一次连接不会调用onFrontConnected（只有断线重连才会），直接登录
            self.login()
            
        # 若已经连接但尚未登录，则进行登录
        else:
            if not self.loginStatus:
                self.login()    
    
    #----------------------------------------------------------------------
    def login(self):
        """连接服务器"""
        # 如果填入了用户名密码等，则登录
        if self.accountID and self.password:
            req = {}
            req['accountID'] = self.accountID
            req['password'] = self.password
            req['loginType'] = 1
            self.reqID += 1
            self.reqUserLogin(req, self.reqID)  
        
    #----------------------------------------------------------------------
    def qryAccount(self):
        """查询账户"""
        self.reqID += 1
        self.reqQryTradingAccount({}, self.reqID)
        
    #----------------------------------------------------------------------
    def qryPosition(self):
        """查询持仓"""
        self.reqID += 1
        self.reqQryInvestorPosition({}, self.reqID)
    
    #----------------------------------------------------------------------
    def getTrade(self):
        """查询成交"""
        self.reqID += 1
        self.reqQryTrade({}, self.reqID)    
    
    #----------------------------------------------------------------------
    def getOrder(self):
        """查询委托"""
        self.reqID += 1
        self.reqQryOrder({}, self.reqID)        
    
    #----------------------------------------------------------------------
    def sendOrder(self, orderReq):
        """发单"""
        self.reqID += 1
        self.orderRef += 1
        
        req = {}
        
        req['instID'] = orderReq.symbol
        req['marketID'] = '10'
        
        req['price'] = orderReq.price
        req['amount'] = orderReq.volume
        
        # 下面如果由于传入的类型本接口不支持，则会返回空字符串
        try:
            req['buyOrSell'] = directionMap[orderReq.direction]
            req['offsetFlag'] = offsetMap[orderReq.offset]
        except KeyError:
            return ''
        
        strOrderID = generateStrLocalID(self.orderRef)
        req['LocalOrderNo'] = strOrderID
        req['seatID'] = self.seatID
        req['tradeCode'] = self.tradeCode
        
        self.reqOrderInsert(req, self.reqID)
        
        # 返回订单号（字符串），便于某些算法进行动态管理
        vtOrderID = '.'.join([self.gatewayName, strOrderID])
        
        # 保存请求到本地，用于在收到回报时查询
        # 金仕达黄金API的onRtnOrder只推送很有限的几个数据，因此整个数据需要自己还原
        order = VtOrderData()
        order.gatewayName = self.gatewayName
        order.symbol = orderReq.symbol
        order.vtSymbol = orderReq.symbol
        order.exchange = EXCHANGE_SGE
        order.orderID = strOrderID
        order.vtOrderID = strOrderID
        order.direction = orderReq.direction
        order.offset = orderReq.offset
        order.price = orderReq.price
        order.totalVolume = orderReq.volume
        order.orderTime = time.strftime('%H:%M:%S')   # 保存委托时间
        self.orderDict[self.orderRef] = order
        
        return vtOrderID
    
    #----------------------------------------------------------------------
    def cancelOrder(self, cancelOrderReq):
        """撤单"""
        self.reqID += 1

        req = {}
        req['localOrderNo'] = cancelOrderReq.orderID
        req['marketID'] = '10'  # API默认只允许交易T+D延期
        
        self.reqOrderAction(req, self.reqID)
    
    #----------------------------------------------------------------------
    def subscribe(self, subscribeReq):
        """订阅行情"""
        # 黄金API的订阅传入的参数不是具体交易合约代码，而是类型代码
        # 考虑到只希望交易T+D，这里直接传入'10'
        self.subscribeMarketData('10')
    
    #----------------------------------------------------------------------
    def close(self):
        """关闭"""
        self.exit()


#----------------------------------------------------------------------
def generateStrLocalID(localID):
    """把整数的本地委托号转化为字符串"""
    return str(localID).rjust(12, '0')

