Esempio n. 1
0
    def _startAccountManager(self, strategyCls):
        """
            启动策略的账户管理者
        """
        if strategyCls.broker is None:
            return True

        if strategyCls.broker not in self._accountManagers:
            # 实例化券商账户管理者
            accountManager = self.accountManagerMap[strategyCls.broker](self._eventEngine, self._info)

            # 账户管理的开盘前准备
            accountManager.onOpen(datetime.now().strftime("%Y-%m-%d"))

            self._accountManagers[strategyCls.broker] = accountManager

            # 登录策略的实盘交易接口
            event = DyEvent(DyEventType.stockLogin)
            event.data['broker'] = strategyCls.broker

            self._eventEngine.put(event)

        self._info.print('股票CTA引擎: 账号[{0}]绑定策略[{1}]'.format(self.accountManagerMap[strategyCls.broker].brokerName, strategyCls.chName), DyLogData.ind1)

        return True
    def _newEntrust(self, type, datetime, strategyCls, code, name, price, volume):
        """
            生成新的委托,并向交易接口发送委托事件
        """
        # check if there's same code and type of entrust not done for different strategy
        entrusts = self._curEntrusts.get(code)
        if entrusts is not None:
            for entrust in entrusts:
                if (not entrust.isDone()) and entrust.type == type and entrust.strategyCls != strategyCls:
                    self._info.print('{}: 策略[{}]委托失败({}, {}, {}, 价格={}, 数量={}): 策略[{}]有未完成的委托'.format(self.__class__.__name__, strategyCls.chName,
                                                                                                   code, name, type, price, volume,
                                                                                                   entrust.strategyCls.chName), DyLogData.warning)
                                                                                                               
                    return None

        # create a new entrust
        curEntrustCount = self.newCurEntrustCount()

        entrust = DyStockEntrust(datetime, type, code, name, price, volume)
        entrust.dyEntrustId = '{0}.{1}_{2}'.format(self.broker, self._curTDay, curEntrustCount)
        entrust.strategyCls = strategyCls

        # add into 当日委托
        self._curEntrusts.setdefault(code, [])
        self._curEntrusts[code].append(copy.copy(entrust))

        # put buy/sell event
        eventType = DyEventType.stockBuy if type == DyStockOpType.buy else DyEventType.stockSell
        event = DyEvent(eventType + self.broker)
        event.data = copy.copy(entrust)

        self._eventEngine.put(event)

        return entrust
    def cancel(self, cancelEntrust):
        """
            取消委托
        """
        # find corresponding entrust
        entrusts = self._curEntrusts.get(cancelEntrust.code)
        if entrusts is None:
            return False

        for entrust in entrusts:
            if entrust.dyEntrustId == cancelEntrust.dyEntrustId:
                break
        else:
            return False

        if entrust.isDone():
            self._info.print('{}: 撤销委托失败: {}'.format(self.__class__.__name__, entrust.__dict__, DyLogData.warning))
            return False

        if entrust.brokerEntrustId is None:
            self._curWorkingCancelEntrusts.append(entrust)
            return True

        # put cancel event
        event = DyEvent(DyEventType.stockCancel + self.broker)
        event.data = copy.copy(entrust)

        self._eventEngine.put(event)

        return True
Esempio n. 4
0
    def putStockMarketStrengthUpdateEvent(self, strategyCls, time, marketStrengthInfo):
        event = DyEvent(DyEventType.stockMarketStrengthUpdate)
        event.data['class'] = strategyCls
        event.data['time'] = time
        event.data['data'] = marketStrengthInfo

        self._eventEngine.put(event)
Esempio n. 5
0
    def _stopAccountManager(self, strategyCls, oneKeyHangUp=False):
        """
            停止策略的账户管理者
            @oneKeyHangUp: 一键挂机导致的
        """
        if strategyCls.broker is None:
            return

        if strategyCls.broker not in self._accountManagers:
            return

        self._info.print('股票CTA引擎: 账号[{0}]解除绑定策略[{1}]'.format(self.accountManagerMap[strategyCls.broker].brokerName, strategyCls.chName), DyLogData.ind1)

        # check if other running strategies use the same account manager
        for strategy, _ in self._strategies.values():
            if strategy.name != strategyCls.name and \
                strategyCls.broker == strategy.broker and \
                strategy.state.isState(DyStockStrategyState.running):
                return

        # 退出策略的实盘交易接口
        event = DyEvent(DyEventType.stockLogout)
        event.data['broker'] = strategyCls.broker
        event.data['oneKeyHangUp'] = oneKeyHangUp

        self._eventEngine.put(event)

        # 销毁券商账户管理者
        self._accountManagers[strategyCls.broker].exit()
        del self._accountManagers[strategyCls.broker]
Esempio n. 6
0
    def print(self, description, type=DyLogData.info):
        if not self._enabled and type != DyLogData.error and type != DyLogData.warning: return

        event = DyEvent(DyEventType.subLog_ + '_' + str(self._paramGroupNo) + str(self._period))
        event.data = DyLogData(description, type)

        self._outQueue.put(event)
Esempio n. 7
0
    def _stockMarketTicksHandler(self, event):
        if self._pushAction is None:
            return

        ticks = event.data

        strategyData = self._latestStrategyData.get('DyST_IndexMeanReversion')
        if strategyData is None:
            return

        strategyCls = strategyData[0]

        tick = ticks.get(strategyCls.targetCode)
        if tick is None:
            return

        # event
        event = DyEvent(DyEventType.stockStrategyManualBuy)
        event.data['class'] = strategyCls
        event.data['tick'] = tick
        event.data['volume'] = 100

        event.data['price'] = round(tick.preClose * 0.92, 3)

        self._eventEngine.put(event)

        self._info.print('通过WX委托买入{0}, {1}股, 价格{2}'.format(tick.name, event.data['volume'], event.data['price']), DyLogData.ind1)

        # sent to WX
        self._sendWx('委托买入{0}, {1}股, 价格{2}'.format(tick.name, event.data['volume'], event.data['price']))

        # clear
        self._pushAction = None
        self._eventEngine.unregister(DyEventType.stockMarketTicks, self._stockMarketTicksHandler, DyStockTradeEventHandType.wxEngine)
    def _stockStrategyDataPrepareHandler(self, event):
        date = event.data['date']
        classes = event.data['classes']

        if self._strategyClsCount == 0: # new start
            self._strategyClsCount = len(classes)
        else:
            if self._isStopped:
                # reset
                self._strategyClsCount = 0
                self._isStopped = False

                # send stopAck
                self._eventEngine.put(DyEvent(DyEventType.stopAck))

                return

        # only process the first one
        self._stockStrategyDataPrepare(date, classes[0])
        self._stockStrategyPosDataPrepare(date, classes[0])
        self._stockStrategySimuTraderPosCloseUpdate(date, classes[0])

        self._strategyClsCount -= 1

        # check if finish or not
        if self._strategyClsCount == 0: # finish
            self._eventEngine.put(DyEvent(DyEventType.finish))

        else:
            # send left
            event = DyEvent(DyEventType.stockStrategyDataPrepare)
            event.data['date'] = date
            event.data['classes'] = classes[1:]

            self._eventEngine.put(event)
    def runStrategy(self, strategyCls, paramters):
        self._info.print("开始准备运行选股策略: {0}".format(strategyCls.chName), DyLogData.ind)
        self._info.initProgress()

        # init
        self._init()

        # create strategy instance
        self._strategy = strategyCls(paramters, self._info)

        # run
        if self._run():
            # ack
            event = DyEvent(DyEventType.stockSelectStrategySelectAck)
            event.data['class'] = strategyCls
            event.data['result'] = self._result
            event.data['baseDate'] = self._strategy.baseDate

            self._eventEngine.put(event)

            # finish
            self._eventEngine.put(DyEvent(DyEventType.finish))

            ret = True
        else:
            # fail
            self._eventEngine.put(DyEvent(DyEventType.fail))

            ret = False

        return ret
Esempio n. 10
0
    def _updateStockHistDays_Handler(self, event):
        # unpack
        codes = event.data

        # check stop flag firstly
        if self._isStopped:
            self._printCount()
            self._eventEngine.put(DyEvent(DyEventType.stopAck))
            return

        # update one code each time
        code = sorted(codes)[0]
        self._updateOneCode(code, codes[code])

        # update progress
        self._progress.update()

        # delete updated code
        del codes[code]

        if not codes: # all codes are are updated
            self._printCount()
            self._eventEngine.put(DyEvent(DyEventType.finish))
            return

        # send for next updating
        event = DyEvent(DyEventType.updateStockHistDays_)
        event.data = codes

        self._eventEngine.put(event)
def dyStockSelectRegressionEngineProcess(outQueue, inQueue, tradeDays, strategy, codes, histDaysDataSource):
    strategyCls = strategy['class']
    parameters = strategy['param']

    DyStockCommon.defaultHistDaysDataSource = histDaysDataSource

    dummyEventEngine = DyDummyEventEngine()
    queueInfo = DyQueueInfo(outQueue)

    selectEngine = DyStockSelectSelectEngine(dummyEventEngine, queueInfo, False)
    selectEngine.setTestedStocks(codes)

    for day in tradeDays:
        try:
            event = inQueue.get_nowait()
        except queue.Empty:
            pass

        parameters['基准日期'] = day

        if selectEngine.runStrategy(strategyCls, parameters):
            event = DyEvent(DyEventType.stockSelectStrategyRegressionAck)
            event.data['class'] = strategyCls
            event.data['period'] = [tradeDays[0], tradeDays[-1]]
            event.data['day'] = day
            event.data['result'] = selectEngine.result

            outQueue.put(event)
        else:
            queueInfo.print('回归选股策略失败:{0}, 周期[{1}, {2}], 基准日期{3}'.format(strategyCls.chName, tradeDays[0], tradeDays[-1], day), DyLogData.error)
Esempio n. 12
0
    def _histDaysMannualUpdate(self):
        if self._histDaysMannualUpdateAction.text() == '停止':
            self._mainEngine._info.print('停止股票(指数)历史日线数据手动更新...', DyLogData.ind)

            # change UI
            self._stopRunningMutexAction()

            event = DyEvent(DyEventType.stopUpdateStockHistDaysReq)
            self._mainEngine.eventEngine.put(event)

        else: # 开始手动更新
            data = {}
            if DyStockDataHistDaysManualUpdateDlg(data, self).exec_():
                self._mainEngine._info.print('开始股票(指数,基金)历史日线数据手动更新[{0}, {1}]...'.format(data['startDate'], data['endDate']), DyLogData.ind)

                # change UI
                self._startRunningMutexAction(self._histDaysMannualUpdateAction)

                event = DyEvent(DyEventType.updateStockHistDays)
                event.data = data
                event.data['codes'] = DyStockCommon.getDyStockCodes(event.data['codes'])

                # 是否要更新指数的日线数据
                if event.data['index']:
                    if event.data['codes'] is None:
                        event.data['codes'] = []

                    event.data['codes'].extend(list(DyStockCommon.indexes))

                self._mainEngine.eventEngine.put(event)
Esempio n. 13
0
    def handle_msg_all(self, msg):
        if msg['user']['name'] == 'self':

            event = DyEvent(DyEventType.wxQueryStockStrategy)
            event.data = msg['content']['data']

            self._eventEngine.put(event)
Esempio n. 14
0
    def _putTickEvent(self, ctaTickDatas):
        if not ctaTickDatas:
            return

        event = DyEvent(DyEventType.stockMarketTicks)
        event.data = ctaTickDatas

        self._eventEngine.put(event)
Esempio n. 15
0
    def _updateStrategyDeal(self, deal):
        """
            向UI推送策略成交事件
        """
        event = DyEvent(DyEventType.stockStrategyDealsUpdate + deal.strategyCls.name)
        event.data = [deal]

        self._eventEngine.put(event)
Esempio n. 16
0
    def progressSingle(self, percent):
        if self._progressSingle != percent:
            self._progressSingle = percent

            event = DyEvent(DyEventType.progressSingle)
            event.data = percent

            self._eventEngine.put(event)
Esempio n. 17
0
    def progressTotal(self, percent):
        if self._progressTotal != percent:
            self._progressTotal = percent

            event = DyEvent(DyEventType.progressTotal)
            event.data = percent

            self._eventEngine.put(event)
Esempio n. 18
0
    def _stockPosSyncFromBrokerHandler(self, event):
        """
            收到来自券商接口的持仓同步事件
            !!!如果交易不是通过策略,或者持仓同步不是在开盘时,可能会导致信息不一致或者错误。
        """
        # unpack
        header = event.data['header']
        rows = event.data['rows']

        # it's synchronized from broker
        self._curPosSyncData = {}

        for data in rows:
            # unpack from 券商接口持仓数据
            code = DyStockCommon.getDyStockCode(data[header.index(self.headerNameMap['position']['code'])])
            name = data[header.index(self.headerNameMap['position']['name'])]

            totalVolume = float(data[header.index(self.headerNameMap['position']['totalVolume'])])
            availVolume = float(data[header.index(self.headerNameMap['position']['availVolume'])])

            price = float(data[header.index(self.headerNameMap['position']['price'])])
            cost = float(data[header.index(self.headerNameMap['position']['cost'])])

            # get position
            pos = self._curPos.get(code)
            if pos is None:
                continue

            # set sync data firstly
            self._curPosSyncData[code] = {'volumeAdjFactor': totalVolume/pos.totalVolume,
                                          'priceAdjFactor': pos.cost/cost,
                                          'cost': cost
                                          }

            # syn with positions from broker
            pos.price = price
            pos.cost = cost

            pos.totalVolume = totalVolume
            pos.availVolume = availVolume

            pos.priceAdjFactor = self._curPosSyncData[code]['priceAdjFactor']
            pos.volumeAdjFactor = self._curPosSyncData[code]['volumeAdjFactor']

            if pos.priceAdjFactor != 1:
                pos.xrd = True

            pos.sync = True

        # 发送行情监控事件
        self._putStockMarketMonitorEvent()

        # 发送股票持仓同步事件
        event = DyEvent(DyEventType.stockPosSyncFromAccountManager)
        event.data['broker'] = self.broker
        event.data['data'] = self._curPosSyncData

        self._eventEngine.put(event)
Esempio n. 19
0
    def _updateStrategyEntrust(self, entrust):
        """
            向UI推送策略委托事件
        """
        # because only one entrust, no need OrderedDict
        event = DyEvent(DyEventType.stockStrategyEntrustsUpdate + entrust.strategyCls.name)
        event.data = {entrust.dyEntrustId: copy.copy(entrust)}

        self._eventEngine.put(event)
Esempio n. 20
0
    def _updateStrategyPos(self, strategy):
        """
            向UI推送策略持仓更新事件
        """
        # 持仓
        event = DyEvent(DyEventType.stockStrategyPosUpdate + strategy.name)
        event.data = copy.deepcopy(strategy.curPos)

        self._eventEngine.put(event)
