Esempio n. 1
0
 def test_annualized_return_rate(self):
     start = pd.Timestamp(equity.index[0])
     end = pd.Timestamp(equity.index[-1])
     logger.info('---start end---: {} {}'.format(start, end))
     rate = analysis.annualized_return_rate(equity, capital, start, end)
     logger.info('---annualized_return_rate---: {}'.format(rate))
     self.assertEqual(rate, -60.66)
Esempio n. 2
0
 def test_get_trade_bars(self):
     op = analysis._true_func
     lenth_bar1 = analysis._get_trade_bars(ohlc_data, trade_log, op)
     self.assertEquals(lenth_bar1, [19, 9, 3, 2, 8, 5])
     logger.info('trade_log: ', trade_log)
     trade_log1 = trade_log.drop([0, 1]).reset_index(drop=True)
     logger.debug('----------------------------------')
     logger.debug('trade_log: ', trade_log1)
     lenth_bar2 = analysis._get_trade_bars(ohlc_data, trade_log1, op)
     self.assertEquals(lenth_bar2, [9, 3, 2, 8, 5])
Esempio n. 3
0
 def average_true_range(self, period: int) -> float:
     """period个周期内的平均真实波幅,一般称为ATR"""
     if not isinstance(period, int):
         logger.info('period must be int, please input int')
     high = self.high(period)  # type:numpy.ndarray,可以直接进行列表计算
     low = self.low(period)  # type:numpy.ndarray
     last_open = self.open(period + 1)[:-1]  # type:numpy.ndarray
     high_low = high - low  # type:numpy.ndarray
     high_last_open = high - last_open
     last_open_low = last_open - low
     # true_range = max(high - low, abs(high - last_open), abs(last_open - low))
     true_range = [
         max(high_low[i], high_last_open[i], last_open_low[i])
         for i in range(period)
     ]
     average_true_range = talib.SMA(np.array(true_range), period)
     return average_true_range[-1]
Esempio n. 4
0
 def min_low(self, period: int, index=0):
     """
     获取 period 个周期内的最低价,
     index 为 0 表示 period + 1 日前到当日的最低价,
     index 为 1 表示 period + 2 日前到昨日的最低价,
     +2 是因为要与上一个 index 的周期相同,便于比较,
     用于计算 cross up 和 cross down
     """
     if index not in [0, 1]:
         logger.warning(
             'index must be 0 or 1, please choose the right index')
         logger.info('index set to 0 by default')
         index = 0
     if not index:
         low = self.get_basic_data(period, ohlc='low')
     if index == 1:
         low = self.get_basic_data(period + 1, ohlc='low')[:-1]
     return min(low)
Esempio n. 5
0
 def max_high(self, period: int, index=0):
     """
     获取 period 个周期内的最高价,
     1 表示当前周期,2 表示上一周期到当前周期,类推,
     index 为 0 表示 period - 1 日前到当日的最高价,即 period 个周期内的最高价,
     index 为 1 表示 period 日前到昨日的最高价,也是 period 个周期内的最高价,
     index 是为比较函数 cross_up 和 cross_down 设置的
     """
     logger.debug('period, index : {} {}'.format(period, index))
     if index not in [0, 1]:
         logger.warning(
             'index must be 0 or 1, please choose the right index')
         logger.info('index set to 0 by default')
         index = 0
     if not index:
         high = self.get_basic_data(period, ohlc='high')
     if index == 1:
         high = self.get_basic_data(period + 1, ohlc='high')[:-1]
     return max(high)
