原创文章第71篇,专注“个人成长与财富自由、世界运作的逻辑, AI量化投资”。
李笑来竟然写了一本关于微信视频号的书。
里面谈及了长期价值的理念,确实,很多事情难在“长期”。需要长时间才能显现出来,越往后复利越高,而在初期甚至可能感觉不到进展。这是大多数人终生䘵䘵无为根本原因。
今天继续说backtrader,按传统的量化投资,需要用指标来建立量化规则,所以指标计算很重要。
01 backtrader的指标
指标是基于基础数据来进行“衍生”计算。基础数据包含是宏观数据,基本面财务数据,基于交易时间序列的OHLCV数据以及另类数据,比如港口的船只数量,商超的人数等等。
这些指标可以是“已经计算”好的,比如昨天说的PE/PB,那么我们直接通过PandasData引入即可。
而像布林带,唐奇安通道这种通过Indicators来“临时计算”。
指标在__init__计算,在next里使用。
import backtrader as bt import backtrader.indicators as ind # 导入策略分析模块 class MyStrategy(bt.Strategy): # 先在 __init__ 中提前算好指标 def __init__(self): sma1 = ind.SimpleMovingAverage(self.data) ema1 = ind.ExponentialMovingAverage() close_over_sma = self.data.close > sma1 close_over_ema = self.data.close > ema1 sma_ema_diff = sma1 - ema1 # 生成交易信号 self.buy_sig = bt.And(close_over_sma, close_over_ema, sma_ema_diff > 0) # 在 next 中直接调用计算好的指标 def next(self): if self.buy_sig: self.buy()
Sma1, ema1, sma_ema_diff,buy_sig都是line对象,(类似pandas里的series),另外指标全称一般比较长,可以简化写为alias:
ind.SimpleMovingAverage=ind.SMA
计算指标或编写策略逻辑时,离不开算术运算、关系运算、逻辑运算、条件运算等等,Bt封装的运算符,是针对line对象进行,返回的结果也是line。bt.And、bt.Or、bt.If、bt.All、bt.Any、bt.Max、bt.Min、bt.Sum 计算返回的结果与在next()中对当前时点通过常规 python 运算语法返回的结果是一致的。
class MyStrategy2(bt.Strategy): def __init__(self): self.sma5 = ind.SimpleMovingAverage(period=5) # 5日均线 self.sma10 = ind.SimpleMovingAverage(period=10) # 10日均线 # bt.And 中所有条件都满足时返回 1;有一个条件不满足就返回 0 self.And = bt.And(self.data > self.sma5, self.data > self.sma10, self.sma5 > self.sma10) # bt.Or 中有一个条件满足时就返回 1;所有条件都不满足时返回 0 self.Or = bt.Or(self.data > self.sma5, self.data > self.sma10, self.sma5 > self.sma10) # bt.If(a, b, c) 如果满足条件 a,就返回 b,否则返回 c self.If = bt.If(self.data > self.sma5, 1000, 5000) # bt.All,同 bt.And self.All = bt.All(self.data > self.sma5, self.data > self.sma10, self.sma5 > self.sma10) # bt.Any,同 bt.Or self.Any = bt.Any(self.data > self.sma5, self.data > self.sma10, self.sma5 > self.sma10) # bt.Max,返回同一时刻所有指标中的最大值 self.Max = bt.Max(self.data, self.sma10, self.sma5) # bt.Min,返回同一时刻所有指标中的最小值 self.Min = bt.Min(self.data, self.sma10, self.sma5) # bt.Sum,对同一时刻所有指标进行求和 self.Sum = bt.Sum(self.data, self.sma10, self.sma5) # bt.Cmp(a,b), 如果 a>b ,返回 1;否则返回 -1 self.Cmp = bt.Cmp(self.data, self.sma5)
关于不同周期的指标
都是“日频”,但有些数据,如财务指标是“季度”频度。比如seelf.data1是月度数据,那么self.month也是月度数据。这时可以有两种做法:使用self.month[0](),或者对齐整个指标对象:self.month_ = self.month(),然后使用就一样的。
推荐第二种。
在bt框架中使用talib,整合得很好,bt里叫period,而talib里通常叫timeperiod。
class TALibStrategy(bt.Strategy): def __init__(self): # 计算 5 日均线 bt.talib.SMA(self.data.close, timeperiod=5) bt.indicators.SMA(self.data, period=5) # 计算布林带 bt.talib.BBANDS(self.data, timeperiod=25) bt.indicators.BollingerBands(self.data, period=25)
02 自定义自己的指标
内置140多个指标及运算符,外加talib的扩展,仍然不够,比如RSRS指标。
继承自bt.Indicator,lines里定义我们这个指标有几个line, params是参数,在init里做一次性的“向量”运算,在next里按元素计算。
import backtrader as bt import numpy as np import statsmodels.api as sm class RSRS(bt.Indicator): lines = ('rsrs', 'R2') params = (('N', 18), ('value', 5)) def __init__(self): self.high = self.data.high self.low = self.data.low def next(self): high_N = self.high.get(ago=0, size=self.p.N) low_N = self.low.get(ago=0, size=self.p.N) try: X = sm.add_constant(np.array(low_N)) model = sm.OLS(np.array(high_N), X) results = model.fit() self.lines.rsrs[0] = results.params[1] self.lines.R2[0] = results.rsquared except:
self.lines.rsrs[0] = 0
class RSRS_Norm(bt.Indicator):
lines = ('rsrs_norm','rsrs_r2','beta_right')
params = (('N', 18), ('M', 600))
def __init__(self):
self.rsrs = RSRS(self.data)
self.lines.rsrs_norm = (self.rsrs - bt.ind.Average(self.rsrs, period=self.p.M))/bt.ind.StandardDeviation(self.rsrs, period= self.p.M)
self.lines.rsrs_r2 = self.lines.rsrs_norm * self.rsrs.R2
self.lines.beta_right = self.rsrs * self.lines.rsrs_r2
02 小结
Backtrader的指标模块 Indicator 侧重的是技术分析,提供了各式各样的技术指标计算函数,考虑到大家技术指标的计算习惯,还内接了 TA-Lib 指标库,bt的line还可以自动匹配时间,对齐周期,这个着实非常方便。
指标都是标准的bt的lines对象。
尽管这里我们说的是“技术指标”,但针对机器学习的因子计算,这里的公式依然有效。
明天可以正式进行“交易”环节了。