🏠
AI量化交易 · 第13章
目录
Chapter 13

技术指标——MACD、RSI、布林带是怎么算出来的

知其然,更要知其所以然。每个指标背后,都有一套数学逻辑在等待被理解

🎯 本章学习目标

技术指标是量化策略的"砖块"——绝大多数策略都是由一个或多个指标组合而成的。但如果你只会调用`talib.MACD()`而不理解它在算什么,你就无法灵活地修改、优化和创新。读完这一章,你将能够:

💬 一个真实的故事

小刘是个技术分析爱好者,每天看盘第一件事就是看MACD和KDJ。他坚信"金叉买入、死叉卖出"是稳赚不赔的真理。2023年初,他看到某只股票的MACD在低位形成了金叉,兴奋地把全部积蓄投了进去——结果这只股票又跌了30%,金叉变成了"假金叉"。

小刘崩溃了,他去问一个做量化研究的朋友:"MACD不是最靠谱的指标吗?为什么我会亏?"朋友问他:"你知道MACD的DIF线是怎么算出来的吗?"小刘摇头。朋友又问:"你知道为什么在震荡市里MACD的假信号特别多吗?"小刘又摇头。

朋友叹了口气说:"你就像一个司机,只知道踩油门和刹车,但完全不懂发动机是怎么工作的。这样开车,不出事故才是奇迹。"

这个故事告诉我们:技术指标不是魔法咒语,它们只是价格数据的数学变换。理解它们的计算原理,才能知道什么时候该信、什么时候不该信。

核心洞察:所有技术指标都是对价格数据的"二次加工"。MACD看的是趋势动能,RSI看的是涨跌力度,布林带看的是波动范围。它们各有盲区,组合使用才能相互验证。

⚡ 核心知识:三大指标的数学本质

1. MACD:趋势动能的"雷达"

MACD(Moving Average Convergence Divergence,指数平滑异同移动平均线)是最著名的趋势指标之一。它的核心思想是:用两条不同周期的指数移动平均线的差值,来衡量趋势的动能和方向

MACD由三个部分组成:

MACD计算流程:

flowchart TD A[收盘价] --> B[计算12日EMA] A --> C[计算26日EMA] B --> D[DIF = EMA12 - EMA26] D --> E[计算DEA = DIF的9日EMA] D --> F[柱状图 = DIF - DEA] E --> G[金叉/死叉判断] F --> G style A fill:#f97316,stroke:#fff,color:#fff style G fill:#06b6d4,stroke:#fff,color:#fff

为什么MACD在震荡市会失灵?因为震荡市的价格围绕均值上下波动,没有明确的趋势方向。此时DIF和DEA会频繁交叉,产生大量假信号。MACD是趋势指标,只有在趋势明确的市场中才有效

2. RSI:涨跌力度的"温度计"

RSI(Relative Strength Index,相对强弱指数)由威尔斯·威尔德(Welles Wilder)于1978年提出。它的核心思想是:用一段时间内上涨幅度和下跌幅度的比值,来衡量市场的超买超卖状态

RSI的计算步骤如下:

  1. 计算每日价格变动:今日收盘价 - 昨日收盘价
  2. 分离涨跌:上涨日的变动记为Gain,下跌日的变动记为Loss(取绝对值)
  3. 计算平均涨幅和平均跌幅:通常用14日简单移动平均或指数移动平均
  4. 计算RS:RS = 平均涨幅 / 平均跌幅
  5. 计算RSI:RSI = 100 - (100 / (1 + RS))

RSI的取值范围是0到100。传统上:

关键认知:RSI的超买超卖信号在震荡市中比较有效,在强趋势市中经常"失效"。这不是指标的问题,而是使用者没有理解指标的适用场景。

3. 布林带:波动范围的"轨道"

布林带(Bollinger Bands)由约翰·布林格(John Bollinger)于1980年代发明。它的核心思想是:价格的波动范围可以用均值加减若干个标准差来描述

布林带由三条线组成:

布林带的核心交易逻辑:

  1. "触轨反弹":价格触及上轨后回落,或触及下轨后反弹。这适用于震荡市。
  2. "轨道突破":价格突破上轨并持续运行,说明趋势很强;突破下轨并持续运行,说明下跌趋势很强。
  3. "收口与开口":布林带收口(上下轨靠近)意味着波动率降低,可能酝酿大行情;开口意味着波动率增大,趋势可能加速。

