量化交易backtrader实践(三)_指标与策略篇(3)_内置指标B-E

官方文档Indicators - Reference - Backtrader列出了276个指标,而源码里是按组进行分类的,我们根据指标列表进行比较详细的实践。上一节开始学习和实践backtrader的内置指标A开头的系列,细致实践了加速度振荡器、考夫曼自适应均线以及阿隆指标,这一节继续看B开头到E开头的内置指标。

内置指标B-E

根据官方文档,B-E一共有29个指标,其中有一些是其他指标组中的,比如'DownDay' 和 'DownDayBoll' 就是属于RSI的组;还有一些指标组里的指标在A开头系列已经出现过了例如 'AverageDirectionalMovementIndex' 就是DMI系列中的ADI等。


  'BollingerBands',                     # 布林线
  'BollingerBandsPct',

  'BaseApplyN',  'CointN',  'DownDay', 'DownDayBool', 'DetrendedPriceOscillator',

  'CommodityChannelIndex',              # CCI

  'CrossDown',                          # crossover系列
  'CrossOver',
  'CrossUp',

  'DV2',

  'DemarkPivotPoint',       # 属于PivotPoint
  'FibonacciPivotPoint',    # 属于PivotPoint

  'DicksonMovingAverage',                 # DMA 系列
  'DicksonMovingAverageEnvelope',
  'DicksonMovingAverageOscillator',

  'DirectionalIndicator',                 # DMI系列
  'DirectionalMovement',
  'DirectionalMovementIndex',
+ 'AverageDirectionalMovementIndex',      # ADI(属于DMI)
+ 'AverageDirectionalMovementIndexRating',# ADIR(属于DMI)
  'DownMove'

  'DoubleExponentialMovingAverage',         # DEMA 系列
  'DoubleExponentialMovingAverageEnvelope',
  'DoubleExponentialMovingAverageOscillator',

  'ExponentialMovingAverage',               # EMA 系列
  'ExponentialMovingAverageEnvelope',
  'ExponentialMovingAverageOscillator',
  'ExponentialSmoothing',
  'ExponentialSmoothingDynamic',
 
  'Envelope',
  

经过简单分组,B-E的内置指标主要是以下几个,其中有几个移动平均线的变异如DMA,DEMA,EMA

  1. 布林带指标
  2. CCI指标
  3. CROSSOVER系列
  4. DV2
  5. DMI系列
  6. DMA
  7. DEMA
  8. EMA
  9. Envelope带
  10. 其他

004_布林带BollingerBands

A_简介与公式

布林带指标是非常经典的指标,它有着广泛的群众基础,在某些股票软件中把它分在了路径类指标中,我们来看一下AI对布林带指标公式的简介:

布林带指标公式

布林带(Bollinger Bands,简称BOLL)是一种常用的技术分析工具,由三条线组成:

中间的移动平均线(Middle Band)和围绕它的上轨(Upper Band)和下轨(Lower Band)。布林带的计算基于简单移动平均(Simple Moving Average,SMA)和标准差(Standard Deviation,SD)。

布林带计算公式

  1. 中间带(Middle Band):通常采用N日简单移动平均,计算公式为 MB = SUM(CLOSE, N) / N,其中CLOSE代表收盘价,N为移动平均的周期数(常见的周期数为20日)。

  2. 上轨(Upper Band):计算公式为 UP = MB + k * SD,其中k为标准差的倍数(通常为2),SD为价格相对于移动平均的标准差。

  3. 下轨(Lower Band):计算公式为 DOWN = MB - k * SD

布林带的宽度会随着价格波动性的变化而变化,当市场波动性增大时,布林带会扩大;当市场波动性减小时,布林带会缩小。投资者通常使用布林带来识别市场的超买或超卖状况,以及趋势的强度和可能的反转信号. 

在第1节中,我们就学习了平均值与正态分布的概念,当样本数量足够多的时候,就可以用正态分布来计算,在正态分布中,

  • 大约68%的数据位于均值(μ)的一个标准差(σ)范围内,
  • 大约95%的数据位于两个标准差范围内,
  • 大约99.7%的数据位于三个标准差范围内。

而布林带指标用移动平均线做中轨,用2倍的Sigma(标准差)得到上轨和下轨,也就意味着一般股票价格会有95%都在上轨和下轨之间运行,一旦触碰下轨后就大概率拐头,于是我们可以在下轨与上轨之间做价差进行买卖赚取收益,并且按照帕累托原则(Pareto Principle),一旦穿出95%,很有可能就是极致行情,这个时候布林通道会扩张。布林带指标是很少的将物极必反原则和趋势原则能结合进来的优秀的指标,它很好的使用了概率统计中的平均值与正态分布理论。在股市中也有很多人对它的应用炉火纯青,有必要好好学习和实践一下。

根据源码,布林带指标有2个类,一个BollingerBands就是标准的布林带,另一个BollingerBandsPct从名字上看也知道它是百分比的表现形式。

class BollingerBands(Indicator):    
    def __init__(self):
        self.lines.mid = ma = self.p.movav(self.data, period=self.p.period)
        stddev = self.p.devfactor * StdDev(self.data, ma, period=self.p.period,
                                           movav=self.p.movav)
        self.lines.top = ma + stddev
        self.lines.bot = ma - stddev

