Exemplo n.º 1
0
    def processContractsEvent(self, event):
        contract_data = event.dict_['data']
        nl = []

        # 交易的合约代码也应可以用配置文件配置
        tradingContractData = []
        # 打开设置文件
        with open(getJsonPath(self.settingFileName, __file__)) as f:
            drsettings = json.load(f)
            if 'active' in drsettings:
                actives = drsettings['active']
                if actives != {}:
                    for i in actives:
                        tradingContractData.append(i[:-4])
                for ocn in contract_data:
                    contract = ocn.decode()
                    if contract[:1] in tradingContractData or contract[:2] in tradingContractData:
                        nl.append([contract, "CTP"])

                from operator import itemgetter, attrgetter
                filename = 'openInterest.json'
                # 打开持仓量文件
                with open(getJsonPath(filename, __file__)) as f:
                    openinterests = json.load(f)
                    if 'oi' in openinterests:
                        ois = openinterests['oi']
                        for item in tradingContractData:
                            oiarray = []
                            # 获取持仓量列表
                            for oi in ois:
                                if oi[:1] == item or oi[:2] == item:
                                    oiarray.append([oi, ois[oi]])
                            # 排序持仓量列表
                            if oiarray != []:
                                sortedioarray = sorted(oiarray, key=itemgetter(1), reverse=True)
                                if sortedioarray[0][1] / sortedioarray[1][1] > 1.1 and sortedioarray[0][0] > sortedioarray[1][0]:
                                    if actives[item + '.HOT'] != sortedioarray[0][0]:
                                        actives[item + '.HOT'] = sortedioarray[0][0]
        json_data = {'working': True, 'tick': {}, 'bar': nl, 'active': actives}
        d1 = json.dumps(json_data, sort_keys=True, indent=4)

        f = open(os.path.join(os.getcwd(), self.settingFileName), 'w')
        f.write(d1)
        f.close()
        self.loadSetting()
