添加新的基于 CSV 的数据馈送很容易。现有的基类 CSVDataBase 提供的框架将大部分工作从子类中移除,在大多数情况下可以简单地完成:
def _loadline(self, linetokens): # parse the linetokens here and put them in self.lines.close, # self.lines.high, etc return True # if data was parsed, else ... return False
这显示在 CSV数据馈送开发中。
基类负责参数、初始化、打开文档、读取行、将行拆分为标记以及其他事情,例如跳过不适合用户可能已定义的日期范围( fromdate
, todate
)的行.
开发非 CSV 数据馈送遵循相同的模式,而无需深入到已经拆分的行标记。
要做的事情:
从backtrader .feed.DataBase 派生
添加您可能需要的任何参数
如果需要初始化,覆盖
__init__(self)
和/或start(self)
如果需要任何清理代码,请覆盖
stop(self)
工作发生在必须始终被覆盖的方法内部:
_load(self)
让我们看看backtrader .feed.DataBase
已经提供的参数:
class DataBase(six.with_metaclass(MetaDataBase, dataseries.OHLCDateTime)): params = (('dataname', None), ('fromdate', datetime.datetime.min), ('todate', datetime.datetime.max), ('name', ''), ('compression', 1), ('timeframe', TimeFrame.Days), ('sessionend', None))
具有以下含义:
dataname
允许数据馈送识别如何获取数据。在CSVDataBase
的情况下,此参数意味着文档的路径或已经是类似文档的对象。fromdate
和todate
定义将传递给策略的日期范围。 Feed 提供的超出此范围的任何值都将被忽略name
是用于绘图目的的化妆品timeframe
和compression
是美观和信息丰富的。它们确实在数据重采样和数据重放中发挥作用。sessionend
如果通过(一个datetime
对象)将被添加到允许识别会话结束的数据馈送日期时间行
示例二进制数据馈送
backtrader
已经为VisualChart的导出定义了一个 CSV 数据馈送 ( VChartCSVData
),但也可以直接读取二进制数据文档。
让我们开始吧(完整的数据馈送代码可以在底部找到)
初始化
二进制 VisualChart 数据文档可以包含每日(.fd 扩展名)或日内数据(.min 扩展名)。在这里,信息参数timeframe
将用于区分正在读取的文档类型。
在__init__
期间,设置了每种类型不同的常量。
def __init__(self): super(VChartData, self).__init__() # Use the informative "timeframe" parameter to understand if the # code passed as "dataname" refers to an intraday or daily feed if self.p.timeframe >= TimeFrame.Days: self.barsize = 28 self.dtsize = 1 self.barfmt = 'IffffII' else: self.dtsize = 2 self.barsize = 32 self.barfmt = 'IIffffII'
开始
Datafeed 将在回测开始时启动(实际上可以在优化期间启动多次)
在start
方法中,除非传递了类似文档的对象,否则二进制文档是打开的。
def start(self): # the feed must start ... get the file open (or see if it was open) self.f = None if hasattr(self.p.dataname, 'read'): # A file has been passed in (ex: from a GUI) self.f = self.p.dataname else: # Let an exception propagate self.f = open(self.p.dataname, 'rb')
停止
回测完成时调用。
如果一个文档是打开的,它将被关闭
def stop(self): # Close the file if any if self.f is not None: self.f.close() self.f = None
实际加载
实际工作在_load
中完成。调用以加载下一组数据,在本例中为下一组:datetime、 open 、 high 、 low 、 close 、 volume 、 openinterest 。在backtrader
中,“实际”时刻对应于索引 0。
将从打开的文档中读取一些字节(由__init__
期间设置的常量确定),用struct
模块解析,如果需要进一步处理(如日期和时间的 divmod 操作)并存储在数据lines
中提要:日期时间、开盘价、最高价、最低价、收盘价、交易量、持仓量。
如果无法从文档中读取数据,则假定已到达文档结尾 (EOF)
- 返回
False
表示没有更多数据可用
否则,如果数据已加载并解析:
- 返回
True
表示数据集加载成功
def _load(self): if self.f is None: # if no file ... no parsing return False # Read the needed amount of binary data bardata = self.f.read(self.barsize) if not bardata: # if no data was read ... game over say "False" return False # use struct to unpack the data bdata = struct.unpack(self.barfmt, bardata) # Years are stored as if they had 500 days y, md = divmod(bdata[0], 500) # Months are stored as if they had 32 days m, d = divmod(md, 32) # put y, m, d in a datetime dt = datetime.datetime(y, m, d) if self.dtsize > 1: # Minute Bars # Daily Time is stored in seconds hhmm, ss = divmod(bdata[1], 60) hh, mm = divmod(hhmm, 60) # add the time to the existing atetime dt = dt.replace(hour=hh, minute=mm, second=ss) self.lines.datetime[0] = date2num(dt) # Get the rest of the unpacked data o, h, l, c, v, oi = bdata[self.dtsize:] self.lines.open[0] = o self.lines.high[0] = h self.lines.low[0] = l self.lines.close[0] = c self.lines.volume[0] = v self.lines.openinterest[0] = oi # Say success return True
其他二进制格式
相同的模型可以应用于任何其他二进制源:
数据库
分层数据存储
在线资源
又是步骤:
__init__
-> 实例的任何初始化代码,只有一次start
-> 开始回测(如果要运行优化,则进行一次或多次)例如,这将打开到数据库的连接或到在线服务的套接字
stop
-> 清理,例如关闭数据库连接或打开套接字_load
-> 查找数据库或在线源以获取下一组数据并将其加载到对象的lines
中。标准字段为:datetime、 open 、 high 、 low 、 close 、 volume 、 openinterest
VChartData 测试
VCharData
从本地“.fd”文档为 Google 加载 2006 年的数据。
它只是关于加载数据,因此甚至不需要Strategy
的子类。
from __future__ import (absolute_import, division, print_function, unicode_literals) import datetime import backtrader as bt from vchart import VChartData if __name__ == '__main__': # Create a cerebro entity cerebro = bt.Cerebro(stdstats=False) # Add a strategy cerebro.addstrategy(bt.Strategy) # Create a Data Feed datapath = '../datas/goog.fd' data = VChartData( dataname=datapath, fromdate=datetime.datetime(2006, 1, 1), todate=datetime.datetime(2006, 12, 31), timeframe=bt.TimeFrame.Days ) # Add the Data Feed to Cerebro cerebro.adddata(data) # Run over everything cerebro.run() # Plot the result cerebro.plot(style='bar')
VChartData 完整代码
from __future__ import (absolute_import, division, print_function, unicode_literals) import datetime import struct from backtrader.feed import DataBase from backtrader import date2num from backtrader import TimeFrame class VChartData(DataBase): def __init__(self): super(VChartData, self).__init__() # Use the informative "timeframe" parameter to understand if the # code passed as "dataname" refers to an intraday or daily feed if self.p.timeframe >= TimeFrame.Days: self.barsize = 28 self.dtsize = 1 self.barfmt = 'IffffII' else: self.dtsize = 2 self.barsize = 32 self.barfmt = 'IIffffII' def start(self): # the feed must start ... get the file open (or see if it was open) self.f = None if hasattr(self.p.dataname, 'read'): # A file has been passed in (ex: from a GUI) self.f = self.p.dataname else: # Let an exception propagate self.f = open(self.p.dataname, 'rb') def stop(self): # Close the file if any if self.f is not None: self.f.close() self.f = None def _load(self): if self.f is None: # if no file ... no parsing return False # Read the needed amount of binary data bardata = self.f.read(self.barsize) if not bardata: # if no data was read ... game over say "False" return False # use struct to unpack the data bdata = struct.unpack(self.barfmt, bardata) # Years are stored as if they had 500 days y, md = divmod(bdata[0], 500) # Months are stored as if they had 32 days m, d = divmod(md, 32) # put y, m, d in a datetime dt = datetime.datetime(y, m, d) if self.dtsize > 1: # Minute Bars # Daily Time is stored in seconds hhmm, ss = divmod(bdata[1], 60) hh, mm = divmod(hhmm, 60) # add the time to the existing atetime dt = dt.replace(hour=hh, minute=mm, second=ss) self.lines.datetime[0] = date2num(dt) # Get the rest of the unpacked data o, h, l, c, v, oi = bdata[self.dtsize:] self.lines.open[0] = o self.lines.high[0] = h self.lines.low[0] = l self.lines.close[0] = c self.lines.volume[0] = v self.lines.openinterest[0] = oi # Say success return True