最后但并非最不重要的一点是,在这个关于 Python 的隐藏功能如何在 backtrader中使用的系列中,一些神奇的变量是如何出现的。

self.datas和其他人来自哪里?

通常的可疑类(或其子类)、、、、 Strategy具有自动定义的属性,例如包含数据馈送的数组。IndicatorAnalyzerObserver

数据馈送被添加到这样的cerebro实例中:

from datetime import datetime
import backtrader as bt

cerebro = bt.Cerebro()
data = bt.YahooFinanceData(dataname=my_ticker, fromdate=datetime(2016, 1, 1))
cerebro.adddata(data)

...

当收盘价高于简单移动平均线时,我们的示例获胜策略将做多。 我们将使用 Signals 使示例更短:

class MyStrategy(bt.SignalStrategy):
    params = (('period', 30),)

    def __init__(self):
        mysig = self.data.close > bt.indicators.SMA(period=self.p.period)
        self.signal_add(bt.signal.SIGNAL_LONG, mysig)

哪个被添加到混合中:

cerebro.addstrategy(MyStrategy)

任何读者都会注意到:

  • __init__不带参数,命名与否
  • 没有super调用,因此没有直接要求基类进行初始化
  • mysig引用的定义self.data可能与YahooFinanceData添加到的实例有关cerebro确实如此!

实际上还有其他属性,但在示例中没有看到。例如:

  • self.datas: 一个数组,包含添加到 的所有数据提要cerebro
  • self.dataX:其中X是一个数字,它反映了将数据添加到 cerebro 的顺序(data0将是上面添加的数据)
  • self.data: 指向self.data0. 只是为了方便起见,因为大多数示例和策略仅针对单个数据

更多可以在文档中找到:

这些属性是如何创建的?

在本系列的第 2文章中,看到了类创建机制和实例创建机制被拦截。后者用于执行此操作。

  • cerebro通过接收课程adstrategy
  • 它会在需要时实例化它并将自身添加为属性
  • 策略的newclassmethod 在创建Strategy实例的过程中被拦截,并检查哪些数据源可用cerebro它确实创建了上面提到的数组别名

此机制适用于反向交易者生态系统中的许多其他对象,以简化最终用户必须执行的操作。像这样:

  • 例如,无需不断创建包含名为的参数的函数原型,datas也无需将其分配给 self.datas因为它是在后台自动完成的

这种拦截的另一个例子

让我们定义一个获胜指标并将其添加到获胜策略中。我们将重新包装关闭 SMA的想法:

class MyIndicator(bt.Indicator):
    params = (('period', 30),)
    lines = ('signal',)

    def __init__(self):
        self.lines.signal = self.data - bt.indicators.SMA

从上面的代码显然有一个计算发生在 MyIndicator

class MyStrategy(bt.Strategy):
    params = (('period', 30),)

    def __init__(self):
        self.mysig = MyIndicator(period=self.p.period)

    def next(self):
        if self.mysig:
            pass  # do something like buy ...

但它似乎无处可做。正如本系列的第 1 篇文章中所见该操作生成一个object,该 object 被分配给self.lines.signal并发生以下情况:

  • 这个对象也拦截了它的创建过程
  • 它扫描堆栈以了解正在创建的上下文,在本例中是在MyIndicators
  • 并且在其初始化完成后,将自身加入到内部结构中MyIndicator
  • 稍后当MyIndicator被计算时,它会依次计算被引用的对象内部的操作self.lines.signal

好,但谁计算MyIndicator

遵循完全相同的过程:

  • MyIndicator在创建期间扫描堆栈并找到 MyStrategy
  • 并将自身添加到结构中MyStrategy
  • 就在next调用之前,MyIndicator要求重新计算自身,而后者又告诉self.lines.signal重新计算自身

该过程可以有多个间接层。

对用户来说最好的东西:

  • 无需像register_operation创建某些东西时那样添加调用
  • 无需手动触发计算

结束语

本系列的最后一篇文章展示了如何使用类/实例创建拦截来使最终用户的生活更轻松的另一个示例:

  • 在需要它们的地方添加生态系统中的对象并创建别名
  • 自动注册课程和触发计算

评论被关闭。