class BollingerBandsPct(BollingerBands):
    
    def __init__(self):
        super(BollingerBandsPct, self).__init__()
        self.l.pctb = (self.data - self.l.bot) / (self.l.top - self.l.bot)

B_布林百分比显示

按道理,我们只需要在策略类的init()中加一句就能显示出图线

class St_BOLL_calss2(BaseSt):
     params = (
         ('stra_name','BOLLPct'),
         ('p1',20), ('p2', 2.0),
         ('tradeCnt',1),
         ('sucessCnt',0),)            
               
     def __init__(self): 
         self.order = None
         
         BOLL1 = bt.indicators.BollingerBands(self.data,period=20,devfactor=2.0)
         BOLLpct = bt.indicatorsBollingerBandsPct(self.data,period=self.p.p1, devfactor=self.p.p2)

---------

但实际上无论用BollingerBands还是用BollingerBandsPct,都只能显示出布林的上、中、下三轨,而不能显示出pctb的图线!

原因分析,这是因为Pct继承自BBands这个类,而布林线的绘图属性是在主图绘制,但百分比的线它的范围大概是0~1(布林百分比可以小于0)与价格的范围并不重合,所以即使绘制了也看不到。

解决方法有很多,比如自定义一个类来写BOLLpct,比如继承自BollingerBandsPct然后改变它的plotinfo属性为副图显示,这样就能在副图上显示出布林百分比。

class myBollpct(bt.indicators.BollingerBandsPct):

    lines = ('pctb',)
    plotlines = dict(pctb=dict(_name='%B'))  
    plotinfo=dict(subplot=True)                # 绘图放在副图

    def __init__(self):
        super(myBollpct, self).__init__()
        self.l.pctb = (self.data - self.l.bot) / (self.l.top - self.l.bot)

------
         BOLLpct = myBollpct(self.data)

见上面副图,同时绘制了上中下三轨以及布林百分比的图线。

C_经典布林策略

既然是经典布林策略,就是被市场证明过的,这个在股票软件中大多都会内置,并给了一个好听的感觉高大上的名称——"专家系统",如下图

上穿还是下穿

然后我们可以点开专家系统的公式代码,买入点是收盘价上穿下轨,卖出点是收盘价上穿上轨,这里的设置非常的巧妙,感觉是专为中国市场不能卖空做了相应的调整,其潜在的含义就是下跌由于不能卖空,需要避开连续的下跌,只能等反转了才能买入(收盘上穿下轨)。

MID :=MA(CLOSE,N);
UPPER:=MID+2*STD(CLOSE,N);
LOWER:=MID-2*STD(CLOSE,N);
ENTERLONG:CROSS(CLOSE,LOWER);
EXITLONG:CROSS(CLOSE,UPPER);

但是,上穿上轨这个就值得考量一下,由于在backtrader回测中position的存在,一次买入对应一次卖出,不会出现多次,于是我们放到股票软件中来观察,如下图所示,在不考虑持仓(是否已经买入)的情况下,只观察卖出点,这段一共出现5次卖点,其中2次是第2根K线收盘价就低于卖点的收盘价了,可以认为是成功的(Y)。还有一次标为HY,是第2根K线高于卖点,但第3根又低于卖点(第3根下穿上轨)。另外还有2次标为N,即crossup的收盘价要比crossdown的收盘价低,特别是左起第2个绿箭头的第2天是大涨。

由上面分析可知,如果这支股票股性一般或者处于振荡行情,那么上穿布林上轨卖出后多半是会下跌的,这样的情况下使用上穿上轨就会有比较好的收益;而如果这支股票会连续上涨并使布林轨道扩张开的,上穿上轨卖出就会错失大涨行情,从获取可能比较多的超额收益的观点来讲,可能更倾向于使用下穿上轨再卖的策略。

错过的买卖点

根据上图,卖点虽然出现了5次,但买点只出现了1次,也就是根据策略可以卖出了,但手里根本没有持仓!图中左起1/4以及最右侧均有刺破下轨或踩下轨不破的情况出来,但根据上穿下轨的语句就是不能触发买点。因此需要增加买入条件——回踩下轨不破,即当日最低价破下轨但收盘回到下轨之上,在股票软件的图线中可以看到至少提供了2个位置的买点,能够让两次的卖出是有持仓的(有效的卖出),否则就成了假卖点。

B1:=CROSS(C,LB);
B2:=L<LB AND C>LB;

S1:=CROSS(UB,C);
S2:=H>UB AND C<UB;

ENTERLONG:B1 or B2;
EXITLONG:S1 or S2;

然后看到右起1/3处有2个高点,但没有碰到上轨,这个后续可以调节布林中轨的参数拟合,但就算是拟合,这两个点也不能碰发上穿上轨或下穿上轨,也需要添加冲突上轨收盘回到上轨下方的条件。

我们把经典的上穿,修改的上轨下穿以及回踩不破和冲高回落的当天穿回这三个布林带变异策略进行多股票回测,结果列表如下

从结果上看,增加了当天穿回的条件后,交易次数会略增多,且某些胜率小增,表示有一部分股票容易走出当天穿回的走势,并且11支中有4支回测收益都是当日穿回的正收益最高,特别是国芳的收益从经典布林策略的4416增加到了35032,暴涨8倍多。某些股票负收益但当日穿回策略也是亏损最少的如中国一重,当然也有部分股票的BOLL当天穿回策略没有效果,如神马的BOLL的经典策略胜率也不高于40%,表明某些股票不适用于布林带策略。因此,可以暂时认为把经典的两个上穿的策略改为买入上穿,卖出下穿加当日穿回的改进版是大概率有效的。对于当天穿回,在布林通道扩张的过程中,会增加失败概率,它只适用于布林通道稳定的阶段。

