量化交易学习(十七)backtrader基本概念6

今天这篇是backtrader文档的学习笔记。主要介绍了backtrader中的运算符重载。

官方文档链接:https://www.backtrader.com/docu/concepts

例子中的代码都需要导入以下库:

1
2
3
4
5
6
# 导入backtrader
import backtrader as bt
# 导入指标库
import backtrader.indicators as btind
# 导入数据源库
import backtrader.feeds as btfeeds

自然地使用运算符

为了实现“易用性”的目标,backtrader支持在python的语法限制范围内使用运算符。运算符可以在两个阶段中使用。

阶段1——使用运算符创建对象

在指标和策略等对象的初始化阶段(__init__方法),运算符能够创建可以被操作、被赋值或保留作为参考的以供以后在策略逻辑的评估阶段使用的对象。

下面以一个简单移动平均线的代码实现,来说明怎么在指标构建时使用运算符:
指标的 __init__ 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def __init__(self):

# 求 N 周期的值之和 - datasum 是一个 Lines 对象,
# 通过运算符 [] 和索引 0 进行查询时,会返回当前的和。
datasum = btind.SumN(self.data, period=self.params.period)

# datasum(是有一条线的Lines对象)
# 在这里可以很自然地与整数或浮点数作除法。
# 事实上它也可以与另一个Lines对象作除法。
# 该运算返回一个被分配给“av”的对象,
# 当用[0]查询时,该对象会再次返回当前时间点的当前平均值。
av = datasum / self.params.period

# av 线对象(类型为 Lines)可以直接赋值给此指标的命名线sma。
# 使用此指标的其他对象将可以直接访问计算结果。
self.line.sma = av

下面是一个更完整的策略初始化时的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyStrategy(bt.Strategy):

def __init__(self):

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

close_over_sma = self.data.close > sma
sma_dist_to_high = self.data.high - sma

sma_dist_small = sma_dist_to_high < 3.5

# “and” 在 Python 中不能被重载,
# 因为它是一个语言结构而不是一个运算符,
# 因此backtrader通过一个函数来模拟它。
sell_sig = bt.And(close_over_sma, sma_dist_small)

在上述运算完成后,sell_sig 会变成一个 Lines 对象,随后可以在策略的逻辑中使用,以作为是否满足卖出条件的信号线。

阶段2 - Operators true to nature

在策略类中有一个next方法,系统在处理每个k线柱时都会调用该方法。这就是运算符处于第二阶段模式的地方。基于前面的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class MyStrategy(bt.Strategy):

def __init__(self):

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

close_over_sma = self.data.close > sma
self.sma_dist_to_high = self.data.high - sma

sma_dist_small = sma_dist_to_high < 3.5

# “and” 在 Python 中不能被重载,
# 因为它是一个语言结构而不是一个运算符,
# 因此backtrader通过一个函数来模拟它。

self.sell_sig = bt.And(close_over_sma, sma_dist_small)

def next(self):

# 虽然乍一看不像一个“操作符”,
# 但实际上它可以算作一个操作符,
# 因为它将对象进行比较得出True和False值。

if self.sma > 30.0:
print('sma is greater than 30.0')

if self.sma > self.data.close:
print('sma is above the close price')

if self.sell_sig: # if sell_sig == True: would also be valid
print('sell sig is True')
else:
print('sell sig is False')

if self.sma_dist_to_high > 5.0:
print('distance from sma to hig is greater than 5.0')

这不是一个非常实用的策略,仅作为一个例子。在第二阶段,操作符会返回预期的值(比较真假时返回布尔值,与浮点数比较时返回浮点数),算术运算也一样。

注意:

在比较时没有使用 [] 操作符。这是为了进一步简化操作。

if self.sma > 30.0: 将self.sma[0]与30.0进行比较(第一条线的当前值)。

if self.sma > self.data.close:将self.sma[0]与self.data.close[0]进行比较。

一些不可重载的运算符/函数

Python不支持重载所有的东西,因此backtrader提供了一些函数来处理无法重载的情况

注意

仅适用于第一阶段,用于创建稍后提供值的对象。

运算符:

  • and -> And
  • or -> Or

逻辑控制:

  • if -> If

函数:

  • any -> Any
  • all -> All
  • cmp -> Cmp
  • max -> Max
  • min -> Min
  • sum -> Sum
  • Sum 实际上在底层使用 math.fsum ,因为平台使用浮点数,使用常规的sum 可能会对精度产生影响。
  • reduce -> Reduce

这些运算符/函数可以对可迭代对象进行操作。可迭代对象中的元素可以是常规的 Python 数字类型(整数、浮点数……),也可以是Lines 对象。

一个生成非常愚蠢的买入信号的示例:

1
2
3
4
5
6
7
8
9
10
class MyStrategy(bt.Strategy):

def __init__(self):

sma1 = btind.SMA(self.data.close, period=15)
self.buysig = bt.And(sma1 > self.data.close, sma1 > self.data.high)

def next(self):
if self.buysig[0]:
pass # do something here

很明显,如果sma1比最高价高,那么它一定比收盘价高。这里仅仅用来解释bt.And的使用

下面是一个使用bt.If的例子:

1
2
3
4
5
6
7
class MyStrategy(bt.Strategy):

def __init__(self):

sma1 = btind.SMA(self.data.close, period=15)
high_or_low = bt.If(sma1 > self.data.close, self.data.low, self.data.high)
sma2 = btind.SMA(high_or_low, period=15)

详细解释下上面的代码:

  • 首先生成一条周期为15的均线sma1
  • 之后使用bt.If判断如果sma1的值比close高则返回low否则返回high
  • 注意,在调用bt.If时不会返回实际值,它返回一个线对象,就像SimpleMovingAverage那样
  • 在之后策略运行时才计算实际值
  • 最后,通过bt.If生成的线对象被作为btind.SMA的输入,计算出另一条均线sma2

除线对象外,这些函数也能用于数值。对上面的例子稍加修改:

1
2
3
4
5
6
7
class MyStrategy(bt.Strategy):

def __init__(self):

sma1 = btind.SMA(self.data.close, period=15)
high_or_30 = bt.If(sma1 > self.data.close, 30.0, self.data.high)
sma2 = btind.SMA(high_or_30, period=15)

现在,第二条移动平均线使用30.0或high的价格来执行计算,具体取决于sma与close比较结果。

注意

值30在内部会被转换成一条永远返回30的假的可迭代对象。

江达小记