拒绝踏空和卖飞,魔改CCI指标主升浪战法!

〇、写在前边

其实最应该学习量化的,就是散户。

作为散户,我们能获取的只有公开信息,这使得我们天然就落后于机构、大户和内幕狗。

那么我们可以利用公开信息来提升投资表现吗?当然可以。

网上有大量免费或者低成本就能获取的数据,比如历史行情、基本面数据等,这些数据中隐藏着大量的规律,哪怕我们能从中找出一条有效的策略,可能就足以保证我们长期跑赢大盘了。

想要长期赚钱,仅靠运气是万万不行的,你的运气要是够好,你早就买彩票中大奖了不是吗?

机构、大户们在有那么多优势的情况下,还会投入大量的精力和成本在研究上,我们如果不认真提升自己的投资能力和操作水平,那么市场凭什么让你赚钱,凭你的追涨杀跌?凭你是“长期股东”?凭你有着发财梦?凭你……穷?

散户最应该抛掉的想法,就是想挣所有的钱。因为这样大概率所有的钱都挣不到。

很多人,既想做好高频、又想做好长线,最后往往是想买超短做个T,结果最后又成了长线股东。

很多人,既想做趋势投资者,又想做好均值回归型投资,最好往往是散户三连:高位接盘、山腰加仓、低位割肉。

以散户的水平和精力,想要掌握非常丰富的、多元的投资策略几乎是不可能的。我的建议是大家根据自己的性格和交易习惯,找到最适合自己的风格,然后把这个风格做深,不断挖掘这个风格下好用的指标和交易策略。

基础差也可以做量化,量化远没你想的那么复杂。

数学、编程,可能会让你在这条路上起步得更加顺利,但是哪怕这两个你都不会,也不代表你做不了量化。

量化可以很复杂,也可以很简单。其实很多量化策略的背后只不过是一些简单的规则,比如一个均线金叉死叉的策略,能有什么复杂的呢?如果你通过研究甚至是一个个试探的方式找到了合适的参数,那你完全可以认为这就是一条量化策略。

量化的本质,就是通过数据来验证一个个假设,得到更加可靠的结论,这和大家通过经验来判断涨跌或者根据玄学来猜涨跌有着本质的不同。我们在使用量化武器时,就是要通过不断执行大概率正确或平均期望收益更高的动作,以此来追求长期正向回报。一次两次的胜利只能是运气,只有持续胜利,那才是真正的能力。

工具方面大家也不用担心,现在有很多工具可以帮助大家低成本地启动量化研究。比如掘金、聚宽、QMT、PTrade等量化平台或交易软件,很多都带有策略编写、选股、回测之类的能力。后续我也会针对常用的工具出一些教程,大家按喜好进行选择就好。

而我们的核心关注点,是策略本身。

言归正传。

一、CCI:顺势指标的本质

今天要介绍的,是一个已经四十多岁的指标。这个指标由唐纳德·兰伯特在上世纪80年代初创造、原本用于商品市场。我强烈建议大家看到最后,可能很多人都没意识到,这个指标最流行的用法其实是错误的!

CCI的全称是Commodity Channel Index,通常翻译成顺势指标。一般来说,该指标下穿+100时有走弱迹象,上穿-100时有走强迹象,这是最常规的逃顶、抄底的信号。当然,在A股,这种所有人都知道的规律大概率已经是个反向指标了。

我们了解它的最终目的就是为了改造它,从而使得它更好用。我们来看下它的计算公式,其相对于RSI、BOLL等略显复杂,但整体来说利用的还是很粗浅的统计知识。我们先看下具体的步骤:

T P = h i g h + l o w + c l o s e 3 A T P = S M A ( T P , N ) M D = M A D N ( T P ) C C I = T P − A T P 0.015 ∗ M D \begin{align} TP = &\frac{high + low + close}{3}\\ \nonumber\\ ATP &= SMA(TP, N) \\ \nonumber\\ MD &= MAD_N(TP)\\ \nonumber\\ CCI &= \frac{TP - ATP}{0.015 * MD} \end{align} TP=ATPMDCCI3high+low+close=SMA(TP,N)=MADN(TP)=0.015MDTPATP