Esempio n. 21
0
    def _startStockCtaStrategyHandler(self, event):
        """ 启动策略, 创建唯一策略实例 """
        strategyCls = event.data['class']
        state = event.data['state']

        self._info.print('开始启动策略: {0}, 状态: {1},...'.format(strategyCls.chName, state.state), DyLogData.ind)

        # 是否是唯一策略实例
        if strategyCls.name in self._strategies:
            self._info.print('重复启动策略: {0}'.format(strategyCls.chName), DyLogData.error)
            return

        # !!!It's tricky for live trading but not accurate.
        sleep(1) # sleep so that UI related dynamic windows can be created firstly.

        # 实例化策略
        strategy = strategyCls(self, self._info, state)

        # 策略开盘前初始化
        if not strategy.onOpen(datetime.now().strftime("%Y-%m-%d"), strategy.onOpenCodes()):
            self._info.print('策略: {0}启动失败'.format(strategyCls.chName), DyLogData.error)
            return

        # 启动策略的账户管理
        if state.isState(DyStockStrategyState.running):
            if strategy.broker is not None: # Strategy has configured the broker
                if not self._startAccountManager(strategyCls):
                    return

                # 从券商管理类同步策略持仓
                self._accountManagers[strategy.broker].syncStrategyPos(strategy)
        
        # 获取策略要监控的股票池
        monitoredStocks = strategy.onMonitor()

        # 添加到策略字典
        self._strategies[strategyCls.name] = (strategy, DyStockMarketFilter(monitoredStocks))

        # 添加到bar聚合字典
        if 'bar' in strategyCls.liveMode:
            if strategyCls.liveMode not in self._barAggs:
                self._barAggs[strategyCls.liveMode] = DyStockCtaBarAggFast(strategyCls.liveMode, monitoredStocks)
            else:
                self._barAggs[strategyCls.liveMode].add(monitoredStocks)

        # 向股票市场发送监控的股票池
        monitoredStocks = monitoredStocks + [DyStockCommon.etf300, DyStockCommon.etf500] # always add ETF300 and ETF500 for 大盘参考。主要原因是历史分笔数据没有指数的,所以只能用ETF替代。
        if monitoredStocks:
            event = DyEvent(DyEventType.stockMarketMonitor)
            event.data = monitoredStocks

            self._eventEngine.put(event)

        # 向UI推送策略的账户相关事件
        self._updateStrategyAccount(strategy)

        self._info.print('策略: {0}启动成功'.format(strategyCls.chName), DyLogData.ind)
Esempio n. 22
0
    def _stockPositionUpdateHandler(self, event):
        """
            收到来自券商接口的账户持仓更新事件
        """
        # unpack
        header = event.data['header']
        rows = event.data['rows']

        # 先前持仓代码
        codes = list(self._curPos)

        for data in rows:
            # unpack from 券商接口持仓数据
            code = DyStockCommon.getDyStockCode(data[header.index(self.headerNameMap['position']['code'])])
            name = data[header.index(self.headerNameMap['position']['name'])]

            totalVolume = float(data[header.index(self.headerNameMap['position']['totalVolume'])])
            availVolume = float(data[header.index(self.headerNameMap['position']['availVolume'])])

            price = float(data[header.index(self.headerNameMap['position']['price'])])
            cost = float(data[header.index(self.headerNameMap['position']['cost'])])

            # get position
            if code in self._curPos:
                pos = self._curPos[code]
                codes.remove(code)
            else:
                # new pos, we just take time now without accuracy
                if totalVolume > 0:
                    pos = DyStockPos(datetime.now(), None, code, name, price, totalVolume, 0)
                    pos.sync = True
                else:
                    continue

            # syn with positions from broker
            pos.price = price
            pos.cost = cost

            pos.totalVolume = totalVolume
            pos.availVolume = availVolume

            # write back
            self._curPos[code] = pos

        # 删除不在券商接口数据里的持仓
        for code in codes:
            del self._curPos[code]

        # 发送行情监控事件
        self._putStockMarketMonitorEvent()

        # 发送券商账户股票持仓更新事件
        event = DyEvent(DyEventType.stockOnPos)
        event.data['broker'] = self.broker
        event.data['pos'] = copy.deepcopy(self._curPos)

        self._eventEngine.put(event)
    def _focusAnalysisAct(self):
        data = {}
        if not DyDateDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'focusAnalysis'

        self._mainEngine.eventEngine.put(event)
Esempio n. 24
0
    def putEvent(self, type, data):
        """
            推送一个事件到事件引擎
            @type: 事件类型
            @data: 事件数据,一般为dict
        """
        event = DyEvent(type)
        event.data = data

        self._eventEngine.put(event)
Esempio n. 25
0
    def uncheckAll(self, strategyCls, eventEngine):
        if self._state is None:
            return

        self._state = None

        event = DyEvent(DyEventType.stopStockCtaStrategy)
        event.data['class'] = strategyCls

        eventEngine.put(event)
Esempio n. 26
0
    def progressTotal(self, percent):
        if not self._enabled: return

        if self._progressTotal != percent:
            self._progressTotal = percent

            event = DyEvent(DyEventType.subProgressTotal_ + '_' + str(self._paramGroupNo) + str(self._period))
            event.data = percent

            self._outQueue.put(event)
    def _limitUpStatsAct(self):
        data = {}
        if not DyDateDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'limitUpStats'

        self._mainEngine.eventEngine.put(event)
    def _bBandsStats(self):
        data = {}
        if not DyStockSelectBBandsStatsDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'bBandsStats'

        self._mainEngine.eventEngine.put(event)
    def _jaccardIndexAct(self):
        data = {}
        if not DyStockSelectJaccardIndexDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'jaccardIndex'

        self._mainEngine.eventEngine.put(event)
    def _highLowDistAct(self):
        data = {}
        if not DyDateDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'highLowDist'

        self._mainEngine.eventEngine.put(event)
class DyStockTradeCapitalWidget(DyTableWidget):
    """
        股票交易账户资金状况窗口
        !!!券商接口推送的原始数据
    """
    signal = QtCore.pyqtSignal(type(DyEvent()))


    def __init__(self, eventEngine, broker):
        super().__init__(None, True, False)

        self._eventEngine = eventEngine
        self._broker = broker

        self._headerSet = False

        self._registerEvent()

    def _signalEmitWrapper(self, event):
        self.signal.emit(event)

    def _registerEvent(self):
        self.signal.connect(self._stockCapitalUpdateHandler)
        self._eventEngine.register(DyEventType.stockCapitalUpdate + self._broker, self._signalEmitWrapper)
        self._eventEngine.register(DyEventType.stockCapitalTickUpdate + self._broker, self._signalEmitWrapper)

    def _unregisterEvent(self):
        self.signal.disconnect(self._stockCapitalUpdateHandler)
        self._eventEngine.unregister(DyEventType.stockCapitalUpdate + self._broker, self._signalEmitWrapper)
        self._eventEngine.unregister(DyEventType.stockCapitalTickUpdate + self._broker, self._signalEmitWrapper)
        
    def _stockCapitalUpdateHandler(self, event):
        header = event.data['header']
        rows = event.data['rows']

        if not self._headerSet:
            self.setColNames(header)
            self._headerSet = True

        # strip
        for row in rows:
            if isinstance(row[0], str):
                row[0] = row[0].strip()

        self[0] = rows[0]

    def closeEvent(self, event):
        self._unregisterEvent()

        return super().closeEvent(event)
    def _histTicksVerifyAct(self):
        if self._histTicksVerifyAction.text() == '停止':
            self._mainEngine._info.print('停止股票(基金)历史分笔数据校验...', DyLogData.ind)

            # change UI
            self._stopRunningMutexAction()

            event = DyEvent(DyEventType.stopVerifyStockHistTicksReq)
            self._mainEngine.eventEngine.put(event)

        else:  # 开始数据校验
            data = {}
            if DyStockDataHistTicksVerifyDlg(data, self).exec_():
                self._mainEngine._info.print('开始股票(基金)历史分笔数据校验...',
                                             DyLogData.ind)

                # change UI
                self._startRunningMutexAction(self._histTicksVerifyAction)

                event = DyEvent(DyEventType.verifyStockHistTicks)
                event.data = data

                self._mainEngine.eventEngine.put(event)
    def _strategyDataPrepare(self):
        if self._strategyDataPrepareAction.text() == '停止':
            self._mainEngine._info.print('停止实盘策略准备数据和持仓准备数据...', DyLogData.ind)

            # change UI
            self._stopRunningMutexAction()

            event = DyEvent(DyEventType.stopStockStrategyDataPrepareReq)
            self._mainEngine.eventEngine.put(event)

        else:  # 开始策略准备数据
            data = {}
            if DyStockDataStrategyDataPrepareDlg(data, self).exec_():
                self._mainEngine._info.print('开始实盘策略准备数据和持仓准备数据...',
                                             DyLogData.ind)

                # change UI
                self._startRunningMutexAction(self._strategyDataPrepareAction)

                event = DyEvent(DyEventType.stockStrategyDataPrepare)
                event.data = data

                self._mainEngine.eventEngine.put(event)
Esempio n. 34
0
    def putStockMarketMonitorUiEvent(self, strategyCls, data=None, newData=False, op=None, signalDetails=None, datetime_=None):
        """
            触发策略行情监控事件(通常用于通知GUI更新)
            @strategyCls: strategy class
            @data: 策略显示数据, [[]]
            @newData: True-策略显示数据是全新的数据,False-只是更新策略显示数据
            @op: 策略操作数据, [[]]。
                 若策略是运行状态(实盘状态)并且绑定券商账户,则op是实盘操作。若策略是监控状态或者没有绑定券商账户,则op就是非实盘操作。
                 其实非实盘时,可以不向UI推送操作,这么做只是为了非真正实盘时,能看到策略的操作明细。
                 信号明细和操作明细是不同的,有信号未必就会有操作,因为实盘时的操作牵涉到风控和资金管理。
            @signalDetails: 策略信号明细数据, [[]]
            @datetime_: 行情数据时有效
        """
        event = DyEvent(DyEventType.stockMarketMonitorUi + strategyCls.name)

        # data
        if data:
            event.data['data'] = {'data': data,
                                  'new': newData,
                                  'datetime': datetime_
                                  }

        # indication
        ind = {}
        if op:
            ind['op'] = op

        if signalDetails:
            ind['signalDetails'] = signalDetails

        if ind:
            event.data['ind'] = ind

        # put event
        if event.data:
            event.data['class'] = strategyCls
            self._eventEngine.put(event)
Esempio n. 35
0
    def _autoUpdate(self):
        # get latest date from DB
        latestDate = self._commonEngine.getLatestDateInDb()

        if latestDate is None:
            self._info.print("数据库里没有日线数据", DyLogData.error)
            self._eventEngine.put(DyEvent(DyEventType.fail))
            return

        # 贪婪法获得最大的更新结束日期
        endDate = datetime.now().strftime("%Y-%m-%d")

        # check if now is after trading time
        ret = self._gateway.isNowAfterTradingTime()

        if ret is None:  # error
            self._eventEngine.put(DyEvent(DyEventType.fail))
            return

        if ret is False:  # now is trade day and before end of trading time
            self._info.print("今天是交易日, 请18:00后更新今日日线数据", DyLogData.error)
            self._eventEngine.put(DyEvent(DyEventType.fail))
            return

        startDate = DyTime.getDateStr(latestDate,
                                      1)  # next date after latest date in DB

        # compare dates
        if endDate < startDate:
            # update progress UI
            self._progress.init(0)

            self._info.print("数据库日线数据已经是最新", DyLogData.ind)
            self._eventEngine.put(DyEvent(DyEventType.finish))
            return

        self._update(startDate, endDate, DyStockDataCommon.dayIndicators)
Esempio n. 36
0
class DyStockTradeCurDealsWidget(DyTableWidget):
    """ 股票交易账户当日成交窗口 """

    signal = QtCore.pyqtSignal(type(DyEvent()))

    def __init__(self, eventEngine, broker):
        super().__init__(None, True, False, floatRound=3)

        self._eventEngine = eventEngine
        self._broker = broker

        self._headerSet = False

        self._registerEvent()

    def _signalEmitWrapper(self, event):
        """ !!!Note: The value of signal.emit will always be changed each time you getting.
        """
        self.signal.emit(event)

    def _registerEvent(self):
        self.signal.connect(self._stockCurDealsUpdateHandler)
        self._eventEngine.register(
            DyEventType.stockCurDealsUpdate + self._broker,
            self._signalEmitWrapper)

    def _unregisterEvent(self):
        self.signal.disconnect(self._stockCurDealsUpdateHandler)
        self._eventEngine.unregister(
            DyEventType.stockCurDealsUpdate + self._broker,
            self._signalEmitWrapper)

    def _stockCurDealsUpdateHandler(self, event):
        header = event.data['header']
        rows = event.data['rows']

        if not self._headerSet:
            self.setColNames(header)
            self._headerSet = True

        self.fastAppendRows(rows, new=True)

        self.setItemsForeground(range(self.rowCount()),
                                (('买入', Qt.red), ('卖出', Qt.darkGreen)))

    def closeEvent(self, event):
        self._unregisterEvent()

        return super().closeEvent(event)
Esempio n. 37
0
    def _histTicksDataSourceAct(self):
        self._curHistTicksDataSourceAction.setChecked(False)

        # get triggered action
        for action in self._histTicksDataSourceMenu.actions():
            if action.isChecked():
                # 设置历史分笔数据源
                dataSource = action.text()

                # set default
                DyStockDataCommon.defaultHistTicksDataSource = dataSource

                event = DyEvent(DyEventType.updateHistTicksDataSource)
                event.data = dataSource

                self._mainEngine.eventEngine.put(event)

                self._curHistTicksDataSourceAction = action
                self._histTicksDataSourceMenuAction.setText(
                    '历史分笔数据源:{0}'.format(dataSource))
                break

        if not self._curHistTicksDataSourceAction.isChecked():
            self._curHistTicksDataSourceAction.setChecked(True)
Esempio n. 38
0
    def _newEntrust(self, type, datetime, strategyCls, code, name, price,
                    volume):
        """
            生成新的委托,并向交易接口发送委托事件
        """
        if not self._canNewEntrust(type, datetime, strategyCls, code, name,
                                   price, volume):
            return None

        # create a new entrust
        curEntrustCount = self.newCurEntrustCount()

        entrust = DyStockEntrust(datetime, type, code, name, price, volume)
        entrust.dyEntrustId = '{0}.{1}_{2}'.format(self.broker, self._curTDay,
                                                   curEntrustCount)
        entrust.strategyCls = strategyCls

        # check price correct or not
        # 实盘发现股票的委托价超过小数点两位
        if entrust.code in DyStockCommon.funds:
            entrust.price = round(entrust.price, 3)
        else:
            entrust.price = round(entrust.price, 2)

        # add into 当日委托
        self._curEntrusts.setdefault(code, [])
        self._curEntrusts[code].append(copy.copy(entrust))

        # put buy/sell event
        eventType = DyEventType.stockBuy if type == DyStockOpType.buy else DyEventType.stockSell
        event = DyEvent(eventType + self.broker)
        event.data = copy.copy(entrust)

        self._eventEngine.put(event)

        return entrust
Esempio n. 39
0
    def _updateHistDays(self,
                        startDate,
                        endDate,
                        indicators,
                        isForced=False,
                        codes=None):

        # get updated codes data info
        codes = self._getUpdatedCodes(startDate, endDate, indicators, isForced,
                                      codes)
        if codes is None: return

        # init
        self._isStopped = False
        self._updatedCodeCount = 0
        self._progress.init(len(codes), 10)

        self._info.print("开始更新{0}只股票(指数,基金)的历史日线数据...".format(len(codes)))

        # send for updating
        event = DyEvent(DyEventType.updateStockHistDays_)
        event.data = codes

        self._eventEngine.put(event)