布林带的本质:它不是预测工具,而是"波动率可视化工具"。它告诉你当前价格相对于历史波动处于什么位置,但不能告诉你未来会怎么走。

4. 指标使用的重要原则

掌握了计算原理后,你还需要知道如何正确使用这些指标,避免落入常见陷阱。

原则说明
指标不是水晶球所有指标都是基于历史价格的计算,不能预测未来。它们只是提供一种观察角度。
没有万能指标每个指标都有适用场景和盲区。MACD适合趋势市,RSI适合震荡市,布林带适合波动率分析。
参数不是固定的默认参数(如MACD的12/26/9)是针对美股设计的。A股可能需要调整参数以适应不同的波动特征。
多指标相互验证单一指标的信号可靠性有限。建议至少用2-3个不同类型的指标进行交叉验证。
结合量价分析指标信号配合成交量放大/萎缩,可以大大提高判断的准确性。

🤖 与AI一起练习

练习1:让AI从零写MACD

Prompt模板:

请用纯Python(不调用talib或pandas的EMA函数)
实现MACD指标的计算,要求:
1. 手动实现EMA的计算公式
2. 输入:收盘价列表(list或numpy数组)
3. 输出:DIF、DEA、MACD柱状图三个序列
4. 默认参数:fast=12, slow=26, signal=9
5. 给出计算过程的详细注释
6. 用一组示例数据验证结果正确性

练习2:让AI解释参数影响

Prompt模板:

请解释MACD的三个参数(fast, slow, signal)
分别控制什么,以及:
1. 如果把fast从12改为5,指标会变得怎样?
2. 如果把slow从26改为60,指标会变得怎样?
3. 这些参数调整后,适用于什么类型的市场?
请用具体的数值例子说明。

练习3:让AI设计指标组合策略

Prompt模板:

我想设计一个使用MACD + RSI + 布林带的组合策略。
要求:
1. MACD提供趋势方向(只做多头趋势)
2. RSI提供入场时机(不超买时入场)
3. 布林带提供止损位置(跌破下轨止损)

请给出完整的策略逻辑、参数建议和Python代码框架。
并指出这个策略在哪些市场环境下可能失效。

🔧 动手实践:从零实现三大指标

下面这段代码展示了如何不依赖任何现成库,纯用Python和NumPy实现MACD、RSI和布林带。这是理解指标本质的最好方式。

PYTHON indicators_from_scratch.py
import numpy as np
import pandas as pd

# ==========================================
# 工具函数:手动计算EMA
# ==========================================

def ema_manual(data, period):
    """
    手动计算指数移动平均线(EMA)
    EMA_today = alpha * Price_today + (1-alpha) * EMA_yesterday
    alpha = 2 / (period + 1)
    """
    alpha = 2 / (period + 1)
    ema = np.zeros_like(data, dtype=float)
    ema[0] = data[0]  # 首日EMA用首日价格初始化
    
    for i in range(1, len(data)):
        ema[i] = alpha * data[i] + (1 - alpha) * ema[i-1]
    
    return ema

# ==========================================
# 指标1:MACD
# ==========================================

def macd_manual(close, fast=12, slow=26, signal=9):
    """
    手动计算MACD指标
    返回:DIF, DEA, Histogram
    """
    print(f"📈 计算MACD (fast={fast}, slow={slow}, signal={signal})...")
    
    # 1. 计算两条EMA
    ema_fast = ema_manual(close, fast)
    ema_slow = ema_manual(close, slow)
    
    # 2. DIF = 快线 - 慢线
    dif = ema_fast - ema_slow
    
    # 3. DEA = DIF的EMA(信号线)
    dea = ema_manual(dif, signal)
    
    # 4. MACD柱状图
    histogram = dif - dea
    
    print(f"   DIF最新值: {dif[-1]:.4f}")
    print(f"   DEA最新值: {dea[-1]:.4f}")
    print(f"   柱状图最新值: {histogram[-1]:.4f}")
    
    # 判断金叉死叉
    if histogram[-2] < 0 and histogram[-1] > 0:
        print("   ✅ 最新信号: 金叉(买入)")
    elif histogram[-2] > 0 and histogram[-1] < 0:
        print("   ❌ 最新信号: 死叉(卖出)")
    else:
        print("   ⏸ 最新信号: 无交叉")
    
    return dif, dea, histogram

