Ejemplo n.º 1
0
    def __init__(self,
                 feature,
                 quantile=0.998,
                 init_cash=50000000,
                 rolling=5,
                 portfolioname='feature',
                 mongo_ip=mongo_ip,
                 clickhouse_host=clickhouse_ip,
                 clickhouse_port=clickhouse_port,
                 clickhouse_user=clickhouse_user,
                 clickhouse_password=clickhouse_password) -> None:
        """
        feature --> standard QAFeature
        quantile -> prue long only   upper quantile can be selected

        init_cash --> account backtest initcash

        rolling --> dategap for rolling sell

        clickhouse should be save data first
        
        mongoip -->  use to save qifiaccount

        """
        self.feature = feature.reset_index().drop_duplicates(
            ['date', 'code']).set_index(['date',
                                         'code']).sort_index().dropna()
        self.featurename = feature.columns[0]
        self.start = str(self.feature.index.levels[0][0])[0:10]
        self.end = str(self.feature.index.levels[0][-1])[0:10]
        self.codelist = self.feature.index.levels[1].tolist()

        self.client = QACKClient(host=clickhouse_host,
                                 port=clickhouse_port,
                                 user=clickhouse_user,
                                 password=clickhouse_password)
        self.quantile = quantile
        self.preload = self.feature.groupby(
            level=0, as_index=False,
            group_keys=False).apply(lambda x: self.slice_feature(x))
        self.datacenter = self.client.get_stock_day_qfq_adv(
            self.codelist, self.start, self.end)
        self.closepanel = self.datacenter.closepanel.bfill(
        )  ## 向前复权 匹配股票停牌模式 使用复牌后第一个收盘价卖出
        self.account = QIFI_Account(init_cash=init_cash,
                                    username='******'.format(
                                        self.featurename, uuid4()),
                                    broker_name='feature',
                                    portfolioname=portfolioname,
                                    password='******',
                                    nodatabase=False,
                                    model='BACKTEST',
                                    trade_host=mongo_ip)
        self.tradetable = {}
        self.rolling = rolling
        self.cashpre = init_cash / rolling

        self.account.initial()
Ejemplo n.º 2
0
    def _debug_sim(self):
        self.running_mode = 'sim'

        self._old_data = QA.QA_fetch_stock_min(
            self.code,
            QA.QA_util_get_last_day(
                QA.QA_util_get_real_date(str(datetime.date.today()))),
            str(datetime.datetime.now()),
            format='pd',
            frequence=self.frequence).set_index(['datetime', 'code'])

        self._old_data = self._old_data.loc[:, [
            'open', 'high', 'low', 'close', 'volume'
        ]]

        self.database = pymongo.MongoClient(mongo_ip).QAREALTIME

        self.client = self.database.account
        self.subscriber_client = self.database.subscribe

        self.acc = QIFI_Account(username=self.strategy_id,
                                password=self.strategy_id,
                                trade_host=mongo_ip)
        self.acc.initial()

        self.pub = publisher_routing(exchange='QAORDER_ROUTER',
                                     host=self.trade_host,
                                     port=self.trade_port,
                                     user=self.trade_user,
                                     password=self.trade_password)

        self.subscribe_data(self.code, self.frequence, self.data_host,
                            self.data_port, self.data_user, self.data_password)

        self.database.strategy_schedule.job_control.update(
            {'strategy_id': self.strategy_id}, {
                'strategy_id': self.strategy_id,
                'taskid': self.taskid,
                'filepath': os.path.abspath(__file__),
                'status': 200
            },
            upsert=True)

        # threading.Thread(target=, daemon=True).start()
        self.sub.start()
