You are here:  Home » 量化交易与机器学习 » backtrader » 初步了解backtrader回测平台/框架 – backtrader中文教程

线迭代器

为了参与操作,平台使用线迭代器的概念。它们在 Python 的迭代器之后被松散地建模,但实际上与它们无关。

策略和指标是线迭代器。

线迭代器概念试图描述以下内容:

  • 线迭代器踢从线迭代器告诉他们迭代
  • Line Iterator 然后迭代它自己声明的命名行设置值

与常规 Python 迭代器一样,迭代的关键是:

  • next方法_每次迭代都会调用它。线迭代器具有并用作逻辑/计算基础的datas数组将已经被平台移动到下一个索引(除非数据重放)

    当满足行迭代器的最小周期时调用。下面对此进行更多介绍。

但是因为它们不是常规迭代器,所以存在两个额外的方法:

  • prenext在满足 line iterator`的最小周期之前调用。
  • nextstart当满足 line iterator`最小周期时恰好调用一次。

    默认行为是将呼叫转发到next,但如果需要,当然可以覆盖。

指标的额外方法

为了加快操作,Indicators 支持批量操作模式,也就是 runonce。它不是严格需要的(一种next方法就足够了),但它大大减少了时间。

runonce 方法规则使索引为 0 的 get/set 点无效,并依赖于对保存数据的底层数组的直接访问,并为每个状态传递正确的索引。

 

 

定义的方法遵循下一个系列的命名:

  • once(self, start, end)在满足最短期限时调用。内部数组必须在 start 和 end 之间处理,从内部数组的开头开始为零
  • preonce(self, start, end)在达到最短期限之前调用。
  • oncestart(self, start, end)满足最短期限时恰好调用一次。

    默认行为是将呼叫转发到once,但如果需要,当然可以覆盖。

最短期限

一张图片值一千字,在这种情况下也可能是一个例子。SimpleMovingAverage 能够解释它:

class SimpleMovingAverage(Indicator):
    lines = ('sma',)
    params = dict(period=20)

    def __init__(self):
        ...  # 与解释无关

    def prenext(self):
        print('prenext:: current period:', len(self))

    def nextstart(self):
        print('nextstart:: current period:', len(self))
        # 模拟默认行为 ... 调用下一个
        self.next()

    def next(self):
        print('next:: current period:', len(self))

实例化可能如下所示:

sma = btind.SimpleMovingAverage(self.data, period=25)