Esempio n. 6
0
    def get_analysis(self, instrument):
        """输出详细的结果分析"""
        logger.info('-----get_analysis-----')
        ohlc_data = self.feed_list[0].bar.df
        ohlc_data = ohlc_data.drop(len(ohlc_data) - 1)  # 最后一条数据为重复的数据
        logger.debug('---feed_list---: {} {}'.format(self.feed_list,
                                                     self.feed_list[0].bar))
        ohlc_data.set_index('time', inplace=True)  # 设立新索引
        ohlc_data.index = pd.DatetimeIndex(
            ohlc_data.index)  # 将新索引转换为 DatetimeIndex
        logger.debug('------ohlc_data.index------: {}'.format(ohlc_data.index))
        logger.debug('------type of ohlc_data.index------: {}'.format(
            type(ohlc_data.index)))
        self.context.ohlc_data = ohlc_data  # pd.DataFrame

        dbal = self.fill.equity.df  # 权益
        start = dbal.index[0]  # 开始日期
        end = dbal.index[-1]  # 结束日期
        logger.debug('----start----: {}'.format(start))
        logger.debug('----end----: {}'.format(end))
        logger.debug('----type of end----: {}'.format(type(end)))
        logger.debug('----context.start_date----: {}'.format(
            self.context.start_date))
        logger.debug('----context.end_date----: {}'.format(
            self.context.end_date))
        logger.debug('----type of context.end_date----: {}'.format(
            type(self.context.end_date)))
        # capital = self.fill.initial_cash  # 初始资金
        trade_log = self.get_trade_log(instrument)
        # logger.info('------trade_log-----: {}'.format(trade_log))
        trade_log = trade_log[trade_log['lots'] != 0]
        trade_log.to_csv(date + '_trade_log.csv')
        self.context.trade_log = trade_log
        logger.info('------trade_log-----: {}'.format(trade_log))
        logger.debug('------context.fill-----: {}'.format(
            self.context.fill.long_realized_gain_and_loss.df))
        trade_log.reset_index(drop=True, inplace=True)
        analysis = stats(self.context)
        logger.info('----------------------stats-----------------------')
        logger.debug('---analysis_table---: {}'.format(analysis))
        equity = self.context.fill.equity.df
        equity.to_csv(date + '_equity.csv')
        analysis_table = dict_to_table(analysis)
        with open(date + '_analysis_table.txt', 'w') as f:
            f.write(str(analysis_table))
        logger.info('analysis_table: {}'.format(analysis_table))
Esempio n. 7
0
 def get_last_closed_equity():
     """获取上一次开仓后的权益"""
     df = self.position.df
     logger.info('---type of df---: {} {}'.format(type(df), df))
     closed_date = df[df == 0].dropna().index[-1]  # 找到最近一个不持仓的时间
     index_num = df.index.get_loc(closed_date) + 1
     logger.info('---index_num---: {}'.format(index_num))
     eq_df = self.equity.df
     logger.info('---type of df---: {} {}'.format(type(eq_df), eq_df))
     index = eq_df.index[index_num]
     equity = eq_df[eq_df.index == index]['equity'][0]
     return equity
