4. 技术指标计算与实现:均线系统(MA/EMA)、MACD指标、RSI指标、布林带、KDJ指标

技术指标这东西,说白了就是给价格数据「化妆」。裸K线看起来太素,加上几条线、几个柱状图,趋势和买卖点就清晰多了。我个人习惯把指标分成两类:趋势跟踪型震荡型。均线、MACD属于前者,RSI、KDJ属于后者。布林带嘛,它两边都沾点。

今天咱们就把这五个最常用的指标,从公式到代码,一个一个啃下来。

4.1 均线系统:MA 与 EMA

均线是最基础的指标,没有之一。它就是把过去N天的收盘价做个平均,然后连成一条线。

4.1.1 简单移动平均线(MA)

公式很简单:

MA(N) = (Close₁ + Close₂ + ... + Closeₙ) / N

代码实现也不复杂:

def calculate_ma(close_prices, window=20):
    """
    计算简单移动平均线
    :param close_prices: 收盘价序列,list或numpy数组
    :param window: 窗口大小,默认20
    :return: MA序列,前window-1个值为NaN
    """
    import numpy as np
    prices = np.array(close_prices, dtype=float)
    ma = np.full_like(prices, np.nan)
    
    for i in range(window - 1, len(prices)):
        ma[i] = np.mean(prices[i - window + 1 : i + 1])
    
    return ma

小提示:实际项目中别用循环算MA,太慢了。直接用 pandas.Series.rolling(window).mean(),一行搞定。我在处理百万级K线数据时,循环版本跑了3秒,pandas版本0.03秒——差了100倍。

4.1.2 指数移动平均线(EMA)

EMA和MA最大的区别是:MA对每一天的数据一视同仁,EMA则更看重最近的数据。你想想看,昨天的价格和30天前的价格,哪个对今天更有参考价值?显然是昨天。

EMA的公式稍微绕一点:

EMA(t) = α × Close(t) + (1 - α) × EMA(t-1)

其中 α = 2 / (N + 1)

注意,第一个EMA值通常用第一个收盘价或者前N天的MA来初始化。

def calculate_ema(close_prices, window=20):
    """
    计算指数移动平均线
    """
    import numpy as np
    prices = np.array(close_prices, dtype=float)
    ema = np.full_like(prices, np.nan)
    
    alpha = 2 / (window + 1)
    
    # 初始化:用第一个价格作为EMA起点
    ema[0] = prices[0]
    
    for i in range(1, len(prices)):
        ema[i] = alpha * prices[i] + (1 - alpha) * ema[i - 1]
    
    return ema

注意:EMA的初始值选择会影响前几十个数据点的结果。我曾经在回测中发现,用第一个价格初始化会导致前20个EMA值偏低,后来改用前N天的MA做初始化,效果好了很多。如果你做高频交易,这个细节尤其重要。

4.2 MACD指标

MACD是均线系统的升级版。它由三部分组成:DIF线(快线)、DEA线(慢线)、柱状图。

计算步骤:

  1. 计算快线:EMA(12) - EMA(26)
  2. 计算慢线:对DIF再做一次EMA(9)
  3. 柱状图:DIF - DEA

说白了,MACD就是在看「短期均线和长期均线的差距」的变化趋势。

def calculate_macd(close_prices, fast=12, slow=26, signal=9):
    """
    计算MACD指标
    返回: (dif, dea, histogram)
    """
    ema_fast = calculate_ema(close_prices, fast)
    ema_slow = calculate_ema(close_prices, slow)
    
    dif = ema_fast - ema_slow
    dea = calculate_ema(dif, signal)
    histogram = 2 * (dif - dea)  # 乘以2是为了让柱状图更明显
    
    return dif, dea, histogram

实战经验:MACD的金叉死叉信号在震荡行情中特别容易失效。我一般会加一个过滤条件——只有当DIF线突破零轴时,才认为金叉有效。这样能过滤掉大概30%的假信号。

4.3 RSI指标

RSI衡量的是「上涨的力度」和「下跌的力度」谁更强。取值范围0-100,一般认为超过70是超买,低于30是超卖。

公式:

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

这里的「平均」通常用SMA或者 Wilder's Smoothing 来计算。

def calculate_rsi(close_prices, window=14):
    """
    计算RSI指标
    """
    import numpy as np
    prices = np.array(close_prices, dtype=float)
    deltas = np.diff(prices)
    
    gains = np.where(deltas > 0, deltas, 0)
    losses = np.where(deltas < 0, -deltas, 0)
    
    avg_gain = np.full_like(prices, np.nan)
    avg_loss = np.full_like(prices, np.nan)
    
    # 初始平均值用SMA
    avg_gain[window] = np.mean(gains[:window])
    avg_loss[window] = np.mean(losses[:window])
    
    # 后续用Wilder's Smoothing
    for i in range(window + 1, len(prices)):
        avg_gain[i] = (avg_gain[i - 1] * (window - 1) + gains[i - 1]) / window
        avg_loss[i] = (avg_loss[i - 1] * (window - 1) + losses[i - 1]) / window
    
    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    
    return rsi