Exemplo n.º 2
0
class CtaEngine(object):
    """CTA策略引擎"""
    settingFileName = 'CTA_setting.json'
    settingfilePath = getJsonPath(settingFileName, __file__)
    
    STATUS_FINISHED = set([STATUS_REJECTED, STATUS_CANCELLED, STATUS_ALLTRADED])

    #----------------------------------------------------------------------
    def __init__(self, mainEngine, eventEngine):
        """Constructor"""
        self.mainEngine = mainEngine
        self.eventEngine = eventEngine
        
        # 当前日期
        self.today = todayDate()
        
        # 保存策略实例的字典
        # key为策略名称,value为策略实例,注意策略名称不允许重复
        self.strategyDict = {}
        
        # 保存vtSymbol和策略实例映射的字典(用于推送tick数据)
        # 由于可能多个strategy交易同一个vtSymbol,因此key为vtSymbol
        # value为包含所有相关strategy对象的list
        self.tickStrategyDict = {}
        
        # 保存vtOrderID和strategy对象映射的字典(用于推送order和trade数据)
        # key为vtOrderID,value为strategy对象
        self.orderStrategyDict = {}     
        
        # 本地停止单编号计数
        self.stopOrderCount = 0
        # stopOrderID = STOPORDERPREFIX + str(stopOrderCount)
        
        # 本地停止单字典
        # key为stopOrderID,value为stopOrder对象
        self.stopOrderDict = {}             # 停止单撤销后不会从本字典中删除
        self.workingStopOrderDict = {}      # 停止单撤销后会从本字典中删除
        
        # 保存策略名称和委托号列表的字典
        # key为name,value为保存orderID(限价+本地停止)的集合
        self.strategyOrderDict = {}
        
        # 成交号集合,用来过滤已经收到过的成交推送
        self.tradeSet = set()
        
        # 引擎类型为实盘
        self.engineType = ENGINETYPE_TRADING
        
        # 注册日式事件类型
        self.mainEngine.registerLogEvent(EVENT_CTA_LOG)
        
        # 注册事件监听
        self.registerEvent()
 
    #----------------------------------------------------------------------
    def sendOrder(self, vtSymbol, orderType, price, volume, strategy):
        """发单"""
        contract = self.mainEngine.getContract(vtSymbol)
        
        req = VtOrderReq()
        req.symbol = contract.symbol
        req.exchange = contract.exchange
        req.vtSymbol = contract.vtSymbol
        req.price = self.roundToPriceTick(contract.priceTick, price)
        req.volume = volume
        
        req.productClass = strategy.productClass
        req.currency = strategy.currency        
        
        # 设计为CTA引擎发出的委托只允许使用限价单
        req.priceType = PRICETYPE_LIMITPRICE    
        
        # CTA委托类型映射
        if orderType == CTAORDER_BUY:
            req.direction = DIRECTION_LONG
            req.offset = OFFSET_OPEN
            
        elif orderType == CTAORDER_SELL:
            req.direction = DIRECTION_SHORT
            req.offset = OFFSET_CLOSE
                
        elif orderType == CTAORDER_SHORT:
            req.direction = DIRECTION_SHORT
            req.offset = OFFSET_OPEN
            
        elif orderType == CTAORDER_COVER:
            req.direction = DIRECTION_LONG
            req.offset = OFFSET_CLOSE
            
        # 委托转换
        reqList = self.mainEngine.convertOrderReq(req)
        vtOrderIDList = []
        
        if not reqList:
            return vtOrderIDList
        for convertedReq in reqList:
            vtOrderID = self.mainEngine.sendOrder(convertedReq, contract.gatewayName)    # 发单
            self.orderStrategyDict[vtOrderID] = strategy                                 # 保存vtOrderID和策略的映射关系
            self.strategyOrderDict[strategy.name].add(vtOrderID)                         # 添加到策略委托号集合中
            vtOrderIDList.append(vtOrderID)
        self.writeCtaLog(u'策略%s发送委托,%s,%s,%s@%s' 
                         %(strategy.name, vtSymbol, req.direction, volume, price))
        
        return vtOrderIDList
    
    #----------------------------------------------------------------------
    def cancelOrder(self, vtOrderID):
        """撤单"""
        # 查询报单对象
        order = self.mainEngine.getOrder(vtOrderID)
        
        # 如果查询成功
        if order:
            # 检查是否报单还有效,只有有效时才发出撤单指令
            orderFinished = (order.status==STATUS_ALLTRADED or order.status==STATUS_CANCELLED)
            if not orderFinished:
                req = VtCancelOrderReq()
                req.symbol = order.symbol
                req.exchange = order.exchange
                req.frontID = order.frontID
                req.sessionID = order.sessionID
                req.orderID = order.orderID
                self.mainEngine.cancelOrder(req, order.gatewayName)    

    #----------------------------------------------------------------------
    def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy):
        """发停止单(本地实现)"""
        self.stopOrderCount += 1
        stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount)
        
        so = StopOrder()
        so.vtSymbol = vtSymbol
        so.orderType = orderType
        so.price = price
        so.volume = volume
        so.strategy = strategy
        so.stopOrderID = stopOrderID
        so.status = STOPORDER_WAITING
        
        if orderType == CTAORDER_BUY:
            so.direction = DIRECTION_LONG
            so.offset = OFFSET_OPEN
        elif orderType == CTAORDER_SELL:
            so.direction = DIRECTION_SHORT
            so.offset = OFFSET_CLOSE
        elif orderType == CTAORDER_SHORT:
            so.direction = DIRECTION_SHORT
            so.offset = OFFSET_OPEN
        elif orderType == CTAORDER_COVER:
            so.direction = DIRECTION_LONG
            so.offset = OFFSET_CLOSE           
        
        # 保存stopOrder对象到字典中
        self.stopOrderDict[stopOrderID] = so
        self.workingStopOrderDict[stopOrderID] = so
        
        # 保存stopOrderID到策略委托号集合中
        self.strategyOrderDict[strategy.name].add(stopOrderID)
        
        # 推送停止单状态
        strategy.onStopOrder(so)
        
        return [stopOrderID]
    
    #----------------------------------------------------------------------
    def cancelStopOrder(self, stopOrderID):
        """撤销停止单"""
        # 检查停止单是否存在
        if stopOrderID in self.workingStopOrderDict:
            so = self.workingStopOrderDict[stopOrderID]
            strategy = so.strategy
            
            # 更改停止单状态为已撤销
            so.status = STOPORDER_CANCELLED
            
            # 从活动停止单字典中移除
            del self.workingStopOrderDict[stopOrderID]
            
            # 从策略委托号集合中移除
            s = self.strategyOrderDict[strategy.name]
            if stopOrderID in s:
                s.remove(stopOrderID)
            
            # 通知策略
            strategy.onStopOrder(so)

    #----------------------------------------------------------------------
    def processStopOrder(self, tick):
        """收到行情后处理本地停止单(检查是否要立即发出)"""
        vtSymbol = tick.vtSymbol
        
        # 首先检查是否有策略交易该合约
        if vtSymbol in self.tickStrategyDict:
            # 遍历等待中的停止单,检查是否会被触发
            for so in list(self.workingStopOrderDict.values()):
                if so.vtSymbol == vtSymbol:
                    longTriggered = so.direction==DIRECTION_LONG and tick.lastPrice>=so.price        # 多头停止单被触发
                    shortTriggered = so.direction==DIRECTION_SHORT and tick.lastPrice<=so.price     # 空头停止单被触发
                    
                    if longTriggered or shortTriggered:
                        # 买入和卖出分别以涨停跌停价发单(模拟市价单)
                        if so.direction==DIRECTION_LONG:
                            price = tick.upperLimit
                        else:
                            price = tick.lowerLimit
                        
                        # 发出市价委托
                        self.sendOrder(so.vtSymbol, so.orderType, price, so.volume, so.strategy)
                        
                        # 从活动停止单字典中移除该停止单
                        del self.workingStopOrderDict[so.stopOrderID]
                        
                        # 从策略委托号集合中移除
                        s = self.strategyOrderDict[so.strategy.name]
                        if so.stopOrderID in s:
                            s.remove(so.stopOrderID)
                        
                        # 更新停止单状态,并通知策略
                        so.status = STOPORDER_TRIGGERED
                        so.strategy.onStopOrder(so)

    #----------------------------------------------------------------------
    def processTickEvent(self, event):
        """处理行情推送"""
        tick = event.dict_['data']
        
        tick = copy(tick)
        
        # 收到tick行情后,先处理本地停止单(检查是否要立即发出)
        self.processStopOrder(tick)
        
        # 推送tick到对应的策略实例进行处理
        if tick.vtSymbol in self.tickStrategyDict:
            # tick时间可能出现异常数据,使用try...except实现捕捉和过滤
            try:
                # 添加datetime字段
                if not tick.datetime:
                    tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f')
            except ValueError:
                self.writeCtaLog(traceback.format_exc())
                return
                
            # 逐个推送到策略实例中
            l = self.tickStrategyDict[tick.vtSymbol]
            for strategy in l:
                self.callStrategyFunc(strategy, strategy.onTick, tick)
    
    #----------------------------------------------------------------------
    def processOrderEvent(self, event):
        """处理委托推送"""
        order = event.dict_['data']
        
        vtOrderID = order.vtOrderID
        
        if vtOrderID in self.orderStrategyDict:
            strategy = self.orderStrategyDict[vtOrderID]            
            
            # 如果委托已经完成(拒单、撤销、全成),则从活动委托集合中移除
            if order.status in self.STATUS_FINISHED:
                s = self.strategyOrderDict[strategy.name]
                if vtOrderID in s:
                    s.remove(vtOrderID)
            
            self.callStrategyFunc(strategy, strategy.onOrder, order)
    
    #----------------------------------------------------------------------
    def processTradeEvent(self, event):
        """处理成交推送"""
        trade = event.dict_['data']
        
        # 过滤已经收到过的成交回报
        if trade.vtTradeID in self.tradeSet:
            return
        self.tradeSet.add(trade.vtTradeID)
        
        # 将成交推送到策略对象中
        if trade.vtOrderID in self.orderStrategyDict:
            strategy = self.orderStrategyDict[trade.vtOrderID]
            
            # 计算策略持仓
            if trade.direction == DIRECTION_LONG:
                strategy.pos += trade.volume
            else:
                strategy.pos -= trade.volume
            
            self.callStrategyFunc(strategy, strategy.onTrade, trade)
            
            # 保存策略持仓到数据库
            self.saveSyncData(strategy)              
    
    #----------------------------------------------------------------------
    def registerEvent(self):
        """注册事件监听"""
        self.eventEngine.register(EVENT_TICK, self.processTickEvent)
        self.eventEngine.register(EVENT_ORDER, self.processOrderEvent)
        self.eventEngine.register(EVENT_TRADE, self.processTradeEvent)
 
    #----------------------------------------------------------------------
    def insertData(self, dbName, collectionName, data):
        """插入数据到数据库(这里的data可以是VtTickData或者VtBarData)"""
        self.mainEngine.dbInsert(dbName, collectionName, data.__dict__)
    
    #----------------------------------------------------------------------
    def loadBar(self, dbName, collectionName, days):
        """从数据库中读取Bar数据,startDate是datetime对象"""
        startDate = self.today - timedelta(days)
        
        d = {'datetime':{'$gte':startDate}}
        barData = self.mainEngine.dbQuery(dbName, collectionName, d, 'datetime')
        
        l = []
        for d in barData:
            bar = VtBarData()
            bar.__dict__ = d
            l.append(bar)
        return l
    
    #----------------------------------------------------------------------
    def loadTick(self, dbName, collectionName, days):
        """从数据库中读取Tick数据,startDate是datetime对象"""
        startDate = self.today - timedelta(days)
        
        d = {'datetime':{'$gte':startDate}}
        tickData = self.mainEngine.dbQuery(dbName, collectionName, d, 'datetime')
        
        l = []
        for d in tickData:
            tick = VtTickData()
            tick.__dict__ = d
            l.append(tick)
        return l    
    
    #----------------------------------------------------------------------
    def writeCtaLog(self, content):
        """快速发出CTA模块日志事件"""
        log = VtLogData()
        log.logContent = content
        log.gatewayName = 'CTA_STRATEGY'
        event = Event(type_=EVENT_CTA_LOG)
        event.dict_['data'] = log
        self.eventEngine.put(event)   
    
    #----------------------------------------------------------------------
    def loadStrategy(self, setting):
        """载入策略"""
        try:
            name = setting['name']
            className = setting['className']
        except Exception:
            msg = traceback.format_exc()
            self.writeCtaLog(u'载入策略出错:%s' %msg)
            return
        
        # 获取策略类
        strategyClass = STRATEGY_CLASS.get(className, None)
        if not strategyClass:
            self.writeCtaLog(u'找不到策略类:%s' %className)
            return
        
        # 防止策略重名
        if name in self.strategyDict:
            self.writeCtaLog(u'策略实例重名:%s' %name)
        else:
            # 创建策略实例
            strategy = strategyClass(self, setting)  
            self.strategyDict[name] = strategy
            
            # 创建委托号列表
            self.strategyOrderDict[name] = set()
            
            # 保存Tick映射关系
            if strategy.vtSymbol in self.tickStrategyDict:
                l = self.tickStrategyDict[strategy.vtSymbol]
            else:
                l = []
                self.tickStrategyDict[strategy.vtSymbol] = l
            l.append(strategy)
            
    #----------------------------------------------------------------------
    def subscribeMarketData(self, strategy):
        """订阅行情"""
        # 订阅合约
        contract = self.mainEngine.getContract(strategy.vtSymbol)
        if contract:
            req = VtSubscribeReq()
            req.symbol = contract.symbol
            req.exchange = contract.exchange
            
            # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取
            req.currency = strategy.currency
            req.productClass = strategy.productClass
            
            self.mainEngine.subscribe(req, contract.gatewayName)
        else:
            self.writeCtaLog(u'%s的交易合约%s无法找到' %(strategy.name, strategy.vtSymbol))

    #----------------------------------------------------------------------
    def initStrategy(self, name):
        """初始化策略"""
        if name in self.strategyDict:
            strategy = self.strategyDict[name]
            
            if not strategy.inited:
                strategy.inited = True
                self.callStrategyFunc(strategy, strategy.onInit)
                self.loadSyncData(strategy)                             # 初始化完成后加载同步数据
                self.subscribeMarketData(strategy)                      # 加载同步数据后再订阅行情
            else:
                self.writeCtaLog(u'请勿重复初始化策略实例:%s' %name)
        else:
            self.writeCtaLog(u'策略实例不存在:%s' %name)        

    #---------------------------------------------------------------------
    def startStrategy(self, name):
        """启动策略"""
        if name in self.strategyDict:
            strategy = self.strategyDict[name]
            
            if strategy.inited and not strategy.trading:
                strategy.trading = True
                self.callStrategyFunc(strategy, strategy.onStart)
        else:
            self.writeCtaLog(u'策略实例不存在:%s' %name)
    
    #----------------------------------------------------------------------
    def stopStrategy(self, name):
        """停止策略"""
        if name in self.strategyDict:
            strategy = self.strategyDict[name]
            
            if strategy.trading:
                strategy.trading = False
                self.callStrategyFunc(strategy, strategy.onStop)
                
                # 对该策略发出的所有限价单进行撤单
                for vtOrderID, s in self.orderStrategyDict.items():
                    if s is strategy:
                        self.cancelOrder(vtOrderID)
                
                # 对该策略发出的所有本地停止单撤单
                for stopOrderID, so in self.workingStopOrderDict.items():
                    if so.strategy is strategy:
                        self.cancelStopOrder(stopOrderID)   
        else:
            self.writeCtaLog(u'策略实例不存在:%s' %name)    
            
    #----------------------------------------------------------------------
    def initAll(self):
        """全部初始化"""
        for name in self.strategyDict.keys():
            self.initStrategy(name)    
            
    #----------------------------------------------------------------------
    def startAll(self):
        """全部启动"""
        for name in self.strategyDict.keys():
            self.startStrategy(name)
            
    #----------------------------------------------------------------------
    def stopAll(self):
        """全部停止"""
        for name in self.strategyDict.keys():
            self.stopStrategy(name)    
    
    #----------------------------------------------------------------------
    def saveSetting(self):
        """保存策略配置"""
        with open(self.settingfilePath, 'w') as f:
            l = []
            
            for strategy in self.strategyDict.values():
                setting = {}
                for param in strategy.paramList:
                    setting[param] = strategy.__getattribute__(param)
                l.append(setting)
            
            jsonL = json.dumps(l, indent=4)
            f.write(jsonL)
    
    #----------------------------------------------------------------------
    def loadSetting(self):
        """读取策略配置"""
        with open(self.settingfilePath) as f:
            l = json.load(f)
            
            for setting in l:
                self.loadStrategy(setting)
    
    #----------------------------------------------------------------------
    def getStrategyVar(self, name):
        """获取策略当前的变量字典"""
        if name in self.strategyDict:
            strategy = self.strategyDict[name]
            varDict = OrderedDict()
            
            for key in strategy.varList:
                varDict[key] = strategy.__getattribute__(key)
            
            return varDict
        else:
            self.writeCtaLog(u'策略实例不存在:' + name)    
            return None
    
    #----------------------------------------------------------------------
    def getStrategyParam(self, name):
        """获取策略的参数字典"""
        if name in self.strategyDict:
            strategy = self.strategyDict[name]
            paramDict = OrderedDict()
            
            for key in strategy.paramList:  
                paramDict[key] = strategy.__getattribute__(key)
            
            return paramDict
        else:
            self.writeCtaLog(u'策略实例不存在:' + name)    
            return None
    
    #----------------------------------------------------------------------
    def getStrategyNames(self):
        """查询所有策略名称"""
        return self.strategyDict.keys()        
        
    #----------------------------------------------------------------------
    def putStrategyEvent(self, name):
        """触发策略状态变化事件(通常用于通知GUI更新)"""
        strategy = self.strategyDict[name]
        d = {k:strategy.__getattribute__(k) for k in strategy.varList}
        
        event = Event(EVENT_CTA_STRATEGY+name)
        event.dict_['data'] = d
        self.eventEngine.put(event)
        
        d2 = {k:str(v) for k,v in d.items()}
        d2['name'] = name
        event2 = Event(EVENT_CTA_STRATEGY)
        event2.dict_['data'] = d2
        self.eventEngine.put(event2)        
        
    #----------------------------------------------------------------------
    def callStrategyFunc(self, strategy, func, params=None):
        """调用策略的函数,若触发异常则捕捉"""
        try:
            if params:
                func(params)
            else:
                func()
        except Exception:
            # 停止策略,修改状态为未初始化
            strategy.trading = False
            strategy.inited = False
            
            # 发出日志
            content = '\n'.join([u'策略%s触发异常已停止' %strategy.name,
                                traceback.format_exc()])
            self.writeCtaLog(content)
            
    #----------------------------------------------------------------------
    def saveSyncData(self, strategy):
        """保存策略的持仓情况到数据库"""
        flt = {'name': strategy.name,
               'vtSymbol': strategy.vtSymbol}
        
        d = copy(flt)
        for key in strategy.syncList:
            d[key] = strategy.__getattribute__(key)
        
        self.mainEngine.dbUpdate(POSITION_DB_NAME, strategy.className,
                                 d, flt, True)
        
        content = u'策略%s同步数据保存成功,当前持仓%s' %(strategy.name, strategy.pos)
        self.writeCtaLog(content)
    
    #----------------------------------------------------------------------
    def loadSyncData(self, strategy):
        """从数据库载入策略的持仓情况"""
        flt = {'name': strategy.name,
               'vtSymbol': strategy.vtSymbol}
        syncData = self.mainEngine.dbQuery(POSITION_DB_NAME, strategy.className, flt)
        
        if not syncData:
            return
        
        d = syncData[0]
        
        for key in strategy.syncList:
            if key in d:
                strategy.__setattr__(key, d[key])
                
    #----------------------------------------------------------------------
    def roundToPriceTick(self, priceTick, price):
        """取整价格到合约最小价格变动"""
        if not priceTick:
            return price
        
        newPrice = round(price/priceTick, 0) * priceTick
        return newPrice    
    
    #----------------------------------------------------------------------
    def stop(self):
        """停止"""
        pass
    
    #----------------------------------------------------------------------
    def cancelAll(self, name):
        """全部撤单"""
        s = self.strategyOrderDict[name]
        
        # 遍历列表,全部撤单
        # 这里不能直接遍历集合s,因为撤单时会修改s中的内容,导致出错
        for orderID in list(s):
            if STOPORDERPREFIX in orderID:
                self.cancelStopOrder(orderID)
            else:
                self.cancelOrder(orderID)

    #----------------------------------------------------------------------
    def getPriceTick(self, strategy):
        """获取最小价格变动"""
        contract = self.mainEngine.getContract(strategy.vtSymbol)
        if contract:
            return contract.priceTick
        return 0
        
        