Esempio n. 8
0
    def __output_summary(self):
        """输出简略的回测结果"""
        total = pd.DataFrame(self.fill.equity.dict)
        logger.debug('-----------self.fill.equity-------------: {}'.format(
            self.fill.equity))
        logger.debug('-------total---------: {}'.format(total))
        # total.to_csv('total.csv')
        # equity = self.fill.equity.df
        # equity.to_csv('equity.csv')
        logger.debug('-----------total.index----------: {}'.format(
            total.index))
        logger.debug('-----------total.columns----------: {}'.format(
            total.columns))
        drawdown = create_drawdowns(total)
        logger.debug('----------------drawdown done----------')
        # 计算列中的后一个元素与前一个元素差的百分比
        total.set_index('date', inplace=True)  # 去掉 date,保留 equity
        pct_returns = total.pct_change()
        logger.debug('------------pct_change------------')
        total /= self.fill.initial_cash
        logger.debug('-----------total /--------------')
        # max_drawdown_pct, duration_for_pct = create_drawdowns(total['equity'])
        # logger.info('------------max_drawdown, duration----------: {} {}'.format(max_drawdown_pct, duration_for_pct))
        # logger.info('------------max_drawdown, duration----------: {} {}'.format(max_drawdown_value, duration_for_value))
        # 计算测试天数
        date_type = '%Y-%m-%d'
        start_date = datetime.strptime(self.context.start_date, date_type)
        end_date = datetime.strptime(self.context.end_date, date_type)
        self.context.test_days = (end_date - start_date).days
        results = OrderedDict()
        results['测试开始时间'] = self.context.start_date
        results['测试结束时间'] = self.context.end_date
        results['测试天数'] = self.context.test_days  # 从测试数据开始到结束的天数
        results['测试周期'] = self.context.count  # 测试数据的 K 线数
        results['指令总数'] = len(self.fill.completed_list)  # 指令总数
        results['初始资金'] = self.fill.initial_cash
        results['最终权益'] = round(self.fill.equity[-1], 2)  # 最终权益
        logger.info('------------results-----------: {}'.format(results))
        total_return = round(results['最终权益'] / self.fill.initial_cash - 1, 4)
        logger.info(
            '------------total_return-----------: {}'.format(total_return))
        results['夏普比率'] = round(create_sharpe_ratio(pct_returns), 2)
        results['盈利率'] = str(round(total_return * 100, 2)) + '%'
        results['最大回撤'] = drawdown['drawdown'].max()
        results['最大回撤时间'] = drawdown['date'][
            drawdown['drawdown'].idxmax()]  # 只能找出一个 index
        results['最大回撤比'] = str(round(drawdown['pct'].max() * 100, 2)) + '%'
        results['最大回撤比时间'] = drawdown['date'][drawdown['pct'].idxmax()]
        logger.debug('----short_realized_gain_and_loss----: {}'.format(
            self.fill.short_realized_gain_and_loss.dict))
        logger.debug(
            '---type of short_realized_gain_and_loss.list---: {}'.format(
                type(self.fill.short_realized_gain_and_loss.list)))
        logger.debug('---short_realized_gain_and_loss.list---: {}'.format(
            self.fill.short_realized_gain_and_loss.list))

        long_profit = [
            i for i in self.fill.long_realized_gain_and_loss.list if i >= 0
        ]
        long_loss = [
            i for i in self.fill.long_realized_gain_and_loss.list if i < 0
        ]
        long_commission = self.fill.long_commission.list
        short_profit = [
            i for i in self.fill.short_realized_gain_and_loss.list if i >= 0
        ]
        short_loss = [
            i for i in self.fill.short_realized_gain_and_loss.list if i < 0
        ]
        short_commission = self.fill.short_commission.list
        logger.debug(
            '---self.fill.long_realized_gain_and_loss.list---: {}'.format(
                self.fill.long_realized_gain_and_loss.list))
        logger.debug(
            '---self.fill.short_realized_gain_and_loss.list---: {}'.format(
                self.fill.short_realized_gain_and_loss.list))
        logger.debug('----long_profit, long_loss---: {} {}'.format(
            long_profit, long_loss))
        logger.debug('----long_commission---: {}'.format(long_commission))
        logger.debug('----short_profit, short_loss---: {} {}'.format(
            short_profit, short_loss))
        logger.debug('----short_commission---: {}'.format(short_commission))
        results['多头总盈利'] = round(sum(long_profit), 2)
        results['多头总亏损'] = round(sum(long_loss), 2)
        results['空头总盈利'] = round(sum(short_profit), 2)
        results['空头总亏损'] = round(sum(short_loss), 2)
        results['手续费'] = sum(long_commission) + sum(short_commission)
        logger.info('---------results---------: {}'.format(results))
        results_table = dict_to_table(results)
        with open(date + '_results.txt', 'w') as f:
            f.write(results_table)
