第四章 均值回归策略详解:RSI反转策略、布林带回归策略、配对交易策略、统计套利基础

均值回归,这个词听起来挺学术的。说白了,就是价格涨多了会跌,跌多了会涨。就像一根被拉长的弹簧,松手后总要弹回去。我做了这么多年CTA,发现很多新手容易犯一个错误——总觉得趋势会永远持续下去。嗯,其实市场里大部分时间都在震荡,均值回归策略恰恰就是吃这碗饭的。

核心思想:价格围绕某个均值上下波动,偏离越远,回归的概率越大。这不是玄学,是统计规律。

4.1 RSI反转策略:超买超卖的信号游戏

RSI(相对强弱指标)是我个人最早接触的反转指标之一。它的逻辑很简单:计算一段时间内上涨和下跌的幅度比值,然后归一化到0-100之间。通常,RSI超过70算超买,低于30算超卖。

但这里有个坑——我刚开始做的时候,直接拿70和30作为阈值,结果被来回打脸。为什么?因为强趋势行情里,RSI可以在80以上待很久,你去做空,那就是接飞刀。

我的经验:RSI反转策略更适合震荡市。判断震荡还是趋势,可以结合ADX指标。ADX低于25时,用RSI反转效果最好。

来看一个简单的RSI反转策略实现:

import pandas as pd
import numpy as np

def rsi_reversal_strategy(df, period=14, overbought=70, oversold=30):
    """
    RSI反转策略
    df: 包含'close'列的DataFrame
    """
    # 计算RSI
    delta = df['close'].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
    df['rsi'] = 100 - (100 / (1 + rs))
    
    # 生成信号
    df['signal'] = 0
    df.loc[df['rsi'] < oversold, 'signal'] = 1   # 超卖,买入
    df.loc[df['rsi'] > overbought, 'signal'] = -1 # 超买,卖出
    
    return df

你可能会问,为什么用14作为周期?这是 Wilder 当年提出的标准参数。但我建议你根据品种调整。比如股指期货波动大,我习惯用10;农产品波动小,用20更稳。

4.2 布林带回归策略:通道里的买卖点

布林带是我最喜欢的均值回归工具之一。它由三条线组成:中轨是移动平均线,上下轨分别是中轨加减两倍标准差。价格碰到上轨,说明偏离均值太远,大概率要回调;碰到下轨,同理。

我记得有一次做螺纹钢的夜盘,价格连续三根K线贴着上轨走。很多同事觉得要突破了,但我看到布林带开口并没有明显扩大,说明波动率没起来。结果呢?第四根K线直接砸回中轨。那次我赚了不少。

注意:布林带在波动率急剧变化时容易失效。比如突发消息导致跳空,价格直接突破上轨,这时候不要急着反向开仓。等一根K线确认,或者结合成交量判断。

布林带回归策略的代码实现:

def bollinger_reversal(df, window=20, num_std=2):
    """
    布林带回归策略
    """
    df['ma'] = df['close'].rolling(window).mean()
    df['std'] = df['close'].rolling(window).std()
    
    df['upper'] = df['ma'] + num_std * df['std']
    df['lower'] = df['ma'] - num_std * df['std']
    
    # 信号:价格触及上下轨后反向
    df['signal'] = 0
    df.loc[df['close'] < df['lower'], 'signal'] = 1
    df.loc[df['close'] > df['upper'], 'signal'] = -1
    
    # 过滤:只在价格回到中轨时平仓
    df['exit'] = 0
    df.loc[df['close'] > df['ma'], 'exit'] = 1  # 多头平仓
    df.loc[df['close'] < df['ma'], 'exit'] = -1 # 空头平仓
    
    return df

这里有个细节:我习惯用2倍标准差,但如果你做的是高频一点的周期,比如15分钟K线,1.5倍标准差可能更灵敏。参数没有标准答案,回测说了算。

4.3 配对交易策略:两个品种的价差游戏

配对交易是均值回归的进阶玩法。它不赌单个品种的方向,而是赌两个相关品种的价差会回归。比如螺纹钢和热卷,豆粕和菜粕,它们价格走势高度相关,但偶尔会走偏。

我曾经做过一个焦煤和焦炭的配对策略。这两个品种相关性高达0.9以上,但焦炭波动更大。当价差扩大到两个标准差时,我做空焦炭、做多焦煤,等价差回归后平仓。那一年这个策略的夏普比率做到了2.3。

配对交易的步骤:

  1. 找配对:计算相关系数,选相关性高的品种(一般要求0.8以上)
  2. 算价差:用回归或协整检验确定价差公式
  3. 设阈值:价差偏离到一定程度时开仓
  4. 等回归:价差回到均值时平仓

