Python 的隐藏力量(1)
原文: https://www.backtrader.com/blog/posts/2016-11-20-python-hidden-powers/hidden-powers/
只有在遇到backtrader的真正用户时,我们才能意识到平台中使用的抽象和 Python 功能是否有意义。
在不放弃pythonic座右铭的情况下,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 的 algo 交易员来说,答案可能是令人费解的
- 它是一个惰性评估对象,在cerebro.run阶段,即在策略的next方法中,通过[]操作符计算并传递值。
底线:在next方法中,数组索引操作符[]将允许您访问过去和当前时间矩的计算值。
秘密就在酱汁里
而操作符覆盖才是真正的酱汁。我们来分解一下高低平均值的计算:
self.hi_lo_avg = (self.data.high + self.data.low) / 2.0 
组成部分:
- self.data.high和- self.data.low本身就是对象(反向交易者命名方案中的行)
在许多情况下,它们被误认为是纯数组,但事实并非如此。它们成为对象的原因:
- 
在反向交易者中实施 0和-1索引方案
- 
控制缓冲区大小和链接到其他对象 
在这种情况下,最重要的方面是:
- 重写运算符以返回对象
这就是为什么下面的操作返回一个行对象。让我们开始:
temp = self.data.high - self.data.low 
然后将临时对象除以2.0并分配给成员变量:
self.hi_lo_avg = temp / 2.0 
这再次返回另一个行对象。因为运算符重写不仅适用于直接在行对象之间执行的操作,还适用于,例如,类似于此除法的算术操作。
这意味着self.hi_lo_avg引用了行对象。该对象在策略的next方法中有用,或作为指标或其他计算的输入。
一个逻辑运算符示例
上述示例在__init__期间使用算术运算符,之后在next中使用[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[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() 
我们做了两件事:
- 
创建一个名为 self.signal的行对象,将高低平均值与简单移动平均值进行比较如上所述,该对象在 next中是有用的,当它被计算时
- 
检查 signal是否为True时,删除next中[0]的用法。这是可能的,因为布尔运算也重写了运算符
结论
希望这能为在__init__中执行操作时实际发生的情况以及运算符重写的实际发生方式提供一些线索。

