Python量化交易学习笔记(40)——backtrader的resample浅析

本文主要包含以下三部分内容:

  1. backtrader的日志功能。
  2. backtrader的交易日历。
  3. backtrader的resample结果浅析。

日志功能

可以通过下面的代码在backtrader中添加日志功能:

cerebro.addwriter(bt.WriterFile, out = 'log.csv', csv = True)

日志信息将被输出到工作目录下的log.csv文件中,输出内容包括:

  • 种子数据(Data Feeds)
  • 策略数据(lines和参数)
  • 指标和观察者(Observer)数据(lines和参数)
  • 分析数据(参数和分析结果数据)

交易日历

交易日历适用于以下场景:

  1. 由日线数据resample得到周线数据时,使用交易日历可以准确识别每周的最后1根日线。
    以2019年1月1日至12月31日的数据为例,在策略的next()方法中打印一下内容:
        print('Strategy len {} datetime {}'.format(
            len(self), self.datetime.date()), end=' ')

        print('Data0 len {} datetime {}'.format(
            len(self.data0), self.data0.datetime.date()), end=' ')

        if len(self.data1):
            print('Data1 len {} datetime {}'.format(
                len(self.data1), self.data1.datetime.date()))
        else:
            print()

当不使用交易日历时,部分输出结果为:

Strategy len 1 datetime 2019-01-02 Data0 len 1 datetime 2019-01-02 
Strategy len 2 datetime 2019-01-03 Data0 len 2 datetime 2019-01-03 
Strategy len 3 datetime 2019-01-04 Data0 len 3 datetime 2019-01-04
Strategy len 4 datetime 2019-01-07 Data0 len 4 datetime 2019-01-07 Data1 len 1 datetime 2019-01-04
Strategy len 5 datetime 2019-01-08 Data0 len 5 datetime 2019-01-08 Data1 len 1 datetime 2019-01-04
Strategy len 6 datetime 2019-01-09 Data0 len 6 datetime 2019-01-09 Data1 len 1 datetime 2019-01-04
Strategy len 7 datetime 2019-01-10 Data0 len 7 datetime 2019-01-10 Data1 len 1 datetime 2019-01-04
Strategy len 8 datetime 2019-01-11 Data0 len 8 datetime 2019-01-11 Data1 len 1 datetime 2019-01-04
Strategy len 9 datetime 2019-01-14 Data0 len 9 datetime 2019-01-14 Data1 len 2 datetime 2019-01-11
...

在输出的每一行中,第一个日期为策略所使用的日期,第二个日期为当前日线的日期,第三个日期为当前月线的日期。
其中,第3行1月4日为星期五,应该出现第一根周线数据,但是输出结果中却没有周线数据。第8行1月11日为周五,从1月7日至1月11日的5根日线可以合成1根周线,后面的周线数据也应该更新为1月11日,但是输出结果却是1月4日。
产生以上结果的原因是,backtrader把2019年1月1日当作了交易日,只是没有读入数据。这样1月1日至4日,再加上7日共计5根日线,就在1月7日合成了第一根周线;同样1月8日至11日,再加上14日共计5根日线,在1月14日合成了第二根周线。这显然不是想要的结果。

解决上述问题的方案是,通过继承bt.TradingCalendar定义新的日历,然后在日历中设定节假日。如以下代码所示,将2019年1月1日添加到holidays列表中,然后在cerebro添加该日历:

class AStockCalendar(bt.TradingCalendar):
    params = dict(
        holidays=[
            datetime.date(2019, 1, 1),
        ],
        open=datetime.time(9, 30),
        close=datetime.time(15, 0),
    )
   
cerebro.addcalendar(AStockCalendar())

当使用交易日历时,部分输出结果为:

