只有当遇到 backtrader 的真实用户时,人们才能意识到平台中使用的抽象和Python功能是否有意义。
在不撇开python的座右铭的情况下, backtrader 试图为用户提供尽可能多的控制权,同时通过将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()
其中一个非常迅速弹出的问题是:
- 不能也使用期间
[]
__init__
?。
提出问题是因为用户已经尝试过,Python已停止运行,但出现异常。
答案是:
- 不。在
[]
初始化期间不使用。
接下来的问题是:
- 那么,如果它不是数组,那么在期间
__init__
实际存储self.hi_lo_avg
了什么呢?
对于程序师来说,答案并不令人费解,但对于使用Python的算法交易者来说,答案可能很令人困惑。
- 它是一个懒惰评估的对象,它将在阶段(
cerebro.run
即:在策略的方法中next
)通过[]
操作数计算和传递值。
归纳总结:在该方法中next
,数组索引操作符 []
将允许您访问过去和当前时间时刻的计算值。
秘诀就在酱汁中
操作员覆盖是真正的调味料。让我们分解一下 high-low- 平均值的计算:
self.hi_lo_avg = (self.data.high + self.data.low) / 2.0
组件:
self.data.high
并且self.data.low
本身就是对象(backtrader命名方案中的lines)
在许多情况下,它们被错误地视为纯数字,但事实并非如此。它们成为对象的原因:
-
0
backtrader实施索引-1
计划和索引计划 -
控制缓冲区大小调整和链接到其他对象
在这种情况下,最重要的方面是:
- 重写操作符以返回对象
这就是为什么下面的操作返回一个 lines 对象。让我们开始:
temp = self.data.high - self.data.low
然后,将临时对象除以2.0
成员变量并将其分配给成员变量:
self.hi_lo_avg = temp / 2.0
这将再次返回另一个 lines 对象。因为操作符重写不仅适用于直接在 lines 对象之间运行的操作,还适用于例如,像这种除法这样的算术运算。
这意味着 具有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()
我们做了两件事:
-
创建一个名为 lines 对象,该对象
self.signal
将 high-low- 平均值与简单移动平均值的值进行比较如上所述,当计算出此对象时,此对象在
next
-
在检查 if
signal
True
时删除 innext
的使用[0]
。这是可能的,因为布尔运算也覆盖了运算符
结论
希望这能为运行__init__
操作时实际发生的情况以及操作数重写的实际发生方式增加一些线索。