BackTrader -Indicators 03
本系列是使用Backtrader在量化领域的学习与实践,着重介绍Backtrader的使用。Backtrader 中几个核心组件:
- Cerebro:BackTrader的基石,所有的操作都是基于Cerebro的。
- Feed:将运行策略所需的基础数据加载到Cerebro中,一般为K线数据。
- Indicator:BackTader自带的指标,并集成了talib中的指标。我们也可以选择继承一个Indicator实现自己的指标。
- Strategy:交易策略。这里是整个过程中最复杂的部分,需要我们计算买入/卖出信号。
- Analyzer:分析器,以图形和风险收益等指标对交易策略的回测结果进行分析评价。
- Order:订单,记录了与当前订单相关的所有数据。
- Trader:交易,记录了与当前交易相关的所有数据。
- Position:持仓,记录了与当前持仓相关的所有数据。
- Broker:可以理解成经纪人,整个策略的初始资金、交易费率、滑点等参数需要通过Broker进行设置。
- Observer:观察者,对数据进行监控观察,比如资金曲线等等。
- Plotting:可视化组件
本次介绍Backtrader中Indicators模块,Indicators模块是Backtrader核心模块之一,其提供众多技术指标,并支持talib指标的调用。
在使用Backtrader时,结合量化策略编写过程通常会考虑:
- 在Backtrader都有哪些指标?
- 怎么使用内置指标?是否可以自定义指标?
- 对于不同周期情况下如何处理?
…
Backtrader 大致有 2 种获取指标的方式:
- 1、直接通过 DataFeeds 模块导入 已经计算好 的指标,比如 导入新增指标 PE、PB;
- 2、在编写策略时调用 Indicators 指标模块 临时计算 指标,比如 5 日均线、布林带等 。
import backtrader as bt
import pandas as pd
import numpy as np
import datetime
daily_price = pd.read_csv("./data/daily_price.csv", parse_dates=['datetime'])
# 筛选 600466.SH 和 603228.SH 2只股票的数据集
data1 = daily_price.query(f"sec_code=='600466.SH'").set_index('datetime').drop(columns=['sec_code'])
data2 = daily_price.query(f"sec_code=='603228.SH'").set_index('datetime').drop(columns=['sec_code'])
Backtrader都有哪些指标
Indicators 指标模块提供了 140 多个技术分析指标计算函数,大部分指标与 TA-Lib 库里的指标是一致的。
参考官方网站对每个技术指标说明,包含:
-
Alias 函数别名,一般技术技术指标含有全英文名称和简称,在bt中可以使用的访问该指标名称的方式。例如:ExponentialMovingAverage,它的别名有:EMA, MovingAverageExponential,那么它的访问方式有:
- btind.EMA();
- btind.MovingAverageExponential();
- btind.ExponentialMovingAverage();
-
Formula:技术指标算法说明,以EMA为例:
- movav = prev * (1.0 - smoothfactor) + newdata * smoothfactor
-
Lines:函数返回的指标对象中包含哪些 lines,例如 ExponentialMovingAverage 返回ema。通过xxx.lines.ema 或 xxx.ema
-
Params:技术指标可以设置的参数。例如:ExponentialMovingAverage 中 period (30) 默认值为30天均值。
-
PlotInfo:绘制指标时
- plot = True,是否显示这个指标值,True的显示,False不显示 ;
- subplot = True,是否把指标显示到另一个窗口,True显示到另一个窗口,False显示在主图;
- plotname = “”, 显示 line 的名称,默认是 class.name;
- plotabove = False, 指标绘制的位置,False 指标画在主图下方,True 指标画在主图上方;
- plotlinelabels = False,False 显示的是指标函数的名称,True 显示指标线的名称;
- plotymargin=0.0,画图的时候距离顶部和底部的距离;
- plotyticks=[ ], y 轴刻度范围,取值为空列表时会自动计算;
- plothlines=[ ],用于绘制水平线;
- plotyhlines=[ ],用同一个参数,控制 plotyticks 和 plothlines 的取值;
- plotforce=False,如果该绘制的指标没有被绘制,就将 plotforce 设置为 True 进行 强制绘图。,支持设置的图形参数。
-
PlotLines:绘制的曲线样式
如何在策略中使用指标
指标使用原则
在编制策略时,遵循“init() 负责指标计算,next() 负责指标调用”原则,即在__init__()中计算好指标,在next()中调用已经算好的指标。如此可以减少计算消耗,提升策略效率。
# 遵循“__init__() 负责指标计算,next() 负责指标调用”原则
import backtrader.indicators as btind
class TestStrategy(bt.Strategy):
def __init__(self):
sma1 = btind.SmoothedMovingAverage(self.data)
ema1 = btind.ExponentialMovingAverage()
close_over_sma = self.data.close > sma1
close_over_ema = self.data.close > ema1
sma_ema_diff = sma1 - ema1
# 交易信号
self.buy_signal= bt.And(close_over_sma,close_over_ema,sma_ema_diff>0)
def next(self):
if self.buy_signal:
print('Buy 买入')
cerebro= bt.Cerebro()
st_date = datetime.datetime(2019,1,2)
ed_date = datetime.datetime(2021,1,28)
datafeed1 = bt.feeds.PandasData(dataname=data1,fromdate=st_date,todate=ed_date)
cerebro.adddata(datafeed1,name='600466.SH')
cerebro.addstrategy(TestStrategy)
result = cerebro.run()
指标调用形式
-
调用 Indicators 模块的函数计算指标时,默认是对 self.datas 数据对象中的第一张表格中的第一条line (默认第一条line是 close line)计算相关指标。
-
调用指标时会涉及 line 的索引和切片操作,在 next() 中调用当前时刻指标值时,可以写成:self.sma5[0] 或 self.sma5 或 self.data.close[0] 或 self.data.close。
class TestStrategy(bt.Strategy):
def __init__(self):
self.sma5 = btind.SimpleMovingAverage(period=5) # 5日均线
self.sma10 = btind.SimpleMovingAverage(period=10) # 10日均线
self.buy_sig = self.sma5 > self.sma10 # 5日均线上穿10日均线
def next(self):
# 提取当前时间点
print('datetime', self.datas[0].datetime.date(0))
# 打印当前值
print('close', self.data.close[0], self.data.close)
print('sma5', self.sma5[0], self.sma5)
print('sma10', self.sma10[0], self.sma10)
print('buy_sig', self.buy_sig[0], self.buy_sig)
# 比较收盘价与均线的大小
if self.data.close > self.sma5:
print('------收盘价上穿5日均线------')
if self.data.close[0] > self.sma10:
print('------收盘价上穿10日均线------')
if self.buy_sig:
print('------ buy ------')
cerebro = bt.Cerebro()
st_date = datetime.datetime(2019,1,2)
end_date = datetime.datetime(2021,1,28)
datafeed1 = bt.feeds.PandasData(dataname=data1, fromdate=st_date, todate=end_date)
cerebro.adddata(datafeed1, name='600466.SH')
cerebro.addstrategy(TestStrategy)
rasult = cerebro.run()
自定义新指标
在 Backtrader 中,可以根据自身需要编写新指标,新指标的编写需要满足以下条件:
- 继承原始的 bt.Indicator 类: 如果涉及到自定义操作,一般都是通过继承原始的父类,然后在新的子类里自定义属性,自定义新指标,需要继承原始的 bt.Indicator 类,然后在新的子类里构建指标。
- lines = (‘xxx’, ‘xxx’, ‘xxx’,):定义指标函数返回的 lines 名称,方便后面通过名称调用具体的指标,如 self.lines.xxx、self.l.xxx、self.xxx
- params = ((‘xxx’, n),):定义参数,方便在子类里全局调用,也方便在使用指标函数时修改参数取值;
- init() 方法:同策略 Strategy 里的 init() 类似,对整条 line 进行运算,运算结果也以整条 line 的形式返回;
- next() 方法:同策略 Strategy 里的 next() 类似,每个 bar 都会运行一次,在 next() 中是对数据点进行运算;
- once() 方法:这个方法只运行一次,但是需要从头到尾循环计算指标;
class DummyInd(bt.Indicator):
lines = ('dummyline',)
params = (('value', 5),)
def next(self):
self.lines.dummyline[0] = max(0.0, self.params.value)
def once(self, start, end):
dummy_array = self.lines.dummyline.array
for i in xrange(start, end):
dummy_array[i] = max(0.0, self.params.value)
不同周期中指标的使用
在不同周期的指标情况下,可以使用“ ( ) ”语法操作来对齐不同周期的数据,对齐的方向是“大周期向小周期对齐”,可以选择指标对象中的某条 line 进行对齐,也可以对整个指标对象进行对齐。在使用该语法时,要将 cerebro.run() 中的 runonce 设置为 False,才能实现对齐操作。
# self.data0 是日度行情、self.data1 是月度行情
self.month = btind.xxx(self.data1) # 计算返回的 self.month 指标也是月度的
# 选择指标对象中的第一条 line 进行对齐
self.sellsignal = self.data0.close < self.month.lines[0]()
# 对齐整个指标对象
self.month_ = self.month()
self.signal = self.data0.close < self.month_.lines[0]
cerebro.run(runonce=False)
调用 TA-Lib 库
在Backtrader中使用talib与其内置指标的方式类似。
在此,以20日的SMA指标为例:
import backtrader as bt
class MyStrategy(bt.Strategy):
params = (('period', 20),)
def __init__(self):
# 计算 20 日均线
self.sma1 = bt.indicators.SMA(self.data, period=self.p.period)
self.sma2 = bt.talib.SMA(self.data, timeperiod=self.p.period)
ta-lib 指标文档会自动解析并添加到 backtrader 文档中。您也可以查看 ta-lib 源代码/文档。
print(bt.talib.SMA.__doc__)
print(bt.talib.SMA.__doc__)
SMA([input_arrays], [timeperiod=30])
Simple Moving Average (Overlap Studies)
Inputs:
price: (any ndarray)
Parameters:
timeperiod: 30
Outputs:
real