Exemplo n.º 3
0
class RsEngine(object):
    """RPC服务引擎"""
    
    settingFileName = 'RS_setting.json'
    settingFilePath = getJsonPath(settingFileName, __file__)    
    
    name = u'RPC服务'

    #----------------------------------------------------------------------
    def __init__(self, mainEngine, eventEngine):
        """Constructor"""
        self.mainEngine = mainEngine
        self.eventEngine = eventEngine
        
        self.server = None                  # RPC服务对象
        self.repAddress = EMPTY_STRING      # REP地址
        self.pubAddress = EMPTY_STRING      # PUB地址
        
        self.functionDict = {}              # 调用过的函数对象缓存字典
        
        self.loadSetting()
        self.registerEvent()
        
    #----------------------------------------------------------------------
    def loadSetting(self):
        """读取配置"""
        with open(self.settingFilePath) as f:
            d = json.load(f)
            
            self.repAddress = d['repAddress']
            self.pubAddress = d['pubAddress']
            
            self.server = RpcServer(self.repAddress, self.pubAddress)
            self.server.usePickle()
            self.server.register(self.call)
            self.server.start()
            
    #----------------------------------------------------------------------
    def registerEvent(self):
        """注册事件监听"""
        self.eventEngine.registerGeneralHandler(self.processEvent)
        
    #----------------------------------------------------------------------
    def call(self, d):
        """调用函数"""
        nameList = d['nameList']        # 对象属性列表
        nameTuple = tuple(nameList)     # 转化为元组
        args = d['args']                # 调用参数
        kwargs = d['kwargs']
        
        # 如果已经有缓存,则直接调用
        if nameTuple in self.functionDict:
            function = self.functionDict[nameTuple]
            result = function(*args, **kwargs)
            return result
        # 逐层寻找函数对象
        else:
            # 根对象为主引擎
            obj = self.mainEngine
            
            # 逐层寻找对象属性
            for name in nameTuple:
                obj = obj.__getattribute__(name)
            
            # 缓存结果
            self.functionDict[nameTuple] = obj
            
            # 调用最终对象
            result = obj(*args, **kwargs)
            return result
        
    #----------------------------------------------------------------------
    def processEvent(self, event):
        """处理事件推送"""
        self.server.publish(''.encode(encoding='utf-8'), event)
    
    #----------------------------------------------------------------------
    def stop(self):
        """停止"""
        self.server.stop()