def dyStockBackTestingStrategyEngineProcess(outQueue,
                                            inQueue,
                                            reqData,
                                            config=None):
    """
        股票回测处理实体。每个回测处理实体由一个参数组合和一个回测周期组成。
        每个交易日回测结束后向UI推送持仓和成交信息
    """
    paramGroupNo = reqData.paramGroupNo
    period = [reqData.tDays[0], reqData.tDays[-1]]

    if config is not None:
        DyStockConfig.setConfigForBackTesting(config)

    # set DB Cache
    useDbCache = __setDbCache(reqData)

    # Engines
    eventEngine = DyDummyEventEngine()
    info = DySubInfo(paramGroupNo, period, outQueue)
    dataEngine = DyStockDataEngine(eventEngine,
                                   info,
                                   False,
                                   dbCache=useDbCache)

    # create stock back testing CTA engine
    ctaEngine = DyStockBackTestingCtaEngine(eventEngine,
                                            info,
                                            dataEngine,
                                            reqData,
                                            dbCache=useDbCache)

    for tDay in reqData.tDays:
        try:
            event = inQueue.get_nowait()
        except queue.Empty:
            pass

        # 回测当日数据
        if not ctaEngine.run(tDay):
            break

        # 发送当日回测结果数据事件
        event = DyEvent(DyEventType.stockStrategyBackTestingAck)
        event.data = ctaEngine.getCurAckData()

        outQueue.put(event)

    # 发送'股票回测策略引擎处理结束'事件
    event = DyEvent(DyEventType.stockBackTestingStrategyEngineProcessEnd)
    event.data[paramGroupNo] = period

    outQueue.put(event)
Esempio n. 41
0
    def _updateWindow(self):
        assert(self._windowSize > 0)

        # decrease
        self._windowSize -= 1

        # hook stop event
        if self._isStopped:
            if self._windowSize == 0: # send stopAck event
                self._printCounts()

                self._eventEngine.put(DyEvent(DyEventType.stopAck))

            return

        # Note: To get good Gateway throughput, size can be more than 1. factor * @stockHistTicksHandNbr is a good choice.
        # Note: Don't forget intial number of requests already sent to Gateway.
        self._increaseWindowSize(self.shiftWindowSize)
    def start(self):
        assert self._scheduler is None

        isTradeDay = self._checkDay()
        if isTradeDay is None:
            return False

        # 推送endTradeDay事件
        if not isTradeDay or datetime.now().strftime('%H:%M:%S') > '15:45:00':
            self._eventEngine.put(DyEvent(DyEventType.endStockTradeDay))

        if self.testMode:
            threading.Thread(target=self._testModeRun).start()
        else:
            self._scheduler = DyScheduler()

            self._scheduler.addJob(self._beginDay, {1, 2, 3, 4, 5}, '08:30:00')
            self._scheduler.addJob(self._endDay, {1, 2, 3, 4, 5}, '15:45:00')

            self._scheduler.start()

        return True
    def _stockRegression(self):
        strategyCls, param = self._widgetStrategy.getStrategy()
        if strategyCls is None: return

        data = {}
        if not DyDateDlg(data).exec_():
            return

        # change UI
        self._startRunningMutexAction(self._stockRegressionAction)

        event = DyEvent(DyEventType.stockSelectStrategyRegressionReq)
        event.data['class'] = strategyCls
        event.data['param'] = param
        event.data['startDate'] = data['startDate']
        event.data['endDate'] = data['endDate']

        self._mainEngine.eventEngine.put(event)
Esempio n. 44
0
    def _updateStrategyAccount(self, strategy):
        """
            向UI推送策略的账户相关事件
            只有不改变的对象内容可以在多线程之间传递。策略账户相关的对象可能会改变,所以做deepcopy或者copy
        """
        # 持仓
        self._updateStrategyPos(strategy)

        # 委托
        event = DyEvent(DyEventType.stockStrategyEntrustsUpdate + strategy.name)
        event.data = copy.deepcopy(strategy.curEntrusts)

        self._eventEngine.put(event)

        # 成交
        event = DyEvent(DyEventType.stockStrategyDealsUpdate + strategy.name)
        event.data = list(strategy.curDeals.values()) # [DyStockDeal]

        self._eventEngine.put(event)
    def _histDaysForcedUpdate(self):
        if self._histDaysForcedUpdateAction.text() == '停止':
            self._mainEngine._info.print('停止股票(指数)历史日线数据强制更新...', DyLogData.ind)

            # change UI
            self._stopRunningMutexAction()

            event = DyEvent(DyEventType.stopUpdateStockHistDaysReq)
            self._mainEngine.eventEngine.put(event)

        else: # 开始手动更新
            data = {}
            if DyDateDlg(data, self).exec_():
                self._mainEngine._info.print('开始股票(指数,基金)历史日线数据强制更新[{0}, {1}]...'.format(data['startDate'], data['endDate']), DyLogData.ind)

                # change UI
                self._startRunningMutexAction(self._histDaysForcedUpdateAction)

                event = DyEvent(DyEventType.updateStockHistDays)
                event.data = data
                event.data['indicators'] = DyStockDataCommon.dayIndicators
                event.data['forced'] = True

                self._mainEngine.eventEngine.put(event)
Esempio n. 46
0
    def _ok(self):
        try:
            if self._codeLabel.text() != self._tick.code:
                QMessageBox.warning(self, '错误', '没有指定代码的Tick数据!')
                return
        except Exception:
            QMessageBox.warning(self, '错误', '没有指定代码的Tick数据!')
            return

        event = DyEvent(DyEventType.stockStrategyManualBuy)
        event.data['class'] = self._strategyCls
        event.data['tick'] = self._tick
        event.data['volume'] = float(self._buyVolumeLineEdit.text()) * 100

        # 不指定价格,则根据tick买入
        price = self._buyPriceLineEdit.text()
        event.data['price'] = float(price) if price else None

        self._eventEngine.put(event)

        self._unregisterEvent()

        self.accept()
    def _histTicksMannualUpdate(self):
        if self._histTicksMannualUpdateAction.text() == '停止':
            self._mainEngine._info.print('停止股票(基金)历史分笔数据手动更新...', DyLogData.ind)

            # change UI
            self._stopRunningMutexAction()

            event = DyEvent(DyEventType.stopUpdateStockHistTicksReq)
            self._mainEngine.eventEngine.put(event)

        else: # 开始手动更新
            data = {}
            codeLabelText = '股票(基金)代码(空代表所有代码), e.g. 600016,510300,002213,...'
            if DyCodeDateDlg(codeLabelText, data, self).exec_():
                self._mainEngine._info.print('开始股票(基金)历史分笔数据手动更新...', DyLogData.ind)

                # change UI
                self._startRunningMutexAction(self._histTicksMannualUpdateAction)

                event = DyEvent(DyEventType.updateStockHistTicks)
                event.data = data
                event.data['codes'] = DyStockCommon.getDyStockCodes(event.data['codes'])

                self._mainEngine.eventEngine.put(event)
class DyStockSelectMainWindow(DyBasicMainWindow):
    name = 'DyStockSelectMainWindow'

    signalPlot = QtCore.pyqtSignal(type(DyEvent()))

    def __init__(self, parent=None):
        
        self._mainEngine = DyStockSelectMainEngine()

        super().__init__(self._mainEngine.eventEngine, self._mainEngine.info, parent)
        
        self._initUi()

        self._registerEvent()

    def _initUi(self):
        """ 初始化界面 """
        self.setWindowTitle('选股')

        self._initCentral()
        self._initMenu()
        self._initToolBar()

        self._loadWindowSettings()

        # at last, raise log dock widget
        self._dockLog.raise_()
        
    def _initCentral(self):
        """ 初始化中心区域 """
        widgetParam, dockParam = self._createDock(DyStockSelectParamWidget, '策略参数', Qt.RightDockWidgetArea)

        self._widgetStrategy, dockStrategy = self._createDock(DyStockSelectStrategyWidget, '策略', Qt.LeftDockWidgetArea, widgetParam)
        widgetProgress, dockProgress = self._createDock(DyProgressWidget, '进度', Qt.LeftDockWidgetArea, self._mainEngine.eventEngine)

        widgetLog, self._dockLog = self._createDock(DyLogWidget, '日志', Qt.RightDockWidgetArea, self._mainEngine.eventEngine)
        self._widgetSelectResult, dockSelectResult = self._createDock(DyStockSelectSelectResultWidget, '选股结果', Qt.RightDockWidgetArea, self._mainEngine.eventEngine, widgetParam)
        self._widgetRegressionResult, dockRegressionResult = self._createDock(DyStockSelectRegressionResultWidget, '回归结果', Qt.RightDockWidgetArea, self._mainEngine.eventEngine, widgetParam)

        self.tabifyDockWidget(self._dockLog, dockSelectResult)
        self.tabifyDockWidget(self._dockLog, dockRegressionResult)

    def _initMenu(self):
        """ 初始化菜单 """
        # 创建菜单
        menuBar = self.menuBar()
        
        # ----- 添加'数据'菜单 -----
        menu = menuBar.addMenu('数据')

        # 测试操作
        action = QAction('测试', self)
        action.triggered.connect(self._test)
        menu.addAction(action)

        # 打开策略选股/回归结果
        action = QAction('打开策略选股/回归结果...', self)
        action.triggered.connect(self._openStrategySelectResultAct)
        menu.addAction(action)

        # 布林统计操作
        action = QAction('布林统计...', self)
        action.triggered.connect(self._bBandsStats)
        menu.addAction(action)

        # 杰拉德指数操作
        action = QAction('杰卡德指数...', self)
        action.triggered.connect(self._jaccardIndexAct)
        menu.addAction(action)

        # 指数连续日阴线或者阳线统计
        lineStatsMenu = menu.addMenu('指数连续日K线统计')

        self._greenLineStatsAction = QAction('指数连续日阴线统计...', self)
        self._greenLineStatsAction.triggered.connect(self._indexConsecutiveDayLineStatsAct)
        lineStatsMenu.addAction(self._greenLineStatsAction)
        self._greenLineStatsAction.setCheckable(True)

        self._redLineStatsAction = QAction('指数连续日阳线统计...', self)
        self._redLineStatsAction.triggered.connect(self._indexConsecutiveDayLineStatsAct)
        lineStatsMenu.addAction(self._redLineStatsAction)
        self._redLineStatsAction.setCheckable(True)

        # 封板率统计
        action = QAction('封板率统计...', self)
        action.triggered.connect(self._limitUpStatsAct)
        menu.addAction(action)

        # 热点分析
        action = QAction('热点分析...', self)
        action.triggered.connect(self._focusAnalysisAct)
        menu.addAction(action)

        # 股票日内最高和最低价分布
        action = QAction('最高和最低价分布...', self)
        action.triggered.connect(self._highLowDistAct)
        menu.addAction(action)
        
        # ----- 添加'设置'菜单 -----
        menu = menuBar.addMenu('设置')

        self._setProcessNbrAction = QAction('回归进程数...', self)
        self._setProcessNbrAction.triggered.connect(self._setProcessNbr)
        menu.addAction(self._setProcessNbrAction)

        self._setDayKChartPeriodNbrAction = QAction('日K线前后周期数...', self)
        self._setDayKChartPeriodNbrAction.triggered.connect(self._setDayKChartPeriodNbr)
        menu.addAction(self._setDayKChartPeriodNbrAction)

        self._enableSelectEngineExceptionAction = QAction('选股引擎的异常捕捉', self)
        self._enableSelectEngineExceptionAction.triggered.connect(self._enableSelectEngineExceptionAct)
        menu.addAction(self._enableSelectEngineExceptionAction)
        self._enableSelectEngineExceptionAction.setCheckable(True)

        # ----- 添加'测试'菜单 -----
        menu = menuBar.addMenu('测试')

        self._testedStocksAction = QAction('调试股票...', self)
        self._testedStocksAction.triggered.connect(self._testedStocks)
        menu.addAction(self._testedStocksAction)
        self._testedStocksAction.setCheckable(True)

    def _enableSelectEngineExceptionAct(self):
        DyStockSelectCommon.enableSelectEngineException = self._enableSelectEngineExceptionAction.isChecked()

    def _test(self):
        event = DyEvent(DyEventType.plotReq)
        event.data['type'] = 'test'

        self._mainEngine.eventEngine.put(event)

    def _openStrategySelectResultAct(self):
        defaultDir = DyCommon.createPath('Stock/User/Save/Strategy/选股')
        fileName, _ = QFileDialog.getOpenFileName(self, "打开策略选股/回归结果", defaultDir, "JSON files (*.json)")

        # open
        try:
            with open(fileName) as f:
                data = json.load(f)
        except Exception:
            return

        if not data:
            return

        # create strategy class
        strategyClsName = data.get('strategyCls')
        if not strategyClsName:
            return

        strategyCls = eval(strategyClsName)

        # load
        if self._widgetSelectResult.load(data, strategyCls):
            return

        self._widgetRegressionResult.load(data, strategyCls)

    def _indexConsecutiveDayLineStatsAct(self):
        if self._greenLineStatsAction.isChecked():
            greenLine = True
            self._greenLineStatsAction.setChecked(False)
        else:
            greenLine = False
            self._redLineStatsAction.setChecked(False)

        data = {}
        if not DyDateDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'indexConsecutiveDayLineStats'
        event.data['greenLine'] = greenLine

        self._mainEngine.eventEngine.put(event)

    def _limitUpStatsAct(self):
        data = {}
        if not DyDateDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'limitUpStats'

        self._mainEngine.eventEngine.put(event)

    def _highLowDistAct(self):
        data = {}
        if not DyDateDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'highLowDist'

        self._mainEngine.eventEngine.put(event)

    def _focusAnalysisAct(self):
        data = {}
        if not DyDateDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'focusAnalysis'

        self._mainEngine.eventEngine.put(event)

    def _jaccardIndexAct(self):
        data = {}
        if not DyStockSelectJaccardIndexDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'jaccardIndex'

        self._mainEngine.eventEngine.put(event)

    def _bBandsStats(self):
        data = {}
        if not DyStockSelectBBandsStatsDlg(data).exec_():
            return

        event = DyEvent(DyEventType.plotReq)
        event.data =  data
        event.data['type'] = 'bBandsStats'

        self._mainEngine.eventEngine.put(event)

    def _setProcessNbr(self):
        data = {'nbr':DyStockSelectRegressionEngine.periodNbr}
        if DyProcessNbrDlg(data, self).exec_():
            DyStockSelectRegressionEngine.periodNbr = data['nbr']

            self._mainEngine._info.print('回归进程数设为{0}'.format(data['nbr']), DyLogData.ind)

    def _setDayKChartPeriodNbr(self):
        data = {'periodNbr': DyStockCommon.dayKChartPeriodNbr}
        if DyStockSelectDayKChartPeriodDlg(data, self).exec_():
            DyStockCommon.dayKChartPeriodNbr = data['periodNbr']

            self._mainEngine._info.print('股票(指数)日K线前后交易日周期设为{0}'.format(data['periodNbr']), DyLogData.ind)

    def _testedStocks(self):
        isTested =  self._testedStocksAction.isChecked()

        codes = None
        if isTested:
            data = {}
            if DyStockSelectTestedStocksDlg(data).exec_():
                codes = data['codes']
            else:
                self._testedStocksAction.setChecked(False)

        # put event
        event = DyEvent(DyEventType.stockSelectTestedCodes)
        event.data = codes

        self._mainEngine.eventEngine.put(event)

    def _stockSelect(self):
        strategyCls, param = self._widgetStrategy.getStrategy()
        if strategyCls is None: return

        # change UI
        self._startRunningMutexAction(self._stockSelectAction)

        event = DyEvent(DyEventType.stockSelectStrategySelectReq)
        event.data['class'] = strategyCls
        event.data['param'] = param

        self._mainEngine.eventEngine.put(event)

    def _stockSelectForTrade(self):
        strategyCls, param = self._widgetStrategy.getStrategy()
        if strategyCls is None: return

        # change UI
        self._startRunningMutexAction(self._stockSelectForTradeAction)

        event = DyEvent(DyEventType.stockSelectStrategySelectReq)
        event.data['class'] = strategyCls
        event.data['param'] = param
        event.data['param']['forTrade'] = None

        self._mainEngine.eventEngine.put(event)

    def _stockRegression(self):
        strategyCls, param = self._widgetStrategy.getStrategy()
        if strategyCls is None: return

        data = {}
        if not DyDateDlg(data).exec_():
            return

        # change UI
        self._startRunningMutexAction(self._stockRegressionAction)

        event = DyEvent(DyEventType.stockSelectStrategyRegressionReq)
        event.data['class'] = strategyCls
        event.data['param'] = param
        event.data['startDate'] = data['startDate']
        event.data['endDate'] = data['endDate']

        self._mainEngine.eventEngine.put(event)

    def _initToolBar(self):
        """ 初始化工具栏 """
        # 操作工具栏
        toolBar = self.addToolBar('选股和回归')
        toolBar.setObjectName('选股和回归')

        # 创建操作工具栏的操作
        self._stockSelectAction = QAction('选股', self)
        self._stockSelectAction.setEnabled(False)
        self._stockSelectAction.triggered.connect(self._stockSelect)
        self._addMutexAction(self._stockSelectAction)
        toolBar.addAction(self._stockSelectAction)

        self._stockRegressionAction = QAction('回归', self)
        self._stockRegressionAction.setEnabled(False)
        self._stockRegressionAction.triggered.connect(self._stockRegression)
        self._addMutexAction(self._stockRegressionAction)
        toolBar.addAction(self._stockRegressionAction)

        """
        self._stockSelectForTradeAction = QAction('实盘选股', self)
        self._stockSelectForTradeAction.setEnabled(False)
        self._stockSelectForTradeAction.triggered.connect(self._stockSelectForTrade)
        self._addMutexAction(self._stockSelectForTradeAction)
        toolBar.addAction(self._stockSelectForTradeAction)
        """

        #self._widgetStrategy.setRelatedActions([self._stockSelectAction, self._stockRegressionAction, self._stockSelectForTradeAction])
        self._widgetStrategy.setRelatedActions([self._stockSelectAction, self._stockRegressionAction])

        # K线工具栏
        toolBar = self.addToolBar('K线')
        toolBar.setObjectName('K线')

        # K线周期菜单Action
        self._kPeriodMenuAction = QAction('K线周期', self)
        toolBar.addAction(self._kPeriodMenuAction)
        self._kPeriodMenuAction.triggered.connect(self._kPeriodMenuAct)

        # K线周期菜单
        self._kPeriodMenu = QMenu(self)

        # 创建K线周期菜单的操作
        actions = [QAction('{0}'.format(x), self) for x in [60, 90, 120, 180, 250, 500]]
        for action in actions:
            action.setCheckable(True)
            action.triggered.connect(self._kPeriodAct)
            self._kPeriodMenu.addAction(action)

            # set default K period
            if int(action.text()) == DyStockCommon.dayKChartPeriodNbr:
                action.setChecked(True)
                self._curKPeriodAction = action
                self._kPeriodMenuAction.setText('K线周期:{0}'.format(DyStockCommon.dayKChartPeriodNbr))

        # 滑动窗口(w)菜单Action
        self._rollingWindowWMenuAction = QAction('滑动窗口(w)', self)
        toolBar.addAction(self._rollingWindowWMenuAction)
        self._rollingWindowWMenuAction.triggered.connect(self._rollingWindowWMenuAct)

        # 滑动窗口(w)菜单
        self._rollingWindowWMenu = QMenu(self)

        # 创建滑动窗口(w)菜单的操作
        actions = [QAction('{0}'.format(x), self) for x in [1, 2, 3, 4, 5, 6, 7, 8, 9]]
        for action in actions:
            action.setCheckable(True)
            action.triggered.connect(self._rollingWindowWAct)
            self._rollingWindowWMenu.addAction(action)

            # set default rolling window w
            if int(action.text()) == DyStockCommon.rollingWindowW:
                action.setChecked(True)
                self._curRollingWindowWAction = action
                self._rollingWindowWMenuAction.setText('滑动窗口(w):{0}'.format(DyStockCommon.rollingWindowW))

        # 支撑和阻力菜单Action
        self._hsarMenuAction = QAction('支撑和阻力', self)
        toolBar.addAction(self._hsarMenuAction)
        self._hsarMenuAction.triggered.connect(self._hsarMenuAct)

        # 支撑和阻力菜单
        self._hsarMenu = QMenu(self)

        actions = [QAction(x, self) for x in ['成本', '极值平均', '极值之极']]
        for action in actions:
            action.setCheckable(True)
            action.triggered.connect(self._hsarsAct)
            self._hsarMenu.addAction(action)

            if action.text() == DyStockCommon.hsarMode:
                action.setChecked(True)
                self._curHsarAction = action
                self._hsarMenuAction.setText('支撑和阻力:{0}'.format(DyStockCommon.hsarMode))

        # 趋势线周期菜单Action
        self._trendLinePeriodMenuAction = QAction('趋势线周期', self)
        toolBar.addAction(self._trendLinePeriodMenuAction)
        self._trendLinePeriodMenuAction.triggered.connect(self._trendLinePeriodMenuAct)

        # 趋势线周期菜单
        self._trendLinePeriodMenu = QMenu(self)

        # 清除所有Action
        action = QAction('清除所有', self)
        action.triggered.connect(self._trendLinePeriodClearAllAct)
        self._trendLinePeriodMenu.addAction(action)

        # 创建趋势线周期菜单的操作
        actions = [QAction('{0}'.format(x), self) for x in list(range(10, 121, 5)) + [180, 250, 500]]
        for action in actions:
            action.setCheckable(True)
            action.triggered.connect(self._trendLinePeriodAct)
            self._trendLinePeriodMenu.addAction(action)

            # set default trend line periods
            if int(action.text()) in DyStockCommon.trendLinePeriods:
                action.setChecked(True)
                self._trendLinePeriodMenuAction.setText('趋势线周期:{0}'.format(','.join([str(x) for x in DyStockCommon.trendLinePeriods])))

    def _kPeriodMenuAct(self):
        self._kPeriodMenu.popup(QCursor.pos())

    def _trendLinePeriodMenuAct(self):
        self._trendLinePeriodMenu.popup(QCursor.pos())

    def _rollingWindowWMenuAct(self):
        self._rollingWindowWMenu.popup(QCursor.pos())

    def _hsarMenuAct(self):
        self._hsarMenu.popup(QCursor.pos())

    def _trendLinePeriodAct(self):
        periods = []
        for action in self._trendLinePeriodMenu.actions():
            if action.isChecked():
                periods.append(int(action.text()))

        DyStockCommon.trendLinePeriods = periods
        self._trendLinePeriodMenuAction.setText('趋势线周期:{0}'.format(','.join([str(x) for x in DyStockCommon.trendLinePeriods])))

    def _trendLinePeriodClearAllAct(self):
        # clear all
        for action in self._trendLinePeriodMenu.actions():
            if action.isChecked():
                action.setChecked(False)

        DyStockCommon.trendLinePeriods = []
        self._trendLinePeriodMenuAction.setText('趋势线周期:{0}'.format(','.join([str(x) for x in DyStockCommon.trendLinePeriods])))

    def _kPeriodAct(self):
        self._curKPeriodAction.setChecked(False)

        # get triggered action
        for action in self._kPeriodMenu.actions():
            if action.isChecked():
                # 设置K线周期
                DyStockCommon.dayKChartPeriodNbr = int(action.text())

                self._curKPeriodAction = action
                self._kPeriodMenuAction.setText('K线周期:{0}'.format(DyStockCommon.dayKChartPeriodNbr))
                break

        if not self._curKPeriodAction.isChecked():
            self._curKPeriodAction.setChecked(True)

    def _hsarsAct(self):
        self._curHsarAction.setChecked(False)

        # get triggered action
        for action in self._hsarMenu.actions():
            if action.isChecked():
                # 设置支撑和阻力
                DyStockCommon.hsarMode = action.text()

                self._curHsarAction = action
                self._hsarMenuAction.setText('支撑和阻力:{0}'.format(DyStockCommon.hsarMode))
                break

        if not self._curHsarAction.isChecked():
            self._curHsarAction.setChecked(True)

    def _rollingWindowWAct(self):
        self._curRollingWindowWAction.setChecked(False)

        # get triggered action
        for action in self._rollingWindowWMenu.actions():
            if action.isChecked():
                # 设置rolling window w
                DyStockCommon.rollingWindowW = int(action.text())

                self._curRollingWindowWAction = action
                self._rollingWindowWMenuAction.setText('滑动窗口(w):{0}'.format(DyStockCommon.rollingWindowW))
                break

        if not self._curRollingWindowWAction.isChecked():
            self._curRollingWindowWAction.setChecked(True)

    def closeEvent(self, event):
        """ 关闭事件 """
        self._mainEngine.exit()

        return super().closeEvent(event)
            
    def _plotAckHandler(self, event):
        # unpack
        plot = event.data['plot']

        # plot
        plot(event)

    def _registerEvent(self):
        """ 注册GUI更新相关的事件监听 """
        self.signalPlot.connect(self._plotAckHandler)

        self._mainEngine.eventEngine.register(DyEventType.plotAck, self.signalPlot.emit)