当前我们的买卖策略已经从简单的金叉买/死叉卖进阶到多条件"或"的买卖,这虽然只是一小步,但这一步至关重要。

股票软件中的持仓

股票软件默认的情况下,只要有买卖点就会显示出来,但实际上没有买入没有持仓的情况下是无物可卖的,并且在没有分仓操作的情况下也是不能多次买入的。这个在backtrader中就很清楚,永远是1次买入对应1次卖出。

那么,在股票软件中能否也实现类似持仓的判断呢?也就是买入1次后必须卖出1次。这个使用一些条件判断是可以实现的。其实对于金叉买死叉卖或者进出条件相同的天生就是1次买对应1次卖的。

在股票软件中,写代码的区域类似于init与next的结合,但是使用的变量是局部变量,而不像backtrader里可以用self.xxx做成全局变量进行使用。因此,我们不可能在股票软件的代码公式区中赋初值(init)然后再计算(next),要想得到持续的布尔标志位就需要借助其他的变量进行计算才行。以多次触发buy的信号为例,我们不能直接用一个position来记录,因为在next里赋值就相当于局部函数重置,我们只能考虑用全局的一些变量来曲线控制,比如说周期计数(BARSLAST)与计数器(COUNT)的联合使用。示例代码公式如下,应用的逻辑就是假设已经买入,现在满足卖出条件,于是看一下从买入到当前的周期范围内发生了几次卖出,如果计数大于1则不再卖出:

B1:=CROSS(C,LB);  //上穿下轨为买点b1
S1:=CROSS(UB,C);  //下穿上轨为卖点s1

N1:=BARSLAST(B1);  //记录最近1次买点的周期 
N2:=BARSLAST(S1); //记录最近1次卖点的周期

B3:=COUNT(B1,N2)>1; // 根据最近1次卖出的位置到现在的的买点b1出现的次数是否大于1
                    // True 代表卖掉后已经出现至少2次买点了
S3:=COUNT(S1,N1)>1; // 同上,True代表买入后至少出现2次卖点了

DRAWICON(B1 AND NOT(B3),LB,7);  // 只显示第1个买点
DRAWICON(S1 AND NOT(S3),UB,8);  // 只显示第1个卖点

更改股票软件中的公式代码后,就实现了与backtrader买卖点一致的功能,如下图:

有了这样的控制,那么就能更有效率的在股票软件中对于图形的变化来调整各项参数。

条件判断与分段策略

从上面的图形,可以比较清晰的看到,

  • 当布林带中轨向上运行过程中,会连续触发卖出,但价格不会回到下轨,产生不了买点!通常早早就卖出了,收益效率很低。
  • 当布林带中轨向下运行过程中,会连续触发买入,但价格通常连中轨都穿不上去,自然产生不了卖点,不断的下跌会造成严重的亏损(如7.5跌到5.0,跌幅达到30%以上)

因此,布林带的经典策略在振荡行情中胜率较高,但最大收益不高;在上涨行情中容易错失后端的疯狂上涨;而在下跌行情中不止损就会产生严重的亏损。

据此,对于一个增强型的布林带策略,就当使用分段策略,即上涨时捂住了不要轻易卖出,振荡行情按照经典策略就可以,下跌段不参与。分段策略就能获得比较好的收益和控制住亏损。

不过分段或者分仓策略后面再详细讨论和实践,这一节重点还是内置指标的学习和实践。

D_布林百分比策略

布林百分比是一个会小于0也会大于1的数,在TDX中直接在副图中显示出来如下图:

每支股票的布林百分比的上下限并不相同,某些股票的下限甚至会低于-0.25,而有的股票上限不超过1,所以保守起见,我们设置buy为上穿0.05,sell为下穿0.95,策略类代码相应更改

class St_BOLL_pct_1(BaseSt):
     params = (
         ('stra_name','BOLL百分比'),
         ('p1',18), 
         ('p2',2),
         ('tradeCnt',1),
         ('sucessCnt',0),)            
               
     def __init__(self): 
         self.order = None
         
         BOLL1 = bt.indicators.BollingerBandsPct(self.data)
         self.BOLLpct = BOLL1.l.pctb
         self.crsup = bt.indicators.CrossUp(self.BOLLpct, 0.05)
         self.crsdn = bt.indicators.CrossDown(self.BOLLpct, 0.95)
     
     def next(self):
         if self.order:  # 检查是否有指令等待执行
             return
      
         if not self.position:  # 没有持仓 才会进入 
             if self.crsup:
                 self.order = self.buy()  # 执行买入
         else:
             if self.crsdn:   
                 self.order = self.sell()  # 执行卖出 

 确定代码工作正常后,对11支股票进行回测,并加入BOLL当日穿回的策略做对比:

由数据可知,布林百分比策略其稳健性比布林带改进版的当日穿回策略要好一些,11支股票回测,正收益的5个稍低于布林带策略,但2个亏损的在百分比策略下会变成正收益,原亏损较大的也减少了一点亏损。

布林百分比策略设置比较简单,就不需要考虑那么多的情况,天然的触发情况也会减少一点点,虽然应用较少,但的确是一个不错的指标。