Exemplo n.º 4
0
class DrEngine(object):
    """数据记录引擎"""
    
    settingFileName = 'DR_setting.json'
    settingFilePath = getJsonPath(settingFileName, __file__)  

    #----------------------------------------------------------------------
    def __init__(self, mainEngine, eventEngine):
        """Constructor"""
        self.mainEngine = mainEngine
        self.eventEngine = eventEngine
        
        # 当前日期
        self.today = todayDate()
        
        # 主力合约代码映射字典,key为具体的合约代码(如IF1604),value为主力合约代码(如IF0000)
        self.activeSymbolDict = {}
        
        # Tick对象字典
        self.tickSymbolSet = set()

        self.barSymbolSet = set()

        # K线合成器字典
        self.bmDict = {}

        # 配置字典
        self.settingDict = OrderedDict()
        
        # 负责执行数据库插入的单独线程相关
        self.active = False                     # 工作状态
        self.queue = Queue()                    # 队列
        self.thread = Thread(target=self.run)   # 线程
        
        # 载入设置,订阅行情
        #self.loadSetting()
        
        # 启动数据插入线程
        self.start()
    
        # 注册事件监听
        self.registerEvent()  
    
    #----------------------------------------------------------------------
    def loadSetting(self):
        """加载配置"""
        with open(self.settingFilePath) as f:
            drSetting = json.load(f)

            # 如果working设为False则不启动行情记录功能
            working = drSetting['working']
            if not working:
                return

            # Tick记录配置
            if 'tick' in drSetting:
                l = drSetting['tick']

                for setting in l:
                    symbol = setting[0]
                    gateway = setting[1]
                    vtSymbol = symbol

                    req = VtSubscribeReq()
                    req.symbol = setting[0]

                    # 针对LTS和IB接口,订阅行情需要交易所代码
                    if len(setting)>=3:
                        req.exchange = setting[2]
                        vtSymbol = '.'.join([symbol, req.exchange])

                    # 针对IB接口,订阅行情需要货币和产品类型
                    if len(setting)>=5:
                        req.currency = setting[3]
                        req.productClass = setting[4]

                    self.mainEngine.subscribe(req, gateway)

                    #tick = VtTickData()           # 该tick实例可以用于缓存部分数据(目前未使用)
                    #self.tickDict[vtSymbol] = tick

                    self.tickSymbolSet.add(vtSymbol)
                    
                    # 保存到配置字典中
                    if vtSymbol not in self.settingDict:
                        d = {
                            'symbol': symbol,
                            'gateway': gateway,
                            'tick': True
                        }
                        self.settingDict[vtSymbol] = d
                    else:
                        d = self.settingDict[vtSymbol]
                        d['tick'] = True

            # 分钟线记录配置
            if 'bar' in drSetting:
                l = drSetting['bar']

                for setting in l:
                    symbol = setting[0]
                    gateway = setting[1]
                    vtSymbol = symbol

                    req = VtSubscribeReq()
                    req.symbol = symbol                    

                    if len(setting)>=3:
                        req.exchange = setting[2]
                        vtSymbol = '.'.join([symbol, req.exchange])

                    if len(setting)>=5:
                        req.currency = setting[3]
                        req.productClass = setting[4]                    

                    self.mainEngine.subscribe(req, gateway)  

                    self.barSymbolSet.add(vtSymbol)
                    # 保存到配置字典中
                    if vtSymbol not in self.settingDict:
                        d = {
                            'symbol': symbol,
                            'gateway': gateway,
                            'bar': True
                        }
                        self.settingDict[vtSymbol] = d
                    else:
                        d = self.settingDict[vtSymbol]
                        d['bar'] = True     
                        
                    # 创建BarManager对象
                    #self.bmDict[vtSymbol] = BarGenerator(self.onBar)
                    self.bmDict[vtSymbol] = RecorderBarManager(self.onBar, self.onXBar)

            # 主力合约记录配置
            if 'active' in drSetting:
                d = drSetting['active']
                self.activeSymbolDict = {vtSymbol:activeSymbol for activeSymbol, vtSymbol in d.items()}
    
    #----------------------------------------------------------------------
    def getSetting(self):
        """获取配置"""
        return self.settingDict, self.activeSymbolDict

    #----------------------------------------------------------------------
    def procecssTickEvent(self, event):
        """处理行情事件"""
        tick = event.dict_['data']
        vtSymbol = tick.vtSymbol
        
        # 生成datetime对象
        if not tick.datetime:
            tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S')

        self.onTick(tick)
        
        bm = self.bmDict.get(vtSymbol, None)
        if bm:
            bm.updateTick(tick)

    def isDirtyData(self,tick):
        """ 判断脏数据 """

        #期货判断交易时间
        dt = datetime.now().time()

        # 如果在交易事件内,则为有效数据,无需清洗
        if ((MORNING_START <= dt < MORNING_REST) or
            (MORNING_RESTART <= dt < MORNING_END) or
            (AFTERNOON_START <= dt < AFTERNOON_END) or
            (dt >= NIGHT_START) or
            (dt < NIGHT_END)):
            return False

        #中间启停去掉脏数据
        if not tick.datetime:
            tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S')

            #判断脏数据
            time_now = datetime.now()
            time_delt = (tick.datetime - time_now).total_seconds()

            if time_delt < 180 and  time_delt > -180: #大于3分钟 脏数据
                return False

        return True
        
    #----------------------------------------------------------------------
    def onTick(self, tick):
        """Tick更新"""
        vtSymbol = tick.vtSymbol
        
        if vtSymbol in self.tickSymbolSet:
            self.insertData(TICK_DB_NAME, vtSymbol, tick)
            
            if vtSymbol in self.activeSymbolDict:
                activeSymbol = self.activeSymbolDict[vtSymbol]
                self.insertData(TICK_DB_NAME, activeSymbol, tick)
            
            
            self.writeDrLog(text.TICK_LOGGING_MESSAGE.format(symbol=tick.vtSymbol,
                                                             time=tick.time, 
                                                             last=tick.lastPrice, 
                                                             bid=tick.bidPrice1, 
                                                             ask=tick.askPrice1))
    
    #----------------------------------------------------------------------
    def onBar(self, bar):
        """分钟线更新"""
        vtSymbol = bar.vtSymbol
        
        self.insertData(MINUTE_DB_NAME, vtSymbol, bar)

        bm = self.bmDict.get(vtSymbol, None)
        if bm:
            bm.updateBar(bar)
        
        if vtSymbol in self.activeSymbolDict:
            activeSymbol = self.activeSymbolDict[vtSymbol]
            self.insertData(MINUTE_DB_NAME, activeSymbol, bar)                    
        
        self.writeDrLog(text.BAR_LOGGING_MESSAGE.format(symbol=bar.vtSymbol, 
                                                        time=bar.time, 
                                                        open=bar.open, 
                                                        high=bar.high, 
                                                        low=bar.low, 
                                                        close=bar.close))

        # ----------------------------------------------------------------------

    def onXBar(self, xmin, bar):
            """X分钟线更新"""
            vtSymbol = bar.vtSymbol

            self.insertData(MINUTE_TO_DB_NAME[xmin], vtSymbol, bar)

            if vtSymbol in self.activeSymbolDict:
                activeSymbol = self.activeSymbolDict[vtSymbol]
                self.insertData(MINUTE_TO_DB_NAME[xmin], activeSymbol, bar)

            self.writeDrLog(text.BAR_LOGGING_MESSAGE.format(symbol=bar.vtSymbol,
                                                            time=bar.time,
                                                            open=bar.open,
                                                            high=bar.high,
                                                            low=bar.low,
                                                            close=bar.close))


    #----------------------------------------------------------------------
    def registerEvent(self):
        """注册事件监听"""
        self.eventEngine.register(EVENT_TICK, self.procecssTickEvent)
        self.eventEngine.register(EVENT_RECORDER_DAY, self.handleRecorderDay)
        self.eventEngine.register(EVENT_ALLCONTRACTS, self.processContractsEvent)


    #--------------------------------------------------------------------------
    def processContractsEvent(self, event):
        contract_data = event.dict_['data']
        nl = []

        # 交易的合约代码也应可以用配置文件配置
        tradingContractData = []
        # 打开设置文件
        with open(getJsonPath(self.settingFileName, __file__)) as f:
            drsettings = json.load(f)
            if 'active' in drsettings:
                actives = drsettings['active']
                if actives != {}:
                    for i in actives:
                        tradingContractData.append(i[:-4])
                for ocn in contract_data:
                    contract = ocn.decode()
                    if contract[:1] in tradingContractData or contract[:2] in tradingContractData:
                        nl.append([contract, "CTP"])

                from operator import itemgetter, attrgetter
                filename = 'openInterest.json'
                # 打开持仓量文件
                with open(getJsonPath(filename, __file__)) as f:
                    openinterests = json.load(f)
                    if 'oi' in openinterests:
                        ois = openinterests['oi']
                        for item in tradingContractData:
                            oiarray = []
                            # 获取持仓量列表
                            for oi in ois:
                                if oi[:1] == item or oi[:2] == item:
                                    oiarray.append([oi, ois[oi]])
                            # 排序持仓量列表
                            if oiarray != []:
                                sortedioarray = sorted(oiarray, key=itemgetter(1), reverse=True)
                                if sortedioarray[0][1] / sortedioarray[1][1] > 1.1 and sortedioarray[0][0] > sortedioarray[1][0]:
                                    if actives[item + '.HOT'] != sortedioarray[0][0]:
                                        actives[item + '.HOT'] = sortedioarray[0][0]
        json_data = {'working': True, 'tick': {}, 'bar': nl, 'active': actives}
        d1 = json.dumps(json_data, sort_keys=True, indent=4)

        f = open(os.path.join(os.getcwd(), self.settingFileName), 'w')
        f.write(d1)
        f.close()
        self.loadSetting()

    # #------------------------------------------------------------------------
    # def saveContractMain(self):
    #
    #     from operator import itemgetter, attrgetter
    #     filename = 'openInterest.json'
    #     activefilename = 'active.json'
    #     # 交易的合约代码也应可以用配置文件配置
    #     tradingContractData = []
    #     # 打开设置文件
    #     with open(getJsonPath(activefilename, __file__)) as f:
    #         activejson = json.load(f)
    #         if 'active' in activejson:
    #             actives = activejson['active']
    #             if actives != {}:
    #                 for i in actives:
    #                     tradingContractData.append(i)
    #         activejson['active'] = {}
    #     # 打开持仓量文件
    #     with open(getJsonPath(filename, __file__)) as f:
    #         openinterests = json.load(f)
    #         if 'oi' in openinterests:
    #             ois = openinterests['oi']
    #             for item in tradingContractData:
    #                 oiarray = []
    #                 # 获取持仓量列表
    #                 for oi in ois:
    #                     if oi[:1] == item or oi[:2] == item:
    #                         oiarray.append([oi, ois[oi]])
    #                 # 排序持仓量列表
    #                 if oiarray != []:
    #                     sortedioarray = sorted(oiarray, key=itemgetter(1), reverse=True)
    #                     if sortedioarray[0][1] / sortedioarray[1][1] > 1.1:
    #                         if actives[item] != sortedioarray[0][0]:
    #                             actives[item] = sortedioarray[0][0]
    #             # 把主力合约的代码添加到设置文件中
    #             activejson['active'] = actives
    #             d1 = json.dumps(activejson, sort_keys=True, indent=4)
    #             f = open(os.path.join(os.getcwd(), activefilename), 'w')
    #             f.write(d1)
    #             f.close()




    #--------------------------------------------------------------------------
    ##处理日线数据
    def handleRecorderDay(self, event):
        """从数据库中读取Bar数据,startDate是datetime对象"""

        oi = {}
        for contact_ in self.barSymbolSet:

            time_now = datetime.now()
            if datetime.today().weekday() == 0:
                #周一接上周五的夜盘
                time_yes = time_now + timedelta(-3)
            else:
                time_yes = time_now  + timedelta(-1)
            startDate = datetime(time_yes.year, time_yes.month,time_yes.day,21) #前一天 9点

            d = {'datetime':{'$gte':startDate}}

            barData = self.mainEngine.dbQuery(MINUTE_15_DB_NAME, contact_, d, 'datetime')

            day_bar =None
            for bar in barData:
                # 尚未创建对象
                if not day_bar:
                    day_bar = VtBarData()

                    day_bar.vtSymbol = bar['vtSymbol']
                    day_bar.symbol = bar['symbol']
                    day_bar.exchange = bar['exchange']

                    day_bar.open = bar['open']
                    day_bar.high = bar['high']
                    day_bar.low = bar['low']
                # 累加老K线
                else:
                    day_bar.high = max(day_bar.high, bar['high'])
                    day_bar.low = min(day_bar.low, bar['low'])

                # 通用部分
                day_bar.close = bar['close']
                day_bar.datetime = bar['datetime']
                day_bar.openInterest = bar['openInterest']

                day_bar.volume += int(bar['volume'])

            if day_bar:
                day_bar.datetime = datetime(time_now.year, time_now.month,time_now.day)
                day_bar.date = day_bar.datetime.strftime('%Y%m%d')
                day_bar.time = day_bar.datetime.strftime('%H:%M:%S')


                self.mainEngine.dbInsert(DAILY_DB_NAME, contact_, day_bar.__dict__)

                if contact_ in self.activeSymbolDict:
                    activeSymbol = self.activeSymbolDict[contact_]
                    self.mainEngine.dbInsert(DAILY_DB_NAME, activeSymbol, day_bar.__dict__)

                # 写入持仓量数据
                oi[day_bar.symbol] = day_bar.openInterest

                    # 保存持仓量数据
        filename = 'openInterest.json'
        json_data = {'oi': oi}
        d1 = json.dumps(json_data, sort_keys=True, indent=4)
        f = open(os.path.join(os.getcwd(), filename), 'w')
        f.write(d1)
        f.close()



 
    #----------------------------------------------------------------------
    def insertData(self, dbName, collectionName, data):
        """插入数据到数据库(这里的data可以是VtTickData或者VtBarData)"""
        self.queue.put((dbName, collectionName, data.__dict__))
        
    #----------------------------------------------------------------------
    def run(self):
        """运行插入线程"""
        while self.active:
            try:
                dbName, collectionName, d = self.queue.get(block=True, timeout=1)
                
                # 这里采用MongoDB的update模式更新数据,在记录tick数据时会由于查询
                # 过于频繁,导致CPU占用和硬盘读写过高后系统卡死,因此不建议使用
                #flt = {'datetime': d['datetime']}
                #self.mainEngine.dbUpdate(dbName, collectionName, d, flt, True)
                
                # 使用insert模式更新数据,可能存在时间戳重复的情况,需要用户自行清洗
                try:
                    self.mainEngine.dbInsert(dbName, collectionName, d)
                except DuplicateKeyError:
                    self.writeDrLog(u'键值重复插入失败,报错信息:%s' %traceback.format_exc())
            except Empty:
                pass
            
    #----------------------------------------------------------------------
    def start(self):
        """启动"""
        self.active = True
        self.thread.start()
        
    #----------------------------------------------------------------------
    def stop(self):
        """退出"""
        if self.active:
            self.active = False
            self.thread.join()
        
    #----------------------------------------------------------------------
    def writeDrLog(self, content):
        """快速发出日志事件"""
        log = VtLogData()
        log.logContent = content
        event = Event(type_=EVENT_DATARECORDER_LOG)
        event.dict_['data'] = log
        self.eventEngine.put(event)