Esempio n. 49
0
    def print(self, description, type=DyLogData.info):
        if type == DyLogData.error or type == DyLogData.warning:
            event = DyEvent(DyEventType.log)
            event.data = DyLogData(description, type)

            self._eventEngine.put(event)
Esempio n. 50
0
class DyStockTradeStrategyMarketMonitorWidget(QWidget):
    """
        股票策略实时监控窗口,动态创建
    """
    signal = QtCore.pyqtSignal(type(DyEvent()))

    def __init__(self, eventEngine, strategyCls, strategyState):
        super().__init__()

        self._eventEngine = eventEngine
        self._strategyCls = strategyCls

        self._cloneDataWidgets = []

        self._registerEvent()

        self._initUi(strategyState)

    def _initUi(self, strategyState):
        self._dataWidget = DyStockTradeStrategyMarketMonitorDataWidget(
            self._strategyCls, self)
        self._indWidget = DyStockTradeStrategyMarketMonitorIndWidget(
            self._eventEngine, self._strategyCls, strategyState)

        self._dataLabel = QLabel('数据')
        self._indLabel = QLabel('指示')

        grid = QGridLayout()
        grid.setSpacing(0)

        grid.addWidget(self._dataLabel, 0, 0)
        grid.addWidget(self._dataWidget, 1, 0)
        grid.addWidget(self._indLabel, 2, 0)
        grid.addWidget(self._indWidget, 3, 0)

        grid.setRowStretch(0, 1)
        grid.setRowStretch(1, 30)
        grid.setRowStretch(2, 1)
        grid.setRowStretch(3, 30)

        self.setLayout(grid)

        # set menu for labels
        self._dataLabel.setContextMenuPolicy(Qt.CustomContextMenu)
        self._dataLabel.customContextMenuRequested.connect(
            self._showDataLabelContextMenu)

        self._indLabel.setContextMenuPolicy(Qt.CustomContextMenu)
        self._indLabel.customContextMenuRequested.connect(
            self._showIndLabelContextMenu)

        self._dataLabelMenu = QMenu(self)

        action = QAction('叠加', self)
        action.triggered.connect(self._overlapAct)
        self._dataLabelMenu.addAction(action)

        action = QAction('克隆', self)
        action.triggered.connect(self._cloneDataWidgetAct)
        self._dataLabelMenu.addAction(action)

        self._indLabelMenu = QMenu(self)

        action = QAction('叠加', self)
        action.triggered.connect(self._overlapAct)
        self._indLabelMenu.addAction(action)

    def _stockMarketMonitorUiHandler(self, event):
        if 'data' in event.data:
            data = event.data['data']['data']
            new = event.data['data']['new']

            strategyCls = event.data['class']
            if strategyCls.maxUiDataRowNbr is not None:
                data = data[:strategyCls.maxUiDataRowNbr]

            self._dataWidget.update(data, new)
            for w in self._cloneDataWidgets:
                w.update(data, new)

        if 'ind' in event.data:
            self._indWidget.update(event.data['ind'])

    def _signalEmitWrapper(self, event):
        """ !!!Note: The value of signal.emit will always be changed each time you getting.
        """
        self.signal.emit(event)

    def _registerEvent(self):
        self.signal.connect(self._stockMarketMonitorUiHandler)
        self._eventEngine.register(
            DyEventType.stockMarketMonitorUi + self._strategyCls.name,
            self._signalEmitWrapper)

    def _unregisterEvent(self):
        self.signal.disconnect(self._stockMarketMonitorUiHandler)
        self._eventEngine.unregister(
            DyEventType.stockMarketMonitorUi + self._strategyCls.name,
            self._signalEmitWrapper)

    def closeEvent(self, event):
        self._dataWidget.close()
        self._indWidget.close()

        self._unregisterEvent()

        return super().closeEvent(event)

    def _showDataLabelContextMenu(self, position):
        self._dataLabelMenu.popup(QCursor.pos())

    def _showIndLabelContextMenu(self, position):
        self._indLabelMenu.popup(QCursor.pos())

    def _overlapAct(self):
        grid = self.layout()

        # remove
        self._dataLabel.setText('')
        self._indLabel.setText('')

        grid.removeWidget(self._dataLabel)
        grid.removeWidget(self._dataWidget)
        grid.removeWidget(self._indLabel)
        grid.removeWidget(self._indWidget)

        # add
        self._tabWidget = QTabWidget()

        self._tabWidget.addTab(self._dataWidget, '数据')
        self._tabWidget.addTab(self._indWidget, '指示')

        grid.addWidget(self._tabWidget, 0, 0)

        grid.setRowStretch(0, 1)
        grid.setRowStretch(1, 0)
        grid.setRowStretch(2, 0)
        grid.setRowStretch(3, 0)

        # 设置Tab右键菜单事件
        tabBar = self._tabWidget.tabBar()
        tabBar.setContextMenuPolicy(Qt.CustomContextMenu)
        tabBar.customContextMenuRequested.connect(self._showTabContextMenu)

        # 创建TabBar菜单
        self._tabBarMenu = QMenu(self)

        action = QAction('平铺', self)
        action.triggered.connect(self._flatAct)
        self._tabBarMenu.addAction(action)

    def _showTabContextMenu(self, position):
        self._tabBarMenu.popup(QCursor.pos())

    def _flatAct(self):
        grid = self.layout()

        # remove
        self._tabWidget.removeTab(0)
        self._tabWidget.removeTab(0)

        grid.removeWidget(self._tabWidget)

        self._tabWidget.hide()

        # add
        self._dataLabel.setText('数据')
        self._indLabel.setText('指示')

        grid.addWidget(self._dataLabel, 0, 0)
        grid.addWidget(self._dataWidget, 1, 0)
        grid.addWidget(self._indLabel, 2, 0)
        grid.addWidget(self._indWidget, 3, 0)

        self._dataWidget.show()
        self._indWidget.show()

        grid.setRowStretch(0, 1)
        grid.setRowStretch(1, 30)
        grid.setRowStretch(2, 1)
        grid.setRowStretch(3, 30)

    def removeCloneDataWidget(self, cloneWidget):
        try:
            self._cloneDataWidgets.remove(cloneWidget)
        except:
            pass

    def _cloneDataWidgetAct(self):
        dataWidget = self._dataWidget.clone()
        self._cloneDataWidgets.append(dataWidget)

        dataWidget.setWindowTitle('策略[{}]: 数据'.format(
            self._strategyCls.chName))
        dataWidget.showMaximized()