005_CCI指标

A_从布林百分比到CCI

在布林百分比实践过程中,偶然的机会在这个指标的下方显示了CCI指标,如下图

从图上我们惊奇的发现,布林百分比的图线走势与CCI的图线太像了,就像孪生兄弟一样。

CCI的英文全称为CommodityChannelIndex,直译为商品路径指标,我们又把它称作顺势指标,CCI指标是美国股市技术分析 家唐纳德·蓝伯特(Donald Lambert)于20世纪80年代提出的,专门测量股价、外汇或者贵金属交易是否已超出常态分布范围。属于超买超卖类指标中较特殊的一种。波动于正无穷大和负无穷大之间。但是,又不需要以0为中轴线,这一点也和波动于正无穷大和负无穷大的指标不同。

直接从Indicators - Reference - Backtrader看CCI的公式如下:

CCI公式

  • tp = typical_price = (high + low + close) / 3

  • tpmean = MovingAverage(tp, period)

  • deviation = tp - tpmean

  • meandev = MeanDeviation(tp)

  • cci = deviation / (meandeviation * factor)

由公式可知,CCI首先在价格上采用了typical_price(典型价格)的计算方法,然后计算了tp的移动平均值,以及计算了平均偏差。对比布林带百分比公式,主要的差别就在这2点上,布林带使用的价格是收盘价,而偏差计算使用的是标准差。典型价格与收盘价计算会有细微的差别,而平均偏差与标准差在趋势上是完全一致的,只是在具体的数值上有点差别,这也就是它们俩在图上显示像是孪生兄弟的原因。

布林带公式

  • midband = SimpleMovingAverage(close, period)

  • topband = midband + devfactor * StandardDeviation(data, period)

  • botband = midband - devfactor * StandardDeviation(data, period)

  • pctb = (self.data - self.l.bot) / (self.l.top - self.l.bot)

 既然是孪生兄弟,那么可不可以兄弟同心,其利断金呢?能否联合起来制作更好的指标?这个我们在后续的指标进阶中再看。

B_价格计算方法

与大多数单一利用股票的收盘价、开盘价、最高价或最低价而发明出的各种技术分析指标不同,CCI指标是根据统计学原理,引进价格与固定期间的股价平均区间的偏离程度的概念,强调股价平均绝对偏差在股市技术分析中的重要性,是一种比较独特的技术指标

于是我们先了解价格计算法,在编写指标公式或进行技术分析时,了解这些术语的具体计算方法是非常重要的。

  • 收盘价(Close):通常指的是某一交易日结束时的最后成交价格。
  • 中位数(Middle Price):有时也称为“真实中值”(True Range Median),计算方法是取当日最高价和最低价的平均值,即 (H+L)/2。
  • 典型价格(Typical Price, TP):也称为“平均价格”(Average Price),计算方法是取当日最高价、最低价和收盘价的平均值,即 (H+L+C)/3。
  • 加权价格(Weighted Close):计算方法是取当日最高价、最低价、开盘价和收盘价的加权平均,其中收盘价的权重通常较大,有时也会看到使用 (L+H+2*C)/4 的计算方式,这种计算方式给予收盘价双倍的权重。

利用股票软件绘图功能强大的特点,在其中制作收盘价,典型价格以及加权价格的连线和各自的10日SMA平均线进行观察:

收盘价系列用红色线表示,典型价格用蓝色线表示,而加权价格用绿线表示,从上图可以看到,10日的平均线三者相差非常小,单日的连线上,很明显上涨的区间由于收盘价通常高于典型价格和加权价格,因此红色线处于最上方,而典型与加权显得更加的柔和,其中由于加权有2份的收盘价,因此会略高于蓝线一点点;当处于下跌区间时正好相反,红色线(收盘价)在最下面,绿色(加权)在中间,而典型价格的蓝线处于最上面。可以理解为收盘价在上涨或下跌趋势中都领先于典型价格和加权价格,它更加的偏执。

再使用CCI的公式来计算收盘价的CCIC,可以得到与CCI非常相似的曲线:

由典型价格计算的CCI与由收盘价计算的CCIC有着细微的差别,并且CCI还有一些细节:

  1. 首先,SMA是一直在跟着趋势变化的,以左起第1次上涨为例,收盘价连续3天收在差不多的位置,但SMA发生了上移,因此平均偏差是变小的,CCIC(红色线)也是变小;而蓝色线由于Typ的值第2天明显高于第1天,所以CCI仍在上升,但第三天Typ的值在上升不多,而SMA上来了,就造成CCI向下,也就是上涨的幅度没有超过移动平均线上涨的幅度大。
  2. 以收盘价的波动要明显大一些,但是否可以利用这一点,做当收盘价的CCI上穿Typ的CCI时,再根据CCI的值的范围,来制作策略,但这个策略粗略的情况下触发次数会明显增多,就会变得过于敏感,对于敏感类型的指标,我们在下面几节仔细实践过KDJ、RSI和WR后再来看它是否有研究的价值,现在知识的广度还不够,先不要着急深入,否则容易迷路。
  3. 另外,中间红箭头位置下跌CCI已经低于-200并上穿,这个仅对于CCI的值而言属于严重超跌,但这个位置却并不是抄底的好时机,因为后续没有触发卖点又继续下跌,再次下跌时由于SMA已经从上一次的比较高的位置跌下来很多,所以就造成平均偏差反而变小了,所以CCI在股价最低点时反而比前面一段还要高。这也就形成了所谓的CCI底背离(股价比上次低,而指标比上次高),也可以尝试制作CCI底背离的策略来得到更优的买点(底背离时CCI不需要非常低)。