代码实现:

import statsmodels.api as sm

def pairs_trading(df1, df2, lookback=60, entry_z=2, exit_z=0.5):
    """
    配对交易策略
    df1, df2: 两个品种的价格序列
    """
    # 计算价差
    spread = df1['close'] - df2['close']
    
    # 滚动计算均值和标准差
    spread_mean = spread.rolling(lookback).mean()
    spread_std = spread.rolling(lookback).std()
    
    # 计算Z-score
    z_score = (spread - spread_mean) / spread_std
    
    # 信号
    df1['signal'] = 0
    df2['signal'] = 0
    
    # 价差过大,做空价差(卖1买2)
    df1.loc[z_score > entry_z, 'signal'] = -1
    df2.loc[z_score > entry_z, 'signal'] = 1
    
    # 价差过小,做多价差(买1卖2)
    df1.loc[z_score < -entry_z, 'signal'] = 1
    df2.loc[z_score < -entry_z, 'signal'] = -1
    
    # 价差回归,平仓
    df1.loc[abs(z_score) < exit_z, 'signal'] = 0
    df2.loc[abs(z_score) < exit_z, 'signal'] = 0
    
    return df1, df2

避坑指南:我曾经用相关系数选配对,结果发现两个品种虽然相关,但价差并不平稳。后来改用协整检验(Engle-Granger方法),效果好了很多。记住:相关不等于协整。

4.4 统计套利基础:从配对到多品种

统计套利是配对交易的升级版。它不局限于两个品种,而是用一组品种构建一个投资组合,使得组合的净值序列是平稳的。当净值偏离均值时,我们反向操作。

说白了,统计套利就是找一堆品种,让它们互相抵消掉趋势部分,只留下震荡部分。这个震荡部分,就是我们的利润来源。

基础步骤:

  1. 选品种池:通常选同板块的品种,比如黑色系、化工系
  2. 建模型:用主成分分析(PCA)或多元回归,找到组合权重
  3. 算残差:残差就是组合的净值序列,检验其平稳性
  4. 交易:残差偏离时开仓,回归时平仓

来看一个简单的PCA统计套利框架:

from sklearn.decomposition import PCA

def statistical_arbitrage(price_df, n_components=1, lookback=60):
    """
    基于PCA的统计套利
    price_df: 多品种价格DataFrame,每列一个品种
    """
    # 标准化
    returns = price_df.pct_change().dropna()
    scaled_returns = (returns - returns.mean()) / returns.std()
    
    # PCA分解
    pca = PCA(n_components=n_components)
    pca.fit(scaled_returns)
    
    # 主成分得分(市场共同因子)
    scores = pca.transform(scaled_returns)
    
    # 残差 = 原始收益 - 主成分解释的部分
    residuals = scaled_returns - pca.inverse_transform(scores)
    
    # 组合净值 = 残差的累积和
    portfolio_nav = residuals.cumsum()
    
    # 交易信号
    z_score = (portfolio_nav - portfolio_nav.rolling(lookback).mean()) / \
              portfolio_nav.rolling(lookback).std()
    
    return z_score

核心要点:统计套利不是无风险套利。它赌的是统计规律会持续,但极端行情下(比如2020年3月),所有相关性都会崩溃。所以仓位管理比策略本身更重要。

知识体系总览

下面这张图是我自己整理的均值回归策略知识结构,你可以把它当作学习地图:

均值回归策略知识体系 均值回归策略 RSI反转策略 布林带回归策略 配对交易策略 统计套利基础 超买超卖阈值设定 结合ADX过滤趋势 标准差倍数选择 波动率变化应对 相关性 vs 协整性 Z-score阈值设定 PCA主成分分解 残差平稳性检验 共同核心:价格偏离均值 → 反向开仓 → 回归平仓 ⚠ 注意事项 趋势行情中均值回归策略容易亏损,需结合趋势过滤 参数优化避免过拟合,建议使用滚动窗口验证

这张图把四种策略的关系理清楚了。你会发现,从RSI到统计套利,其实是一个从简单到复杂、从单品种到多品种的演进过程。我个人建议初学者先从RSI和布林带入手,等熟悉了均值回归的节奏,再尝试配对交易和统计套利。

最后说一句:均值回归策略不是万能药。它最怕的就是单边趋势行情。所以,我通常会把均值回归策略和趋势策略组合在一起,用权重分配来平滑收益曲线。具体怎么组合,后面的章节会详细讲。