Esempio n. 51
0
class DyStockTableWidget(DyStatsTableWidget):
    """
        股票基类窗口
        默认每行以‘代码’,‘名称’开始
        提供关于股票所有相关的操作和信息展示
        决定股票表的两个因子:name和baseDate
    """
    # header signal
    stockTableAddColumnsActAckSignal = QtCore.pyqtSignal(type(DyEvent()))

    # item signal
    stockTableIndustryCompareActAckSignal = QtCore.pyqtSignal(type(DyEvent()))

    def __init__(self,
                 eventEngine,
                 parent=None,
                 name=None,
                 baseDate=None,
                 readOnly=True,
                 index=False,
                 autoScroll=False,
                 floatRound=2
                 ):
        super().__init__(parent=parent,
                         readOnly=readOnly,
                         index=index,
                         autoScroll=autoScroll,
                         floatRound=floatRound
                         )

        self._name = name
        self._baseDate = baseDate
        self._eventEngine = eventEngine

        self._windows = []
        self._curActionOngoing = False

        self._initDataViewer()

        self._registerEevent()

        self.itemDoubleClicked.connect(self._itemDoubleClicked)

    def _registerEevent(self):
        # header
        self.stockTableAddColumnsActAckSignal.connect(self._stockTableAddColumnsActAckHandler)
        self._eventEngine.register(DyEventType.stockTableAddColumnsActAck, self._stockTableAddColumnsActAckSignalEmitWrapper)

        # item
        self.stockTableIndustryCompareActAckSignal.connect(self._stockTableIndustryCompareActAckHandler)
        self._eventEngine.register(DyEventType.stockTableIndustryCompareActAck, self._stockTableIndustryCompareActAckSignalEmitWrapper)

    def _unregisterEevent(self):
        # header
        self.stockTableAddColumnsActAckSignal.disconnect(self._stockTableAddColumnsActAckHandler)
        self._eventEngine.unregister(DyEventType.stockTableAddColumnsActAck, self._stockTableAddColumnsActAckSignalEmitWrapper)

        # item
        self.stockTableIndustryCompareActAckSignal.disconnect(self._stockTableIndustryCompareActAckHandler)
        self._eventEngine.unregister(DyEventType.stockTableIndustryCompareActAck, self._stockTableIndustryCompareActAckSignalEmitWrapper)

    def closeEvent(self, event):
        self._unregisterEevent()

        return super().closeEvent(event)

    def _initDataViewer(self):
        # 省去非错误log的输出
        errorInfo = DyErrorInfo(self._eventEngine)
        self._dataEngine = DyStockDataEngine(self._eventEngine, errorInfo, registerEvent=False)
        self._dataViewer = DyStockDataViewer(self._dataEngine, errorInfo)
        self._daysEngine = self._dataEngine.daysEngine
        self._ticksEngine = self._dataEngine.ticksEngine

        self._errorProgressInfo = DyErrorProgressInfo(self._eventEngine)

    def _initHeaderMenu(self):
        """
            初始化表头右键菜单
            子类可以改写添加定制菜单
        """
        super()._initHeaderMenu()

        self._headerMenu.addSeparator()

        action = QAction('新窗口', self)
        action.triggered.connect(self._newWindowAct)
        self._headerMenu.addAction(action)

        self._headerMenu.addSeparator()

        # '添加列'
        menu = self._headerMenu.addMenu('添加列')

        action = QAction('个股资料...', self)
        action.triggered.connect(self._addStockInfoColumnsAct)
        menu.addAction(action)

        menu.addSeparator()

        action = QAction('涨幅...', self)
        action.triggered.connect(self._addIncreaseColumnsAct)
        menu.addAction(action)

        action = QAction('最大最小涨幅...', self)
        action.triggered.connect(self._addMaxMinIncreaseColumnsAct)
        menu.addAction(action)

        action = QAction('最大振幅...', self)
        action.triggered.connect(self._addMaxAmplitudeColumnsAct)
        menu.addAction(action)

        action = QAction('分钟涨幅(ETF)...', self) # 本来应该添加当日大盘开盘开始的分钟涨幅,主要为了T+1的策略。由于没有大盘指数的分笔数据,所以使用对应的ETF。
        action.triggered.connect(self._addMinuteIncreaseColumnsAct)
        menu.addAction(action)

        action = QAction('开盘缺口...', self) # 添加开盘缺口,0代表是当日
        action.triggered.connect(self._addOpenGapColumnsAct)
        menu.addAction(action)

        action = QAction('开盘涨幅', self)
        action.triggered.connect(self._addOpenIncreaseColumnsAct)
        menu.addAction(action)

        menu.addSeparator()

        action = QAction('ER(效率系数)...', self)
        action.triggered.connect(self._addErColumnsAct)
        menu.addAction(action)

        action = QAction('波动率...', self)
        action.triggered.connect(self._addVolatilityColumnsAct)
        menu.addAction(action)

        action = QAction('日收益率大盘相关系数...', self)
        action.triggered.connect(self._addDayReturnIndexCorrColumnsAct)
        menu.addAction(action)

        menu.addSeparator()

        action = QAction('列运算...', self)
        action.triggered.connect(self._addColumnOperateColumnsAct)
        menu.addAction(action)

        # '列操作'
        menu = self._headerMenu.addMenu('列操作')

        self._upDownRatioAction = QAction('涨跌比', self)
        self._upDownRatioAction.triggered.connect(self._upDownRatioAct)
        menu.addAction(self._upDownRatioAction)

        self._limitUpRatioAction = QAction('涨停比', self)
        self._limitUpRatioAction.triggered.connect(self._limitUpRatioAct)
        menu.addAction(self._limitUpRatioAction)

        action = QAction('过滤...', self)
        action.triggered.connect(self._filterAct)
        self._headerMenu.addAction(action)

        self._headerMenu.addSeparator()

        action = QAction('导出到同花顺...', self)
        action.triggered.connect(self._export2JqkaAct)
        self._headerMenu.addAction(action)

        action = QAction('保存...', self)
        action.triggered.connect(self._saveAsAct)
        self._headerMenu.addAction(action)

    def _initItemMenu(self):
        """
            初始化Item右键菜单
            子类可以改写添加定制菜单
        """
        super()._initItemMenu()

        self._itemMenu.addSeparator()

        # 分时图
        self._timeShareChartMenu = self._itemMenu.addMenu('分时图')

        actions = [QAction('基准日期', self)] + [QAction('向前{0}日'.format(day), self) for day in range(1, 10)]
        for action in actions:
            action.triggered.connect(self._timeShareChartAct)
            action.setCheckable(True)
            self._timeShareChartMenu.addAction(action)

        # 成交分布
        self._dealsDistMenu = self._itemMenu.addMenu('成交分布')

        actions = [QAction('基准日期', self)] + [QAction('向前{0}日'.format(day), self) for day in [5, 10, 20, 30, 60, 90, 120]]
        for action in actions:
            action.triggered.connect(self._dealsDistAct)
            action.setCheckable(True)
            self._dealsDistMenu.addAction(action)

        # 成交明细
        action = QAction('成交明细', self)
        action.triggered.connect(self._dealDetailsAct)
        self._itemMenu.addAction(action)

        # 日内K线图
        self._intraDayKLineMenu = self._itemMenu.addMenu('日内K线图')

        actions = [QAction('{0}秒'.format(s), self) for s in range(5, 60, 5)] + [QAction('{0}分'.format(m), self) for m in range(1, 16)]
        for action in actions:
            action.triggered.connect(self._intraDayKLineAct)
            action.setCheckable(True)
            self._intraDayKLineMenu.addAction(action)

        self._itemMenu.addSeparator()

        # 个股资料
        action = QAction('个股资料', self)
        action.triggered.connect(self._stockInfoAct)
        self._itemMenu.addAction(action)

        # 行业对比
        action = QAction('行业对比...', self)
        action.triggered.connect(self._industryCompareAct)
        self._itemMenu.addAction(action)

        self._itemMenu.addSeparator()

        # 水平支撑和阻力
        action = QAction('水平支撑和阻力', self)
        action.triggered.connect(self._hsarsAct)
        self._itemMenu.addAction(action)

        # Swing
        action = QAction('Swing', self)
        action.triggered.connect(self._swingAct)
        self._itemMenu.addAction(action)

        # Trend channel
        action = QAction('趋势通道', self)
        action.triggered.connect(self._trendChannelAct)
        self._itemMenu.addAction(action)

        # 波动分布
        action = QAction('波动分布...', self)
        action.triggered.connect(self._volatilityDistAct)
        self._itemMenu.addAction(action)

        # ATR Extreme通道
        action = QAction('ATR Extreme', self)
        action.triggered.connect(self._atrExtremeAct)
        self._itemMenu.addAction(action)

        # 波动率
        action = QAction('波动率', self)
        action.triggered.connect(self._volatilityAct)
        self._itemMenu.addAction(action)

    #------------------------------------------- Item Actions -------------------------------------------
    def _intraDayKLineAct(self):
        code, date = self.getRightClickCodeDate()
        if code is None: return

        # get triggered action
        for action in self._intraDayKLineMenu.actions():
            if action.isChecked():
                action.setChecked(False)

                text = action.text()
                barUnit = 'min' if text[-1] == '分' else 'S'
                bar = text[:-1] + barUnit
                break

        self._dataViewer.plotIntraDayCandleStick(code, [0, date, 0], bar)

    def _dealsDistAct(self):
        code, date = self.getRightClickCodeDate()
        if code is None: return

        # get triggered action
        for action in self._dealsDistMenu.actions():
            if action.isChecked():
                action.setChecked(False)

                text = action.text()
                try:
                    n = -int(text[2:-1]) + 1
                except Exception as ex:
                    n = 0

                break

        self._dataViewer.plotDealsDist(code, date, n)

    def _timeShareChartAct(self):
        code, date = self.getRightClickCodeDate()
        if code is None: return

        # get triggered action
        for action in self._timeShareChartMenu.actions():
            if action.isChecked():
                action.setChecked(False)

                text = action.text()
                try:
                    n = -int(text[2:-1])
                except Exception as ex:
                    n = 0

                break

        self._dataViewer.plotTimeShareChart(code, date, n)

    def _dealDetailsAct(self):
        code, date = self.getRightClickCodeDate()
        if code is None: return

        window = DyStockDealDetailsMainWindow(self._dataViewer, self)
        window.set(code, date)

        window.show()

    def _stockInfoAct(self):
        code, name = self.getRightClickCodeName()
        if code is None: return

        browser = DyWebView()
        url = 'http://basic.10jqka.com.cn/32/{0}/'.format(code[:-3])
        browser.load(QUrl(url))

        browser.setWindowTitle(name)
        
        rect = QApplication.desktop().availableGeometry()
        taskBarHeight = QApplication.desktop().height() - rect.height()

        browser.resize(rect.width()//3 * 2, rect.height() - taskBarHeight)
        browser.move((rect.width() - browser.width())//2, 0)

        browser.show()

        self._windows.append(browser)

    def _newIndustryCompareWindow(self, code, name, baseDate, dfs):
        window = DyStockIndustryCompareWindow(self._eventEngine, DyStockTableWidget, code, name, baseDate)

        window.addCategorys(dfs)

        window.setWindowTitle('行业对比[{0}]-基准日期[{1}]'.format(name, baseDate))
        window.showMaximized()

        self._windows.append(window)

    def _stockTableIndustryCompareActAckSignalEmitWrapper(self, event):
        self.stockTableIndustryCompareActAckSignal.emit(event)

    def _stockTableIndustryCompareActAckHandler(self, event):
        if self is not event.data['self']:
            return

        code, name, baseDate, categoryDfs = event.data['args']
        if not categoryDfs:
            return

        self._newIndustryCompareWindow(code, name, baseDate, categoryDfs)

        self._curActionOngoing = False

    def _industryCompareAct(self):
        def _func(self, code, name, baseDate, forwardNTDays, industry2, industry3):
            categoryDfs = self._getIndustryCompare(code, baseDate, forwardNTDays, industry2, industry3)

            self._actAck(DyEventType.stockTableIndustryCompareActAck, code, name, baseDate, categoryDfs)

        code, date = self.getRightClickCodeDate()
        if code is None: return

        code, name = self.getRightClickCodeName()

        data = {}
        if not DyStockIndustryCompareDlg(name, date, data).exec_():
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, code, name, date, data['forwardNTDays'], data['industry2'], data['industry3']))
        t.start()

    def _hsarsAct(self):
        code, date = self.getRightClickCodeDate()
        if code is None: return

        self._dataViewer.plotHSARs(code, [-DyStockCommon.dayKChartPeriodNbr, date, DyStockCommon.dayKChartPeriodNbr])

    def _swingAct(self):
        code, date = self.getRightClickCodeDate()
        if code is None: return

        self._dataViewer.plotSwingChart(code, [-DyStockCommon.dayKChartPeriodNbr, date, DyStockCommon.dayKChartPeriodNbr])

    def _trendChannelAct(self):
        code, date = self.getRightClickCodeDate()
        if code is None: return

        self._dataViewer.plotTrendChannelChart(code, [-DyStockCommon.dayKChartPeriodNbr, date, DyStockCommon.dayKChartPeriodNbr])

    def _volatilityDistAct(self):
        code, date = self.getRightClickCodeDate()
        if code is None: return

        code, name = self.getRightClickCodeName()

        data = {}
        if not DySingleEditDlg(data, '波动分布[{0}]'.format(name), '基准日期[{0}]向前N日(不包含基准日期)'.format(date), 90).exec_():
            return

        self._dataViewer.plotVolatilityDist(code, date, data['data'])

    def _atrExtremeAct(self):
        code, date = self.getRightClickCodeDate()
        if code is None: return

        self._dataViewer.plotAtrExtreme(code, [-DyStockCommon.dayKChartPeriodNbr, date, DyStockCommon.dayKChartPeriodNbr])

    def _volatilityAct(self, item):
        code, date = self.getRightClickCodeDate()
        if code is None:
            return

        data = {}
        if DySingleEditDlg(data, '波动率*√量比(%)', '均值周期', default=20).exec_():
            volatilityVolumePeriod = int(data['data'])

            self._dataViewer.plotVolatilityChart(code, [-DyStockCommon.dayKChartPeriodNbr, date, DyStockCommon.dayKChartPeriodNbr], volatilityVolumePeriod)

    def _itemDoubleClicked(self, item):
        code, baseDate = self.getCodeDate(item)
        if code is None or baseDate is None:
            return

        self._dataViewer.plotCandleStick(code, [-DyStockCommon.dayKChartPeriodNbr, baseDate, DyStockCommon.dayKChartPeriodNbr])

    def _export2Jqka(self, file, stocks):
        """
            @file: 要保存的绝对路径文件名
        """
        if not stocks: return

        now = datetime.now().strftime("%Y_%m_%d %H-%M-%S")

        f = open(file, 'wb')

        nbr = struct.pack('<H', len(stocks))
        f.write(nbr)

        for stock in stocks:
            prefix = bytearray.fromhex('0721')
            f.write(prefix)

            code = stock[:-3]
            code = code.encode('ascii')
            f.write(code)

        f.close()

    #---------------------------------------------- Header Actions ----------------------------------------------
    def _limitUpRatioAct(self):
        colData = self.getColumnsData([self._rightClickHeaderItem.text()])

        limitUpNbr, nonLimitUpNbr = 0, 0
        for row in colData:
            if row is None:
                continue

            value = row[0]

            try:
                value = float(value)
            except Exception:
                continue

            if value >= DyStockCommon.limitUpPct:
                limitUpNbr += 1
            else:
                nonLimitUpNbr += 1

        totalNbr = limitUpNbr + nonLimitUpNbr

        table = DyTableWidget(readOnly=True, index=False)
        table.setColNames(['涨停', '非涨停', '涨停占比(%)'])
        table.appendRow([limitUpNbr, nonLimitUpNbr, limitUpNbr/totalNbr*100])

        table.setWindowTitle('涨停比')
        table.resize(QApplication.desktop().size().width()//2, QApplication.desktop().size().height()//3)
        table.show()

        self._windows.append(table)

    def _upDownRatioAct(self):
        colData = self.getColumnsData([self._rightClickHeaderItem.text()])

        upNbr, downNbr, noChangeNbr = 0, 0, 0
        for row in colData:
            if row is None:
                continue
            value = row[0]

            try:
                value = float(value)
            except Exception:
                continue

            if value > 0:
                upNbr += 1
            elif value < 0:
                downNbr += 1
            else:
                noChangeNbr += 1

        totalNbr = upNbr + downNbr + noChangeNbr

        table = DyTableWidget(readOnly=True, index=False)
        table.setColNames(['涨', '跌', '平', '上涨占比(%)', '下跌占比(%)', '平占比(%)'])
        table.appendRow([upNbr, downNbr, noChangeNbr, upNbr/totalNbr*100, downNbr/totalNbr*100, noChangeNbr/totalNbr*100])

        table.setWindowTitle('涨跌比')
        table.resize(QApplication.desktop().size().width()//2, QApplication.desktop().size().height()//3)
        table.show()

        self._windows.append(table)

    def _actAck(self, eventType, *args):
        event = DyEvent(eventType)
        event.data['self'] = self
        event.data['args'] = args

        self._eventEngine.put(event)

    def _stockTableAddColumnsActAckSignalEmitWrapper(self, event):
        self.stockTableAddColumnsActAckSignal.emit(event)

    def _addIncreaseColumnsAct(self):
        def _func(self, dateCodeList, data):
            dateCodeIncreaseList = DyStockDataAssembler.getStockIndexIncrease(self._daysEngine, dateCodeList, data['days'], data['backward'], DyProgress(self._errorProgressInfo))
            if dateCodeIncreaseList is None:
                self._actAck(DyEventType.stockTableAddColumnsActAck, None, None)
                return

            colNames, colData = DyStockDataAssembler.flatStockIndexIncrease(dateCodeIncreaseList, data['days'], data['backward'])

            self._actAck(DyEventType.stockTableAddColumnsActAck, colNames, colData)

        data = {}
        if not DyStockTableAddColumnsDlg(data, '涨幅').exec_():
            return

        dateCodeList = self.getDateCodeList()
        if not dateCodeList:
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, dateCodeList, data))
        t.start()

    def _addMaxMinIncreaseColumnsAct(self):
        def _func(self, dateCodeList, data):
            dateCodeIncreaseList = DyStockDataAssembler.getStockIndexMaxMinIncrease(self._daysEngine, dateCodeList, data['days'], data['backward'], DyProgress(self._errorProgressInfo))
            if dateCodeIncreaseList is None:
                self._actAck(DyEventType.stockTableAddColumnsActAck, None, None)
                return

            colNames, colData = DyStockDataAssembler.flatStockIndexMaxMinIncrease(dateCodeIncreaseList, data['days'], data['backward'])

            self._actAck(DyEventType.stockTableAddColumnsActAck, colNames, colData)

        data = {}
        if not DyStockTableAddColumnsDlg(data, '最大最小涨幅').exec_():
            return

        dateCodeList = self.getDateCodeList()
        if not dateCodeList:
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, dateCodeList, data))
        t.start()

    def _addMaxAmplitudeColumnsAct(self):
        def _func(self, dateCodeList, data):
            dateCodeIncreaseList = DyStockDataAssembler.getStockIndexMaxAmplitude(self._daysEngine, dateCodeList, data['days'], data['backward'], DyProgress(self._errorProgressInfo))
            if dateCodeIncreaseList is None:
                self._actAck(DyEventType.stockTableAddColumnsActAck, None, None)
                return

            colNames, colData = DyStockDataAssembler.flatStockIndexMaxAmplitude(dateCodeIncreaseList, data['days'], data['backward'])

            self._actAck(DyEventType.stockTableAddColumnsActAck, colNames, colData)

        data = {}
        if not DyStockTableAddColumnsDlg(data, '振幅').exec_():
            return

        dateCodeList = self.getDateCodeList()
        if not dateCodeList:
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, dateCodeList, data))
        t.start()

    def _addOpenGapColumnsAct(self):
        """
            添加开盘缺口列,0代表是当日。若T日没有缺口,则T日是None。正值是向上缺口,负值是向下缺口。
        """
        def _func(self, dateCodeList, data):
            rows = DyStockDataAssembler.getStockOpenGap(self._daysEngine, dateCodeList, data['days'], data['backward'], DyProgress(self._errorProgressInfo))
            if rows is None:
                self._actAck(DyEventType.stockTableAddColumnsActAck, None, None)
                return

            colNames, colData = DyStockDataAssembler.flatStockOpenGap(rows, data['days'], data['backward'])

            self._actAck(DyEventType.stockTableAddColumnsActAck, colNames, colData)

        data = {'days': [0, 1]}
        if not DyStockTableAddColumnsDlg(data, '开盘缺口(0只能出现在向前)', backward=False).exec_():
            return

        dateCodeList = self.getDateCodeList()
        if not dateCodeList:
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, dateCodeList, data))
        t.start()

    def _stockTableAddColumnsActAckHandler(self, event):
        if self is not event.data['self']:
            return

        colNames, colData = event.data['args']
        if colNames is None:
            return

        self.fastAppendColumns(colNames, colData)

        self._curActionOngoing = False

    def _addErColumnsAct(self):
        def _func(self, dateCodeList, data):
            dateCodeErList = DyStockDataAssembler.getStockIndexEr(self._daysEngine, dateCodeList, data['days'], data['backward'], DyProgress(self._errorProgressInfo))
            if dateCodeErList is None:
                self._actAck(DyEventType.stockTableAddColumnsActAck, None, None)
                return

            colNames, colData = DyStockDataAssembler.flatStockIndexEr(dateCodeErList, data['days'], data['backward'])

            self._actAck(DyEventType.stockTableAddColumnsActAck, colNames, colData)

        data = {'days': [20, 30, 40, 50, 60]}
        if not DyStockTableAddColumnsDlg(data, 'Efficiency Ratio', backward=False).exec_():
            return

        dateCodeList = self.getDateCodeList()
        if not dateCodeList:
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, dateCodeList, data))
        t.start()

    def _addVolatilityColumnsAct(self):
        def _func(self, dateCodeList, data):
            dateCodeVolatilityList = DyStockDataAssembler.getStockIndexVolatility(self._daysEngine, dateCodeList, data['days'], data['backward'], DyProgress(self._errorProgressInfo))
            if dateCodeVolatilityList is None:
                self._actAck(DyEventType.stockTableAddColumnsActAck, None, None)
                return

            colNames, colData = DyStockDataAssembler.flatStockIndexVolatility(dateCodeVolatilityList, data['days'], data['backward'])

            self._actAck(DyEventType.stockTableAddColumnsActAck, colNames, colData)

        data = {'days': [20, 30, 40, 50, 60]}
        if not DyStockTableAddColumnsDlg(data, '波动率', backward=False).exec_():
            return

        dateCodeList = self.getDateCodeList()
        if not dateCodeList:
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, dateCodeList, data))
        t.start()

    def _addMinuteIncreaseColumnsAct(self):
        def _func(self, dateCodeList, data):
            retData = DyStockDataAssembler.getStockEtfMinuteIncrease(self._dataEngine, dateCodeList, data['data'], DyProgress(self._errorProgressInfo))
            if retData is None:
                self._actAck(DyEventType.stockTableAddColumnsActAck, None, None)
                return

            colNames, colData = DyStockDataAssembler.flatStockEtfMinuteIncrease(retData, data['data'])

            self._actAck(DyEventType.stockTableAddColumnsActAck, colNames, colData)

        data = {'data': '5,10,15'}
        if not DySingleEditDlg(data, '分钟涨幅(%)', '基准日期开盘几分钟涨幅(%)').exec_():
            return
        
        if isinstance(data['data'], str):
            data['data'] = [int(x) for x in data['data'].split(',')]
        else:
            data['data'] = [data['data']]

        dateCodeList = self.getDateCodeList()
        if not dateCodeList:
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, dateCodeList, data))
        t.start()

    def _addOpenIncreaseColumnsAct(self):
        def _func(self, dateCodeList):
            retData = DyStockDataAssembler.getStockIndexOpenIncrease(self._daysEngine, dateCodeList, DyProgress(self._errorProgressInfo))
            if retData is None:
                self._actAck(DyEventType.stockTableAddColumnsActAck, None, None)
                return

            colNames, colData = DyStockDataAssembler.flatStockIndexOpenIncrease(retData)

            self._actAck(DyEventType.stockTableAddColumnsActAck, colNames, colData)

        dateCodeList = self.getDateCodeList()
        if not dateCodeList:
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, dateCodeList))
        t.start()

    def _addDayReturnIndexCorrColumnsAct(self):
        def _func(self, dateCodeList, data):
            colNames, colData = DyStockDataAssembler.getStockIndexDayReturnCorr(self._daysEngine, dateCodeList, data['days'], data['backward'], DyProgress(self._errorProgressInfo))

            self._actAck(DyEventType.stockTableAddColumnsActAck, colNames, colData)

        data = {'days': [20, 30, 40, 50, 60]}
        if not DyStockTableAddColumnsDlg(data, '股票大盘日收益率相关系数', backward=False).exec_():
            return

        dateCodeList = self.getDateCodeList()
        if not dateCodeList:
            return

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, dateCodeList, data))
        t.start()

    def _newWindowAct(self):
        self._newWindow()

    def _newWindow(self, rows=None):
        """
            子类可改写
        """
        window = DyStockTableWidget(self._eventEngine,
                                    name=self._name,
                                    baseDate=self._baseDate,
                                    readOnly=True,
                                    index=False,
                                    autoScroll=False,
                                    floatRound=2
                                    )

        if rows is None:
            rows = self.getAll()

        window.appendStocks(rows, self.getColNames(), self.getAutoForegroundColName())

        window.setWindowTitle(window.name)
        window.showMaximized()

        self._windows.append(window)

    def _addStockInfoColumnsAct(self):
        def _func(self, codePrices, indicators):
            progress = DyProgress(self._errorProgressInfo)
            progress.init(len(codePrices))

            # get data from Spider
            names = []
            data = []
            first = True
            for code, price in codePrices:
                rowData = []

                # 公司资料
                colNames, colData = DyStockDataSpider.getCompanyInfo(code, indicators)
                if first:
                    names += colNames

                rowData += colData

                # 股本
                if '实际流通股(亿)' in indicators:
                    freeShares, type = DyStockDataSpider.getLatestRealFreeShares(code)
                    if first:
                        names += ['实际流通股(亿)', '股份类型']

                    rowData += [freeShares, type]

                if '实际流通市值(亿元)' in indicators:
                    if '实际流通股(亿)' not in indicators:
                        freeShares, type = DyStockDataSpider.getLatestRealFreeShares(code)

                    if first:
                        names += ['实际流通市值(亿元)']

                    rowData += [freeShares*price]

                if '机构占比流通(%)' in indicators:
                    fundPosRatio, fundNbr = DyStockDataSpider.getLatestFundPositionsRatio(code)

                    if first:
                        names += ['机构占比流通(%)']

                    rowData += [fundPosRatio]
                
                # post process
                if rowData:
                    data.append(rowData)

                first = False
                progress.update()

            self._actAck(DyEventType.stockTableAddColumnsActAck, names, data)

        codePrices = self.getCodePriceList()
        if not codePrices: return

        data = {}
        if not DyStockInfoDlg(data).exec_():
            return

        indicators = data['indicators']

        self._curActionOngoing = True
        t = threading.Thread(target=_func, args=(self, codePrices, indicators))
        t.start()

    def _addColumnOperateColumnsAct(self):
        data = {}
        if DyStockTableColumnOperateDlg(data, self.getColNames()).exec_():
            self.addColumnOperateColumns(data['exp'])

    def _filterAct(self):
        data = {}
        if DyStockTableFilterDlg(data, self.getColNames()).exec_():
            self._filter(data['filter'], data['newWindow'], data['highlight'])

    def _filter(self, filter, newWindow, highlight):
        filterRows = self.filter(filter, highlight)

        if newWindow:
            self._newWindow(rows=filterRows)

    def _export2JqkaAct(self):
        data = {}
        if not DyStockTableSelectDlg(data, '{0}导出到同花顺'.format(self.getUniqueName())).exec_():
            return

        defaultFileName = '{0}.sel' if data['all'] else '{0}_高亮.sel'
        defaultFileName = defaultFileName.format(self.getUniqueName())

        defaultDir = DyCommon.createPath('Stock/User/Save/Strategy/同花顺')
        fileName, _ = QFileDialog.getSaveFileName(self, '导出到同花顺', os.path.join(defaultDir, defaultFileName), "同花顺files (*.sel);;all files(*.*)")
        if fileName:
            self.export2Jqka(fileName)

    def _saveAsAct(self):
        data = {}
        if not DyStockTableSelectDlg(data, '{0}保存'.format(self.getUniqueName())).exec_():
            return

        defaultFileName = '{0}.json' if data['all'] else '{0}_高亮.json'
        defaultFileName = defaultFileName.format(self.getUniqueName())

        defaultDir = DyCommon.createPath('Stock/User/Save/Strategy')
        fileName, _ = QFileDialog.getSaveFileName(self, '保存股票表', os.path.join(defaultDir, defaultFileName), "JSON files (*.json);;all files(*.*)")
        if fileName:
            self._saveAs(fileName, data['all'])

    def _saveAs(self, fileName, all=True):
        """
            重载@getCustomSaveData定义自己的保存格式
            共同数据:
            {
            'autoForegroundColName': autoForegroundColName,
            'data': {'colNames': colNames, 'rows': rows}
            }
        """
        rows = self.getAll() if all else self.getHighlights()
        colNames, autoForegroundColName = self.getColNames(), self.getAutoForegroundColName()
        
        # 子类可以重载@getCustomSaveData
        customData = self.getCustomSaveData()

        data = {'name': self._name,
                'autoForegroundColName': autoForegroundColName,
                'baseDate': self._baseDate,
                'data': {'colNames': colNames, 'rows': rows}
               }

        data.update(**customData)

        with open(fileName, 'w') as f:
            f.write(json.dumps(data, indent=4))

    #-------------------------------------- 股票行业比较 --------------------------------------
    # !!!原则上应该放到@DyStockDataSpider,但改动比较大,暂时先这么实现
    def _toFloat(self, value):
        try:
            value = float(value)
        except:
            try:
                value = float(value[:-1]) # e.g value like '15.06%'
            except:
                value = 0

        return value

    def _getCompanyFinanceOutline1(self, code, indicators):
        """
            从财务报表获取指定的指标
            @indicators: []
        """
        mainLink = 'http://basic.10jqka.com.cn/{0}/flash/main.txt'.format(code[:-3])
        r = requests.get(mainLink)

        table = dict(json.loads(r.text))

        values = {}
        for indicator in indicators:
            # get @indicator position
            pos = None
            for i, e in enumerate(table['title']):
                if isinstance(e, list):
                    if e[0] == indicator:
                        pos = i
                        break

            # 指标最近的值
            value = self._toFloat(table['report'][pos][0])
            values[indicator] = value

        return values

    def _getCompanyFinanceOutline(self, code):
        """
            从同花顺'最新动态'网页获取财务概要信息
        """
        def getIndex(x):
            for i, name in enumerate(colNames):
                if x in name:
                    return i

            return None

        colNames = ['市盈率(动态)', '市净率', '每股收益', '每股现金流', '每股净资产', '净资产收益率(%)', '营业收入YoY(%)', '净利润YoY(%)', '流通A股(亿股)', '总股本(亿股)']
        colData = [None]*len(colNames)

        # 缺失的数据从财务报表里获取
        latest2FinanceMap = {'营业收入YoY(%)': '营业总收入同比增长率', '净利润YoY(%)': '净利润同比增长率'}
        finance2LatestMap = {value: key for key, value in latest2FinanceMap.items()}

        try:
            r = requests.get('http://basic.10jqka.com.cn/16/{0}/'.format(code[:-3]))

            soup = BeautifulSoup(r.text, 'lxml')

            table = soup.find('table', class_="m_table m_table_db mt10")
            tds = table.find_all('td')

            for td in tds:
                spans = td.find_all('span')

                indicator = str(spans[0].string)[:-1]
                index = getIndex(indicator)
                if index is None: continue

                value = None
                if '%' in colNames[index]:
                    for span in spans[1:]:
                        if '%' in str(span.string):
                            value = str(span.string)
                            break
                else:
                    value = str(spans[1].string)

                if value is not None:
                    positive = True if '下降' not in value else False

                    value = re.findall(r"-?\d+\.?\d*", value)
                    if value:
                        value = float(value[0]) if positive else -float(value[0])
                        colData[index] = value

            # 处理缺失数据
            indicators = []
            for key, value in latest2FinanceMap.items():
                index = colNames.index(key)
                if colData[index] is None:
                    indicators.append(value)

            # 从财务报表获取缺失数据的最新值
            if indicators:
                data = self._getCompanyFinanceOutline1(code, indicators)
                for key, value in data.items():
                    index = colNames.index(finance2LatestMap[key])
                    colData[index] = value

        except Exception as ex:
            pass

        return colNames, colData

    def _getIndustryCompareTable(self, div, id):
        colNames = ['销售毛利率']
        colNamesForReturn = ['销售毛利率(%)']
        colPoses = {}
        colData = {}

        try:
            table = div.find('table', class_='m_table m_hl', id=id)

            # 获取每个指标的位置
            tag = table.find('thead')
            ths = tag.find_all('th')

            for i, th in enumerate(ths):
                indicator = str(th.contents[0])
                if indicator in colNames:
                    colPoses[indicator] = i

            assert(len(colNames) == len(colPoses))

            # 获取指定date的指标值
            tag = table.find('tbody')
            trs = tag.find_all('tr')

            for tr in trs:
                tds = tr.find_all('td')
                code = DyStockCommon.getDyStockCode(str(tds[0].string))
                name = str(tds[1].string)

                data = [code, name]
                for indicator in colNames:
                    data.append(self._toFloat(tds[colPoses[indicator]].string))

                colData[code] = data

        except Exception as ex:
            pass

        return ['代码', '名称'] + colNamesForReturn, colData

    def _getIndustryComparePartly(self, code, industry2, industry3):
        totalData = {}
        tableColNames = []

        try:
            r = requests.get('http://basic.10jqka.com.cn/16/{0}/field.html'.format(code[:-3]))

            soup = BeautifulSoup(r.text, 'lxml')

            divIds = {"hy3_div": industry3, "hy2_div": industry2}
            pTexts = {"hy3_div": '三级行业分类:', "hy2_div": '二级行业分类:'}
            tableIds = {"hy3_div": ["hy3_table_1", "hy3_table_2"], "hy2_div": ["hy2_table_1", "hy2_table_2"]}
        
            for divId, bool in divIds.items():
                if not bool: continue

                div = soup.find('div', id=divId)
                if div is None: continue

                # 行业分类
                category = div.parent.find(text=pTexts[divId])
                category = category.parent
                categoryHead = str(category.contents[0])
                span = category.find('span')
                categoryBody = str(span.contents[0][:-3])

                category = categoryHead + categoryBody

                # table
                tableColDataTotal = {}
                for tableId in tableIds[divId]:
                    tableColNames, tableColData = self._getIndustryCompareTable(div, tableId)

                    for code in tableColData:
                        if code not in tableColDataTotal:
                            tableColDataTotal[code] = tableColData[code]

                totalData[category] = tableColDataTotal
        except Exception as ex:
            pass

        return tableColNames, totalData

    def _calcIndustryCompareScore(self, categoryDfs):
        for category, df in categoryDfs.items():
            # rank for each indicator, think rank as score that the high score is the better is
            seriesList = []

            series = df['市盈率(动态)'].rank(ascending=False)
            seriesList.append(series)

            series = df['市净率'].rank(ascending=False)
            seriesList.append(series)

            series = df['净资产收益率(%)'].rank()
            seriesList.append(series)

            series = df['每股现金流'].rank()
            seriesList.append(series)

            series = df['营业收入YoY(%)'].rank()
            seriesList.append(series)

            series = df['净利润YoY(%)'].rank()
            seriesList.append(series)

            series = df['销售毛利率(%)'].rank()
            seriesList.append(series)

            rankDf = pd.concat(seriesList, axis=1)

            # total rank
            series = rankDf.sum(axis=1)*100/(len(seriesList) * rankDf.shape[0])
            series.name = '得分'

            df = pd.concat([df, series], axis=1)
            columns = list(df.columns)
            df = df.reindex(columns=columns[:2] + columns[-1:] + columns[2:-1])

            categoryDfs[category] = df

    def _getIndustryCompare(self, code, baseDate, forwardNTDays, industry2, industry3):
        # 获取同行业的数据
        name, data = self._getIndustryComparePartly(code, industry2, industry3)

        # 合并代码表
        codes = set()
        for _, data_ in data.items():
            codes.update(list(data_.keys()))
        codes = list(codes)

        # 获取股票的基本财务信息
        progress = DyProgress(self._errorProgressInfo)
        progress.init(len(codes))

        financeOutline = {}
        for code in codes:
            outlineNames, outlineData = self._getCompanyFinanceOutline(code)
            financeOutline[code] = outlineData

            progress.update()

        financeOutlineDf = pd.DataFrame(financeOutline, index=outlineNames).T

        # 根据行业分级合并数据
        categoryDfs = {}
        for category, data_ in data.items():
            df = pd.DataFrame(data_, index=name).T
            df = pd.concat([df, financeOutlineDf], axis=1)
            df = df[df[name[0]].notnull()]

            df = df.reindex(columns=name[:2] + outlineNames[:-2] + name[2:] + outlineNames[-2:])

            categoryDfs[category] = df

        # 计算得分
        self._calcIndustryCompareScore(categoryDfs)

        # 获取前N日涨幅
        daysEngine = self._daysEngine
        if not daysEngine.load([baseDate, -forwardNTDays], codes=codes):
            return categoryDfs

        # 计算前N日涨幅
        autoForegroundColName = '前{0}日涨幅(%)'.format(forwardNTDays)
        pcts = {}
        for code in codes:
            df = daysEngine.getDataFrame(code)
            if df is not None and not df.empty:
                pct = (df.ix[-1, 'close'] - df.ix[0, 'close'])*100/df.ix[0, 'close']
                pcts[code] = [df.ix[-1, 'close'], pct]
            
        # 获取指定周期内停牌股票的最新收盘价
        for code in codes:
            if code not in pcts:
                # 同花顺可能会含有终止上市的股票或者没有上市的股票
                if daysEngine.loadCode(code, [baseDate, 0]):
                    df = daysEngine.getDataFrame(code)
                    pcts[code] = [df.ix[-1, 'close'], None]

        pctDf = pd.DataFrame(pcts, index=['当日价格', autoForegroundColName]).T

        # 根据行业分级合并数据
        for category, df in categoryDfs.items():
            df = pd.concat([df, pctDf], axis=1)
            df = df[df.ix[:,0].notnull()]

            df.sort_values('得分', axis=0, ascending=False, inplace=True)

            # 添加市值
            df['流通市值(亿元)'] = df['流通A股(亿股)'] * df['当日价格']
            df['总市值(亿元)'] = df['总股本(亿股)'] * df['当日价格']

            columns = list(df.columns)
            df = df.reindex(columns=columns[:-4] + columns[-2:] + columns[-4:-2])

            categoryDfs[category] = df
         
        return categoryDfs

    #---------------------------------------------- interfaces ----------------------------------------------
    def export2Jqka(self, fileName):
        self._export2Jqka(fileName, self.getCodeList())

    def setBaseDate(self, baseDate):
        self._baseDate = baseDate

    def appendStocks(self, rows, header=None, autoForegroundColName=None, new=False):
        if header is not None:
            self.setColNames(header)

        self.fastAppendRows(rows, autoForegroundColName=autoForegroundColName, new=new)

    #---------------------------------------------- 由子类根据自己的Table格式改写 ----------------------------------------------
    def getDateCodeList(self):
        if self._baseDate is None:
            raise AttributeError

        codes = self.getColumnsData(['代码'])

        return [[self._baseDate, code[0]] for code in codes]

    def getCodeList(self):
        codes = self.getColumnsData(['代码'])

        return [code[0] for code in codes]

    def getCodePriceList(self):
        return self.getColumnsData(['代码', '当日价格'])

    def getRightClickCodeDate(self):
        item = self.itemAt(self._rightClickPoint)
        if item is None:
            return None, None

        code = self[item.row(), '代码']

        return code, self._baseDate

    def getRightClickCodeName(self):
        item = self.itemAt(self._rightClickPoint)
        if item is None:
            return None, None

        code = self[item.row(), '代码']
        name = self[item.row(), '名称']

        return code, name

    def getCodeDate(self, item):
        row = self.row(item)
        code = self[row, '代码']

        return code, self._baseDate

    def getUniqueName(self):
        """
            Get unique name of this table. Usually it's combined with name + baseDate
        """
        return '{0}_{1}'.format(self._name, self._baseDate)

    def getCustomSaveData(self):
        """
            获取每个类的定制保存数据
            @return: dict
        """
        return {}

    def customizeHeaderContextMenu(self, headerItem):
        """
            子类改写
        """
        self._upDownRatioAction.setEnabled('涨幅' in headerItem.text())
        self._limitUpRatioAction.setEnabled('涨幅' in headerItem.text())

    #---------------------------------------------- 属性 ----------------------------------------------
    @property
    def dataViewer(self):
        return self._dataViewer

    @property
    def name(self):
        return self._name

    @property
    def eventEngine(self):
        return self._eventEngine

    @property
    def baseDate(self):
        return self._baseDate
    def _test(self):
        event = DyEvent(DyEventType.plotReq)
        event.data['type'] = 'test'

        self._mainEngine.eventEngine.put(event)