# ==========================================
# 指标2:RSI
# ==========================================

def rsi_manual(close, period=14):
    """
    手动计算RSI指标
    RSI = 100 - (100 / (1 + RS))
    RS = 平均涨幅 / 平均跌幅
    """
    print(f"\n🌡 计算RSI (period={period})...")
    
    # 1. 计算每日价格变动
    delta = np.diff(close)
    
    # 2. 分离涨跌
    gains = np.where(delta > 0, delta, 0)
    losses = np.where(delta < 0, -delta, 0)
    
    # 3. 计算平均涨幅和平均跌幅(使用Wilder平滑法)
    avg_gains = np.zeros_like(gains, dtype=float)
    avg_losses = np.zeros_like(losses, dtype=float)
    
    # 第一个平均值用简单平均
    avg_gains[period-1] = np.mean(gains[:period])
    avg_losses[period-1] = np.mean(losses[:period])
    
    # 后续使用平滑公式
    for i in range(period, len(gains)):
        avg_gains[i] = (avg_gains[i-1] * (period - 1) + gains[i]) / period
        avg_losses[i] = (avg_losses[i-1] * (period - 1) + losses[i]) / period
    
    # 4. 计算RS和RSI
    rs = avg_gains / (avg_losses + 1e-10)  # 防止除零
    rsi = 100 - (100 / (1 + rs))
    
    # 前period-1天没有有效RSI值,用NaN填充
    full_rsi = np.full_like(close, np.nan, dtype=float)
    full_rsi[1:] = rsi  # delta比close少一个元素
    
    print(f"   RSI最新值: {full_rsi[-1]:.2f}")
    if full_rsi[-1] > 70:
        print("   🔴 状态: 超买区")
    elif full_rsi[-1] < 30:
        print("   🔵 状态: 超卖区")
    else:
        print("   ⚪ 状态: 中性区")
    
    return full_rsi

# ==========================================
# 指标3:布林带
# ==========================================

def bollinger_manual(close, period=20, std_dev=2):
    """
    手动计算布林带
    中轨 = SMA(close, period)
    上轨 = 中轨 + std_dev * 标准差
    下轨 = 中轨 - std_dev * 标准差
    """
    print(f"\n📌 计算布林带 (period={period}, std={std_dev})...")
    
    # 1. 计算中轨(SMA)
    middle = np.convolve(close, np.ones(period)/period, mode='valid')
    
    # 2. 计算标准差
    rolling_std = np.array([
        np.std(close[i:i+period]) 
        for i in range(len(close) - period + 1)
    ])
    
    # 3. 计算上下轨
    upper = middle + std_dev * rolling_std
    lower = middle - std_dev * rolling_std
    
    # 对齐长度(前面period-1天没有值)
    full_middle = np.full_like(close, np.nan, dtype=float)
    full_upper = np.full_like(close, np.nan, dtype=float)
    full_lower = np.full_like(close, np.nan, dtype=float)
    
    full_middle[period-1:] = middle
    full_upper[period-1:] = upper
    full_lower[period-1:] = lower
    
    print(f"   中轨最新值: {full_middle[-1]:.4f}")
    print(f"   上轨最新值: {full_upper[-1]:.4f}")
    print(f"   下轨最新值: {full_lower[-1]:.4f}")
    
    # 判断价格位置
    if close[-1] > full_upper[-1]:
        print("   🔴 价格状态: 突破上轨")
    elif close[-1] < full_lower[-1]:
        print("   🔵 价格状态: 跌破下轨")
    else:
        print("   ⚪ 价格状态: 轨道内运行")
    
    return full_middle, full_upper, full_lower

# ==========================================
# 测试:用示例数据验证
# ==========================================

