只有在遇到backtrader的真实用户时,才能意识到平台中使用的抽象和 Python 功能是否有意义。

在不抛开 Pythonic 座右铭的情况下反向交易者试图为用户提供尽可能多的控制权,同时通过将Python 提供的隐藏功能付诸行动来简化使用。

本系列的第一篇文章中的第一个示例。

它是一个数组还是它是什么?

一个非常简单的例子:

import backtrader as bt

class MyStrategy(bt.Strategy):

    def __init__(self):

        self.hi_lo_avg = (self.data.high + self.data.low) / 2.0

    def next(self):
        if self.hi_lo_avg[0] > another_value:
            print('we have a winner!')

...
...
cerebro.addstrategy(MyStrategy)
cerebro.run()

很快弹出的问题之一是:

  • 不能也使用[]during__init__吗?

之所以提出这个问题,是因为用户已经尝试过并且 Python 已停止运行并出现异常。

答案:

  • []不,在初始化期间不意味着使用。

接下来的问题是:

  • 那么如果不是数组,实际存储self.hi_lo_avg的是什么?__init__

对于程序员来说,答案并不令人费解,但对于选择 Python 的算法交易员来说,这可能是一个谜

  • 它是一个惰性求值的对象,它会[]在阶段通过算子计算并传递值cerebro.run,即:在 next策略的方法中。

底线:在该next方法中,数组索引运算符[]将使您能够访问过去和当前时刻的计算值。

秘诀就在这里

运算符重载是真正的调味汁。让我们分解high-low-average的计算:

self.hi_lo_avg = (self.data.high + self.data.low) / 2.0

组件:

  • self.data.high并且self.data.low它们本身就是对象反向交易者命名方案 中的行)

在许多情况下,它们被错误地视为纯数组,但事实并非如此。它们成为对象的原因:

  • 在反向交易者中实施0-1索引计划
  • 控制缓冲区大小和链接到其他对象

在这种情况下最重要的方面:

  • 重写运算符以返回对象

这就是为什么下面的操作会返回一个lines对象。开始吧:

temp = self.data.high - self.data.low

然后将临时对象除以2.0并分配给成员变量:

self.hi_lo_avg = temp / 2.0

这又返回另一个对象。因为运算符覆盖不仅适用于直接在行对象之间执行的操作还适用于例如这种除法之类的算术运算。

这意味着它self.hi_lo_avg引用了lines对象。next该对象在策略方法中或作为 指标或其他计算的输入很有用。

逻辑运算符示例

__init__上面的示例在和 的组合期间和之后使用了算术运算符[0]>next.

因为运算符重载不限于算术,让我们再举一个例子,在组合中添加一个指标。第一次尝试是:

import backtrader as bt

class MyStrategy(bt.Strategy):

    def __init__(self):
        self.hi_lo_avg = (self.data.high + self.data.low) / 2.0
        self.sma = bt.indicators.SMA(period=30)

    def next(self):
        if self.hi_lo_avg[0] > self.sma[0]:
            print('we have a winner!')

...
...
cerebro.addstrategy(MyStrategy)
cerebro.run()

但在这种情况下,只是从another_value变为 self.sma[0]。让我们改进它:

import backtrader as bt

class MyStrategy(bt.Strategy):

    def __init__(self):
        self.hi_lo_avg = (self.data.high + self.data.low) / 2.0
        self.sma = bt.indicators.SMA(period=30)

    def next(self):
        if self.hi_lo_avg > self.sma:
            print('we have a winner!')

...
...
cerebro.addstrategy(MyStrategy)
cerebro.run()

一个给好人的。运算符覆盖也可以使用next,用户实际上可以删除[0]并直接比较对象。

如果这一切都是可能的,那实际上似乎有点矫枉过正。但好在还有更多。看这个例子:

import backtrader as bt

class MyStrategy(bt.Strategy):

    def __init__(self):
        hi_lo_avg = (self.data.high + self.data.low) / 2.0
        sma = bt.indicators.SMA(period=30)
        self.signal = hi_lo_avg > sma

    def next(self):
        if self.signal:
            print('we have a winner!')

...
...
cerebro.addstrategy(MyStrategy)
cerebro.run()

我们做了两件事:

  1. 创建一个名为lines的对象self.signal,将 高低平均值简单移动平均线的值进行比较

    如上所述,该对象在next计算时很有用

  2. 检查信号是否为 True 时,删除 next 中 [0] 的使用。 这是可能的,因为运算符也已被布尔运算覆盖

结论

希望这可以为执行操作时实际发生的__init__情况以及操作员覆盖实际发生的情况提供一些启示。

评论被关闭。