第四节:技术指标计算——移动平均线、布林带、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 布林带(Bollinger) MACD 震荡/动量类指标 RSI KDJ ATR(波动率) 实战应用 趋势跟踪 | 震荡策略 | 止损设置 | 仓位管理

这张图把指标分成了两大类:左侧是趋势类(SMA、EMA、布林带、MACD),右侧是震荡/动量类(RSI、KDJ、ATR)。底部是它们的实战应用场景。

我个人建议:不要试图同时用所有指标。选2-3个互补的就行。比如我常用的组合是:MACD(看趋势方向)+ RSI(找超买超卖)+ ATR(设止损)。三个指标各司其职,互不干扰。

最后说一句:指标是死的,市场是活的。同样的指标,在趋势行情和震荡行情中表现天差地别。所以,先判断市场状态,再选指标——这才是量化交易的核心思维。

专注资料整理