Esempio n. 53
0
    def _actAck(self, eventType, *args):
        event = DyEvent(eventType)
        event.data['self'] = self
        event.data['args'] = args

        self._eventEngine.put(event)
Esempio n. 54
0
    def _stockPositionUpdateHandler(self, event):
        """
            收到来自券商接口的账户持仓更新事件
        """
        # unpack
        header = event.data['header']
        rows = event.data['rows']

        # 先前持仓代码
        codes = list(self._curPos)

        for data in rows:
            # unpack from 券商接口持仓数据
            code = DyStockCommon.getDyStockCode(data[header.index(
                self.headerNameMap['position']['code'])])
            name = data[header.index(self.headerNameMap['position']['name'])]

            totalVolume = float(data[header.index(
                self.headerNameMap['position']['totalVolume'])])
            availVolume = float(data[header.index(
                self.headerNameMap['position']['availVolume'])])

            price = float(data[header.index(
                self.headerNameMap['position']['price'])])
            cost = float(data[header.index(
                self.headerNameMap['position']['cost'])])

            # get position
            if code in self._curPos:
                pos = self._curPos[code]
                codes.remove(code)
            else:
                # new pos, we just take time now without accuracy
                if totalVolume > 0:
                    pos = DyStockPos(datetime.now(), None, code, name, price,
                                     totalVolume, 0)
                    pos.sync = True
                else:
                    continue

            # syn with positions from broker
            pos.price = price
            pos.cost = cost

            pos.totalVolume = totalVolume
            pos.availVolume = availVolume

            # write back
            self._curPos[code] = pos

        # 删除不在券商接口数据里的持仓
        for code in codes:
            del self._curPos[code]

        # 发送行情监控事件
        self._putStockMarketMonitorEvent()

        # 发送券商账户股票持仓更新事件
        event = DyEvent(DyEventType.stockOnPos)
        event.data['broker'] = self.broker
        event.data['pos'] = copy.deepcopy(self._curPos)

        self._eventEngine.put(event)