Strategy len 1 datetime 2019-01-02 Data0 len 1 datetime 2019-01-02 
Strategy len 2 datetime 2019-01-03 Data0 len 2 datetime 2019-01-03 
Strategy len 3 datetime 2019-01-04 Data0 len 3 datetime 2019-01-04 Data1 len 1 datetime 2019-01-04
Strategy len 4 datetime 2019-01-07 Data0 len 4 datetime 2019-01-07 Data1 len 1 datetime 2019-01-04
Strategy len 5 datetime 2019-01-08 Data0 len 5 datetime 2019-01-08 Data1 len 1 datetime 2019-01-04
Strategy len 6 datetime 2019-01-09 Data0 len 6 datetime 2019-01-09 Data1 len 1 datetime 2019-01-04
Strategy len 7 datetime 2019-01-10 Data0 len 7 datetime 2019-01-10 Data1 len 1 datetime 2019-01-04
Strategy len 8 datetime 2019-01-11 Data0 len 8 datetime 2019-01-11 Data1 len 2 datetime 2019-01-11
Strategy len 9 datetime 2019-01-14 Data0 len 9 datetime 2019-01-14 Data1 len 2 datetime 2019-01-11
...

可以看到,在1月4日(星期五)合成了第一根周线,在1月11日(星期五)合成了第二根周线,得到了正确的结果。
同样,可以把2019年的其他节假日都添加到holidays列表中,来实现整年的周线数据的正确合成。

  1. 日内分时数据resample合成日线数据时,使用交易日历处理提前收盘的情况。
    美股在感恩节后的第一天,会在下午1点休市,这样如果不做处理,使用分钟数据合成日线数据时就会出问题,可以通过添加以下日历来解决:
class CustomCalendar(bt.TradingCalendar):
    params = dict(
        holidays=[
            datetime.date(2016, 1, 1),
            datetime.date(2016, 1, 18),
            datetime.date(2016, 2, 15),
            datetime.date(2016, 3, 25),
            datetime.date(2016, 5, 30),
            datetime.date(2016, 7, 4),
            datetime.date(2016, 9, 5),
            datetime.date(2016, 11, 24),
            datetime.date(2016, 12, 26),
        ],
        earlydays=[
            (datetime.date(2016, 11, 25),
             datetime.time(9, 30), datetime.time(13, ,0))
        ],
        open=datetime.time(9, 30),
        close=datetime.time(16, 0),
    )

以2016年为例,日历中设定了2016年的节假日,一般每日交易时间为9:30至16:00,11月25日的交易时间为9:30至13:00。

resample浅析

借助于日志功能,对resample功能进行简单分析。这里以30分钟数据为数据源,使用resample来合成60分钟数据,同时加载已下载好的60分钟数据用于对比resample结果,部分代码如下:

data = load_data(stk_code, fromdate, todate, '30')
cerebro.adddata(data, name = stk_code + '_30m')
cerebro.resampledata(data, name = stk_code + '_30to60m', timeframe = bt.TimeFrame.Minutes, compression = 60)
data = load_data(stk_code, fromdate, todate, '60')
cerebro.adddata(data, name = stk_code + '_60m'

以加载2020年6月1日至4日平安银行的数据为例,使用日志功能将结果输出至log.csv中,部分结果如下:
Python量化交易学习笔记(40)——backtrader的resample浅析_第1张图片

  1. 标黄的D、L、T列分别表示加载的30分钟时间标签数据、由30分钟数据合成的60分钟时间标签数据、加载的60分钟时间标签数据。
  2. 对比D列和T列可以发现,在加载的60分钟数据中,6月1日首个时间点(10:00)数据为空,在时间点10:30出现第一个数据。在D列时间标签变为11:00时,T列时间标签仍为10:30。在D列时间标签变为11:30时,T列时间标签才变为11:30。这样就保证了,在同时加载多个周期数据进行回测时,不会发生使用未来数据的情况
  3. 对比D列和L列发现,resample结果未按预期得到10:30、11:30、14:00、15:00四个时段的60分钟数据,而是得到10:00、11:00、12:00、14:00、15:00五个整点时间段的数据。笔者尝试了调整函数resampledata()的bar2edge、rightedge、adjbartime、boundoff参数,也尝试了使用交易日历功能设置每日交易起止时间,均未能实现预期的resample结果。目前的解决方案是,在合成分钟级数据时,不使用backtrader的resampledata()函数,而是先离线合成所需周期的数据,然后使用cerebro.adddata()加载合成的数据,再进行多周期策略的回测

你可能感兴趣的:(Python量化交易,python,量化交易,backtrader)