Ejemplo n.º 3
0
class QAStrategyCtaBase():
    def __init__(self,
                 code='rb2005',
                 frequence='1min',
                 strategy_id='QA_STRATEGY',
                 risk_check_gap=1,
                 portfolio='default',
                 start='2020-01-01',
                 end='2020-05-21',
                 init_cash=1000000,
                 send_wx=False,
                 data_host=eventmq_ip,
                 data_port=eventmq_port,
                 data_user=eventmq_username,
                 data_password=eventmq_password,
                 trade_host=eventmq_ip,
                 trade_port=eventmq_port,
                 trade_user=eventmq_username,
                 trade_password=eventmq_password,
                 taskid=None,
                 mongo_ip=mongo_ip,
                 model='py'):
        """
        code 可以传入单个标的 也可以传入一组标的(list)
        会自动基于code来判断是什么市场
        TODO: 支持多个市场同时存在

        self.trade_host 交易所在的eventmq的ip  [挂ORDER_ROUTER的]
        """
        self.username = '******'
        self.password = '******'

        self.trade_host = trade_host
        self.code = code
        self.frequence = frequence
        self.strategy_id = strategy_id

        self.portfolio = portfolio

        self.data_host = data_host
        self.data_port = data_port
        self.data_user = data_user
        self.data_password = data_password
        self.trade_host = trade_host
        self.trade_port = trade_port
        self.trade_user = trade_user
        self.trade_password = trade_password

        self.start = start
        self.end = end
        self.init_cash = init_cash
        self.taskid = taskid

        self.running_time = ''

        self.market_preset = MARKET_PRESET()
        self._market_data = []
        self.risk_check_gap = risk_check_gap
        self.latest_price = {}

        self.isupdate = False
        self.model = model
        self.new_data = {}
        self._systemvar = {}
        self._signal = []
        self.send_wx = send_wx
        if isinstance(self.code, str):

            self.last_order_towards = {self.code: {'BUY': '', 'SELL': ''}}
        else:
            self.last_order_towards = dict(
                zip(self.code, [{
                    'BUY': '',
                    'SELL': ''
                } for i in range(len(self.code))]))
        self.dt = ''
        if isinstance(self.code, str):
            self.market_type = MARKET_TYPE.FUTURE_CN if re.search(
                r'[a-zA-z]+', self.code) else MARKET_TYPE.STOCK_CN
        else:
            self.market_type = MARKET_TYPE.FUTURE_CN if re.search(
                r'[a-zA-z]+', self.code[0]) else MARKET_TYPE.STOCK_CN

        self.bar_order = {
            'BUY_OPEN': 0,
            'SELL_OPEN': 0,
            'BUY_CLOSE': 0,
            'SELL_CLOSE': 0
        }

        self._num_cached = 120
        self._cached_data = []
        self.user_init()

    @property
    def bar_id(self):
        return len(self._market_data)

    @property
    def BarsSinceEntryLong(self):
        return self.bar_id - self.bar_order.get('BUY_OPEN', self.bar_id)

    @property
    def BarsSinceEntryShort(self):
        return self.bar_id - self.bar_order.get('SELL_OPEN', self.bar_id)

    @property
    def EntryPriceLong(self):
        code = self.get_code()
        return self.get_positions(code).open_price_long

    @property
    def EntryPriceShort(self):
        code = self.get_code()
        return self.get_positions(code).open_price_short

    def on_sync(self):
        if self.running_mode != 'backtest':
            self.pubacc.pub(json.dumps(self.acc.message),
                            routing_key=self.strategy_id)

    def _debug_sim(self):

        self.running_mode = 'sim'

        if self.frequence.endswith('min'):

            if isinstance(self.code, str):
                self._old_data = QA.QA_fetch_get_future_min(
                    'tdx', self.code.upper(),
                    QA.QA_util_get_last_day(
                        QA.QA_util_get_real_date(str(datetime.date.today()))),
                    str(datetime.datetime.now()),
                    self.frequence)[:-1].set_index(['datetime', 'code'])
                self._old_data = self._old_data.assign(
                    volume=self._old_data.trade
                ).loc[:, ['open', 'high', 'low', 'close', 'volume']]
            else:

                self._old_data = pd.concat([
                    QA.QA_fetch_get_future_min(
                        'tdx', item.upper(),
                        QA.QA_util_get_last_day(
                            QA.QA_util_get_real_date(str(
                                datetime.date.today()))),
                        str(datetime.datetime.now()),
                        self.frequence)[:-1].set_index(['datetime', 'code'])
                    for item in self.code
                ],
                                           sort=False)
                self._old_data = self._old_data.assign(
                    volume=self._old_data.trade
                ).loc[:, ['open', 'high', 'low', 'close', 'volume']]
        else:
            self._old_data = pd.DataFrame()

        self.database = pymongo.MongoClient(mongo_ip).QAREALTIME

        self.client = self.database.account
        self.subscriber_client = self.database.subscribe

        self.acc = QIFI_Account(username=self.strategy_id,
                                password=self.strategy_id,
                                trade_host=mongo_ip,
                                init_cash=self.init_cash)
        self.acc.initial()
        self.acc.on_sync = self.on_sync

        self.pub = publisher_routing(exchange='QAORDER_ROUTER',
                                     host=self.trade_host,
                                     port=self.trade_port,
                                     user=self.trade_user,
                                     password=self.trade_password)
        self.pubacc = publisher_topic(exchange='QAAccount',
                                      host=self.trade_host,
                                      port=self.trade_port,
                                      user=self.trade_user,
                                      password=self.trade_password)

        if isinstance(self.code, str):
            self.subscribe_data(self.code.lower(), self.frequence,
                                self.data_host, self.data_port, self.data_user,
                                self.data_password, self.model)
        else:
            self.subscribe_multi(self.code, self.frequence, self.data_host,
                                 self.data_port, self.data_user,
                                 self.data_password, self.model)
        print('account {} start sim'.format(self.strategy_id))
        self.database.strategy_schedule.job_control.update(
            {'strategy_id': self.strategy_id}, {
                'strategy_id': self.strategy_id,
                'taskid': self.taskid,
                'filepath': os.path.abspath(__file__),
                'status': 200
            },
            upsert=True)

    def debug_sim(self):
        self._debug_sim()
        threading.Thread(target=self.sub.start, daemon=True).start()

    def run_sim(self):
        self._debug_sim()

        self.sub.start()

    def run_backtest(self):
        self.debug()
        self.acc.save()

        risk = QA_Risk(self.acc)
        risk.save()

        try:
            """add rank flow if exist

            QARank是我们内部用于评价策略ELO的库 此处并不影响正常使用
            """
            from QARank import QA_Rank
            QA_Rank(self.acc).send()
        except:
            pass

    def user_init(self):
        """
        用户自定义的init过程
        """
        pass

    def debug(self):
        self.running_mode = 'backtest'
        self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS
        user = QA_User(username=self.username, password=self.password)
        port = user.new_portfolio(self.portfolio)
        self.acc = port.new_accountpro(account_cookie=self.strategy_id,
                                       init_cash=self.init_cash,
                                       market_type=self.market_type,
                                       frequence=self.frequence)
        self.positions = self.acc.get_position(self.code)

        print(self.acc)
        print(self.acc.market_type)
        data = QA.QA_quotation(self.code.upper(),
                               self.start,
                               self.end,
                               source=QA.DATASOURCE.MONGO,
                               frequence=self.frequence,
                               market=self.market_type,
                               output=QA.OUTPUT_FORMAT.DATASTRUCT)

        data.data.apply(self.x1, axis=1)

    def x1(self, item):
        self.latest_price[item.name[1]] = item['close']
        if str(item.name[0])[0:10] != str(self.running_time)[0:10]:
            self.on_dailyclose()
            self.on_dailyopen()
            if self.market_type == QA.MARKET_TYPE.STOCK_CN:
                print('backtest: Settle!')
                self.acc.settle()
        self._on_1min_bar()
        self._market_data.append(copy.deepcopy(item))
        self.running_time = str(item.name[0])
        self.on_bar(item)

    def debug_t0(self):
        self.running_mode = 'backtest'
        # self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS
        # user = QA_User(username=self.username, password=self.password)
        # port = user.new_portfolio(self.portfolio)
        # self.acc = port.new_accountpro(
        #     account_cookie=self.strategy_id, init_cash=self.init_cash, init_hold={
        #         self.code: 100000},
        #     market_type=self.market_type, running_environment=RUNNING_ENVIRONMENT.TZERO)
        # self.positions = self.acc.get_position(self.code)
        data = QA.QA_quotation(self.code.upper(),
                               self.start,
                               self.end,
                               source=QA.DATASOURCE.MONGO,
                               frequence=self.frequence,
                               market=self.market_type,
                               output=QA.OUTPUT_FORMAT.DATASTRUCT)

        def x1(item):
            self.latest_price[item.name[1]] = item['close']
            if str(item.name[0])[0:10] != str(self.running_time)[0:10]:
                self.on_dailyclose()
                for order in self.acc.close_positions_order:
                    order.trade('closebySys', order.price, order.amount,
                                order.datetime)
                self.on_dailyopen()
                if self.market_type == QA.MARKET_TYPE.STOCK_CN:
                    print('backtest: Settle!')
                    self.acc.settle()
            self._on_1min_bar()
            self._market_data.append(copy.deepcopy(item))
            self.running_time = str(item.name[0])
            self.on_bar(item)

        data.data.apply(x1, axis=1)

    def debug_currenttick(self, freq):
        data = QA.QA_fetch_get_future_transaction_realtime(
            'tdx', self.code.upper())
        self.running_mode = 'backtest'
        self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS
        user = QA_User(username=self.username, password=self.password)
        port = user.new_portfolio(self.portfolio)
        self.strategy_id = self.strategy_id + \
            'currenttick_{}_{}'.format(str(datetime.date.today()), freq)
        self.acc = port.new_accountpro(account_cookie=self.strategy_id,
                                       init_cash=self.init_cash,
                                       market_type=self.market_type)
        self.positions = self.acc.get_position(self.code)
        data = data.assign(
            price=data.price /
            1000).loc[:, ['code', 'price', 'volume']].resample(freq).apply({
                'code':
                'last',
                'price':
                'ohlc',
                'volume':
                'sum'
            }).dropna()
        data.columns = data.columns.droplevel(0)
        data = data.reset_index().set_index(['datetime', 'code'])

        def x1(item):
            self.latest_price[item.name[1]] = item['close']
            if str(item.name[0])[0:10] != str(self.running_time)[0:10]:
                self.on_dailyclose()
                self.on_dailyopen()
            self._on_1min_bar()
            self._market_data.append(copy.deepcopy(item))
            self.running_time = str(item.name[0])
            self.on_bar(item)

        data.apply(x1, axis=1)

    def debug_histick(self, freq):
        data = QA.QA_fetch_get_future_transaction('tdx', self.code.upper(),
                                                  self.start, self.end)
        self.running_mode = 'backtest'
        self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS
        user = QA_User(username=self.username, password=self.password)
        port = user.new_portfolio(self.portfolio)
        self.strategy_id = self.strategy_id + \
            'histick_{}_{}_{}'.format(self.start, self.end, freq)
        self.acc = port.new_accountpro(account_cookie=self.strategy_id,
                                       init_cash=self.init_cash,
                                       market_type=self.market_type)
        self.positions = self.acc.get_position(self.code)
        data = data.assign(
            price=data.price /
            1000).loc[:, ['code', 'price', 'volume']].resample(freq).apply({
                'code':
                'last',
                'price':
                'ohlc',
                'volume':
                'sum'
            }).dropna()
        data.columns = data.columns.droplevel(0)
        data = data.reset_index().set_index(['datetime', 'code'])

        def x1(item):
            self.latest_price[item.name[1]] = item['close']
            if str(item.name[0])[0:10] != str(self.running_time)[0:10]:
                self.on_dailyclose()
                self.on_dailyopen()
            self._on_1min_bar()
            self._market_data.append(copy.deepcopy(item))
            self.running_time = str(item.name[0])
            self.on_bar(item)

        data.apply(x1, axis=1)

    def subscribe_data(self,
                       code,
                       frequence,
                       data_host,
                       data_port,
                       data_user,
                       data_password,
                       model='py'):
        """[summary]

        Arguments:
            code {[type]} -- [description]
            frequence {[type]} -- [description]
        """

        if frequence.endswith('min'):
            if model == 'py':
                self.sub = subscriber(exchange='realtime_{}_{}'.format(
                    frequence, code),
                                      host=data_host,
                                      port=data_port,
                                      user=data_user,
                                      password=data_password)
            elif model == 'rust':
                self.sub = subscriber_routing(
                    exchange='realtime_{}'.format(code),
                    routing_key=frequence,
                    host=data_host,
                    port=data_port,
                    user=data_user,
                    password=data_password)
            self.sub.callback = self.callback
        elif frequence.endswith('s'):

            import re
            self._num_cached = 2 * int(re.findall(r'\d+', self.frequence)[0])
            self.sub = subscriber_routing(exchange='CTPX',
                                          routing_key=code,
                                          host=data_host,
                                          port=data_port,
                                          user=data_user,
                                          password=data_password)
            self.sub.callback = self.second_callback
        elif frequence.endswith('tick'):
            self._num_cached = 1
            self.sub = subscriber_routing(exchange='CTPX',
                                          routing_key=code,
                                          host=data_host,
                                          port=data_port,
                                          user=data_user,
                                          password=data_password)
            self.sub.callback = self.tick_callback

    def subscribe_multi(self,
                        codelist,
                        frequence,
                        data_host,
                        data_port,
                        data_user,
                        data_password,
                        model='py'):
        if frequence.endswith('min'):
            if model == 'rust':
                self.sub = subscriber_routing(exchange='realtime_{}'.format(
                    codelist[0]),
                                              routing_key=frequence,
                                              host=data_host,
                                              port=data_port,
                                              user=data_user,
                                              password=data_password)
                for item in codelist[1:]:
                    self.sub.add_sub(exchange='realtime_{}'.format(item),
                                     routing_key=frequence)
            elif model == 'py':
                self.sub = subscriber_routing(exchange='realtime_{}'.format(
                    codelist[0].lower()),
                                              routing_key=frequence,
                                              host=data_host,
                                              port=data_port,
                                              user=data_user,
                                              password=data_password)
                for item in codelist[1:]:
                    self.sub.add_sub(exchange='realtime_{}'.format(
                        item.lower()),
                                     routing_key=frequence)
            self.sub.callback = self.callback
        elif frequence.endswith('tick'):

            self._num_cached = 1
            self.sub = subscriber_routing(exchange='CTPX',
                                          routing_key=codelist[0].lower(),
                                          host=data_host,
                                          port=data_port,
                                          user=data_user,
                                          password=data_password)
            for item in codelist[1:]:
                self.sub.add_sub(exchange='CTPX', routing_key=item.lower())

            self.sub.callback = self.tick_callback

    @property
    def old_data(self):
        return self._old_data

    def update(self):
        """
        此处是切换bar的时候的节点
        """
        self._old_data = self._market_data
        self._on_1min_bar()

    @property
    def market_datetime(self):
        """计算的market时间点  此api慎用 因为会惰性计算全市场的值

        Returns:
            [type] -- [description]
        """
        return self.market_data.index.levels[0]

    @property
    def market_data(self):

        if self.running_mode == 'sim':
            return self._market_data
        elif self.running_mode == 'backtest':
            return pd.concat(self._market_data[-100:], axis=1, sort=False).T

    def force_close(self):
        # 强平
        if self.positions.volume_long > 0:
            self.send_order('SELL',
                            'CLOSE',
                            price=self.positions.last_price,
                            volume=self.positions.volume_long)
        if self.positions.volume_short > 0:
            self.send_order('BUY',
                            'CLOSE',
                            price=self.positions.last_price,
                            volume=self.positions.volume_short)

    def upcoming_data(self, new_bar):
        """upcoming_bar :

        在这一步中, 我们主要进行的是

        1. 更新self._market_data
        2. 更新账户
        3. 更新持仓

        4. 通知on_bar


        Arguments:
            new_bar {pd.DataFrame} -- [description]
        """
        code = new_bar.index.levels[1][0]
        if len(self._old_data) > 0:
            self._market_data = pd.concat([self._old_data, new_bar],
                                          sort=False)
        else:
            self._market_data = new_bar
        # QA.QA_util_log_info(self._market_data)

        if self.isupdate:
            self.update()
            self.isupdate = False

        self.update_account()
        if isinstance(self.code, str):
            self.positions.on_price_change(float(self.latest_price[code]))
        else:
            for item in self.code:
                self.acc.get_position(item).on_price_change(
                    float(self.latest_price[code]))
        self.on_bar(
            json.loads(new_bar.reset_index().to_json(orient='records'))[0])

    def ind2str(self, ind, ind_type):
        z = ind.tail(1).reset_index().to_dict(orient='records')[0]
        return json.dumps({
            'topic': ind_type,
            'code': self.code,
            'type': self.frequence,
            'data': z
        })

    def second_callback(self, a, b, c, body):
        """在strategy的callback中,我们需要的是

        1. 更新数据
        2. 更新bar
        3. 更新策略状态
        4. 推送事件

        Arguments:
            a {[type]} -- [description]
            b {[type]} -- [description]
            c {[type]} -- [description]
            body {[type]} -- [description]

        second ==> 2*second tick

        b'{"ask_price_1": 4145.0, "ask_price_2": 0, "ask_price_3": 0, "ask_price_4": 0, "ask_price_5": 0, 
        "ask_volume_1": 69, "ask_volume_2": 0, "ask_volume_3": 0, "ask_volume_4": 0, "ask_volume_5": 0, 
        "average_price": 61958.14258714826, 
        "bid_price_1": 4143.0, "bid_price_2": 0, "bid_price_3": 0, "bid_price_4": 0, "bid_price_5": 0, 
        "bid_volume_1": 30, "bid_volume_2": 0, "bid_volume_3": 0, "bid_volume_4": 0, "bid_volume_5": 0, 
        "datetime": "2019-11-20 01:57:08", "exchange": "SHFE", "gateway_name": "ctp", 
        "high_price": 4152.0, "last_price": 4144.0, "last_volume": 0,
        "limit_down": 3872.0, "limit_up": 4367.0, "local_symbol": "ag1912.SHFE", 
        "low_price": 4105.0, "name": "", "open_interest": 277912.0, "open_price": 4140.0, 
        "preSettlementPrice": 4120.0, "pre_close": 4155.0, 
        "symbol": "ag1912", 
        "volume": 114288}'


        tick 会基于热数据的量 self._num_cached 来判断更新/重采样

        """

        self.new_data = json.loads(str(body, encoding='utf-8'))

        self._cached_data.append(self.new_data)
        self.latest_price[
            self.new_data['symbol']] = self.new_data['last_price']

        # if len(self._cached_data) == self._num_cached:
        #     self.isupdate = True

        if len(self._cached_data) > 3 * self._num_cached:
            # 控制缓存数据量
            self._cached_data = self._cached_data[self._num_cached:]

        data = pd.DataFrame(
            self._cached_data).loc[:, ['datetime', 'last_price', 'volume']]
        data = data.assign(datetime=pd.to_datetime(data.datetime)).set_index(
            'datetime').resample(self.frequence).apply({
                'last_price': 'ohlc',
                'volume': 'last'
            }).dropna()
        data.columns = data.columns.droplevel(0)

        data = data.assign(volume=data.volume.diff(),
                           code=self.new_data['symbol'])
        data = data.reset_index().set_index(['datetime', 'code'])

        self.acc.on_price_change(self.new_data['symbol'],
                                 self.latest_price[self.new_data['symbol']])
        # .loc[:, ['open', 'high', 'low', 'close', 'volume', 'tradetime']]
        now = datetime.datetime.now()
        if now.hour == 20 and now.minute == 59 and now.second < 10:
            self.daily_func()
            time.sleep(10)

        self.running_time = self.new_data['datetime']
        # print(data.iloc[-1].index[0])
        if self.dt != data.index[-1][0]:
            self.isupdate = True
            self.dt = data.index[-1][0]
        self.upcoming_data(data.tail(1))

    def tick_callback(self, a, b, c, body):
        self.new_data = json.loads(str(body, encoding='utf-8'))
        self.latest_price[
            self.new_data['symbol']] = self.new_data['last_price']
        self.running_time = self.new_data['datetime']
        self.on_tick(self.new_data)

    def get_code_marketdata(self, code):
        return self.market_data.loc[(slice(None), code), :]

    def get_current_marketdata(self):
        return self.market_data.loc[(self.running_time, slice(None)), :]

    def callback(self, a, b, c, body):
        """在strategy的callback中,我们需要的是

        1. 更新数据
        2. 更新bar
        3. 更新策略状态
        4. 推送事件

        Arguments:
            a {[type]} -- [description]
            b {[type]} -- [description]
            c {[type]} -- [description]
            body {[type]} -- [description]
        """

        self.new_data = json.loads(str(body, encoding='utf-8'))
        self.latest_price[self.new_data['code']] = self.new_data['close']

        if self.dt != str(self.new_data['datetime'])[0:16]:
            # [0:16]是分钟线位数
            self.dt = str(self.new_data['datetime'])[0:16]
            self.isupdate = True

        self.acc.on_price_change(self.new_data['code'], self.new_data['close'])
        # .loc[:, ['open', 'high', 'low', 'close', 'volume', 'tradetime']]

        bar = pd.DataFrame([self.new_data]).set_index(['datetime', 'code'])

        now = datetime.datetime.now()
        if now.hour == 20 and now.minute == 59 and now.second < 10:
            self.daily_func()
            time.sleep(10)

        # res = self.job_control.find_one(
        #     {'strategy_id': self.strategy_id, 'strategy_id': self.strategy_id})
        # self.control_status(res)
        self.running_time = self.new_data['datetime']
        self.upcoming_data(bar)

    def control_status(self, res):
        print(res)

    def add_subscriber(self, qaproid):
        """Add a subscriber
        增加订阅者的QAPRO_ID

        """
        self.subscriber_client.insert_one({
            'strategy_id': self.strategy_id,
            'user_id': qaproid
        })

    @property
    def subscriber_list(self):
        """订阅者

        Returns:
            [type] -- [description]
        """

        return list(
            set([
                item['user_id'] for item in self.subscriber_client.find(
                    {'strategy_id': self.strategy_id})
            ]))

    def load_strategy(self):
        raise NotImplementedError

    def on_dailyopen(self):
        pass

    def on_dailyclose(self):
        pass

    def on_bar(self, bar):
        raise NotImplementedError

    def on_tick(self, tick):
        raise NotImplementedError

    def _on_1min_bar(self):
        #raise NotImplementedError
        if len(self._systemvar.keys()) > 0:
            self._signal.append(copy.deepcopy(self._systemvar))
        try:
            self.on_1min_bar()
        except:
            pass

    def on_deal(self, order):
        """

        order is a dict type
        """
        print('------this is on deal message ------')
        print(order)

    def on_1min_bar(self):
        raise NotImplementedError

    def on_5min_bar(self):
        raise NotImplementedError

    def on_15min_bar(self):
        raise NotImplementedError

    def on_30min_bar(self):
        raise NotImplementedError

    def order_handler(self):
        self._orders = {}

    def daily_func(self):
        QA.QA_util_log_info('DAILY FUNC')

    def risk_check(self):
        pass

    def plot(self, name, data, format):
        """ plot是可以存储你的临时信息的接口, 后期会接入可视化



        Arguments:
            name {[type]} -- [description]
            data {[type]} -- [description]
            format {[type]} -- [description]
        """
        self._systemvar[name] = {
            'datetime': copy.deepcopy(str(self.running_time)),
            'value': data,
            'format': format
        }

    def get_code(self):
        if isinstance(self.code, str):
            return self.code
        else:
            return self.code[0]

    def check_order(self, direction, offset, code=None):
        """[summary]
        同方向不开仓  只对期货市场做限制

        buy - open
        sell - close
        """
        if code == None:
            code = self.get_code()

        if self.market_type == QA.MARKET_TYPE.FUTURE_CN:

            if self.last_order_towards[code][direction] == str(offset):
                return False
            else:
                return True
        else:
            return True

    def on_ordererror(self, direction, offset, price, volume):
        print('order Error ')

    def receive_simpledeal(self,
                           code: str,
                           trade_time,
                           trade_amount,
                           direction,
                           offset,
                           trade_price,
                           message='sell_open'):
        self.send_order(direction=direction,
                        offset=offset,
                        volume=trade_amount,
                        price=trade_price,
                        order_id=QA.QA_util_random_with_topic(
                            self.strategy_id))

    def send_order(self,
                   direction='BUY',
                   offset='OPEN',
                   price=3925,
                   volume=10,
                   order_id='',
                   code=None):
        if code == None:
            code = self.get_code()

        towards = eval('ORDER_DIRECTION.{}_{}'.format(direction, offset))
        order_id = str(uuid.uuid4()) if order_id == '' else order_id

        if isinstance(price, float):
            pass
        elif isinstance(price, pd.Series):
            price = price.values[0]

        if self.running_mode == 'sim':
            # 在此处拦截无法下单的订单
            if (direction == 'BUY' and self.latest_price[code] <= price) or (
                    direction == 'SELL' and self.latest_price[code] >= price):
                QA.QA_util_log_info(
                    '============ {} SEND ORDER =================='.format(
                        order_id))
                QA.QA_util_log_info(
                    'direction{} offset {} price{} volume{}'.format(
                        direction, offset, price, volume))

                if self.check_order(direction, offset, code=code):
                    #self.last_order_towards = {'BUY': '', 'SELL': ''}
                    self.last_order_towards[code][direction] = offset
                    now = str(datetime.datetime.now())

                    order = self.acc.send_order(code=code,
                                                towards=towards,
                                                price=price,
                                                amount=volume,
                                                order_id=order_id)
                    print(order)
                    order['topic'] = 'send_order'
                    self.pub.pub(json.dumps(order),
                                 routing_key=self.strategy_id)

                    self.acc.make_deal(order)
                    self.on_deal(order)
                    self.bar_order['{}_{}'.format(direction,
                                                  offset)] = self.bar_id
                    if self.send_wx:
                        for user in self.subscriber_list:
                            QA.QA_util_log_info(self.subscriber_list)
                            try:
                                requests.post(
                                    'http://www.yutiansut.com/signal?user_id={}&template={}&strategy_id={}&realaccount={}&code={}&order_direction={}&order_offset={}&price={}&volume={}&order_time={}'
                                    .format(user, "xiadan_report",
                                            self.strategy_id, self.acc.user_id,
                                            code.lower(), direction, offset,
                                            price, volume, now))
                            except Exception as e:
                                QA.QA_util_log_info(e)

                else:
                    QA.QA_util_log_info('failed in ORDER_CHECK')
            else:
                self.on_ordererror(direction, offset, price, volume)
        elif self.running_mode == 'backtest':

            self.bar_order['{}_{}'.format(direction, offset)] = self.bar_id

            if self.market_type == 'stock_cn':
                order = self.acc.send_order(code=code,
                                            amount=volume,
                                            time=self.running_time,
                                            towards=towards,
                                            price=price)
                order.trade(order.order_id, order.price, order.amount,
                            order.datetime)
                self.on_deal(order.to_dict())
            else:
                self.acc.receive_simpledeal(code=code,
                                            trade_time=self.running_time,
                                            trade_towards=towards,
                                            trade_amount=volume,
                                            trade_price=price,
                                            order_id=order_id,
                                            realorder_id=order_id,
                                            trade_id=order_id)

                self.on_deal({
                    'code': code,
                    'trade_time': self.running_time,
                    'trade_towards': towards,
                    'trade_amount': volume,
                    'trade_price': price,
                    'order_id': order_id,
                    'realorder_id': order_id,
                    'trade_id': order_id
                })
            self.positions = self.acc.get_position(code)

    def update_account(self):
        if self.running_mode == 'sim':
            QA.QA_util_log_info('{} UPDATE ACCOUNT'.format(
                str(datetime.datetime.now())))

            self.accounts = self.acc.account_msg
            self.orders = self.acc.orders
            if isinstance(self.code, str):
                self.positions = self.acc.get_position(self.code)
            else:
                pass
            self.trades = self.acc.trades
            self.updatetime = self.acc.dtstr

            self.on_sync()
        elif self.running_mode == 'backtest':
            if isinstance(self.code, str):
                self.positions = self.acc.get_position(self.code)
            else:
                pass

    def get_exchange(self, code):
        return self.market_preset.get_exchange(code)

    def get_positions(self, code):
        if self.running_mode == 'sim':
            self.update_account()
            return self.acc.get_position(code)
        elif self.running_mode == 'backtest':
            return self.acc.get_position(code)

    def get_cash(self):
        if self.running_mode == 'sim':
            self.update_account()
            return self.accounts.get('available', '')
        elif self.running_mode == 'backtest':
            return self.acc.cash_available

    def run(self):

        while True:
            time.sleep(self.risk_check_gap)
            self.risk_check()