Esempio n. 9
0
    def next(self):
        # ma2 = self.indicator.SMA(period=2, index=-1)
        # high = self.high
        # low = self.low
        # close = self.close
        # # true_range = max(high - low, abs(close.data(1) - high), abs(close.data(1) - low))
        # # average_true_range = talib.SMA(true_range, 20)
        # average_true_range = self.average_true_range.data(5)
        # money = self.equity[-1]
        # units = self.units
        # trade_lots = int(money * 0.01 / (units * average_true_range)) + 1
        # total_trade_lots = 4 * trade_lots
        # max_high = self.max_high.data(5)
        # min_low = self.min_low.data(5)
        # cross = self.cross
        # if cross.crossup(close, max_high):
        #     self.buy(trade_lots)
        # if cross.crossdown(close, min_low):
        #     self.sell(trade_lots)
        """后续再考虑变量的问题,先把策略写死,往后进行"""
        # high = indicator.Indicators(self.market_event, 'high')
        # logger.info('high.data_dict: {}'.format(high.data_dict))
        # low = indicator.Indicators(self.market_event, 'low')
        # close = indicator.Indicators(self.market_event, 'close')
        # close_ref = indicator.Indicators(self.market_event, close, 1)
        # logger.info('close_ref and type of it: {} {}'.format(close_ref, type(close_ref)))
        # arg1 = high - low
        # value = indicator.Evaluate(arg1)
        # value = value.evaluate()
        # logger.info('value: {}'.format(value))
        # logger.info('arg1 and type of it: {} {}'.format(arg1, type(arg1)))
        # logger.info('arg1.data_dict: {}'.format(arg1.data_dict))
        # arg2 = abs(close_ref - high)
        # logger.info('arg2 and type of it: {} {}'.format(arg2, type(arg2)))
        # arg3 = abs(close_ref - low)
        # tr = indicator.Indicators.max(arg1, arg2, arg3)
        # logger.info('tr.data_dict: {}'.format(tr.data_dict))
        # atr = indicator.Indicators.moving_average(tr, 5)
        # logger.info('self.equity: {}'.format(self.equity))
        # logger.info('self.equity[-1]: {}'.format(self.equity[-1]))
        # money = self.equity[-1]
        # logger.info('money: {}'.format(money))
        # unit = self.units
        # tc = indicator.Indicators.int_part(money * 0.01 / unit * atr)
        # mtc = tc * 4
        # hh = indicator.Indicators.max_high(high, 5)
        # ll = indicator.Indicators.min_low(low, 5)

        #  引用技术指标基类,方便调用各种参数
        indi = indicator.Indicator(self.market_event)
        money = indi.money()
        unit = indi.units()
        atr = indi.average_true_range(4)
        tc = int(money * 0.01 / (unit * atr))
        mtc = 4 * tc
        # 列表中放入数据时,前面周期的数据放前面,当前周期的数据放后面
        close = []
        close.append(indi.close(2)[0])  # 前一周期的 close
        close.append(indi.close(1)[0])  # 当前周期的 close
        logger.info('close list: {}'.format(close))
        max_high = []
        max_high.append(indi.max_high(3, 1))
        max_high.append(indi.max_high(3))
        min_low = []
        min_low.append(indi.min_low(3, 1))
        min_low.append(indi.min_low(3))
        up = indi.cross_up(close, max_high)
        logger.info('up上穿: {}'.format(up))
        down = indi.cross_down(close, max_high)
        logger.info('down下穿: {}'.format(down))
Esempio n. 10
0
 def test_max_consecutive_winning_trades(self):
     number = analysis.max_consecutive_winning_trades(context)
     logger.info('---number---: {}'.format(number))
     self.assertEqual(number, 1)
Esempio n. 11
0
 def test_risk_rate(self):
     risk_rate = round(analysis.risk_rate(equity) / 100, 4)
     logger.info('---risk_rate---: {}'.format(risk_rate))
     self.assertEqual(risk_rate, 0.1798)
Esempio n. 12
0
 def test_sharpe_ratio(self):
     rets = equity['equity'].pct_change()
     # sharpe_ratio = analysis.sharpe_ratio(rets)
     sharpe_ratio = analysis.sharpe_ratio(rets, 500000, 28)
     logger.info('---sharpe_ratio---: {}'.format(sharpe_ratio))
     self.assertEqual(sharpe_ratio, 1.0)
