第四节:技术指标计算——移动平均线、布林带、MACD、RSI、ATR、KDJ
各位同学,欢迎来到第四讲。今天咱们要啃的这块骨头,是所有CTA策略的「地基」——技术指标的计算与可视化。
说实话,我入行头两年,犯过一个特别蠢的错误:把一堆指标堆在一起,以为信号越多越安全。结果呢?回测曲线漂亮得像假的一样,实盘一跑就崩。后来我才明白,理解每个指标背后的数学逻辑和适用场景,比会调库重要一百倍。
今天我会带着大家,把六个最常用的指标——SMA、EMA、布林带、MACD、RSI、ATR、KDJ——从公式到代码,从计算到画图,全部过一遍。嗯,咱们开始吧。
核心观点:技术指标不是「圣杯」,而是「工具」。你越懂它的脾气,它就越听你的话。
4.1 移动平均线:SMA 与 EMA
移动平均线,说白了就是「过去N根K线的平均价格」。它是最简单、也最容易被低估的指标。
SMA(简单移动平均线):每个数据点权重一样。公式很简单:
SMA = (P₁ + P₂ + ... + Pₙ) / N
EMA(指数移动平均线):越近的数据权重越大。公式稍微复杂一点:
EMA_today = (Price_today × α) + (EMA_yesterday × (1 - α))
其中 α = 2 / (N + 1)
我个人习惯,做趋势跟踪时用EMA,因为它反应更快。做震荡策略时用SMA,因为它更平滑、假信号少。
避坑指南:我曾经在螺纹钢上同时用5日SMA和20日SMA做金叉死叉,结果发现参数太敏感,一天开平仓七八次。后来改成EMA(12)和EMA(26),效果好了很多。记住:参数不是越短越好,要看品种的波动周期。
4.2 布林带(Bollinger Bands)
布林带由三条线组成:中轨(SMA)、上轨(中轨 + k倍标准差)、下轨(中轨 - k倍标准差)。
公式:
中轨 = SMA(close, N)
上轨 = 中轨 + k × std(close, N)
下轨 = 中轨 - k × std(close, N)
默认参数:N=20, k=2。但我在实战中发现,对于波动率大的品种(比如原油),k=2.5更合适;对于波动率小的品种(比如国债),k=1.8就够了。
布林带的核心逻辑是「价格会回归均值」。当价格碰到上轨,可能超买;碰到下轨,可能超卖。但注意——强势行情中,价格会沿着上轨一直走,这时候做空就是找死。
4.3 MACD(指数平滑异同移动平均线)
MACD由三部分组成:
- DIF线:快线EMA(12) - 慢线EMA(26)
- DEA线:DIF的EMA(9)
- MACD柱:2 × (DIF - DEA)
你想想看,MACD本质上是在衡量「短期趋势和长期趋势的差距」。当DIF上穿DEA,叫金叉,看多;下穿叫死叉,看空。
但我得提醒一句:MACD有滞后性。我在做股指期货时,经常遇到金叉出现时行情已经走了一半。所以我的做法是:用MACD判断趋势方向,用其他指标找入场点。
4.4 RSI(相对强弱指标)
RSI衡量的是「上涨幅度占总波动的比例」。公式:
RSI = 100 - [100 / (1 + RS)]
其中 RS = 平均上涨幅度 / 平均下跌幅度
默认周期是14。一般认为RSI > 70超买,RSI < 30超卖。
嗯,这里要注意:RSI在震荡行情中特别好用,但在单边行情中会频繁失效。我记得2020年原油暴跌那波,RSI连续两周都在20以下,你要是按超卖做多,早就爆仓了。
4.5 ATR(平均真实波幅)
ATR不预测方向,它只告诉你「市场波动有多大」。公式:
TR = max(high - low, |high - prev_close|, |low - prev_close|)
ATR = SMA(TR, N)
ATR的用途很广:
- 设置止损:比如止损位 = 入场价 - 2 × ATR
- 调整仓位:波动大时减仓,波动小时加仓
- 判断市场状态:ATR上升说明波动加剧,下降说明市场在收敛
重要提醒:ATR是绝对值,不同品种不能直接比较。比如黄金的ATR可能是20,而玉米的ATR可能是2。所以做多品种对比时,要用ATR百分比(ATR / 价格)。
4.6 KDJ(随机指标)
KDJ是RSI的「升级版」,它加入了动量因素。公式稍微复杂:
RSV = (close - lowest_low) / (highest_high - lowest_low) × 100
K = 2/3 × prev_K + 1/3 × RSV
D = 2/3 × prev_D + 1/3 × K
J = 3 × K - 2 × D
默认周期是9。K值 > 80超买,< 20超卖。J值可以超过100或低于0,用来判断极端行情。
我个人觉得,KDJ比RSI更灵敏,但假信号也更多。我的做法是:用KDJ做短线入场,用MACD做趋势过滤。两者结合,胜率能提高不少。
4.7 代码实现与可视化
好了,理论讲完了,咱们上代码。我用的是Python + Pandas + Matplotlib,这是最经典的组合。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 假设df是包含'close','high','low'的DataFrame
# 1. SMA
df['SMA_20'] = df['close'].rolling(window=20).mean()
# 2. EMA
df['EMA_12'] = df['close'].ewm(span=12, adjust=False).mean()
# 3. 布林带
df['BB_mid'] = df['close'].rolling(20).mean()
df['BB_std'] = df['close'].rolling(20).std()
df['BB_upper'] = df['BB_mid'] + 2 * df['BB_std']
df['BB_lower'] = df['BB_mid'] - 2 * df['BB_std']
# 4. MACD
df['EMA_12'] = df['close'].ewm(span=12).mean()
df['EMA_26'] = df['close'].ewm(span=26).mean()
df['DIF'] = df['EMA_12'] - df['EMA_26']
df['DEA'] = df['DIF'].ewm(span=9).mean()
df['MACD_bar'] = 2 * (df['DIF'] - df['DEA'])
# 5. RSI
def calc_rsi(data, period=14):
delta = data.diff()
gain = delta.where(delta > 0, 0)
loss = -delta.where(delta < 0, 0)
avg_gain = gain.rolling(period).mean()
avg_loss = loss.rolling(period).mean()
rs = avg_gain / avg_loss
return 100 - (100 / (1 + rs))
df['RSI_14'] = calc_rsi(df['close'])
# 6. ATR
def calc_atr(df, period=14):
high_low = df['high'] - df['low']
high_close = np.abs(df['high'] - df['close'].shift())
low_close = np.abs(df['low'] - df['close'].shift())
tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
return tr.rolling(period).mean()
df['ATR_14'] = calc_atr(df)
# 7. KDJ
def calc_kdj(df, period=9):
low_min = df['low'].rolling(period).min()
high_max = df['high'].rolling(period).max()
rsv = (df['close'] - low_min) / (high_max - low_min) * 100
df['K'] = rsv.ewm(com=2).mean()
df['D'] = df['K'].ewm(com=2).mean()
df['J'] = 3 * df['K'] - 2 * df['D']
return df
df = calc_kdj(df)
小技巧:计算指标时,记得先处理缺失值。我一般用 df.dropna() 把前N行删掉,因为滚动窗口计算会生成NaN。
4.8 可视化:一张图看懂所有指标
我习惯把指标分成三个子图来展示:
- 子图1:K线 + 移动平均线 + 布林带
- 子图2:MACD(DIF、DEA、柱状图)
- 子图3:RSI + KDJ(K、D、J线)
ATR我一般单独画,或者叠加在K线图上作为背景。
fig, axes = plt.subplots(3, 1, figsize=(14, 10), sharex=True)
# 子图1:价格与均线
axes[0].plot(df.index, df['close'], label='Close', color='black', linewidth=1)
axes[0].plot(df.index, df['SMA_20'], label='SMA 20', linestyle='--')
axes[0].plot(df.index, df['BB_upper'], label='BB Upper', color='gray', alpha=0.5)
axes[0].plot(df.index, df['BB_lower'], label='BB Lower', color='gray', alpha=0.5)
axes[0].fill_between(df.index, df['BB_upper'], df['BB_lower'], alpha=0.1)
axes[0].legend()
axes[0].set_title('Price & Bollinger Bands')
# 子图2:MACD
axes[1].bar(df.index, df['MACD_bar'], label='MACD Bar', color='gray', alpha=0.5)
axes[1].plot(df.index, df['DIF'], label='DIF', color='blue')
axes[1].plot(df.index, df['DEA'], label='DEA', color='red')
axes[1].axhline(0, color='black', linewidth=0.5)
axes[1].legend()
axes[1].set_title('MACD')
# 子图3:RSI + KDJ
axes[2].plot(df.index, df['RSI_14'], label='RSI 14', color='purple')
axes[2].plot(df.index, df['K'], label='K', color='blue')
axes[2].plot(df.index, df['D'], label='D', color='red')
axes[2].plot(df.index, df['J'], label='J', color='green')
axes[2].axhline(70, color='gray', linestyle='--', alpha=0.5)
axes[2].axhline(30, color='gray', linestyle='--', alpha=0.5)
axes[2].legend()
axes[2].set_title('RSI & KDJ')
plt.tight_layout()
plt.show()
4.9 知识体系总览
下面这张图,是我自己整理的技术指标知识体系。你可以把它当作「地图」,随时回来对照。
这张图把指标分成了两大类:左侧是趋势类(SMA、EMA、布林带、MACD),右侧是震荡/动量类(RSI、KDJ、ATR)。底部是它们的实战应用场景。
我个人建议:不要试图同时用所有指标。选2-3个互补的就行。比如我常用的组合是:MACD(看趋势方向)+ RSI(找超买超卖)+ ATR(设止损)。三个指标各司其职,互不干扰。
最后说一句:指标是死的,市场是活的。同样的指标,在趋势行情和震荡行情中表现天差地别。所以,先判断市场状态,再选指标——这才是量化交易的核心思维。