C_平均偏差与标准差

在CCI的指标公式里使用了平均偏差,而在BOLL的指标里使用了标准差,它们到底有着怎么样的关系和差异,我们先对它们的定义进行学习:

方差(Variance)、平均偏差(Mean Absolute Deviation, MAD)与标准差(Standard Deviation)是统计学中衡量数据分布离散程度的三个关键概念。下面将详细解析每个概念:

1. 方差(Variance)

  • 定义:方差是各数据与平均数差的平方的平均数。它是衡量数据分散程度的重要指标。
  • 意义:方差越大,表示数据点分布越分散;方差越小,表示数据点分布越集中。

2. 平均偏差(Mean Absolute Deviation, MAD)

  • 定义:平均偏差是各数据与平均数差的绝对值的平均数。
  • 意义:平均偏差与方差类似,但它是基于绝对值计算的,因此在处理具有正负差值的数据时,它能提供更直观的离散度信息。

3. 标准差(Standard Deviation)

  • 定义:标准差是方差的正平方根,是衡量数据离散程度的另一个重要指标。
  • 意义:标准差与方差相比,单位与原始数据相同,更便于理解和比较。标准差越大,数据的离散程度越高;反之,则表示数据更趋于集中。

接着通过AI助手获取到python计算平均方差及标准差的示例程序代码,在Notebook中运行: 

import numpy as np
import matplotlib.pyplot as plt

# 生成100个随机数据点,这里以正态分布为例
np.random.seed(0)  # 设置随机种子以确保结果可复现
data = np.random.normal(loc=0.0, scale=1.0, size=100)

# 计算平均值
mean = np.mean(data)

# 计算标准差
std_dev = np.std(data)

# 计算平均偏差
mean_abs_dev = np.mean(np.abs(data - mean))

# 绘制散点图
plt.figure(figsize=(10, 6))
plt.scatter(range(len(data)), data, label='Data Points', alpha=0.5)

# 在图上绘制平均线
plt.axhline(y=mean, color='r', linestyle='-', label='Mean')

# 绘制平均偏差线
plt.axhline(y=mean + mean_abs_dev, color='g', linestyle='--', label='Mean + Mean Absolute Deviation')
plt.axhline(y=mean - mean_abs_dev, color='g', linestyle='--')

# 绘制标准差线
plt.axhline(y=mean + std_dev, color='b', linestyle='-.', label='Mean + Standard Deviation')
plt.axhline(y=mean - std_dev, color='b', linestyle='-.', label='Mean - Standard Deviation')

# 添加图例和标题
plt.legend()
plt.title('Data Points with Mean, Mean Absolute Deviation, and Standard Deviation')
plt.xlabel('Data Point Index')
plt.ylabel('Value')

# 显示图形
plt.grid(True)
plt.show()

# 打印计算结果
print(f"Mean: {mean}")
print(f"Standard Deviation: {std_dev}")
print(f"Mean Absolute Deviation: {mean_abs_dev}")

---------------------
Mean: 0.059808015534485
Standard Deviation: 1.0078822447165796
Mean Absolute Deviation: 0.8052222697425467

从上面的代码和图像显示可以知道,平均偏差是每一项与平均值进行ABS计算,累加最后除以次数得到,最简单的例如平均值为0,1个数为1,另1个数为2,则平均偏差 = (1+2)/2 = 1.5;而标准差的计算是先对差值进行平方,然后累加,最后开方,则\sqrt{1*1+2*2}得到的结果是2.236,看起来要比平均偏差大;如果2个数分别为0.2和0.4,则平均偏差= 0.3,标准差=0.44;再如果2个数分别为0.01和0.02则平均偏差为0.015,标准差为0.022。标准差更像是已知直角两边求斜边长度,总是会比这2条直角边大上一些。

标准差与平均偏差在backtrader中的代码位于\indicators\deviation,要查看源码可以从indicators\__init__.py里点.deviation进入,也可以自己用import来进入

# indicators\__init__.py 

# depends on moving averages
from .deviation import *          # 从.deviation进入


# 自己建一个文件
from backtrader.indicators import deviation  # 从 deviation进入

以上,了解了平均偏差与标准差的关系,都是来计算多个数据离散程度的,这也是为什么布林百分比与CCI图线趋势上会那么像的原因,我们再回过来看一下布林百分比是怎么计算出来的:

self.lines.mid = ma = self.p.movav(self.data, period=self.p.period)
stddev = self.p.devfactor * StdDev(self.data, ma, period=self.p.period,
                                           movav=self.p.movav)
self.lines.top = ma + stddev
self.lines.bot = ma - stddev

self.l.pctb = (self.data - self.l.bot) / (self.l.top - self.l.bot)

----------------------
pctb = (C - (mean -stddev)) / (2 *stddev)  # 布林百分比

CCI =   (TYP - mean)        / (factor* meandev)  # CCI

D_CCI策略回测

与布林带策略一样,股票软件中一般都会有CCI的专家系统,打开来代码如下:

INDEX:=CCI(N);
ENTERLONG:CROSS(INDEX,-100);
EXITLONG:CROSS(100,INDEX);

