1. 基于协整的配对交易入门

任务目标

选取两只具有长期均衡关系的股票,通过协整检验确认配对有效性,构建基于价差回归的配对交易策略,并在历史数据上完成回测,输出策略累计收益曲线与关键绩效指标。

环境与依赖

本案例使用 Python 3.9+,所需库:numpy、pandas、statsmodels(协整检验)、yfinance(数据获取)、matplotlib(可视化)。若无法访问 yfinance,可替换为本地 CSV 数据,代码中数据加载部分需相应调整。

实现思路与关键代码

核心流程:① 获取两只股票近两年的日收盘价;② 用 Engle-Granger 两步法检验协整关系(对价格序列做 OLS 回归,对残差做 ADF 检验);③ 若协整成立,计算价差 spread = y - βx,以其均值为中枢、标准差为阈值构建交易信号;④ 回测时,价差上穿 +1 倍标准差时做空价差(卖 y 买 x),下穿 -1 倍标准差时做多价差(买 y 卖 x),回归均值时平仓;⑤ 计算策略净值与基准(等权持有)对比。

import numpy as np
import pandas as pd
import yfinance as yf
import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller
import matplotlib.pyplot as plt

# ---------- 1. 数据获取 ----------
tickers = ['XOM', 'CVX']  # 埃克森美孚 vs 雪佛龙
data = yf.download(tickers, start='2022-01-01', end='2024-01-01')['Adj Close']
data = data.dropna()
x = data[tickers[0]]  # 解释变量
y = data[tickers[1]]  # 被解释变量

# ---------- 2. 协整检验 (Engle-Granger) ----------
X = sm.add_constant(x)
ols = sm.OLS(y, X).fit()
beta = ols.params[tickers[0]]
alpha = ols.params['const']
spread = y - beta * x - alpha  # 价差序列

adf_stat, p_value, _, _, _, _ = adfuller(spread)
print(f'ADF统计量: {adf_stat:.4f}, p值: {p_value:.4f}')
if p_value >= 0.05:
    raise SystemExit('两只股票不存在协整关系,请更换标的')

# ---------- 3. 交易信号生成 ----------
spread_mean = spread.mean()
spread_std = spread.std()
entry_threshold = 1.0   # 开仓阈值(倍标准差)
exit_threshold = 0.0    # 平仓阈值(回归均值)

signal = pd.Series(0, index=spread.index)
position = 0  # 1=多价差, -1=空价差
for i in range(1, len(spread)):
    z = (spread.iloc[i] - spread_mean) / spread_std
    if position == 0:
        if z < -entry_threshold:
            signal.iloc[i] = 1   # 做多价差
            position = 1
        elif z > entry_threshold:
            signal.iloc[i] = -1  # 做空价差
            position = -1
    elif position == 1 and z >= exit_threshold:
        signal.iloc[i] = 0       # 平多
        position = 0
    elif position == -1 and z <= exit_threshold:
        signal.iloc[i] = 0       # 平空
        position = 0

# ---------- 4. 回测 ----------
# 价差收益 = Δspread(做多时),做空时取负
spread_ret = spread.diff().shift(-1)  # 当日收盘开仓,次日收盘平仓的收益
trade_ret = signal * spread_ret
trade_ret = trade_ret.fillna(0)

# 策略净值(假设初始资金1,不考虑交易成本)
equity = (1 + trade_ret).cumprod()
# 基准:等权持有两只股票
benchmark = (data.pct_change().mean(axis=1) + 1).cumprod().fillna(1)

# ---------- 5. 绩效指标 ----------
total_ret = equity.iloc[-1] - 1
sharpe = trade_ret.mean() / trade_ret.std() * np.sqrt(252) if trade_ret.std() != 0 else 0
print(f'策略总收益: {total_ret:.2%}, 夏普比率: {sharpe:.2f}')

# ---------- 6. 可视化 ----------
plt.figure(figsize=(12, 6))
plt.plot(equity.index, equity.values, label='配对交易策略')
plt.plot(benchmark.index, benchmark.values, label='等权持有基准')
plt.legend()
plt.title('配对交易策略回测净值')
plt.savefig('pairs_trading_result.png', dpi=150)
print('净值曲线已保存为 pairs_trading_result.png')

运行方式

将上述代码保存为 pairs_trading.py,在终端执行 python pairs_trading.py。首次运行需联网下载行情数据,若网络受限,可提前用 yf.download 下载并保存为 CSV 后修改数据读取部分。

验证方法与通过条件

  • 协整检验通过:控制台输出的 ADF 检验 p 值小于 0.05,说明价差序列平稳,两只股票存在长期均衡关系。
  • 策略有正收益:回测输出的总收益率大于 0,且净值曲线图(pairs_trading_result.png)中策略曲线在大部分时间位于基准上方或同步增长。
  • 信号逻辑自洽:价差偏离均值超过 1 倍标准差时触发反向交易,回归均值时平仓——可通过打印 signal 中非零值对应的日期与当日价差进行抽查验证。

常见问题

  • 数据长度不足:协整检验需要至少 1~2 年日线数据,若时间窗口过短,p 值可能不显著,可延长回测区间。
  • 未来函数:本代码使用全样本均值与标准差计算 z-score,实盘中需用滚动窗口。若需更严谨的回测,应将 spread_mean 和 spread_std 改为滚动计算(如 60 日窗口)。
  • 交易成本:当前回测未计入手续费与滑点,实盘应用时需在 trade_ret 中扣除双边成本。
配对交易策略流程与信号机制 ① 协整检验 XOM vs CVX 日收盘价 OLS回归 → 残差ADF检验 p < 0.05 才继续 ② 价差与信号 spread = y − βx − α z = (spread − μ) / σ |z| > 1 开仓,z=0 平仓 ③ 回测验证 信号×Δspread 累计净值 vs 基准 输出收益/夏普 价差均值回归与交易触发点 时间 价差 均值 μ +1σ (做空价差) −1σ (做多价差) S S B 平 价差走势 做空点 做多点 平仓点