避坑指南:我曾经在回测中发现RSI在单边行情中会长时间停留在超买/超卖区域,这时候反向做单会亏得很惨。后来我加了一条规则:RSI超买/超卖信号必须配合价格趋势的背离才执行交易。效果立竿见影。

4.4 布林带

布林带由三条线组成:中轨(MA)、上轨(中轨 + k倍标准差)、下轨(中轨 - k倍标准差)。k通常取2。

它的核心思想是:价格大概率在上下轨之间波动,突破上下轨意味着趋势可能发生变化。

def calculate_bollinger_bands(close_prices, window=20, k=2):
    """
    计算布林带
    返回: (upper, middle, lower)
    """
    import numpy as np
    prices = np.array(close_prices, dtype=float)
    
    middle = calculate_ma(prices, window)
    std = np.full_like(prices, np.nan)
    
    for i in range(window - 1, len(prices)):
        std[i] = np.std(prices[i - window + 1 : i + 1])
    
    upper = middle + k * std
    lower = middle - k * std
    
    return upper, middle, lower

个人习惯:我一般把布林带和RSI配合使用。当价格突破上轨且RSI超过80时,我倾向于做空;当价格跌破下轨且RSI低于20时,我倾向于做多。两个指标互相验证,胜率会高很多。

4.5 KDJ指标

KDJ也叫随机指标,它比RSI更灵敏。它计算的是当前收盘价在过去N天价格区间中的位置。

计算步骤:

  1. 计算RSV(未成熟随机值):(Close - Lowest) / (Highest - Lowest) × 100
  2. K值 = 2/3 × 前一日K值 + 1/3 × RSV
  3. D值 = 2/3 × 前一日D值 + 1/3 × K值
  4. J值 = 3 × K值 - 2 × D值
def calculate_kdj(close_prices, high_prices, low_prices, window=9):
    """
    计算KDJ指标
    返回: (k, d, j)
    """
    import numpy as np
    close = np.array(close_prices, dtype=float)
    high = np.array(high_prices, dtype=float)
    low = np.array(low_prices, dtype=float)
    
    rsv = np.full_like(close, np.nan)
    k = np.full_like(close, np.nan)
    d = np.full_like(close, np.nan)
    j = np.full_like(close, np.nan)
    
    for i in range(window - 1, len(close)):
        highest = np.max(high[i - window + 1 : i + 1])
        lowest = np.min(low[i - window + 1 : i + 1])
        
        if highest != lowest:
            rsv[i] = (close[i] - lowest) / (highest - lowest) * 100
        else:
            rsv[i] = 50  # 价格没波动时取中间值
    
    # 初始化K、D
    k[window - 1] = 50
    d[window - 1] = 50
    
    for i in range(window, len(close)):
        k[i] = 2/3 * k[i - 1] + 1/3 * rsv[i]
        d[i] = 2/3 * d[i - 1] + 1/3 * k[i]
    
    j = 3 * k - 2 * d
    
    return k, d, j

注意:KDJ的J值经常超出0-100的范围,这是正常的。J值超过100或低于0时,往往意味着极端行情。我在实盘中遇到过J值冲到120的情况,那波行情确实很猛,但随后就是剧烈回调。

4.6 指标组合使用建议

单个指标容易出错,组合使用才是王道。我常用的组合方式:

交易场景 指标组合 信号确认方式
趋势跟踪 MA + MACD MA多头排列 + MACD零轴上方金叉
震荡交易 布林带 + RSI 价格触及布林带上下轨 + RSI超买超卖
短线交易 KDJ + EMA KDJ金叉 + 价格站上EMA20

嗯,这些指标的实现其实都不难。难的是怎么用、什么时候用。我建议你先拿历史数据跑一遍,看看每个指标在不同行情下的表现。慢慢你就会发现,指标只是工具,真正赚钱的是你对市场的理解。

技术指标知识体系 技术指标 趋势跟踪型 震荡型 综合型 MA均线 EMA均线 MACD RSI KDJ 布林带 组合使用策略 趋势跟踪型 + 震荡型 = 信号互相验证 例如:布林带(综合型)+ RSI(震荡型) 价格突破上轨 + RSI超买 → 做空信号 价格跌破下轨 + RSI超卖 → 做多信号

最后说一句:代码写完了,记得用真实数据测试一下。我刚开始写指标库的时候,有个EMA的初始值算错了,回测结果看起来特别漂亮——后来才发现是未来函数在作怪。嗯,这种事情,经历过一次就长记性了。