KDJ又叫随机指标,是一个适用于短线的技术指标,在股票、期货等市场受到广泛使用。在老Q看来,这是一个很有趣的指标。但是如果你按照经典用法来使用的话,它就变成财富毁灭机了!
下边,老Q就一步步从统计原理、魔改思路、Python计算、策略实战的角度来剖析KDJ指标,告诉你为什么它是一个反向指标!
最重要的,我也会给出一个强力的魔改版本!
一、KDJ的原理与魔改思路
原理部分需要一定的统计基础,不想看的朋友也可以跳过直接看下一部分。但是想要提高技术分析水平的同学不妨花点时间消化下。
KDJ其实是在KD指标的基础之上,又增加了一个J指标,所以我们直接讲完整的KDJ。
R S V = C ( i ) − L ( n ) H ( n ) − L ( n ) × 100 K i = 2 3 K i − 1 + 1 3 R S V i D i = 2 3 D i − 1 + 1 3 K i J i = 3 K i − 2 D i \begin{align} RSV &= \frac{C(i) - L(n)}{H(n) - L(n)} \times 100 \\ \nonumber \\ K_i &= \frac{2}{3} K_{i-1} + \frac{1}{3} RSV_i \\ \nonumber \\ D_i &= \frac{2}{3} D_{i-1} + \frac{1}{3} K_i \\ \nonumber \\ J_i &= 3 K_i - 2 D_i \end{align} RSVKiDiJi=H(n)−L(n)C(i)−L(n)×100=32Ki−1+31RSVi=32Di−1+31Ki=3Ki−2Di
上边是KDJ计算的完整公式,我们来一步步拆解一下。
1. RSV
RSV的全称是Raw Stochastic Value,翻译为“未成熟随机值”。其中C(i)代表当日的收盘价,L(n)和H(n)分别代表过去n个交易日的最高价和最低价。
很明显,分子的含义是当前的价格与过去n个交易日内最低价的差值,它可以反映当前价格在过去n天里相对于最低价的位置。但是这里有一个问题,那就是不同的资产、不同的时期,这个差值的大小含义是不同的,也就是说,我们不管是跨横截面、还是跨时序去对比,都是不合理的。接着往下看。
分母的含义是过去n天的最高价与最低价的差值,他可以理解为我们人为地构建了一个区间(值域),这个区间假设了在一段时间内价格的合理波动范围。有了它,我们的分子就有了意义。
比如说,过去10天最高价减去最低价为10,现在的价格减去过去10天的最低价为1。那就说明,在过去10天这个高度为10的空间里,当前的价格所处的高度为1,是比较低的。因此分子分母相除,得到的就是当前价格在一个合理区间内的相对位置。
有统计学基础的朋友可能已经看出来了,这就是一个非常简单的0-1标准化(又叫极差标准化、Min-Max标准化),它可以将变量映射到[0, 1]的范围内(乘以100后就是[0, 100]),从而让我们可以横向、纵向地去对比不同时期、不同资产的趋势强弱程度。
RSV的魔改思路
既然这里采用了0-1标准化,那么在统计学中更常用的z标准化是不是也可以考虑呢?如果这样的话,RSV就是一个中心化的指标,它就更像是衡量当前价格在当前的布林带中的相对位置。这时,可能的公式是:
R S V Z = C ( i ) − S M A ( C , n ) 2 ∗ S T D ( C , n ) RSV_Z = \frac{C(i) - SMA(C, n)}{2 * STD(C, n)} RSVZ=2∗STD(C,n)C(i)−SMA(C,n)
正值代表上升趋势,负值代表下降趋势。其绝对值越大,代表趋势越强,正常来说,假设价格波动服从正态分布,那么95%的时间里,它的取值应该都在[-1, 1]范围内。当取值小于-1或者大于1时,应该考虑趋势翻转的可能性。
同时,原版的RSV还有一个很大的问题在于完全没有考虑近期价格波动的绝对值(因为通过标准化给抵消掉了)。
但事实上一段时间的小步阴跌和一段时间的大步暴跌对应的含义是完全不同的。因此,我们可以考虑增加一个动态的缩放系数进来,比如近n个交易日的涨幅(或降幅)相比于历史上同样周期的涨幅的均值的倍数。
这样,KDJ在小步阴跌时也会触发信号的问题就可以解决了。后边等这一系列入门文章写完之后,我会带大家一个个实验下这些魔改的思路,并且挑选一些标的回测下看看效果。
2. K值和D值
还记得我们学过的EMA吗?
K = 2 n + 1 E M A = K × i n p u t + ( 1 − K ) × E M A [ − 1 ] ) K = \frac{2}{n+1}\\ \quad\\ EMA = K \times input + (1 - K) \times EMA_{[-1]}) K=n+12EMA=K×input+(1−K)×EMA[−1])
很明显,K值就是对RSV做了一个EMA,其参数就是将上述式子的n赋值5。它能起到平滑RSV趋势的作用,避免其对于价格过分敏感。D值,则是对于K值又做了一个相同参数的EMA。
那么这个D怎么理解呢?老Q的理解是,它的作用是给K值做一个短期参考。当K值线穿过了D值线,我们就可以考虑是不是K值的趋势在扭转。否则我们应该当作K值趋势仍在延续来看待,避免对于波动过于敏感。
那么K值的中长期参考是什么呢?我们刚才说过,K值就是对于RSV的一个平滑,RSV则是价格的一个标准化处理,所以,K值的长期参考就是[0, 100],越接近0代表近期超卖越严重,越接近100代表近期超买越严重。
K值和D值的魔改思路
对于看了历史文章的朋友来说,这里可能都不需要老Q多提了。均值算法、时间周期都可以是我们改造的点,具体应该选择什么算法、什么时间周期,需要结合我们的投资风格、目标标的来决定。
对这一块儿不熟悉的朋友,可以参见专栏内的历史文章,简单移动平均(SMA)、指数移动平均(EMA)和加权移动平均(WMA)三篇。未来老Q还会讲到其他的一些均线类指标,不过这三个是最基础、最常用的均线类指标。
3. J值
J值又代表什么呢?
从公式中我们看到,实际上J值做了一个事情:对比K值和D值。我们刚说到,D值的目的是为了给K值做一个参考,所以这里J值,就代表着K值相对于D值的变化情况。
J值的魔改思路
这里的J值当然也可以魔改。
既然是反映K相对于D值的情况,那么任意的权重组合都是可以考虑的,比如J=2K-3D、J=4K-5D。
我们还可以考虑使用比值的方式。比如J=K/D。
当然,魔改之后,记得要根据魔改后的结果乘以一个缩放参数,以调整值域到[0, 100],便于直观判断相对位置。
如果纯粹量化程序使用的话,这个缩放参数倒是无所谓。
二、KDJ的Python计算和可视化
我们按照公式手动实现一下KDJ指标的计算,并和TA-Lib的结果进行对比。
# 手动计算
df['his_high'] = df.rolling(9)['high'].max()
df['his_low'] = df.rolling(9)['low'].min()
df['RSV'] = (df.close - df.his_low) / (df.his_high - df.his_low) * 100
df['K'] = talib.EMA(df.RSV, 5)
df['D'] = talib.EMA(df.K, 5)
df['J'] = 3 * df.K - 2 * df.D
# 使用TALIB计算
df['K-TA'], df['D-TA'] = talib.STOCH(
df.high, df.low, df.close, fastk_period=9, slowk_period=5,
slowk_matype=1, slowd_period=5, slowd_matype=1)
df['J-TA'] = 3 * df['K-TA'] - 2 * df['D-TA']
df[['ts_code', 'date', 'K', 'K-TA', 'D', 'D-TA', 'J', 'J-TA']].tail(20)
可以看到,我们的计算结果与TALIB完全一致。经过手动查阅,这一结果与同花顺等主流软件的结果也是一致的。
画个图看一下。似乎能观察到一个规律,那就是当J线在100上方时,经常对应着一个局部小高点;当J线在0下方时,往往对应着一个局部低点。但是这种规律在极端行情下可能会失效。
其实还有一个很有意思的点,那就是任意两条线交叉时,第三条线也会穿过这个交叉点,大家可以看下边这张图是不是这样。
其实我们可以很容易地从公式里推导出来:
J = 3 × K − 2 × D J − D = 3 × K − 2 × D − D = 3 × ( K − D ) J = 3 \times K - 2 \times D \\ J - D = 3 \times K - 2 \times D - D = 3 \times (K - D) J=3×K−2×DJ−D=3×K−2×D−D=3×(K−D)
很明显,当J=D时,K=D也成立。所以从金叉死叉的角度来考虑,J线其实没有提供增量信息,我们只用K线和D线也是一样的。
三、KDJ的经典策略验证
首先KDJ最经典的策略莫过于K/D线的金叉策略了,金叉买入、死叉卖出。
那么接下来,我们就看一下逢金叉则次日以开盘价买入、逢死叉则次日以开盘价卖出的策略表现如何。代码在这里就不贴了,有需要的朋友可以加入圈子获取。我们直接来看结果。
从下表可以看出,KDJ的经典金叉策略在过去二十年里大幅跑输了基准收益,也就是说按照KDJ的策略来交易,还不如买了不动。
上图是交易信号的示意图,可以看到,交易操作非常频繁,但是,不光亏手续费,还亏本金。。
接下来我们用掘金量化来对沪深300做一个回测,把手续费、滑点也考虑上。很好,亏损和回撤都更高了。
我们做一个非常离谱的操作,那就是把买卖信号做个交换。结果我们发现,竟然表现变好了很多!胜率大幅提升,在上证、沪深300、创业板指/创业板50上还跑出了超额收益!所以,KDJ的金叉策略,实际上是一个反向指标!
所以原版的KDJ,真不是普通人用得起的。
四、老Q专属魔改策略
那么接下来,我们从两个相对简单的角度来尝试做一下魔改。我们暂且不去动KDJ的公式,只去调参数或者考虑和其他指标搭配使用。
- 第一,我们可以考虑把KDJ的各个环节里的时间周期拉长,既然短线不好用,我们试试长线做趋势能不能行。
- 第二,我们可以考虑利用K值是否处于极端位置来做一个左侧的判断,然后再结合右侧突破均线的信号,来做一个寻找右侧反转信号的策略。
在第一个思路下,简单测了几组参数,找到一组还算可以的版本。在沪深300上实现了年化11%的收益,这至少已经比原版的强了很多了。不过最大回撤到了41%,还有较大的优化空间。
接下来我们试试第二个思路。这个看起来回撤表现就好多了,过去20年最大回撤仅有不到28%。而且在刚刚过去的这一波拉升中,创下了净值新高!
从买卖信号上来看,这是一个让利润奔跑型的策略,对于错误的进场,会及时止损,整体表现还是很不错的。
很多人总是在追求更加爆发的收益表现,事实上控制好回撤才是长期赚钱的关键。
上边的策略是以信号发出后第二天的开盘价买入,那么如果我们调整成以信号当天的收盘价买入呢?回测了一下发现,无论是回撤表现还是整体收益,都有所提升,基本上整个净值曲线一直在创新高的路上。也就是说,这个策略我们可以在收盘集合竞价时来决策是否要买入,这样可能会有更好的收益表现,当然,这对于时机把握的要求更高了一些,对普通人来说,在第二天开盘集合竞价时执行操作会更可行一些。
好了,老规矩,两个思路的魔改策略都会放到圈子里,欢迎大家加入圈子一起交流!