这种经典CCI策略买点是上穿-100,而卖点是下穿100,股票软件中默认周期为14,backtrader中默认为20,而从某些大V那里,有一个把周期设成84的据说胜率非常高,那我们把这些参数都做成策略进行回测,10支股票的结果如下:

首先,14或是20都是比较短的周期,各自适合不同的股票,比如中铝用14的收益达到17529,而用20的收益只有4502,而国芳14的收益7253,20的收益达到33555。

然后,参数调成84后胜率的确是高,这里10支用参数84胜率都是100%,即没有失败过。不过84相当于1/3年了,所以交易次数是非常的低,也就是每年出手1到2次,这种可以说是超级稳健的策略,但是收益却并会太好,容易错过一些特殊的行情,比如横盘后暴涨这种。

E_CCI与布林组合策略等

CCI里面有很多可以研究的东西,比如可以直接研究devation与它的MA5的交叉情况(测下来似乎并不理想;也可以跟布林百分比放在一起进行研究,但这两个图线太像了,似乎没有十分的必要;另外还有TYP与Close的交叉是否有效等,还有devation升高对应着股价还会下跌但CCI已经开始回升了,还有CCI的底背离,顶背离的应用,这些都留到后面的组合指标实践中去,这里先打个桩免得后面忘记了。

CCI指标是专门针对极端情况设计的,也就是说,在一般常态行情下,CCI指标不会发生作用,当CCI扫描到异常股价波动时,立求速战速决,胜负瞬间立即分晓,赌输了也必须立刻了结。

006_DV2简略

DV2这个指标在网上暂时找不到具体的用法,它在backtrader中的源码如下:

class DV2(Indicator):
    '''
    RSI(2) alternative
    Developed by David Varadi of http://cssanalytics.wordpress.com/

    This seems to be the *Bounded* version.

    See also:

      - http://web.archive.org/web/20131216100741/http://quantingdutchman.wordpress.com/2010/08/06/dv2-indicator-for-amibroker/

    '''
    params = (
        ('period', 252),
        ('maperiod', 2),
        ('_movav', SMA),
    )
    lines = ('dv2',)

    def __init__(self):
        chl = self.data.close / ((self.data.high + self.data.low) / 2.0)
        dvu = self.p._movav(chl, period=self.p.maperiod)
        self.lines.dv2 = PercentRank(dvu, period=self.p.period) * 100
        super(DV2, self).__init__()

根据源码,这是RSI的变形指标,但提供的2个链接均不能打开(估计要科学上网),这个指标有些奇怪,它先计算了 收盘价/ 中位值 ,这个应该很接近1,上涨大于1,下跌应该小于1,然后做了这个值的2日平均线,然后做了个252日的PercentRank操作,暂时没弄明白。

输出的图形见下图,dv2要到第252根K线才有计算值,由于没有充分的资料,这个指标先搁置。

 

007_DMI指标

A_简介与公式

DMI指标在股票软件中也有且有相应的专家系统,这说明这个指标很早就被使用了,也有着经典的买卖点策略。不过DMI指标会显示出4条线,在副图小小的一栏里就容易让不了解它的人产生密集恐惧,估计你连看都不想看。

仍然先引用介绍

DMI(Directional Movement Index,方向性运动指数)是由技术分析专家J. Welles Wilder开发的一种动量振荡器,用于评估市场趋势的强度和方向。

DMI指标包括四个主要组成部分:

  • 正方向指标(+DI)、负方向指标(-DI)、
  • 平均方向指数(ADX)和平均方向指数比率(ADXR)。

这些成分共同提供了关于市场趋势和潜在转折点的信息。

这里看到,DMI的指标四个子指标可以分为2个部分,第1部分是正、负方向指标,第2部分是平均方向指数。

我们可以通过backtrader的源码来学习,这样远比直接看股票软件的指标公式要清楚的多:

# backtrader\indicators\__init__.py 中

from .directionalmove import *  # 这里的directionalmove就是DMI指标
--------------------------

# 进入到 directionalmove.py中
    def __init__(self, _plus=True, _minus=True):
        atr = ATR(self.data, period=self.p.period, movav=self.p.movav) #先计算ATR

        upmove = self.data.high - self.data.high(-1)    # 向上运动是当天高-昨天高
        downmove = self.data.low(-1) - self.data.low    # 向下运动是 - (当天低-昨天低)

        if _plus:
            plus = And(upmove > downmove, upmove > 0.0) #upmove>0表示高点在上升
            plusDM = If(plus, upmove, 0.0)
            plusDMav = self.p.movav(plusDM, period=self.p.period)

            self.DIplus = 100.0 * plusDMav / atr

        if _minus:
            minus = And(downmove > upmove, downmove > 0.0) #downmove>0表示低点在下降
            minusDM = If(minus, downmove, 0.0)
            minusDMav = self.p.movav(minusDM, period=self.p.period)

            self.DIminus = 100.0 * minusDMav / atr

B_前置指标ATR

从上面DMI的源码看,第一个计算就要使用到ATR,ATR本身就已经是一个成熟的指标公式,它在股票软件也独立存在

ATR指标,全称为Average True Range,中文名为平均真实波动范围,是由J. Welles Wilder发明的技术分析工具。ATR主要用于衡量市场波动的强度,即价格在一定时间内的平均最大波动范围。它不是一个预测价格走势的指标,而是反映市场波动性的指标,可以帮助交易者评估市场的风险水平和制定相应的交易策略。 

ATR的计算基于真实波幅(TR)的平均值,真实波幅考虑了以下三个值中的最大者:

  1. 当前周期的最高价与最低价之差;
  2. 当前周期最高价与前一周期收盘价之差;
  3. 前一周期收盘价与当前周期最低价之差。

通常情况下,ATR是通过14天或14周期的TR平均值计算得出的。 

我们看到,ATR会计算三种情况的最大值,其中之一是当前周期的最高价与最低价之差,注意这个是价格差,如果把价格差去除以昨天收盘价就得到了当天的振幅(%),这里很有意思的地方就是收盘价要用到昨天的,因为今天还没收盘。

ATR除了相当于计算了当天的振幅之外,还计算了当前周期最高价与昨天收盘价,以及当前周期最低价与昨天收盘价的差,这个主要是在市场出现跳空(gap)时,能够更准确地衡量波动性,详细一点比如说跳空高开并且是一字板涨停,那么波动幅度就是10%(以主板为例),而当天的最高价与最低价之差为0,当天振幅为0;再比如说跳空低开且跌停,那么波动幅度仍然是10%,而当天的振幅可能又是0。

ATR可以说是日内交易者(做T)的非常重要的参考指标,ATR如果差值太小就不适合进行日内交易。通过ATR的一系列计算,还可以辅助我们进行网格的设定等。它是一个非常有拓展性的指标,不过这里我们只是内置指标的实践,就暂不深入讨论了。

ATR的公式,我们把股票软件的公式和backtrader的内置计算放在一起:

N:=14;
MTR:MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));
ATR:MA(MTR,N);