我来详细解释下每一个细节,我尽量使用相对白话的描述,尽可能让没基础的朋友也能看懂。

1. TP和ATP

TP是指Typical Price,是指当天最高价、最低价、收盘价三者的算术平均值,相当于在价格信息中综合考虑了这三个因素。

ATP则是TP的N日简单移动平均,A是Average的开头。一般来说,N采用14,但是我们还是应该从个人风格和实际效果出发,结合自己关注的资产来寻找最恰当的参数。

2. MD

AD对应的是Absolute Deviation,绝对偏差的意思。在这里AD就是指每天的TP减去过去N天的ATP的绝对值,前边再加一个M,对应的是Mean,即对AD再求一个均值,也就是统计学中常见的MAD(平均绝对偏差),这样就得到了我们的MD。

3. CCI

我们用TP减去ATP,再除以MD就得到了CCI。CCI的计算中,0.015是一个常量缩放系数,用于控制值域,可以忽略。

TP-ATP在这里起到的作用是平移,它的目标是将数据中枢移到0的位置,这样我们可以很方便地根据正负值判断当前处于上涨趋势还是下降趋势。

MD起到的作用是缩放,类似于归一化或者标准化,它的目标是为了让不同资产之间具有可比性。举个简单的例子,A资产价格100,B资产价格也是100,那么它们两个谁在超买区、谁在超卖区?我们肯定不知道,但是如果我们知道了A资产一周前的价格只有50,而B资产一周前的价格是200,那么我们就有更大的把握说A超买了,B超卖了。MD在这里就是起到这个作用。

从统计学上来说,CCI指标的计算跟数据标准化的过程有些类似。我们回忆下z标准化的过程:

x ^ = x − μ σ \hat{x} = \frac{x - \mu}{\sigma} x^=σxμ

原值减去均值,并除以标准差,跟CCI的计算是不是很像?区别在于标准化过程中的除数是标准差(STD),而CCI计算过程中的除数是平均绝对偏差(MAD),数据在经历了标准化过程之后,会有95%的值处于均值的两倍标准差范围内,99%的值处于均值的三倍标准差范围内(这里假设了数据近似服从正态分布)。

我们可以大概这么来理解CCI,在大多数情况下,它的取值应该处于±100以内,一旦偏离了这个范围,就说明出现了超买超卖现象。

二、CCI的魔改思路

事实上,CCI中我们可以魔改的地方非常多。比如说ATP中的N和SMA算法,MD中的N和MAD算法。

1. 调整ATP

我们提到,ATP实质上就是TP的简单移动平均。那么这里我们可以考虑调整的就是把移动平均的周期拉长,或者把简单移动平均算法改为EMA、WMA等(详见专栏或合集历史文章)。

以移动平均的周期N为例。假设我们的风格偏长期或者某资产的价值长期稳定,那么我们可以适当提高N的取值,反之亦然。举一个极端的例子,如下图,某资产价格长期在0到2之间波动,1就是价值中枢。

那么很明显,这里长期均值线1就是真正有价值的ATP,价格偏离越多,回归的概率就越大。我们看到,蓝线和红线交叉的点都在1处,这正是这个资产的价值中枢。而蓝线和绿线交叉的点,仅仅能代表短期的趋势。从长期来看,低于红线开始低吸、高于红线逐步卖出大抵是不会错的。而绿线则没有这种指示意义。

一般来说,越长的周期得到的指标,对应的趋势空间也越大。越是长周期的指标发生了巨大的偏离,那么当均值回归时潜在的收益(或落差)就越大。

2. 调整MD

前边我们提到,MD的作用是缩放数据以使得不同资产的指标之间具有可比性。那么这里只要我们使用的指标在量纲上和价格的量纲在一个级别,理论上就是可以考虑的,比如我们可以考虑在统计学中更常用的标准差。

调整之后,如果我们还是希望在指标超过[-100,100]这个范围时发出信号,那么我们需要结合实际情况修改0.015这个系数。或者我们干脆去掉这个系数,结合我们得到的新指标的数据分布来判断指标落在不同值域内的概率,从而调整我们信号触发的阈值。