Ejemplo n.º 4
0
    def _debug_sim(self):

        self.running_mode = 'sim'

        if self.frequence.endswith('min'):

            if isinstance(self.code, str):
                self._old_data = QA.QA_fetch_get_future_min(
                    'tdx', self.code.upper(),
                    QA.QA_util_get_last_day(
                        QA.QA_util_get_real_date(str(datetime.date.today()))),
                    str(datetime.datetime.now()),
                    self.frequence)[:-1].set_index(['datetime', 'code'])
                self._old_data = self._old_data.assign(
                    volume=self._old_data.trade
                ).loc[:, ['open', 'high', 'low', 'close', 'volume']]
            else:

                self._old_data = pd.concat([
                    QA.QA_fetch_get_future_min(
                        'tdx', item.upper(),
                        QA.QA_util_get_last_day(
                            QA.QA_util_get_real_date(str(
                                datetime.date.today()))),
                        str(datetime.datetime.now()),
                        self.frequence)[:-1].set_index(['datetime', 'code'])
                    for item in self.code
                ],
                                           sort=False)
                self._old_data = self._old_data.assign(
                    volume=self._old_data.trade
                ).loc[:, ['open', 'high', 'low', 'close', 'volume']]
        else:
            self._old_data = pd.DataFrame()

        self.database = pymongo.MongoClient(mongo_ip).QAREALTIME

        self.client = self.database.account
        self.subscriber_client = self.database.subscribe

        self.acc = QIFI_Account(username=self.strategy_id,
                                password=self.strategy_id,
                                trade_host=mongo_ip,
                                init_cash=self.init_cash)
        self.acc.initial()
        self.acc.on_sync = self.on_sync

        self.pub = publisher_routing(exchange='QAORDER_ROUTER',
                                     host=self.trade_host,
                                     port=self.trade_port,
                                     user=self.trade_user,
                                     password=self.trade_password)
        self.pubacc = publisher_topic(exchange='QAAccount',
                                      host=self.trade_host,
                                      port=self.trade_port,
                                      user=self.trade_user,
                                      password=self.trade_password)

        if isinstance(self.code, str):
            self.subscribe_data(self.code.lower(), self.frequence,
                                self.data_host, self.data_port, self.data_user,
                                self.data_password, self.model)
        else:
            self.subscribe_multi(self.code, self.frequence, self.data_host,
                                 self.data_port, self.data_user,
                                 self.data_password, self.model)
        print('account {} start sim'.format(self.strategy_id))
        self.database.strategy_schedule.job_control.update(
            {'strategy_id': self.strategy_id}, {
                'strategy_id': self.strategy_id,
                'taskid': self.taskid,
                'filepath': os.path.abspath(__file__),
                'status': 200
            },
            upsert=True)