---------------------
params = (('period', 14),)

self.lines.truehigh = Max(self.data.high, self.data.close(-1))
self.lines.truelow = Min(self.data.low, self.data.close(-1))

self.lines.tr = TrueHigh(self.data) - TrueLow(self.data)
self.lines.atr = self.p.movav(TR(self.data), period=self.p.period)

这里看到backtrader用的算法不那么直观但却巧妙的很,它先比较了昨收和今天最高价谁大,再比较了昨收和今天最低价谁小,这样就已经把三种情况区分开了,昨收如果大于今高,那就是跳空低开了,再用高值减低值即可。最后都做了移动平均线。

C_PDI与MDI解析

学习了ATR之后,再回到DMI的指标公式,atr计算后先放着,现在先要计算其他一些中间变量。

        upmove = self.data.high - self.data.high(-1)    # 向上运动是当天高-昨天高
        downmove = self.data.low(-1) - self.data.low    # 向下运动是 - (当天低-昨天低)

        if _plus:
            plus = And(upmove > downmove, upmove > 0.0) #upmove>0表示高点在上升
            plusDM = If(plus, upmove, 0.0)
            plusDMav = self.p.movav(plusDM, period=self.p.period)

        if _minus:
            minus = And(downmove > upmove, downmove > 0.0) #downmove>0表示低点在下降
            minusDM = If(minus, downmove, 0.0)
            minusDMav = self.p.movav(minusDM, period=self.p.period)

先解决upmove和downmove的视觉印象,根据向上运动就是最高值向更高,向下运动就是最低值向更低得到的Up和dn这两个中间变量,在股票软件中把High点连线,Low点也连线并标出价格

由图上数据可知,蓝线是最高价与计算的Up(high[0]-high[-1]),如果大于0就表示最高值还是向上的,以图中第3根周期线位置为例,high0=15.53,high[-1]=15.20,相减=0.33显示在副图的蓝线尖点就是0.33。同理,low0=15.12, low[-1]=14.75, 相减得到0.37再乘上-1得到downmove(DN)=-0.37。

接着根据条件如果正方向就upmove以及如果负方向取downmove,且只取正的值,

plus = And(upmove > downmove, upmove > 0.0) #upmove>0表示高点在上升
plusDM = If(plus, upmove, 0.0)

那就会把上图两条线所有的负值全部过滤掉,得到下图的情况:

到这里,我们就能明白,PDM即正向(plus)的值表示最高价在向上运动,MDM(Minus)负向的正值表示最低价在向下运动。

再进行SMA的处理后去除以ATR就得到了图中显示的PDI和MDI了

这里一直在运算最高价的走势,最低价的走势,又让我们想起了前面的阿隆指标是也是关于最高价最低价的周期关系,那么它们两个又有什么联系呢?我们在后面的实践中有时间可以研究一下。

DMI的DI部分就这么多,在TDX的专家系统里,DMI虽然还显示了ADX和ADXR,但买卖策略根本就没有使用。其专家系统使用的是PDI与MDI金叉买,死叉卖的策略,这个策略看起来往往收益会回吐,但它始终能形成闭环的1买对应1卖,这也是我们之前说的,死叉不是持仓的卖点而是开空的卖点,在中国股票市场有必要根据ADX或ADXR的因子调整策略。

      - dx = 100 * abs(+di - -di) / (+di + -di)
      - adx = MovingAverage(dx, period)
  • +DI和-DI:这两条线分别代表市场的上升动力和下降动力。当+DI上穿-DI时,通常被解释为买入信号;相反,当-DI上穿+DI时,则被解释为卖出信号。

  • ADX:ADX的值介于0到100之间,它衡量的是市场趋势的强度。ADX值上升通常表明趋势正在加强,而ADX值下降则表明趋势正在减弱或市场处于区间波动中。当ADX值超过25时,通常认为市场中存在一个明显的趋势。

  • ADXR:ADXR是ADX的平滑版本,它提供了ADX的长期视角,有助于确认趋势的持续性。

