第三章 技术指标基础:移动平均线(SMA/EMA)、布林带、RSI、MACD的原理与计算

各位同学,欢迎来到《交易信号生成算法精讲》的第三讲。

今天咱们聊聊技术指标。说实话,刚入行那会儿,我对着满屏的指标也发懵——均线、布林带、RSI、MACD,每个都号称能预测未来。但做了几年量化后,我的看法变了:指标不是圣杯,它们是工具。就像你不会拿锤子去拧螺丝,对吧?

这一章,我会把四个最基础的指标讲透。不仅讲公式,还会聊聊我踩过的坑。

3.1 移动平均线:最简单的趋势跟踪器

移动平均线,说白了就是过去N天收盘价的平均值。它平滑了价格波动,让你看清趋势方向。

3.1.1 简单移动平均线(SMA)

SMA的计算极其简单:

SMA = (P₁ + P₂ + ... + Pₙ) / N

其中P是收盘价,N是周期数。

举个例子,5日SMA就是最近5天收盘价的平均值。每天滑动窗口,去掉最旧的一天,加入最新的一天。

代码实现:

import numpy as np

def sma(prices, window=20):
    """计算简单移动平均线"""
    return np.convolve(prices, np.ones(window)/window, mode='valid')
我的经验:SMA对异常值敏感。有一次我在处理某只小盘股数据时,突然一个涨停板让SMA跳得老高,信号全乱了。后来我改用EMA,情况好多了。

3.1.2 指数移动平均线(EMA)

EMA给近期数据更高的权重。公式稍微复杂点:

EMA_today = (Price_today × α) + (EMA_yesterday × (1 - α))

其中α = 2 / (N + 1),N是周期数。

你看,EMA是递归的——今天的值依赖昨天的值。这意味着它反应更快,但计算时需要注意初始值。

代码实现:

def ema(prices, window=20):
    """计算指数移动平均线"""
    alpha = 2 / (window + 1)
    result = [prices[0]]  # 初始值用第一个价格
    for price in prices[1:]:
        result.append(alpha * price + (1 - alpha) * result[-1])
    return np.array(result)
注意:EMA的初始值设置会影响前几个数据点。我习惯用SMA作为初始值,或者直接丢弃前N个数据点。

3.2 布林带:波动率的可视化

布林带由三条线组成:中轨(SMA)、上轨和下轨。上轨 = 中轨 + K×标准差,下轨 = 中轨 - K×标准差。K通常取2。

布林带的核心理念:价格大概率在上下轨之间波动。当价格触及上轨,可能超买;触及下轨,可能超卖。

计算步骤:

  1. 计算中轨:20日SMA
  2. 计算标准差:过去20日收盘价的标准差
  3. 上轨 = 中轨 + 2×标准差
  4. 下轨 = 中轨 - 2×标准差

代码实现:

def bollinger_bands(prices, window=20, num_std=2):
    """计算布林带"""
    sma_values = sma(prices, window)
    std_values = np.array([np.std(prices[i:i+window]) 
                          for i in range(len(prices)-window+1)])
    upper = sma_values + num_std * std_values
    lower = sma_values - num_std * std_values
    return sma_values, upper, lower

避坑指南:我曾经在震荡行情中过度依赖布林带。价格反复触碰上下轨,我频繁开仓,结果手续费都亏掉了。记住:布林带在趋势行情中效果更好。

3.3 RSI:动量的量化指标

RSI(相对强弱指数)衡量价格变动的速度和幅度。取值范围0-100,通常超过70视为超买,低于30视为超卖。

计算公式:

RSI = 100 - (100 / (1 + RS))
RS = 平均上涨幅度 / 平均下跌幅度

平均上涨幅度 = 过去N天上涨日的涨幅平均值(下跌日取0)
平均下跌幅度 = 过去N天下跌日的跌幅平均值(上涨日取0)

代码实现:

def rsi(prices, window=14):
    """计算RSI"""
    deltas = np.diff(prices)
    gains = np.where(deltas > 0, deltas, 0)
    losses = np.where(deltas < 0, -deltas, 0)
    
    avg_gain = np.mean(gains[:window])
    avg_loss = np.mean(losses[:window])
    
    rsi_values = [100 - (100 / (1 + avg_gain/avg_loss)) if avg_loss != 0 else 100]
    
    for i in range(window, len(deltas)):
        avg_gain = (avg_gain * (window-1) + gains[i]) / window
        avg_loss = (avg_loss * (window-1) + losses[i]) / window
        rs = avg_gain / avg_loss if avg_loss != 0 else float('inf')
        rsi_values.append(100 - (100 / (1 + rs)))
    
    return np.array(rsi_values)

我的习惯:RSI的默认周期是14。但不同品种的最佳参数不同。比特币我常用7日RSI,A股我喜欢21日。你想想看,参数调优是门手艺活。

3.4 MACD:趋势与动量的结合

MACD由三部分组成:快线(DIF)、慢线(DEA)、柱状图(MACD柱)。

计算步骤:

  1. 计算快线:12日EMA - 26日EMA
  2. 计算慢线:快线的9日EMA
  3. 柱状图 = 快线 - 慢线

当快线上穿慢线,是买入信号(金叉);下穿是卖出信号(死叉)。柱状图由负转正,也是买入信号。

代码实现:

def macd(prices, fast=12, slow=26, signal=9):
    """计算MACD"""
    ema_fast = ema(prices, fast)
    ema_slow = ema(prices, slow)
    
    # 对齐长度
    min_len = min(len(ema_fast), len(ema_slow))
    dif = ema_fast[-min_len:] - ema_slow[-min_len:]
    
    dea = ema(dif, signal)
    macd_hist = dif - dea
    
    return dif, dea, macd_hist

我曾经犯过的错:MACD在震荡市中频繁产生假信号。金叉后马上死叉,死叉后又金叉。后来我加了趋势过滤——只在均线多头排列时用金叉信号,效果好了很多。

3.5 指标组合使用策略

单个指标容易出错。我建议组合使用:

组合方式 适用场景 信号确认
SMA + RSI 趋势跟踪 价格在SMA上方 + RSI超卖
布林带 + MACD 震荡突破 价格突破布林带 + MACD金叉
EMA + RSI + MACD 多指标共振 三个指标同时发出同向信号

嗯,这里要注意:指标越多,信号越少,但胜率可能更高。我一般用2-3个指标就够了,再多就是过度拟合。

3.6 实战中的注意事项

  • 参数优化:不要迷信默认参数。不同品种、不同时间周期,最佳参数不同。
  • 滞后性:所有指标都是基于历史数据计算的,天然滞后。你想想看,等指标给出信号,行情可能已经走了一半。
  • 假信号:没有指标能100%准确。我见过有人用RSI在单边行情中连续止损5次。
  • 数据质量:前复权、后复权、除权除息,不同处理方式会影响指标计算。我习惯用后复权数据。

总结一下:技术指标是工具,不是魔法。理解它们的原理和局限,比盲目使用更重要。下一章,我们会把这些指标组合成完整的交易信号生成系统。

好了,今天就到这里。记住:纸上得来终觉浅,绝知此事要躬行。打开你的Python环境,把代码跑一遍,你会理解得更深。