Esempio n. 13
0
    def update_time_index(self, feed_list):
        """
        保持每日开盘后的数据更新,
        若当日无交易,则更新后的数据即为当日数据,
        若当日有交易,则交易后的数据覆盖开盘后更新的数据
        """
        date = feed_list[-1].cur_bar.cur_date
        logger.info('---------------------------------------------------------------')
        logger.info('update_time_index, 开盘后date in backtestfill: {}'.format(date))

        # for feed in feed_list:
        feed = feed_list[-1]
        # 控制计算的价格,同指令成交价一样
        price = feed.cur_bar.cur_close  # 取收盘价为计算价格
        # high = feed.cur_bar.cur_high
        # low = feed.cur_bar.cur_low
        logger.info('price in date in update_time_index: {} {}'.format(price, date))
        self.set_dataseries_instrument(feed.instrument)
        # self.position.copy_last(date)  # 更新仓位
        # logger.debug('self.position in backtestfill: {}'.format(self.position))
        # logger.info('self.position in backtestfill: {}'.format(self.position[-1]))
        logger.debug('self.position[-1] in date in update_time_index: {} {}'.format(self.position[-1], date))

        # 更新保证金
        # margin = self.position[-1] * price * feed.per_margin * feed.mult
        # margin = feed.per_margin * feed.units * price * 300
        margin = self.margin[-1]  # 未持仓时,保证金 = 上一次开仓保证金 或 0(平仓后)
        logger.debug('feed.units: {}'.format(feed.units))
        # margin = abs(self.position[-1]) * price * feed.per_margin
        logger.debug('margin in date in update_time_index: {} {}'.format(margin, date))
        self.margin.add(date, margin)
        # 更新平均价格
        # self.avg_price.copy_last(date)
        # 更新手续费,注意期货手续费需要重新计算
        commission = 0  # 无交易进行时,手续费 = 0
        self.commission.add(date, commission)
        logger.debug('commission in date in update_time_index: {} {}'.format(self.commission[-1], date))
        # self.commission.add(date, commission)
        # 更新浮动盈亏
        cur_avg = self.avg_price[-1]
        self.avg_price.add(date, cur_avg)
        cur_position = self.position[-1]
        self.position.add(date, cur_position)
        logger.debug('cur_position, cur_avg in date update_time_index: {} {} {}'.format(cur_position, cur_avg, date))
        # 浮盈 = (卖出价 - 买入价)× 手数
        # 若卖出平仓,则原仓位为正,浮盈 = (平仓价(卖出)- 持仓均价)× 原仓位
        # 若买入平仓,则原仓位为负,浮盈 = (平仓价(买入)- 持仓均价)× 原仓位
        unrealized_g_l = (price - cur_avg) * cur_position * feed.units
        if self.avg_price[-1] == 0:
            unrealized_g_l = 0
            # unrealized_g_l = unrealized_g_l_high = unrealized_g_l_low = 0
        logger.debug('cur_avg in date in update_time_index: {} {}'.format(cur_avg, date))
        logger.debug('unrealized_g_l in date in update_time_index: {} {}'.format(unrealized_g_l, date))
        self.unrealized_gain_and_loss.add(date, unrealized_g_l)

        def get_last_closed_equity():
            """获取上一次开仓后的权益"""
            df = self.position.df
            logger.info('---type of df---: {} {}'.format(type(df), df))
            closed_date = df[df == 0].dropna().index[-1]  # 找到最近一个不持仓的时间
            index_num = df.index.get_loc(closed_date) + 1
            logger.info('---index_num---: {}'.format(index_num))
            eq_df = self.equity.df
            logger.info('---type of df---: {} {}'.format(type(eq_df), eq_df))
            index = eq_df.index[index_num]
            equity = eq_df[eq_df.index == index]['equity'][0]
            return equity

        # 更新equity
        # last_equity = get_last_closed_equity()
        total_re_profit = sum(self.realized_gain_and_loss.list)
        logger.debug('total_re_profit: {}'.format(total_re_profit))
        logger.debug('self.realized_gain_and_loss.list: {}'.format(self.realized_gain_and_loss.list))
        total_profit = total_re_profit + self.unrealized_gain_and_loss[-1]
        logger.debug('total_profit in date in update_time_index: {} {}'.format(total_profit, date))
        logger.debug('sum(self.commission.list) in date in update_time_index: {} {}'.format(
            sum(self.commission.list), date))
        # logger.debug('self.commission.list in date in update_time_index: {} {}'.format(self.commission.list, date))
        total_commission = sum(self.commission.list)
        logger.debug('total_re_profit: {}'.format(total_re_profit))
        logger.debug('total_commission:{}'.format(total_commission))
        # 持仓时余额 = 上一次开仓后的余额 + 当日浮动盈亏
        # equity = last_equity + unrealized_g_l  # **这里可能有问题**
        equity = self.initial_cash + total_profit - total_commission
        # logger.info('equity, date in update_time_index: {} {} {}'.format(date, equity, equity2))
        self.equity.add(date, equity)

        # 更新cash
        # total_margin = self.margin.total()
        logger.debug('total_margin in update_time_index: {}'.format(margin))
        if self.position[-1] == 0:
            cash = self.equity[-1]
        else:
            cash = np.round(self.equity[-1] - margin, 2)
        logger.debug('cash in date in update_time_index: {} {}'.format(cash, date))
        self.cash.add(date, cash)
        logger.info('####################################')

        # 检查是否爆仓
        if self.equity[-1] <= 0 or self.cash[-1] <= 0:
            for feed in feed_list:
                feed.continue_backtest = False
            logger.info('警告:策略已造成爆仓!')
            logger.info('####################################')