Exemplo n.º 5
0
class RmEngine(object):
    """风控引擎"""
    settingFileName = 'RM_setting.json'
    settingFilePath = getJsonPath(settingFileName, __file__)

    name = u'风控模块'

    #----------------------------------------------------------------------
    def __init__(self, mainEngine, eventEngine):
        """Constructor"""
        self.mainEngine = mainEngine
        self.eventEngine = eventEngine

        # 绑定自身到主引擎的风控引擎引用上
        mainEngine.rmEngine = self

        # 是否启动风控
        self.active = False

        # 流控相关
        self.orderFlowCount = EMPTY_INT  # 单位时间内委托计数
        self.orderFlowLimit = EMPTY_INT  # 委托限制
        self.orderFlowClear = EMPTY_INT  # 计数清空时间(秒)
        self.orderFlowTimer = EMPTY_INT  # 计数清空时间计时

        # 单笔委托相关
        self.orderSizeLimit = EMPTY_INT  # 单笔委托最大限制

        # 成交统计相关
        self.tradeCount = EMPTY_INT  # 当日成交合约数量统计
        self.tradeLimit = EMPTY_INT  # 当日成交合约数量限制

        # 单品种撤单统计
        self.orderCancelLimit = EMPTY_INT  # 撤单总次数限制
        self.orderCancelDict = {}  # 单一合约对应撤单次数的字典

        # 活动合约相关
        self.workingOrderLimit = EMPTY_INT  # 活动合约最大限制

        # 保证金相关
        self.marginRatioDict = {}  # 保证金占账户净值比例字典
        self.marginRatioLimit = EMPTY_FLOAT  # 最大比例限制

        self.loadSetting()
        self.registerEvent()

    #----------------------------------------------------------------------
    def loadSetting(self):
        """读取配置"""
        with open(self.settingFilePath) as f:
            d = json.load(f)

            # 设置风控参数
            self.active = d['active']

            self.orderFlowLimit = d['orderFlowLimit']
            self.orderFlowClear = d['orderFlowClear']

            self.orderSizeLimit = d['orderSizeLimit']

            self.tradeLimit = d['tradeLimit']

            self.workingOrderLimit = d['workingOrderLimit']

            self.orderCancelLimit = d['orderCancelLimit']

            self.marginRatioLimit = d['marginRatioLimit']

    #----------------------------------------------------------------------
    def saveSetting(self):
        """保存风控参数"""
        with open(self.settingFilePath, 'w') as f:
            # 保存风控参数
            d = {}

            d['active'] = self.active

            d['orderFlowLimit'] = self.orderFlowLimit
            d['orderFlowClear'] = self.orderFlowClear

            d['orderSizeLimit'] = self.orderSizeLimit

            d['tradeLimit'] = self.tradeLimit

            d['workingOrderLimit'] = self.workingOrderLimit

            d['orderCancelLimit'] = self.orderCancelLimit

            d['marginRatioLimit'] = self.marginRatioLimit

            # 写入json
            jsonD = json.dumps(d, indent=4)
            f.write(jsonD)

    #----------------------------------------------------------------------
    def registerEvent(self):
        """注册事件监听"""
        self.eventEngine.register(EVENT_TRADE, self.updateTrade)
        self.eventEngine.register(EVENT_TIMER, self.updateTimer)
        self.eventEngine.register(EVENT_ORDER, self.updateOrder)
        self.eventEngine.register(EVENT_ACCOUNT, self.updateAccount)

    #----------------------------------------------------------------------
    def updateOrder(self, event):
        """更新成交数据"""
        # 只需要统计撤单成功的委托
        order = event.dict_['data']
        if order.status != STATUS_CANCELLED:
            return

        if order.symbol not in self.orderCancelDict:
            self.orderCancelDict[order.symbol] = 1
        else:
            self.orderCancelDict[order.symbol] += 1

    #----------------------------------------------------------------------
    def updateTrade(self, event):
        """更新成交数据"""
        trade = event.dict_['data']
        self.tradeCount += trade.volume

    #----------------------------------------------------------------------
    def updateTimer(self, event):
        """更新定时器"""
        self.orderFlowTimer += 1

        # 如果计时超过了流控清空的时间间隔,则执行清空
        if self.orderFlowTimer >= self.orderFlowClear:
            self.orderFlowCount = 0
            self.orderFlowTimer = 0

    #----------------------------------------------------------------------
    def updateAccount(self, event):
        """账户资金更新"""
        account = event.dict_['data']

        # 计算保证金占比
        ratio = 0
        if account.balance:
            ratio = account.margin / account.balance

        # 更新到字典中
        self.marginRatioDict[account.gatewayName] = ratio

    #----------------------------------------------------------------------
    def writeRiskLog(self, content):
        """快速发出日志事件"""
        # 发出报警提示音

        if platform.uname() == 'Windows':
            import winsound
            winsound.PlaySound("SystemHand", winsound.SND_ASYNC)

        # 发出日志事件
        log = VtLogData()
        log.logContent = content
        log.gatewayName = self.name
        event = Event(type_=EVENT_LOG)
        event.dict_['data'] = log
        self.eventEngine.put(event)

    #----------------------------------------------------------------------
    def checkRisk(self, orderReq, gatewayName):
        """检查风险"""
        # 如果没有启动风控检查,则直接返回成功
        if not self.active:
            return True

        # 检查委托数量
        if orderReq.volume <= 0:
            self.writeRiskLog(u'委托数量必须大于0')
            return False

        if orderReq.volume > self.orderSizeLimit:
            self.writeRiskLog(u'单笔委托数量%s,超过限制%s' %
                              (orderReq.volume, self.orderSizeLimit))
            return False

        # 检查成交合约量
        if self.tradeCount >= self.tradeLimit:
            self.writeRiskLog(u'今日总成交合约数量%s,超过限制%s' %
                              (self.tradeCount, self.tradeLimit))
            return False

        # 检查流控
        if self.orderFlowCount >= self.orderFlowLimit:
            self.writeRiskLog(u'委托流数量%s,超过限制每%s秒%s' %
                              (self.orderFlowCount, self.orderFlowClear,
                               self.orderFlowLimit))
            return False

        # 检查总活动合约
        workingOrderCount = len(self.mainEngine.getAllWorkingOrders())
        if workingOrderCount >= self.workingOrderLimit:
            self.writeRiskLog(u'当前活动委托数量%s,超过限制%s' %
                              (workingOrderCount, self.workingOrderLimit))
            return False

        # 检查撤单次数
        if orderReq.symbol in self.orderCancelDict and self.orderCancelDict[
                orderReq.symbol] >= self.orderCancelLimit:
            self.writeRiskLog(
                u'当日%s撤单次数%s,超过限制%s' %
                (orderReq.symbol, self.orderCancelDict[orderReq.symbol],
                 self.orderCancelLimit))
            return False

        # 检查保证金比例(只针对开仓委托)
        if orderReq.offset == OFFSET_OPEN and gatewayName in self.marginRatioDict and self.marginRatioDict[
                gatewayName] >= self.marginRatioLimit:
            self.writeRiskLog(u'%s接口保证金占比%s,超过限制%s' %
                              (gatewayName, self.marginRatioDict[gatewayName],
                               self.marginRatioLimit))
            return False

        # 对于通过风控的委托,增加流控计数
        self.orderFlowCount += 1

        return True

    #----------------------------------------------------------------------
    def clearOrderFlowCount(self):
        """清空流控计数"""
        self.orderFlowCount = 0
        self.writeRiskLog(u'清空流控计数')

    #----------------------------------------------------------------------
    def clearTradeCount(self):
        """清空成交数量计数"""
        self.tradeCount = 0
        self.writeRiskLog(u'清空总成交计数')

    #----------------------------------------------------------------------
    def setOrderFlowLimit(self, n):
        """设置流控限制"""
        self.orderFlowLimit = n

    #----------------------------------------------------------------------
    def setOrderFlowClear(self, n):
        """设置流控清空时间"""
        self.orderFlowClear = n

    #----------------------------------------------------------------------
    def setOrderSizeLimit(self, n):
        """设置委托最大限制"""
        self.orderSizeLimit = n

    #----------------------------------------------------------------------
    def setTradeLimit(self, n):
        """设置成交限制"""
        self.tradeLimit = n

    #----------------------------------------------------------------------
    def setWorkingOrderLimit(self, n):
        """设置活动合约限制"""
        self.workingOrderLimit = n

    #----------------------------------------------------------------------
    def setOrderCancelLimit(self, n):
        """设置单合约撤单次数上限"""
        self.orderCancelLimit = n

    #----------------------------------------------------------------------
    def setMarginRatioLimit(self, n):
        """设置保证金比例限制"""
        self.marginRatioLimit = n / 100  # n为百分数,需要除以100

    #----------------------------------------------------------------------
    def switchEngineStatus(self):
        """开关风控引擎"""
        self.active = not self.active

        if self.active:
            self.writeRiskLog(u'风险管理功能启动')
        else:
            self.writeRiskLog(u'风险管理功能停止')

    #----------------------------------------------------------------------
    def stop(self):
        """停止"""
        self.saveSetting()