Esempio n. 55
0
class DyStockSelectSelectResultWidget(QTabWidget):

    stockSelectStrategySelectAckSignal = QtCore.pyqtSignal(type(DyEvent()))

    def __init__(self, eventEngine, paramWidget, registerSelectAckEvent=True):
        super().__init__()

        self._eventEngine = eventEngine
        self._paramWidget = paramWidget
        self._registerSelectAckEvent = registerSelectAckEvent

        self._strategyWidgets = {}
        self._windows = []  # only for show

        self._registerEvent()

        # 窗口事件相关
        self.setTabsClosable(True)
        self.tabCloseRequested.connect(self._closeTab)

    def _addTab(self, tabName, widget):
        self.addTab(widget, tabName)
        self._strategyWidgets[tabName] = widget

    def _refactory(self, tableWidget, params, newWindow):
        newRows = tableWidget.refactory(params)

        if newWindow:
            window = DyStockSelectSelectResultWidget(self._eventEngine, None,
                                                     False)

            widget = DyStockSelectStrategySelectResultWidget(
                self._dataViewer, tableWidget.baseDate,
                tableWidget.strategyName, self._widgetParam,
                tableWidget.strategyClsName)
            widget.append(newRows, tableWidget.getColNames(),
                          tableWidget.getAutoForegroundColName())

            window._addTab(tableWidget.strategyName, widget)

            window.setWindowTitle(tableWidget.strategyName)
            window.showMaximized()

            self._newWindows.append(window)
        else:
            tableWidget.append(newRows, tableWidget.getColNames())

    def _filter(self, tableWidget, filter, newWindow, highlight):
        filterRows = tableWidget.filter(filter, highlight)

        if newWindow:
            window = DyStockSelectSelectResultWidget(self._eventEngine, None,
                                                     False)

            widget = DyStockSelectStrategySelectResultWidget(
                self._dataViewer, tableWidget.baseDate,
                tableWidget.strategyName, self._widgetParam,
                tableWidget.strategyClsName)
            widget.append(filterRows, tableWidget.getColNames(),
                          tableWidget.getAutoForegroundColName())

            window._addTab(tableWidget.strategyName, widget)

            window.setWindowTitle(tableWidget.strategyName)
            window.showMaximized()

            self._newWindows.append(window)

    def _stockSelectStrategySelectAckHandler(self, event):
        # unpack
        strategyCls = event.data['class']
        result = event.data['result']
        baseDate = event.data['baseDate']
        if result is None: return

        # show result
        if strategyCls.chName in self._strategyWidgets:
            self._strategyWidgets[strategyCls.chName].setBaseDate(baseDate)
        else:
            # create a new widget
            widget = DyStockSelectStrategySelectResultWidget(
                self._eventEngine, strategyCls, baseDate, self._paramWidget)
            self._addTab(strategyCls.chName, widget)

        self._strategyWidgets[strategyCls.chName].appendStocks(
            result[1:], result[0])

        self.parentWidget().raise_()

    def _stockSelectStrategySelectAckSignalEmitWrapper(self, event):
        self.stockSelectStrategySelectAckSignal.emit(event)

    def _registerEvent(self):
        if self._registerSelectAckEvent:
            self.stockSelectStrategySelectAckSignal.connect(
                self._stockSelectStrategySelectAckHandler)
            self._eventEngine.register(
                DyEventType.stockSelectStrategySelectAck,
                self._stockSelectStrategySelectAckSignalEmitWrapper)

    def _unregisterEvent(self):
        if self._registerSelectAckEvent:
            self.stockSelectStrategySelectAckSignal.disconnect(
                self._stockSelectStrategySelectAckHandler)
            self._eventEngine.unregister(
                DyEventType.stockSelectStrategySelectAck,
                self._stockSelectStrategySelectAckSignalEmitWrapper)

    def _closeTab(self, index):
        tabName = self.tabText(index)
        self._strategyWidgets[tabName].close()

        del self._strategyWidgets[tabName]

        self.removeTab(index)

    def closeEvent(self, event):
        self._unregisterEvent()

        return super().closeEvent(event)

    def load(self, data, strategyCls):
        """
            @data: JSON data
        """
        className = data.get('class')
        if not className:
            return False

        if className != 'DyStockSelectStrategySelectResultWidget':
            return False

        window = DyStockSelectStrategySelectResultWidget(
            self._eventEngine, strategyCls, data['baseDate'])
        window.appendStocks(
            data['data']['rows'],
            data['data']['colNames'],
            autoForegroundColName=data['autoForegroundColName'])

        window.setWindowTitle('{0}[{1}]'.format(strategyCls.chName,
                                                data['baseDate']))
        window.showMaximized()

        self._windows.append(window)

        return True