实际上,这里的N我们也可以考虑修改为跟ATP的N取不同的值。比如我们认为某个资产的价格一年的周期里是具有稳定中枢的,那么我们ATP的N可以考虑选择250,但是我们发现这个资产往往一年波动大、一年波动小。那么我们就可以考虑对MD的N取500,这样,我们的CCI指标就可以在更长期的周期里具有可比性。否则,哪怕是同一个权益资产,跨越较长时间的CCI之间的对比也是有问题的,甚至我们看到的CCI的趋势线也未必是可靠的。

现在我们在各大财经类APP上看到的这个经典的指标,都存在这个问题。

三、CCI计算实战

1. 通达信计算

其实通达信中系统内置了这个指标,如果不做魔改的话,在超买超卖型指标中选择这个指标就可以直接使用了。不过这里还是贴一下公式吧,整体来说还是很简单的。

TYP:=(HIGH+LOW+CLOSE)/3;
CCI:(TYP-MA(TYP,N))*1000/(15*AVEDEV(TYP,N));

我再把官方的指标翻译贴一下,如果还有看不懂的可以留言。

TYP赋值:(最高价+最低价+收盘价)/3
输出CCI:(TYP-TYP的N日简单移动平均)×1000 / (15× TYP的N日平均绝对偏差)

2. Python计算

我们先手动实现一版,然后用神器talib计算一版,并与同花顺、通达信等APP进行对比。

首先,我们用tushare获取上证指数今年以来的数据,并按照日期做一个增序排列。

import pandas as pd
import talib

# 从自建数据库中获取沪深300指数数据
# 找不到行情数据的朋友,可以参考前边的金融数据库搭建教程,把常用数据存储下来
df = pd.read_sql('SELECT * FROM index_a.ts_a_index_daily_quotes WHERE ts_code="000300.SH"', con=con)

接下来,我们按照上边的公式实现CCI指标的计算,并用talib计算一版作为对比。

df = pd.read_sql('SELECT * FROM index_a.ts_a_index_daily_quotes WHERE ts_code="000300.SH"', con=con)
df['TP'] = (df.close.values + df.high.values + df.low.values) / 3
df['ATP'] = talib.SMA(df.TP.values, timeperiod=14)
df['MD'] = 0
for i in range(len(df)-13):
    df['MD'].iloc[i+13] = np.mean(abs(df['TP'].values[i:i+14] - df['ATP'].values[i+13]))
df['CCI14_Q'] = (df.TP - df.ATP) / (df.MD * 0.015)
df['CCI14'] = talib.CCI(df.high, df.low, df.close, timeperiod=14)
df.tail(10)[['ts_code', 'date', 'CCI14_Q', 'CCI14']]

可以看到,两种方法的结果完全一致。经过对比,我们的计算与同花顺等APP也保持一致。

image

四、CCI的实战应用

以±100为操作信号到底靠谱吗?Talking is cheap,我们来看看真实的数据表现。

我们先来做个简单的统计,看下CCI14的分布情况:

df.CCI14.quantile([0.01, 0.05, 0.10, 0.25, 0.5, 0.75, 0.90, 0.95, 0.99])

输出为:

0.01   -225.302040
0.05   -164.952592
0.10   -137.246979
0.25    -82.598690
0.50     14.500720
0.75     98.992095
0.90    144.239111
0.95    167.839295
0.99    215.487870
Name: CCI14_Q, dtype: float64

怎么理解呢?对沪深300来说,当CCI14<-225或CCI14>215的时候,是“百日一遇”的顶底极端值区域;有大概10%的时间里,CCI高于168或者低于-165。

我们看下±100之外的数据有多少。

print('低于-100的时间占比: ', len(df[df.CCI14 < -100]) / len(df))
print('高于100的时间占比: ', len(df[df.CCI14 > 100]) / len(df))

输出为:

低于-100的时间占比:  0.20012507817385866
高于100的时间占比:  0.2461955388784657

也就是说,有超过44%的时间里CCI是在±100之外的。

CCI经典的用法是下穿100时卖出,上穿100时买入,那么我们来看下这个策略的表现。

我们假设当某一天CCI上穿-100后,第二天我们以开盘价买入;当某一天CCI下穿100后,第二天我们以开盘价卖出。