简要说明:

  • 假设传递给移动平均线的数据是标准数据馈送,其默认周期是1:数据馈送产生没有初始延迟的柱。
  • 那么“period=25”实例化移动平均线的方法调用如下:
    • prenext24次
    • nextstart1次(依次调用next
    • nextn 次,直到数据馈送用完

让我们来看看杀手级指标:一个 SimpleMovingAverage超过另一个 SimpleMovingAverage。实例化可能如下所示:

sma1 = btind.SimpleMovingAverage(self.data, period=25)

sma2 = btind.SimpleMovingAverage(sma1, period=20)

现在发生了什么:

  • 同上sma1
  • sma2正在接收一个最短周期为 25的数据馈送,这是我们的,因此sma1
  • 这些sma2方法的调用如下所示:
    • prenext前25+18次共43次
    • 25 次,以sma1产生其一个合理的价值
    • 18次累积额外sma1价值
    • 总共 19 个值(25 次调用后 1 个,然后再有 18 个)
    • nextstart然后 1 次(依次调用next
    • next额外的 n 次,直到数据馈送用完

next当系统已处理 44 根柱线时,平台正在调用。

最小周期已根据传入数据自动调整。

策略和指标遵循此行为:

  • 只有当达到自动计算的最小周期时才会 next被调用(除非初始挂钩调用nextstart
  • preonce相同的规则适用于runonceoncestart批处理操作模式once
  • 尽管不推荐,但可以操纵最小周期行为。如果希望setminperiod(minperiod) 在策略或指标中使用该方法

启动并运行

启动和运行至少涉及 3 个Lines对象:

  • 数据馈送
  • A Strategy(实际上是从 Strategy 派生的类)
  • A Cerebro大脑(西班牙语中的大脑)

数据馈送

显然,这些对象提供将通过应用计算(直接和/或使用指标)进行回测的数据

该平台提供了几个数据源:

  • 几种 CSV 格式和一个通用 CSV 阅读器
  • 雅虎在线提取器
  • 支持接收Pandas DataFramesblaze对象
  • 带有交互式经纪人可视化图表Oanda的实时数据馈送

该平台不对数据馈送的内容(例如时间范围和压缩)做出任何假设。这些值连同名称可用于提供信息和高级操作,例如数据馈送重采样(例如将 5 分钟的数据馈送转换为每日数据馈送)

设置 Yahoo Finance Data Feed 的示例:

import backtrader as bt
import backtrader.feeds as btfeeds

...

datapath = 'path/to/your/yahoo/data.csv'

data = btfeeds.YahooFinanceCSVData(
    dataname=datapath,
    reversed=True)

显示 Yahoo的可选reversed参数,因为直接从 Yahoo 下载的 CSV 文件以最新日期开始,而不是从最旧日期开始。

如果您的数据跨越较大的时间范围,实际加载的数据可以限制如下:

data = btfeeds.YahooFinanceCSVData(
    dataname=datapath,
    reversed=True
    fromdate=datetime.datetime(2014, 1, 1),
    todate=datetime.datetime(2014, 12, 31))

还可以添加压缩和名称:

data = btfeeds.YahooFinanceCSVData(
    dataname=datapath,
    reversed=True
    fromdate=datetime.datetime(2014, 1, 1),
    todate=datetime.datetime(2014, 12, 31)
    timeframe=bt.TimeFrame.Days,
    compression=1,
    name='Yahoo'
   )

策略(派生)类

Tips: 在继续之前并使用更简化的方法,如果不希望对策略进行子类化,请检查文档的 信号部分。

使用该平台的任何人的目标都是回测数据,这是在策略(派生类)中完成的。

有两种方法至少需要定制:

  • __init__
  • next

在初始化数据和其他计算指标的过程中,为以后应用逻辑做好准备。

稍后调用下一个方法以对数据的每个条形应用逻辑。

Tips: 如果传递了不同时间范围的数据馈送(因此不同的柱数)next,则将为主数据调用该方法(传递给cerebro的第一个,见下文),它必须是具有较小时间范围的数据

Tips: 如果使用数据重播功能,则在next重播该条的开发时,该方法将针对同一条被多次调用。

一个基本的 Strategy 派生类:

class MyStrategy(bt.Strategy):

    def __init__(self):

        self.sma = btind.SimpleMovingAverage(self.data, period=20)

    def next(self):

        if self.sma > self.data.close:
            self.buy()

        elif self.sma < self.data.close:
            self.sell()

策略有其他可以被覆盖的方法(或挂钩点):

class MyStrategy(bt.Strategy):

    def __init__(self):

        self.sma = btind.SimpleMovingAverage(self.data, period=20)

    def next(self):

        if self.sma > self.data.close:
            submitted_order = self.buy()

        elif self.sma < self.data.close:
            submitted_order = self.sell()

    def start(self):
        print('Backtesting is about to start')

    def stop(self):
        print('Backtesting is finished')

    def notify_order(self, order):
        print('An order new/changed/executed/canceled has been received')

start、stop的方法正如预期的那样,并遵循打印函数中的文本。notify_order当策略需要通知时,将调用该方法。用例:

  • 请求买入或卖出(如下所示)买/卖将返回提交给经纪人的订单。保留对这个提交的订单的引用取决于调用者。

    例如,它可用于确保在订单仍处于待处理状态时不会提交新订单。

  • 如果订单被接受/执行/取消/更改,经纪人将通过 notify 方法将状态更改(例如执行大小)通知回策略

notify_order快速入门指南在该方法中有一个完整且实用的订单管理示例。

其他 Strategy 类可以做更多事情:

  • buy// sell_close使用标的经纪商分级器向经纪商发送买入/卖出订单

    同样可以通过手动创建订单并将其传递给经纪人来完成。但该平台旨在让使用它的人更容易。

    close将获得当前市场头寸并立即平仓。

  • getposition(或属性“位置”)返回当前市场头寸
  • setsizergetsizer(或属性“sizer”)这些允许设置/获取底层股权 Sizer。相同的逻辑可以与 Sizer 进行检查,这些 Sizer 为相同的情况提供不同的赌注(固定大小,与资本成正比,指数)

    有很多文学作品,但 Van K. Tharp 有关于这个主题的优秀书籍。

策略是一个Lines对象和这些支持参数,这些参数是使用标准 Python kwargs 参数收集的:

class MyStrategy(bt.Strategy):

    params = (('period', 20),)

    def __init__(self):

        self.sma = btind.SimpleMovingAverage(self.data, period=self.params.period)

    ...
    ...

请注意如何SimpleMovingAverage不再使用固定值 20 进行实例化,而是使用已为策略定义的参数“周期”进行实例化。

一个大脑

一旦数据馈送可用并定义了策略,Cerebro 实例就可以将所有内容组合在一起并执行操作。实例化一个策略:

cerebro = bt.Cerebro()

如果不希望有什么特别的,默认值正在处理。

  • 创建默认代理
  • 运营不收取佣金
  • 数据馈送将被预加载
  • 默认执行模式为runonce(批量操作),速度更快所有指示灯必须支持runonce全速模式。平台中包含的那些。

    自定义指标不需要实现 runonce 功能。Cerebro将模拟它,这意味着那些非运行一次兼容的指标将运行得更慢。但大部分系统仍将以批处理模式运行。

由于数据馈送和策略(之前创建)已经可用,因此将它们放在一起并启动并运行的标准方法是:

cerebro.adddata(data)
cerebro.addstrategy(MyStrategy, period=25)
cerebro.run()

请注意以下事项:

  • 添加了数据馈送“实例”
  • MyStrategy“类”与将传递给它的参数(kwargs)一起添加。MyStrategy 的实例化将由 cerebro 在后台完成,“addstrategy”中的任何 kwargs 都将传递给它

用户可以根据需要添加尽可能多的策略和数据馈送。策略如何相互通信以实现协调(如果需要)不受平台强制/限制。

当然,Cerebro 提供了额外的可能性:

  • 决定预加载和操作模式:
cerebro = bt.Cerebro(runonce=True, preload=True)
  • 这里有一个约束:runonce需要预加载(如果没有,则无法运行批处理操作)当然预加载数据馈送不会强制执行 runonce
  • setbrokergetbroker(和经纪人财产)如果需要,可以设置自定义代理。也可以访问实际的代理实例
  • 绘图。在常规情况下,如下所示:
cerebro.run()
cerebro.plot()
  • plot 需要一些参数来进行自定义
    • numfigs=1如果地块太密集,它可能会被分成几个地块
    • plotter=None可以传递客户绘图仪实例,并且 cerebro 不会实例化默认的绘图仪实例
    • **kwargs– 标准关键字参数这将传递给绘图仪。

    请参阅绘图部分以获取更多信息。

  • 优化策略。如上所述,Cerebro 获得一个 Strategy 派生类(不是实例)和将在实例化时传递给它的关键字参数,这将在调用“run”时发生。

    这是为了启用优化。同一个 Strategy 类将根据需要使用新参数多次实例化。如果一个实例被传递给了 cerebro……这是不可能的。

    要求优化如下:

cerebro.optstrategy(MyStrategy, period=xrange(10, 20))

该方法optstrategy具有相同的签名,addstrategy但会进行额外的内务管理以确保优化按预期运行。一个策略可能期望一个范围作为策略的正常参数,并且 addstrategy不会对传递的参数做任何假设。

另一方面,optstrategy将理解可迭代是一组值,必须按顺序传递给 Strategy 类的每个实例化。

请注意,传递的不是单个值,而是一系列值。在这个简单的例子中,这个策略将尝试 10 个值 10 -> 19(20 是上限)。

如果使用额外参数开发更复杂的策略,它们都可以传递给optstrategy。不需要优化的参数可以直接传递,最终用户不必创建一个只有一个值的虚拟迭代。例子:

cerebro.optstrategy(MyStrategy, period=xrange(10, 20), factor=3.5)

optstrategy方法看到因子并在后台为具有单个元素的因子创建(需要的)虚拟迭代