Ejemplo n.º 5
0
from QUANTAXIS.QIFI.QifiAccount import QIFI_Account

if __name__ == '__main__':

    account = QIFI_Account(username='******',
                           password='******',
                           model='BACKTEST',
                           nodatabase=False,
                           dbname='ck',
                           clickhouse_port=9000,
                           clickhouse_user='******',
                           clickhouse_password='')
    """
    sendorder
    
    """
    account.initial()
    order = account.send_order("000001", 200, 20, 1, datetime='2021-09-30')
    """
    cancel order
    
    """
    #cancel = account.cancel_order(order['order_id'])
    """
    make deal
    
    """
    account.make_deal(order)
    res = list(account.position_qifimsg.values())

    print(res[0])
Ejemplo n.º 6
0
class QAStrategyStockBase(QAStrategyCTABase):
    def __init__(self,
                 code=['000001'],
                 frequence='1min',
                 strategy_id='QA_STRATEGY',
                 risk_check_gap=1,
                 portfolio='default',
                 start='2019-01-01',
                 end='2019-10-21',
                 send_wx=False,
                 market_type='stock_cn',
                 data_host=eventmq_ip,
                 data_port=eventmq_port,
                 data_user=eventmq_username,
                 data_password=eventmq_password,
                 trade_host=eventmq_ip,
                 trade_port=eventmq_port,
                 trade_user=eventmq_username,
                 trade_password=eventmq_password,
                 taskid=None,
                 mongo_ip=mongo_ip):
        super().__init__(code=code,
                         frequence=frequence,
                         strategy_id=strategy_id,
                         risk_check_gap=risk_check_gap,
                         portfolio=portfolio,
                         start=start,
                         end=end,
                         send_wx=send_wx,
                         data_host=eventmq_ip,
                         data_port=eventmq_port,
                         data_user=eventmq_username,
                         data_password=eventmq_password,
                         trade_host=eventmq_ip,
                         trade_port=eventmq_port,
                         trade_user=eventmq_username,
                         trade_password=eventmq_password,
                         taskid=taskid,
                         mongo_ip=mongo_ip)

        self.code = code
        self.send_wx = send_wx

    def subscribe_data(self, code, frequence, data_host, data_port, data_user,
                       data_password):
        """[summary]
        Arguments:
            code {[type]} -- [description]
            frequence {[type]} -- [description]
        """

        self.sub = subscriber_topic(
            exchange='realtime_stock_{}'.format(frequence),
            host=data_host,
            port=data_port,
            user=data_user,
            password=data_password,
            routing_key='')
        for item in code:
            self.sub.add_sub(exchange='realtime_stock_{}'.format(frequence),
                             routing_key=item)
        self.sub.callback = self.callback

    def upcoming_data(self, new_bar):
        """upcoming_bar :
        Arguments:
            new_bar {json} -- [description]
        """
        self._market_data = pd.concat([self._old_data, new_bar])
        # QA.QA_util_log_info(self._market_data)

        if self.isupdate:
            self.update()
            self.isupdate = False

        self.update_account()
        # self.positions.on_price_change(float(new_bar['close']))
        self.on_bar(new_bar)

    def ind2str(self, ind, ind_type):
        z = ind.tail(1).reset_index().to_dict(orient='records')[0]
        return json.dumps({
            'topic': ind_type,
            'code': self.code,
            'type': self.frequence,
            'data': z
        })

    def callback(self, a, b, c, body):
        """在strategy的callback中,我们需要的是
        1. 更新数据
        2. 更新bar
        3. 更新策略状态
        4. 推送事件
        Arguments:
            a {[type]} -- [description]
            b {[type]} -- [description]
            c {[type]} -- [description]
            body {[type]} -- [description]
        """

        self.new_data = json.loads(str(body, encoding='utf-8'))

        self.latest_price[self.new_data['code']] = self.new_data['close']

        self.running_time = self.new_data['datetime']
        if self.dt != str(self.new_data['datetime'])[0:16]:
            # [0:16]是分钟线位数
            print('update!!!!!!!!!!!!')
            self.dt = str(self.new_data['datetime'])[0:16]
            self.isupdate = True

        self.acc.on_price_change(self.new_data['code'], self.new_data['close'])
        bar = pd.DataFrame([self.new_data]).set_index(
            ['datetime',
             'code']).loc[:, ['open', 'high', 'low', 'close', 'volume']]
        self.upcoming_data(bar)

    def _debug_sim(self):
        self.running_mode = 'sim'

        self._old_data = QA.QA_fetch_stock_min(
            self.code,
            QA.QA_util_get_last_day(
                QA.QA_util_get_real_date(str(datetime.date.today()))),
            str(datetime.datetime.now()),
            format='pd',
            frequence=self.frequence).set_index(['datetime', 'code'])

        self._old_data = self._old_data.loc[:, [
            'open', 'high', 'low', 'close', 'volume'
        ]]

        self.database = pymongo.MongoClient(mongo_ip).QAREALTIME

        self.client = self.database.account
        self.subscriber_client = self.database.subscribe

        self.acc = QIFI_Account(username=self.strategy_id,
                                password=self.strategy_id,
                                trade_host=mongo_ip)
        self.acc.initial()

        self.pub = publisher_routing(exchange='QAORDER_ROUTER',
                                     host=self.trade_host,
                                     port=self.trade_port,
                                     user=self.trade_user,
                                     password=self.trade_password)

        self.subscribe_data(self.code, self.frequence, self.data_host,
                            self.data_port, self.data_user, self.data_password)

        self.database.strategy_schedule.job_control.update(
            {'strategy_id': self.strategy_id}, {
                'strategy_id': self.strategy_id,
                'taskid': self.taskid,
                'filepath': os.path.abspath(__file__),
                'status': 200
            },
            upsert=True)

        # threading.Thread(target=, daemon=True).start()
        self.sub.start()

    def run(self):
        while True:
            pass

    def debug(self):
        self.running_mode = 'backtest'
        self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS
        user = QA_User(username="******", password='******')
        port = user.new_portfolio(self.portfolio)
        self.acc = port.new_accountpro(account_cookie=self.strategy_id,
                                       init_cash=self.init_cash,
                                       market_type=self.market_type,
                                       frequence=self.frequence)
        #self.positions = self.acc.get_position(self.code)

        print(self.acc)

        print(self.acc.market_type)
        data = QA.QA_quotation(self.code,
                               self.start,
                               self.end,
                               source=QA.DATASOURCE.MONGO,
                               frequence=self.frequence,
                               market=self.market_type,
                               output=QA.OUTPUT_FORMAT.DATASTRUCT)

        data.data.apply(self.x1, axis=1)

    def update_account(self):
        if self.running_mode == 'sim':
            QA.QA_util_log_info('{} UPDATE ACCOUNT'.format(
                str(datetime.datetime.now())))

            self.accounts = self.acc.account_msg
            self.orders = self.acc.orders
            self.positions = self.acc.positions

            self.trades = self.acc.trades
            self.updatetime = self.acc.dtstr
        elif self.running_mode == 'backtest':
            #self.positions = self.acc.get_position(self.code)
            self.positions = self.acc.positions

    def send_order(
        self,
        direction='BUY',
        offset='OPEN',
        code=None,
        price=3925,
        volume=10,
        order_id='',
    ):

        towards = eval('ORDER_DIRECTION.{}_{}'.format(direction, offset))
        order_id = str(uuid.uuid4()) if order_id == '' else order_id

        if self.market_type == QA.MARKET_TYPE.STOCK_CN:
            """
            在此对于股票的部分做一些转换
            """
            if towards == ORDER_DIRECTION.SELL_CLOSE:
                towards = ORDER_DIRECTION.SELL
            elif towards == ORDER_DIRECTION.BUY_OPEN:
                towards = ORDER_DIRECTION.BUY

        if isinstance(price, float):
            pass
        elif isinstance(price, pd.Series):
            price = price.values[0]

        if self.running_mode == 'sim':

            QA.QA_util_log_info(
                '============ {} SEND ORDER =================='.format(
                    order_id))
            QA.QA_util_log_info(
                'direction{} offset {} price{} volume{}'.format(
                    direction, offset, price, volume))

            if self.check_order(direction, offset):
                self.last_order_towards = {'BUY': '', 'SELL': ''}
                self.last_order_towards[direction] = offset
                now = str(datetime.datetime.now())

                order = self.acc.send_order(code=code,
                                            towards=towards,
                                            price=price,
                                            amount=volume,
                                            order_id=order_id)
                order['topic'] = 'send_order'
                self.pub.pub(json.dumps(order), routing_key=self.strategy_id)

                self.acc.make_deal(order)
                self.bar_order['{}_{}'.format(direction, offset)] = self.bar_id

            else:
                QA.QA_util_log_info('failed in ORDER_CHECK')

        elif self.running_mode == 'backtest':

            self.bar_order['{}_{}'.format(direction, offset)] = self.bar_id

            self.acc.receive_simpledeal(code=code,
                                        trade_time=self.running_time,
                                        trade_towards=towards,
                                        trade_amount=volume,
                                        trade_price=price,
                                        order_id=order_id)