Esempio n. 56
0
    def _stockPosSyncFromBrokerHandler(self, event):
        """
            收到来自券商接口的持仓同步事件
            !!!如果交易不是通过策略,或者持仓同步不是在开盘时,可能会导致信息不一致或者错误。
        """
        # unpack
        header = event.data['header']
        rows = event.data['rows']

        # it's synchronized from broker
        self._curPosSyncData = {}

        for data in rows:
            # unpack from 券商接口持仓数据
            code = DyStockCommon.getDyStockCode(data[header.index(
                self.headerNameMap['position']['code'])])
            name = data[header.index(self.headerNameMap['position']['name'])]

            totalVolume = float(data[header.index(
                self.headerNameMap['position']['totalVolume'])])
            availVolume = float(data[header.index(
                self.headerNameMap['position']['availVolume'])])

            price = float(data[header.index(
                self.headerNameMap['position']['price'])])
            cost = float(data[header.index(
                self.headerNameMap['position']['cost'])])

            # get position
            pos = self._curPos.get(code)
            if pos is None:
                continue

            # set sync data firstly
            self._curPosSyncData[code] = {
                'volumeAdjFactor': totalVolume / pos.totalVolume,
                'priceAdjFactor': pos.cost / cost,
                'cost': cost
            }

            # syn with positions from broker
            pos.price = price
            pos.cost = cost

            pos.totalVolume = totalVolume
            pos.availVolume = availVolume

            pos.priceAdjFactor = self._curPosSyncData[code]['priceAdjFactor']
            pos.volumeAdjFactor = self._curPosSyncData[code]['volumeAdjFactor']

            if pos.priceAdjFactor != 1:
                pos.xrd = True

            pos.sync = True

        # 发送行情监控事件
        self._putStockMarketMonitorEvent()

        # 发送股票持仓同步事件
        event = DyEvent(DyEventType.stockPosSyncFromAccountManager)
        event.data['broker'] = self.broker
        event.data['data'] = self._curPosSyncData

        self._eventEngine.put(event)
class DyStockTradeStrategySellDlg(QDialog):

    stockMarketTicksSignal = QtCore.pyqtSignal(type(DyEvent()))
    stockStrategyPosAckSignal = QtCore.pyqtSignal(type(DyEvent()))
    stockStrategyPosUpdateSignal = QtCore.pyqtSignal(type(DyEvent()))

    def __init__(self, eventEngine, strategyCls):
        super().__init__()

        self._eventEngine = eventEngine
        self._strategyCls = strategyCls

        self._code = None  # current stock code
        self._tick = None  # current tick of @self._code
        self._curPos = None  # 策略持仓字典

        self._initUi()

        self._registerEvent()

        self._init()

    def _init(self):
        event = DyEvent(DyEventType.stockStrategyPosReq)
        event.data = self._strategyCls

        self._eventEngine.put(event)

    def _initUi(self):
        self.setWindowTitle(self._strategyCls.chName)

        # 策略持仓
        posLabel = QLabel('策略持仓')
        posLabel.setStyleSheet("color:#4169E1")

        self._posWidget = DyStockTradeStrategyPosSellWidget(
            self, self._eventEngine, self._strategyCls)

        # 卖出
        sellCodeLabel = QLabel('股票代码')
        sellCodeLabel.setStyleSheet("color:#4169E1")
        self._sellCodeLineEdit = QLineEdit()

        sellVolumeLabel = QLabel('数量(手)')
        sellVolumeLabel.setStyleSheet("color:#4169E1")
        self._sellVolumeLineEdit = QLineEdit('1')

        sellPriceLabel = QLabel('价格(元)')
        sellPriceLabel.setStyleSheet("color:#4169E1")
        self._sellPriceLineEdit = QLineEdit()

        # 行情
        self._codeLabel = QLabel('股票代码')
        self._nameLabel = QLabel('股票名称')
        self._priceLabel = QLabel('股票现价')
        self._increaseLabel = QLabel('涨幅(%):')

        self._bidAskTable = DyTableWidget(readOnly=True,
                                          index=False,
                                          floatRound=3)
        self._bidAskTable.setColNames([None, '价格(元)', '数量(手)'])
        self._bidAskTable.fastAppendRows([['卖5', None,
                                           None], ['卖4', None, None],
                                          ['卖3', None,
                                           None], ['卖2', None, None],
                                          ['卖1', None,
                                           None], [None, None, None],
                                          ['买1', None,
                                           None], ['买2', None, None],
                                          ['买3', None,
                                           None], ['买4', None, None],
                                          ['买5', None, None]])

        cancelPushButton = QPushButton('Cancel')
        okPushButton = QPushButton('卖出')
        cancelPushButton.clicked.connect(self._cancel)
        okPushButton.clicked.connect(self._ok)

        # 布局
        grid = QGridLayout()
        grid.setSpacing(10)

        grid.addWidget(posLabel, 0, 0)
        grid.addWidget(self._posWidget, 1, 0, 10, 10)

        start = 12

        grid.addWidget(sellCodeLabel, start + 0, 0)
        grid.addWidget(self._sellCodeLineEdit, start + 1, 0)
        grid.addWidget(sellVolumeLabel, start + 2, 0)
        grid.addWidget(self._sellVolumeLineEdit, start + 3, 0)
        grid.addWidget(sellPriceLabel, start + 4, 0)
        grid.addWidget(self._sellPriceLineEdit, start + 5, 0)

        grid.addWidget(self._codeLabel, start + 0, 1, 1, 10)
        grid.addWidget(self._nameLabel, start + 1, 1)
        grid.addWidget(self._priceLabel, start + 2, 1)
        grid.addWidget(self._increaseLabel, start + 3, 1)
        grid.addWidget(self._bidAskTable, start + 4, 1, 30, 10)

        grid.addWidget(okPushButton, start + 6, 0)
        grid.addWidget(cancelPushButton, start + 7, 0)

        self.setLayout(grid)
        self.setMinimumWidth(QApplication.desktop().size().width() // 3)

        self._sellCodeLineEdit.textChanged.connect(self._sellCodeChanged)

    def _ok(self):
        try:
            if self._codeLabel.text() != self._tick.code:
                QMessageBox.warning(self, '错误', '没有指定代码的Tick数据!')
                return
        except Exception:
            QMessageBox.warning(self, '错误', '没有指定代码的Tick数据!')
            return

        event = DyEvent(DyEventType.stockStrategyManualSell)
        event.data['class'] = self._strategyCls
        event.data['tick'] = self._tick
        event.data['volume'] = float(self._sellVolumeLineEdit.text()) * 100

        # 不指定价格,则根据tick买入
        price = self._sellPriceLineEdit.text()
        event.data['price'] = float(price) if price else None

        self._eventEngine.put(event)

        self._unregisterEvent()

        self._posWidget.close()
        self.accept()

    def _cancel(self):
        self._unregisterEvent()

        self._posWidget.close()
        self.reject()

    def setSellCodePrice(self, code, price):
        self._sellCodeLineEdit.setText(code[:6])
        self._sellPriceLineEdit.setText(str(price))

    def _getInputCode(self):
        if not self._curPos:
            return None

        code = self._sellCodeLineEdit.text()
        if len(code) != 6:
            return None

        code = DyStockCommon.getDyStockCode(code)
        if code not in self._curPos:
            return None

        return code

    def _sellCodeChanged(self):
        self._code = self._getInputCode()
        if self._code is None:
            self._codeLabel.setText('输入代码在策略持仓里不存在!')
            return

        self._codeLabel.setText(self._code)

    def _stockStrategyPosAckSignalEmitWrapper(self, event):
        self.stockStrategyPosAckSignal.emit(event)

    def _stockMarketTicksSignalEmitWrapper(self, event):
        self.stockMarketTicksSignal.emit(event)

    def _stockStrategyPosUpdateSignalEmitWrapper(self, event):
        self.stockStrategyPosUpdateSignal.emit(event)

    def _registerEvent(self):
        self.stockMarketTicksSignal.connect(self._stockMarketTicksHandler)
        self._eventEngine.register(DyEventType.stockMarketTicks,
                                   self._stockMarketTicksSignalEmitWrapper)

        self.stockStrategyPosAckSignal.connect(
            self._stockStrategyPosAckHandler)
        self._eventEngine.register(DyEventType.stockStrategyPosAck,
                                   self._stockStrategyPosAckSignalEmitWrapper)

        self.stockStrategyPosUpdateSignal.connect(
            self._stockStrategyPosUpdateHandler)
        self._eventEngine.register(
            DyEventType.stockStrategyPosUpdate + self._strategyCls.name,
            self._stockStrategyPosUpdateSignalEmitWrapper)

    def _unregisterEvent(self):
        self.stockMarketTicksSignal.disconnect(self._stockMarketTicksHandler)
        self._eventEngine.unregister(DyEventType.stockMarketTicks,
                                     self._stockMarketTicksSignalEmitWrapper)

        self.stockStrategyPosAckSignal.disconnect(
            self._stockStrategyPosAckHandler)
        self._eventEngine.unregister(
            DyEventType.stockStrategyPosAck,
            self._stockStrategyPosAckSignalEmitWrapper)

        self.stockStrategyPosUpdateSignal.disconnect(
            self._stockStrategyPosUpdateHandler)
        self._eventEngine.unregister(
            DyEventType.stockStrategyPosUpdate + self._strategyCls.name,
            self._stockStrategyPosUpdateSignalEmitWrapper)

    def _stockStrategyPosAckHandler(self, event):
        self._curPos = event.data

        self._posWidget.update(event.data)

    def _stockStrategyPosUpdateHandler(self, event):
        self._curPos = event.data

        self._posWidget.update(event.data)

    def _stockMarketTicksHandler(self, event):
        ticks = event.data

        self._tick = ticks.get(self._code)
        if self._tick is None:
            return

        tick = self._tick

        self._codeLabel.setText(tick.code)
        self._nameLabel.setText(tick.name)

        self._priceLabel.setText(str(tick.price))
        if tick.price > tick.preClose:
            self._priceLabel.setStyleSheet("color:red")
        elif tick.price < tick.preClose:
            self._priceLabel.setStyleSheet("color:darkgreen")

        increase = round((tick.price - tick.preClose) / tick.preClose * 100, 2)
        self._increaseLabel.setText('涨幅(%): {0}%'.format(increase))
        if increase > 0:
            self._increaseLabel.setStyleSheet("color:red")
        elif increase < 0:
            self._increaseLabel.setStyleSheet("color:darkgreen")

        self._bidAskTable.fastAppendRows(
            [['卖5', tick.askPrices[4],
              round(tick.askVolumes[4] / 100)],
             ['卖4', tick.askPrices[3],
              round(tick.askVolumes[3] / 100)],
             ['卖3', tick.askPrices[2],
              round(tick.askVolumes[2] / 100)],
             ['卖2', tick.askPrices[1],
              round(tick.askVolumes[1] / 100)],
             ['卖1', tick.askPrices[0],
              round(tick.askVolumes[0] / 100)], [None, tick.price, None],
             ['买1', tick.bidPrices[0],
              round(tick.bidVolumes[0] / 100)],
             ['买2', tick.bidPrices[1],
              round(tick.bidVolumes[1] / 100)],
             ['买3', tick.bidPrices[2],
              round(tick.bidVolumes[2] / 100)],
             ['买4', tick.bidPrices[3],
              round(tick.bidVolumes[3] / 100)],
             ['买5', tick.bidPrices[4],
              round(tick.bidVolumes[4] / 100)]],
            new=True)
Esempio n. 58
0
    def _startStockCtaStrategyHandler(self, event):
        """ 启动策略, 创建唯一策略实例 """
        strategyCls = event.data['class']
        state = event.data['state']

        self._info.print(
            '开始启动策略: {0}, 状态: {1},...'.format(strategyCls.chName, state.state),
            DyLogData.ind)

        # 是否是唯一策略实例
        if strategyCls.name in self._strategies:
            self._info.print('重复启动策略: {0}'.format(strategyCls.chName),
                             DyLogData.error)
            return

        # !!!It's tricky for live trading but not accurate.
        sleep(
            1
        )  # sleep so that UI related dynamic windows can be created firstly.

        # 实例化策略
        strategy = strategyCls(self, self._info, state)

        # 策略开盘前初始化
        if not strategy.onOpen(datetime.now().strftime("%Y-%m-%d"),
                               strategy.onOpenCodes()):
            self._info.print('策略: {0}启动失败'.format(strategyCls.chName),
                             DyLogData.error)
            return

        # 启动策略的账户管理
        if state.isState(DyStockStrategyState.running):
            if strategy.broker is not None:  # Strategy has configured the broker
                if not self._startAccountManager(strategyCls):
                    return

                # 从券商管理类同步策略持仓
                self._accountManagers[strategy.broker].syncStrategyPos(
                    strategy)

        # 获取策略要监控的股票池
        monitoredStocks = strategy.onMonitor()

        # 添加到策略字典
        self._strategies[strategyCls.name] = (
            strategy, DyStockMarketFilter(monitoredStocks))

        # 添加到bar聚合字典
        if 'bar' in strategyCls.liveMode:
            if strategyCls.liveMode not in self._barAggs:
                self._barAggs[strategyCls.liveMode] = DyStockCtaBarAggFast(
                    strategyCls.liveMode, monitoredStocks)
            else:
                self._barAggs[strategyCls.liveMode].add(monitoredStocks)

        # 向股票市场发送监控的股票池
        monitoredStocks = monitoredStocks + [
            DyStockCommon.etf300, DyStockCommon.etf500
        ]  # always add ETF300 and ETF500 for 大盘参考。主要原因是历史分笔数据没有指数的,所以只能用ETF替代。
        if monitoredStocks:
            event = DyEvent(DyEventType.stockMarketMonitor)
            event.data = monitoredStocks

            self._eventEngine.put(event)

        # 向UI推送策略的账户相关事件
        self._updateStrategyAccount(strategy)

        self._info.print('策略: {0}启动成功'.format(strategyCls.chName),
                         DyLogData.ind)
    def _init(self):
        event = DyEvent(DyEventType.stockStrategyPosReq)
        event.data = self._strategyCls

        self._eventEngine.put(event)
 def _endDay(self):
     if self._checkDay() or self.testMode:
         self._eventEngine.put(DyEvent(DyEventType.endStockTradeDay))