Backtrader数据馈送开发

  |  

添加新的基于 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数据馈送开发中。

基类负责参数、初始化、打开文档、读取、将拆分为标记以及其他事情,例如跳过不适合用户可能已定义的日期范围( fromdatetodate )的.

开发非 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的情况下,此参数意味着文档的路径或已经是类似文档的对象。

  • fromdatetodate定义将传递给策略的日期范围。 Feed 提供的超出此范围的任何值都将被忽略

  • name是用于绘图目的的化妆品

  • timeframecompression是美观和信息丰富的。它们确实在数据重采样和数据重放中发挥作用。

  • 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、 openhighlowclosevolumeopeninterest 。在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、 openhighlowclosevolumeopeninterest

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

推荐阅读

相关文章

Backtrader期货展期

并非每个供应商都为可以交易的工具提供连续的未来。有时提供的数据是仍然有效的到期日期的数据,即:仍在交易的日期 这在回溯测试方面并不是很有帮助,因为数据分散在几个不同的仪器上,这些仪器另外...时间重叠。 能够正确地将这些仪器的数据从过去连接到连续的流中,可以减轻疼痛。

Backtrader细分佣金计划

不久前,委员会计划的实施进行了重新设计。最重要的是:涉及的部分返工: 保留原始的佣金信息类和行为 打开大门,轻松创建用户定义的佣金 将格式 xx% 作为新佣金方案的默认值,而不是 0.xx(只是一个品味问题),保持行为可配置 扩展委员会概述了基本要素。

Backtrader分数大小

首先,让我们用两行总结反向交易方法的工作原理:它就像一个带有基本构建块 ( Cerebro ) 的建筑套件,可以插入许多不同的部件基本分布包含许多部分,如指针、分析器、观察器、 Sizer 、过滤器、数据馈送、经纪人、佣金/资产信息方案,...

Backtrader教程:安装

要求和版本 backtrader 是独立的,没有外部依赖关系(除非要绘图) 基本要求是: Python 2.7 Python 3.2 / 3.3/ 3.4 / 3.5 pypy/pypy3 如果需要绘图,则其他要求: Matplotlib >= 1.4.

Backtrader标杆

backtrader 包括 2 种不同类型的对象,可帮助进行跟踪: Observers Analyzers 工单 #89 是关于添加资产基准测试的。明智的是,人们实际上可能有一个策略,即使积极,也低于简单地跟踪资产所能提供的策略。

Backtrader实际使用方式

最后,似乎已经付出了开发 backtrader是值得的。 在观察 last 周的欧洲市场时,似乎世界末日了,一位朋友问我是否可以看看我们图表包中的数据,看看与以前类似情况相比,下跌幅度如何。 当然可以,但我说我可以做的不仅仅是查看图表,因为我可以快速: 创建一个快速LegDown 指示器来测量跌落的范围。

Backtrader教程:日志记录 - 编写器

将以下内容写出到流中: csv 流,

Backtrader 教程:Cerebro - 例外

设计目标之一是尽早退出,让用户完全了解错误发生的情况。目的是强迫自己拥有会因异常而中断的代码并强制重新访问受影响的部分。但是时机已经成熟,一些例外可能会慢慢添加到平台中。

Backtrader教程:数据馈送 - 熊猫

注意 pandas 并且必须安装其依赖项 支持Pandas Dataframes似乎受到很多人的关注,他们依赖于已经可用的解析代码来分析不同的数据源(包括CSV)和Pandas提供的其他功能。 数据馈送的重要声明。 注意 这些只是 声明。不要盲目拷贝此代码。

Backtrader多核优化

利用所有可用的内核是我为反向交易者考虑的事情,但从未完成。支持自然运算,删除数组符号,包含新指针和 bla, bla, bla。实际上,我不是优化的忠实拥护者,因此也不是为优化使用所有内核的忠实拥护者。恕我直言,一个好主意值得一百万次优化。笔记最初的多核支持已经存在,并且适用于众所周知的测试用例集。