Exemplo n.º 6
0
class StDataEngine(object):
    """价差数据计算引擎"""
    settingFileName = 'ST_setting.json'
    settingFilePath = getJsonPath(settingFileName, __file__)

    #----------------------------------------------------------------------
    def __init__(self, mainEngine, eventEngine):
        """Constructor"""
        self.mainEngine = mainEngine
        self.eventEngine = eventEngine

        # 腿、价差相关字典
        self.legDict = {}  # vtSymbol:StLeg
        self.spreadDict = OrderedDict()  # name:StSpread
        self.vtSymbolSpreadDict = {}  # vtSymbol:StSpread

        self.registerEvent()

    #----------------------------------------------------------------------
    def loadSetting(self):
        """加载配置"""
        try:
            with open(self.settingFilePath) as f:
                l = json.load(f)

                for setting in l:
                    result, msg = self.createSpread(setting)
                    self.writeLog(msg)

                self.writeLog(u'价差配置加载完成')
        except:
            content = u'价差配置加载出错,原因:' + traceback.format_exc()
            self.writeLog(content)

    #----------------------------------------------------------------------
    def saveSetting(self):
        """保存配置"""
        with open(self.settingFilePath) as f:
            pass

    #----------------------------------------------------------------------
    def createSpread(self, setting):
        """创建价差"""
        result = False
        msg = ''

        # 检查价差重名
        if setting['name'] in self.spreadDict:
            msg = u'%s价差存在重名' % setting['name']
            return result, msg

        # 检查腿是否已使用
        l = []
        l.append(setting['activeLeg']['vtSymbol'])
        for d in setting['passiveLegs']:
            l.append(d['vtSymbol'])

        for vtSymbol in l:
            if vtSymbol in self.vtSymbolSpreadDict:
                existingSpread = self.vtSymbolSpreadDict[vtSymbol]
                msg = u'%s合约已经存在于%s价差中' % (vtSymbol, existingSpread.name)
                return result, msg

        # 创建价差
        spread = StSpread()
        spread.name = setting['name']
        self.spreadDict[spread.name] = spread

        # 创建主动腿
        activeSetting = setting['activeLeg']

        activeLeg = StLeg()
        activeLeg.vtSymbol = str(activeSetting['vtSymbol'])
        activeLeg.ratio = float(activeSetting['ratio'])
        activeLeg.multiplier = float(activeSetting['multiplier'])
        activeLeg.payup = int(activeSetting['payup'])

        spread.addActiveLeg(activeLeg)
        self.legDict[activeLeg.vtSymbol] = activeLeg
        self.vtSymbolSpreadDict[activeLeg.vtSymbol] = spread

        self.subscribeMarketData(activeLeg.vtSymbol)

        # 创建被动腿
        passiveSettingList = setting['passiveLegs']
        passiveLegList = []

        for d in passiveSettingList:
            passiveLeg = StLeg()
            passiveLeg.vtSymbol = str(d['vtSymbol'])
            passiveLeg.ratio = float(d['ratio'])
            passiveLeg.multiplier = float(d['multiplier'])
            passiveLeg.payup = int(d['payup'])

            spread.addPassiveLeg(passiveLeg)
            self.legDict[passiveLeg.vtSymbol] = passiveLeg
            self.vtSymbolSpreadDict[passiveLeg.vtSymbol] = spread

            self.subscribeMarketData(passiveLeg.vtSymbol)

        # 初始化价差
        spread.initSpread()

        self.putSpreadTickEvent(spread)
        self.putSpreadPosEvent(spread)

        # 返回结果
        result = True
        msg = u'%s价差创建成功' % spread.name
        return result, msg

    #----------------------------------------------------------------------
    def processTickEvent(self, event):
        """处理行情推送"""
        # 检查行情是否需要处理
        tick = event.dict_['data']
        if tick.vtSymbol not in self.legDict:
            return

        # 更新腿价格
        leg = self.legDict[tick.vtSymbol]
        leg.bidPrice = tick.bidPrice1
        leg.askPrice = tick.askPrice1
        leg.bidVolume = tick.bidVolume1
        leg.askVolume = tick.askVolume1

        # 更新价差价格
        spread = self.vtSymbolSpreadDict[tick.vtSymbol]
        spread.calculatePrice()

        # 发出事件
        self.putSpreadTickEvent(spread)

    #----------------------------------------------------------------------
    def putSpreadTickEvent(self, spread):
        """发出价差行情更新事件"""
        event1 = Event(EVENT_SPREADTRADING_TICK + spread.name)
        event1.dict_['data'] = spread
        self.eventEngine.put(event1)

        event2 = Event(EVENT_SPREADTRADING_TICK)
        event2.dict_['data'] = spread
        self.eventEngine.put(event2)

    #----------------------------------------------------------------------
    def processTradeEvent(self, event):
        """处理成交推送"""
        # 检查成交是否需要处理
        trade = event.dict_['data']
        if trade.vtSymbol not in self.legDict:
            return

        # 更新腿持仓
        leg = self.legDict[trade.vtSymbol]
        direction = trade.direction
        offset = trade.offset

        if direction == DIRECTION_LONG:
            if offset == OFFSET_OPEN:
                leg.longPos += trade.volume
            else:
                leg.shortPos -= trade.volume
        else:
            if offset == OFFSET_OPEN:
                leg.shortPos += trade.volume
            else:
                leg.longPos -= trade.volume
        leg.netPos = leg.longPos - leg.shortPos

        # 更新价差持仓
        spread = self.vtSymbolSpreadDict[trade.vtSymbol]
        spread.calculatePos()

        # 推送价差持仓更新
        self.putSpreadPosEvent(spread)

    #----------------------------------------------------------------------
    def processPosEvent(self, event):
        """处理持仓推送"""
        # 检查持仓是否需要处理
        pos = event.dict_['data']
        if pos.vtSymbol not in self.legDict:
            return

        # 更新腿持仓
        leg = self.legDict[pos.vtSymbol]
        direction = pos.direction

        if direction == DIRECTION_LONG:
            leg.longPos = pos.position
        else:
            leg.shortPos = pos.position
        leg.netPos = leg.longPos - leg.shortPos

        # 更新价差持仓
        spread = self.vtSymbolSpreadDict[pos.vtSymbol]
        spread.calculatePos()

        # 推送价差持仓更新
        self.putSpreadPosEvent(spread)

    #----------------------------------------------------------------------
    def putSpreadPosEvent(self, spread):
        """发出价差持仓事件"""
        event1 = Event(EVENT_SPREADTRADING_POS + spread.name)
        event1.dict_['data'] = spread
        self.eventEngine.put(event1)

        event2 = Event(EVENT_SPREADTRADING_POS)
        event2.dict_['data'] = spread
        self.eventEngine.put(event2)

    #----------------------------------------------------------------------
    def registerEvent(self):
        """"""
        self.eventEngine.register(EVENT_TICK, self.processTickEvent)
        self.eventEngine.register(EVENT_TRADE, self.processTradeEvent)
        self.eventEngine.register(EVENT_POSITION, self.processPosEvent)

    #----------------------------------------------------------------------
    def subscribeMarketData(self, vtSymbol):
        """订阅行情"""
        contract = self.mainEngine.getContract(vtSymbol)
        if not contract:
            self.writeLog(u'订阅行情失败,找不到该合约%s' % vtSymbol)
            return

        req = VtSubscribeReq()
        req.symbol = contract.symbol
        req.exchange = contract.exchange

        self.mainEngine.subscribe(req, contract.gatewayName)

    #----------------------------------------------------------------------
    def writeLog(self, content):
        """发出日志"""
        log = VtLogData()
        log.logContent = content

        event = Event(EVENT_SPREADTRADING_LOG)
        event.dict_['data'] = log
        self.eventEngine.put(event)

    #----------------------------------------------------------------------
    def getAllSpreads(self):
        """获取所有的价差"""
        return self.spreadDict.values()