添加新的基于 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