backtrader日志、日历及resample

本文摘自https://blog.csdn.net/m0_46603114/article/details/107889583

日志功能

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

cerebro.addwriter(bt.WriterFile, out = 'log.csv', csv = True)
1
日志信息将被输出到工作目录下的log.csv文件中,输出内容包括:

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

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

由日线数据resample得到周线数据时,使用交易日历可以准确识别每周的最后1根日线。
在策略的next()方法中打印一下内容

在输出的每一行中,第一个日期为策略所使用的日期,第二个日期为当前日线的日期,第三个日期为当前月线的日期。
其中,第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())

当使用交易日历时可以看到,在1月4日(星期五)合成了第一根周线,在1月11日(星期五)合成了第二根周线,得到了正确的结果。

同样,可以把2019年的其他节假日都添加到holidays列表中,来实现整年的周线数据的正确合成。

日内分时数据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中,部分结果如下:


标黄的D、L、T列分别表示加载的30分钟时间标签数据、由30分钟数据合成的60分钟时间标签数据、加载的60分钟时间标签数据。
对比D列和T列可以发现,在加载的60分钟数据中,6月1日首个时间点(10:00)数据为空,在时间点10:30出现第一个数据。在D列时间标签变为11:00时,T列时间标签仍为10:30。在D列时间标签变为11:30时,T列时间标签才变为11:30。这样就保证了,在同时加载多个周期数据进行回测时,不会发生使用未来数据的情况。
对比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()加载合成的数据,再进行多周期策略的回测。

posted @ 2022-04-13 07:58  星涅爱别离  阅读(883)  评论(0编辑  收藏  举报