limit = 100
df2['buy_flag'] = df2.apply(lambda row: 1 if row['CCI14_pre'] < -limit and row['CCI14'] > -limit else 0, axis=1)
df2['sell_flag'] = df2.apply(lambda row: 1 if row['CCI14_pre'] > limit and row['CCI14'] < limit else 0, axis=1)
for d in [1, 3, 7, 14, 30]:
    df2['future_{0}_change'.format(d)] = df2.close.shift(d) / df2.close - 1
future_cols = ['future_{0}_change'.format(d) for d in [1, 3, 7, 14, 30]]
df2.groupby(['buy_flag', 'sell_flag'])[future_cols].mean().T

上图中,绿色框代表我们卖出后未来1天、3天、7天、14天和30天股价的累计涨幅。可以看到,在下穿-100后,第二天开盘卖出,3天内的收盘价平均下来和我们的卖出价非常接近。但是拉长周期到7天至30天来看,平均涨幅大概在-3.5到-6.4%。也就是说,拉长到一周以上来看,我们卖出很可能是正确的。

红色框代表我们买入后未来1天、3天、7天、14天和30天股价的累计涨幅。可以看到,买入当天收盘价和我们的买入价基本持平。但是拉长周期看,我们持有3、7、14、30天的收益分别是1.2%、4.8%、6.4%和6.6%。

当然,上边这个估算存在一些问题。因为我们并没有考虑在持有期间触发了卖出信号以及在卖出时触发了买入信号的情况。那么我们就以发出买入信号则立即买入,发出卖出信号则立即卖出,来看下买入和卖出期间沪深300的表现分别是怎么样的。这里我们采用掘金量化终端来实现这一策略,绩效表现见下图。

image

很明显,这一策略大幅跑输了基准,在近20年的时间里仅仅实现了4.36%的总收益,年化0.23%,这可是连银行存款利率都远远不如啊!

所以,网上再讲那些玄学用法时,不要再相信了!要相信科学,相信数据,相信量化的力量!

五、CCI魔改之主升浪战法

既然公开的用法不靠谱,那我们就结合CCI的特性,来设计一个可能有效的策略。

这里的核心思路是:

  1. 每一次大机会,都一定伴随着短期的见底
  2. 每一次大机会,都一定伴随着右侧的突破

所以,我们需要在策略中实现以下几点:

  1. 我们需要判断短期的底部,这里我们通过对CCI指标的周期和阈值做设计来实现这一效果。
  2. 有些底部是V字反转,有些底部是横盘突破,所以CCI出现信号后并不一定马上能看到右侧信号,如果只用当天的CCI和均线做配合,很可能会错过一些横盘突破的主升浪。
  3. 我们需要判断右侧信号,这里可以考虑使用均线突破的策略。

下面我们看下策略的回测表现。可以看到,这个策略在回撤方面控制得非常好,在基准最大回撤超过70%的情况下保持了仅仅28%的最大回撤,同时基本上每次大涨的鱼身段都能吃到,最终在基准收益360%的情况下,策略得到了超过14倍的收益!可以看到,策略的历史高点在20年,在国庆节前这一波上涨之后,已经逼近了历史高点!然而沪深300本身距离历史高点还有不小的距离呢……

image

当然,这个策略也有其劣势。最显而易见的问题就是胜率较低,这是因为这个策略为了保证不错过主升浪,在买入信号方面会相对敏感,不过快进快出的特性也确保了每次试错的成本不会太高,所以综合来看,较低的胜率也可以接受。

另外这个策略本身仍然算是一个相对右侧的策略,所以也摆脱不了右侧的通病,那就是起涨第一波一般会错过一些、见顶后第一波一般逃不过。但是从整体的回撤表现和收益曲线来看,这都不是什么大问题。以本次大涨为例,虽然策略没有在9月18日最低点买入,但是在9月24日收盘进场也没差太多,核心的鱼身段并没有错过。

image

我已经将策略的细节、代码和通达信公式都放到圈子中,欢迎大家加入老Q的圈子,我们一起研究量化,武装自己!

image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

量化祛魅师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值