4、技术指标因子(上):趋势类指标与动量类指标的计算与特征化
技术指标因子,说白了就是把K线图上那些花花绿绿的线,变成我们量化模型能吃的数字特征。很多新手一上来就堆几百个指标,结果过拟合得一塌糊涂。我个人习惯是,先把最经典的几个吃透,再谈创新。
这一章我们先啃两块硬骨头:趋势类指标和动量类指标。趋势类告诉你方向,动量类告诉你力度。两者结合,才能判断行情是「健康的上涨」还是「强弩之末」。
核心观点:技术指标不是拿来直接交易的,而是用来构造「特征向量」的。我们要做的是把指标值、交叉信号、背离信号等转化为数值特征,喂给机器学习模型。
4.1 趋势类指标:跟着大部队走
趋势类指标的核心思想很简单——平滑掉噪音,看清方向。价格上蹿下跳,但趋势不会一天就变。我见过太多人盯着5分钟K线做趋势,结果被来回打脸。记住,趋势的级别要和你的持仓周期匹配。
4.1.1 MA(移动平均线)
MA是最基础的,但也是最容易被低估的。很多人觉得MA太简单,不屑于用。其实不然,MA的斜率、排列顺序、交叉点都是极好的特征。
计算上,就是窗口内价格的均值。但特征化时,我建议做这几步:
- 原始值特征:直接取MA(5)、MA(10)、MA(20)、MA(60)的值
- 斜率特征:MA(5)当前值相比前N期的变化率,反映趋势强度
- 排列特征:短期MA是否在长期MA之上?多头排列还是空头排列?
- 距离特征:当前价格与MA的偏离度((price - MA) / MA),这个在均值回归策略里特别有用
我的经验:MA(5)和MA(20)的交叉信号,单独用胜率不高。但如果你把交叉信号和成交量变化结合起来,效果会好很多。我曾经在回测中发现,交叉当天成交量放大30%以上时,后续趋势延续的概率超过70%。
4.1.2 MACD(异同移动平均线)
MACD = 快线(DIF) + 慢线(DEA) + 柱状线(MACD柱)。本质上就是两条指数移动平均线的差值再平滑一次。
特征化时,我一般提取以下维度:
- DIF值:快慢线差值,反映短期动量
- DEA值:DIF的平滑,作为信号线
- MACD柱:DIF - DEA,反映动量加速度
- 金叉/死叉信号:DIF上穿/下穿DEA,用0/1编码
- 顶背离/底背离:价格创新高但MACD柱没创新高,这是非常强的反转信号
# MACD特征提取示例(Python伪代码)
def compute_macd_features(close, fast=12, slow=26, signal=9):
# 计算DIF、DEA、MACD柱
ema_fast = close.ewm(span=fast).mean()
ema_slow = close.ewm(span=slow).mean()
dif = ema_fast - ema_slow
dea = dif.ewm(span=signal).mean()
macd_bar = dif - dea
# 特征化
features = pd.DataFrame({
'dif': dif,
'dea': dea,
'macd_bar': macd_bar,
'macd_cross': np.where(dif > dea, 1, 0), # 金叉为1,死叉为0
'macd_bar_change': macd_bar.pct_change() # 柱状线变化率
})
return features
避坑指南:我曾经在2015年股灾期间用MACD金叉做多,结果连续被埋了三次。后来复盘发现,极端行情下MACD会频繁发出假信号。解决办法是:加入波动率过滤——当ATR(平均真实波幅)处于历史高位时,降低MACD信号的权重。
4.1.3 DMI(趋向指标)
DMI由三条线组成:+DI(上升方向线)、-DI(下降方向线)、ADX(平均趋向指数)。它不像MACD那么普及,但我觉得它在判断趋势强度方面无可替代。
计算逻辑稍微复杂一点:先算真实波幅TR,再算上升动向+DM和下降动向-DM,然后平滑得到+DI和-DI,最后用两者差值计算ADX。
特征化时,我重点关注:
| 特征名称 | 计算方式 | 业务含义 |
|---|---|---|
| +DI / -DI 差值 | (+DI) - (-DI) | 多空力量对比,正值偏多,负值偏空 |
| ADX 值 | 平滑后的趋向指数 | 趋势强度,>25表示强趋势,<20表示震荡 |
| ADX 变化率 | ADX相比前N期的变化 | 趋势是否在加速或衰减 |
| 交叉信号 | +DI上穿-DI为买入,反之卖出 | 方向转换信号 |
嗯,这里要注意:ADX本身不指示方向,只指示强度。ADX很高时,无论涨跌,趋势都很强。这时候做反向交易就是找死。
4.2 动量类指标:测测市场的脉搏
趋势类指标告诉你「往哪走」,动量类指标告诉你「走得累不累」。动量指标的核心思想是物极必反——涨得太猛了要回调,跌得太狠了要反弹。
4.2.1 RSI(相对强弱指标)
RSI = 100 - 100 / (1 + RS),其中RS = 平均上涨幅度 / 平均下跌幅度。说白了就是比较一段时间内上涨和下跌的力度。
传统用法是RSI > 70超买,RSI < 30超卖。但我觉得这个阈值太死板了。在牛市中,RSI可以长期维持在70以上;在熊市中,RSI可能一直在30以下趴着。
我建议的特征化方式:
- RSI原始值:直接作为连续特征
- RSI分位数:过去N天RSI的百分位排名,比绝对值更有参考意义
- RSI背离:价格创新高但RSI没创新高,这是经典的顶部背离信号
- RSI斜率:RSI曲线的变化率,反映动量变化速度
一个小技巧:我习惯把RSI和布林带结合使用。当RSI突破70且价格触及布林带上轨时,做空胜率会显著提升。反之亦然。这本质上是在「双重确认」极端状态。
4.2.2 WR(威廉指标)
WR = (最高价 - 收盘价) / (最高价 - 最低价) × (-100)。取值范围从0到-100。它和RSI有点像,但WR更敏感,因为它直接比较了收盘价在最近N天价格区间中的位置。
WR的特征化相对简单:
- WR原始值:-80以下为超卖,-20以上为超买
- WR触底/触顶次数:过去N天WR触及-80或-20的次数,反映极端行情的频率
- WR与价格背离:和RSI类似,价格新高但WR没新低(注意WR是反向指标)
你想想看,WR为什么比RSI更敏感?因为RSI用了平滑处理,而WR直接取极值。所以在震荡行情中,WR会频繁发出信号,这时候需要加一个过滤器——比如要求WR在超买/超卖区域停留超过2天再行动。
4.2.3 KDJ(随机指标)
KDJ = K线 + D线 + J线。K是快速线,D是慢速线,J是K和D的差值放大版。计算上,先算未成熟随机值RSV,然后对RSV做指数平滑得到K,再平滑K得到D,最后J = 3K - 2D。
KDJ的特征化维度:
| 特征 | 说明 | 用法 |
|---|---|---|
| K值 | 快速随机线 | K > 80超买,K < 20超卖 |
| D值 | 慢速随机线 | D > 80超买,D < 20超卖 |
| J值 | K与D的差值放大 | J > 100或J < 0时,行情可能反转 |
| K-D差值 | K线减去D线 | 正值且扩大,多头强势;负值且扩大,空头强势 |
| 金叉/死叉 | K上穿D或下穿D | 配合J值使用效果更好 |
我曾经踩过的坑:KDJ在日线级别上效果不错,但到了分钟级别,噪音大得离谱。有一次我用15分钟KDJ做高频交易,金叉买入后下一秒就死叉,连续亏了十几次。后来我加了一个条件:必须同时满足成交量放大和价格突破前高,才确认信号有效。从此以后,KDJ的分钟级信号才变得可用。
4.3 特征化实战要点
讲了这么多指标的计算和含义,最后说说怎么把它们变成模型能用的特征。我个人总结了三条原则:
- 不要直接用原始值:比如RSI=72和RSI=73,对模型来说差别不大。但RSI从70涨到80,这个变化趋势更有意义。
- 交叉信号要离散化:金叉/死叉用0/1编码,或者用-1/0/1三值编码(死叉=-1,无信号=0,金叉=1)。
- 背离检测要自动化:写一个函数,自动检测价格和指标是否出现背离。这个特征在长周期策略中非常有效。
# 背离检测示例
def detect_divergence(price, indicator, window=20):
"""
检测顶背离:价格创新高,但指标没创新高
返回:1=顶背离,-1=底背离,0=无背离
"""
price_high = price.rolling(window).max()
indicator_high = indicator.rolling(window).max()
# 顶背离条件
top_div = (price == price_high) & (indicator < indicator_high.shift(1))
# 底背离条件
bottom_div = (price == price.rolling(window).min()) & (indicator > indicator.rolling(window).min().shift(1))
return np.where(top_div, 1, np.where(bottom_div, -1, 0))
好了,这一章的内容就到这里。趋势类指标和动量类指标是技术分析的两大支柱,把它们吃透了,后面的震荡类指标和量价类指标学起来会轻松很多。记住,指标是工具,不是圣杯。真正重要的是你如何理解市场,以及如何把这种理解转化为有效的特征。
公众号:蓝海资料掘金营,微信deep3321