Ejemplo n.º 7
0
class QAFeatureBacktest():
    def __init__(self,
                 feature,
                 quantile=0.998,
                 init_cash=50000000,
                 rolling=5,
                 portfolioname='feature',
                 mongo_ip=mongo_ip,
                 clickhouse_host=clickhouse_ip,
                 clickhouse_port=clickhouse_port,
                 clickhouse_user=clickhouse_user,
                 clickhouse_password=clickhouse_password) -> None:
        """
        feature --> standard QAFeature
        quantile -> prue long only   upper quantile can be selected

        init_cash --> account backtest initcash

        rolling --> dategap for rolling sell

        clickhouse should be save data first
        
        mongoip -->  use to save qifiaccount

        """
        self.feature = feature.reset_index().drop_duplicates(
            ['date', 'code']).set_index(['date',
                                         'code']).sort_index().dropna()
        self.featurename = feature.columns[0]
        self.start = str(self.feature.index.levels[0][0])[0:10]
        self.end = str(self.feature.index.levels[0][-1])[0:10]
        self.codelist = self.feature.index.levels[1].tolist()

        self.client = QACKClient(host=clickhouse_host,
                                 port=clickhouse_port,
                                 user=clickhouse_user,
                                 password=clickhouse_password)
        self.quantile = quantile
        self.preload = self.feature.groupby(
            level=0, as_index=False,
            group_keys=False).apply(lambda x: self.slice_feature(x))
        self.datacenter = self.client.get_stock_day_qfq_adv(
            self.codelist, self.start, self.end)
        self.closepanel = self.datacenter.closepanel.bfill(
        )  ## 向前复权 匹配股票停牌模式 使用复牌后第一个收盘价卖出
        self.account = QIFI_Account(init_cash=init_cash,
                                    username='******'.format(
                                        self.featurename, uuid4()),
                                    broker_name='feature',
                                    portfolioname=portfolioname,
                                    password='******',
                                    nodatabase=False,
                                    model='BACKTEST',
                                    trade_host=mongo_ip)
        self.tradetable = {}
        self.rolling = rolling
        self.cashpre = init_cash / rolling

        self.account.initial()

    def slice_feature(self, data):
        res = data[data > data.quantile(self.quantile)].dropna()
        res.index = res.index.remove_unused_levels()
        return res

    def get_feature(self, start, end=None):
        start = parser.parse(start).date()

        end = start if end is None else parser.parse(end).date()

        return self.feature.loc[start:end, :, :]

    def get_buy_list(self, date):
        """
        date --> real date
        """
        signaldate = QA_util_get_last_day(date)
        try:
            buy = self.preload.loc[parser.parse(signaldate).date(), :, :]
            buy.index = buy.index.remove_unused_levels()
            return buy.index.levels[1].tolist()
        except:
            return []

    def get_sell_list(self, date):
        #signaldate = QA.QA_util_get_last_day(date, 5)
        try:
            sell = list(self.tradetable[QA_util_get_last_day(
                date, self.rolling - 1)].keys())
            return sell
        except:
            return []

    def run(self, ):
        """
        buy nextday open
        
        sell next Nday close
        """

        for date in QA_util_get_trade_range(self.start, self.end):
            buylist = self.get_buy_list(date)
            selllist = self.get_sell_list(date)
            self.tradetable[date] = {}

            if len(selllist) == 0:
                pass

            else:
                data = self.closepanel.loc[
                    parser.parse(date).date(),
                    selllist].map(lambda x: round(x, 2)).to_dict()
                cashpre = self.cashpre / len(selllist)
                for code in selllist:

                    volume = self.tradetable[QA_util_get_last_day(
                        date, self.rolling - 1)][code]
                    if volume < 100:
                        pass
                    else:
                        order = self.account.send_order(code[0:6],
                                                        volume,
                                                        price=data[code],
                                                        datetime=date +
                                                        ' 15:00:00',
                                                        towards=-1)
                        self.account.make_deal(order)

            if len(buylist) != 0:

                d = self.datacenter.selects(
                    buylist, date, date).open.map(lambda x: round(x, 2))

                d.index = d.index.droplevel(0)

                data = d.to_dict()
                cashpre = self.cashpre / len(buylist)
                for code in buylist:
                    try:
                        volume = int(
                            0.01 * cashpre /
                            data[code]) * 100 if data[code] != 0 else 0
                        if volume < 100:
                            pass
                        else:
                            order = self.account.send_order(code[0:6],
                                                            volume,
                                                            price=data[code],
                                                            datetime=date +
                                                            ' 09:30:00',
                                                            towards=1)
                            self.account.make_deal(order)
                            self.tradetable[date][code] = volume
                    except:
                        """
                        主要是停牌买不入 直接放弃
                        
                        此处买入未加入连续一字板的检测 rust 会增加此处的逻辑
                        
                        """
                        pass
            else:
                pass

            holdinglist = [
                QA_util_code_change_format(code)
                for code in list(self.account.positions.keys())
            ]
            pricepanel = self.closepanel.loc[parser.parse(date).date(),
                                             holdinglist].map(
                                                 lambda x: round(x, 2))
            #pricepanel.index = pricepanel.index.droplevel(0)
            pricepanel = pricepanel.to_dict()
            for code in holdinglist:

                self.account.on_price_change(code[0:6], pricepanel[code])
            self.account.settle()