if __name__ == "__main__":
    print("=" * 55)
    print("🌟 技术指标从零实现演示")
    print("=" * 55)
    
    # 生成示例数据:模拟股价走势
    np.random.seed(42)
    returns = np.random.normal(0.001, 0.02, 100)  # 日均收益0.1%,波动2%
    prices = 100 * np.exp(np.cumsum(returns))  # 从100元起价
    
    print(f"\n示例数据: 100个交易日,起始价100元")
    print(f"最终价格: {prices[-1]:.2f}元")
    
    # 计算三大指标
    dif, dea, hist = macd_manual(prices)
    rsi = rsi_manual(prices)
    mid, up, low = bollinger_manual(prices)
    
    # 汇总输出
    print(f"\n{'=' * 55}")
    print("📋 综合信号汇总")
    print(f"{'=' * 55}")
    
    signals = []
    if hist[-2] < 0 and hist[-1] > 0:
        signals.append("MACD金叉")
    elif hist[-2] > 0 and hist[-1] < 0:
        signals.append("MACD死叉")
    
    if not np.isnan(rsi[-1]):
        if rsi[-1] < 30:
            signals.append("RSI超卖")
        elif rsi[-1] > 70:
            signals.append("RSI超买")
    
    if prices[-1] < low[-1]:
        signals.append("跌破布林带下轨")
    elif prices[-1] > up[-1]:
        signals.append("突破布林带上轨")
    
    if signals:
        print("当前信号: " + " | ".join(signals))
    else:
        print("当前信号: 无明显信号")
    
    print(f"\n{'=' * 55}")
    print("✅ 演示完成!")
    print("你现在理解了这些指标的本质,")
    print("可以自信地修改参数、组合使用、甚至创造新指标。")
    print(f"{'=' * 55}")

交互式指标参数探索器

调整下面的参数,看看MACD和RSI的数值会如何变化。这个模拟器使用简化的计算公式,帮助你直观理解参数的作用。

🤔 思考题

知识自测
1. MACD的DIF线本质上是哪两条线的差值?
  • A. 12日EMA和26日EMA
  • B. 5日SMA和20日SMA
  • C. 收盘价和开盘价
  • D. 最高价和最低价
答案是A。DIF = EMA(12) - EMA(26),这是MACD的核心定义。快线(12日EMA)反映短期趋势,慢线(26日EMA)反映长期趋势,两者的差值代表短期趋势相对于长期趋势的偏离程度。
2. RSI在强趋势市场中为什么会"失效"?
  • A. 因为RSI的计算公式有bug
  • B. 因为强趋势市场中没有涨跌
  • C. 因为强趋势市场中价格可以长期单向运行,RSI会持续处于极端区域
  • D. 因为RSI只能用于日线数据
答案是C。RSI的设计前提是"价格有涨有跌"。在强趋势市场中,价格可以连续多日上涨(或下跌),导致RSI长期维持在70以上(或30以下)。此时RSI的超买超卖信号就失去了意义,因为趋势还在继续。
3. 布林带收口(上下轨靠近)通常意味着什么?
  • A. 趋势即将结束
  • B. 波动率降低,可能酝酿大行情
  • C. 价格即将回归中轨
  • D. 市场流动性不足
答案是B。布林带收口意味着近期价格波动变小,标准差降低,上下轨收窄。这通常发生在盘整期,而盘整往往是趋势酝酿的阶段——一旦突破,就可能产生较大行情。布林格本人把这种现象称为"挤压"(Squeeze)。
深入思考
  1. 如果你发现MACD和RSI给出了相反的信号(比如MACD金叉但RSI超买),你应该相信哪个?为什么?
  2. 布林带的默认参数是20日和2倍标准差。如果你把它改成10日和1.5倍标准差,这个"更紧"的布林带会产生更多还是更少的交易信号?这些信号的可靠性会如何变化?
  3. 为什么很多专业量化策略不会直接使用"MACD金叉买入"这种简单规则,而是会把MACD作为一个过滤条件,配合其他因子使用?

🚀 下一章预告

技术指标已经掌握,但单一指标的力量是有限的。下一章《自定义指标——让AI帮你创造新武器》,我们将打破传统指标的束缚。

你将学会:如何把多个基础指标组合成复合指标;如何用统计学方法(如Z-Score、百分位数)创造全新的信号;以及如何让AI基于你的交易理念生成独特的指标——这是从"使用工具"到"创造工具"的跃迁。

准备好打造属于你自己的技术指标了吗?

觉得有收获?分享给同样对量化交易感兴趣的朋友

上一章
第12章:历史价格数据——最简单的起点
理解价格数据的结构和类型,能够获取、清洗、存储历史价格数据。
下一章
第14章:自定义指标——让AI帮你创造新武器
学会组合基础指标创造复合指标,用AI生成独特的交易信号。

如果这本书对你有所启发,欢迎支持我继续创作

赞赏二维码