ADX是把PDI和MDI给百分比化了,前面已经看到这两个数已经去掉了小于0的部分,因此单边的上涨和单边的下跌都会造成PDI或MDI的增长,其ADX也会不断增大,所以要注意ADX表示的趋势,趋势不变则ADX一直增大。

D_DMI策略回测

首先是经典策略,即PDI和MDI的金叉死叉策略,这里的默认参数是14,我们增加1个9日,1个21日进行回测,结果如下:

由数据统计,经典DMI策略的胜率比较低,平均在30%,出手次数一般,2年内出手30次左右,即1个月出手1~2次,频繁不算高,最大亏损大多能控制在-4000以内,有少数超出。最大盈利默认14有1个达到26243(小商品城),而周期为9有达到40757的,但这支股票最终收益仍然很少。从这些股票的情况看,DMI似乎并不出彩,让人提不起兴趣。

接着,再针对买入后尽量在高位卖出进行策略调整,这里看到有三个可能性,分别是:

ADX上穿PDI, ADXR上穿PDI以及ADX下穿ADXR,把它们分别制作策略后在backtrader中回测,最终统计结果如下,这里去除了一些使用DMI严重亏损的数据

从胜率来看,ADXR上穿PDI的平均胜率会是最高的,从最后的多支股票策略收益的平均值来看,也是ADXR上穿PDI的收益在DMI系列里是最好的,但DMI对于某些股票会严重亏损,就上面10多支股票而言,DMI并不算是非常好的策略,这里也加入了Kama的策略进行对比,我们看到Kama的平均收益为5%,要比DMI系列好上一些。

不过这里只是内置指标的学习和实践,不要着急去选择单个指标的策略,主要还是看看每个指标它的逻辑、算法,拓展自己的认知,随着知识的不断积累,后面肯定会得到一些更好的组合策略的。

008_Envelope指标

B到E的指标主要的还有一个Envelope,中文翻译过来是“信封”,在指标中可以称为包络线。

Envelope指标是一种动量指标,它通过计算资产价格的某个统计量(如简单移动平均线SMA)的一定倍数上下扩展,形成一个价格通道。这个通道包括一个上限(Upper Band)和一个下限(Lower Band),可以用来衡量价格相对于中心线的波动程度。当价格触及或突破这些带宽时,可能预示着趋势的延续或反转。

包络线指标主要用于识别市场的超买和超卖状态。当价格触及上包络线时,可能表明市场处于超买状态,交易者可以考虑卖出;当价格触及下包络线时,可能表明市场处于超卖状态,交易者可以考虑买入。然而,包络线指标不能保证100%的准确性,因此在实际交易中需要结合其他技术分析工具和风险管理方法。

结合其他指标,如相对强弱指数(RSI)或随机振荡器(Stochastic Oscillator),可以增加交易信号的准确性和策略的稳健性。

根据官方文档,Envelope的指标公式为:

src = datasource

top = src * (1 + perc)

bot = src * (1 - perc)

在backtrader里创建策略类并显示如图所示

由上,这个指标直接对就于收盘价加了偏移2.5%的上限和下限,那么通常的情况下,股价会在上下限之间,当日突破上限为超买,短线可卖出。

这个或许可以跟网格交易综合起来,另外2.5%对于某些振幅比较小的股票可能就显得过于宽松了,比如下图。因此,我们可以跟上个一指标中的ATR结合起来,通过计算得到这支股票的这一段的真实波动,再除以它的昨日收盘价得到波动幅度百分比,再做一个移动平均值计算,就可以适用于各支股票了。

本节小结

本节的目标是实践backtrader内置指标从B到E的主要指标,MA相关的例如DMA,DEMA,EMA等就略过不讲了。这里主要实践了布林带,CCI,DMI这三个指标。

并且在布林带指标中重点讨论了上穿还是下穿、当日穿回的问题,解决在股票软件中连续买点或卖点的问题,并提出了条件判断与分段策略的概念。在CCI指标中仔细比较了典型价格与收盘价,平均偏差与标准差的差异,并提出了组合策略的设想。在DMI指标中,学习了其前置指标ATR,解析了DMI运算逻辑的每一步,对于提前卖出在高点做了三个假设并进行了回测验证,最终认为使用ADXR上穿PDI的策略可能最好。

在BOLL和CCI的实践过程中,我不停地在TDX里调整参数以及添加一些条件,试图验证每一个闪过的念头,差一点迷失在过度拟合的海洋中,连续2天后我才认识到自己的当前目标是先把backtrader的内置指标简单的认识一下,由于知识的广度还不够,就不要着急向深了去挖,随着认知不断增加,之前感觉困难的或者无从入手的或许后来就不算个事,就比如说前几天在设置网格交易为了确定一个合适的百分比计算了好多不相干的数据,包括自己去计算每日振幅,计算跳空的情况下怎么办,结果学完ATR后发现人家早就已经解决了这些问题。

自己折腾和创造是一件好事情,但千万不要过度着迷于眼前的事物,随着不断地学习,一切都会水到渠成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值