基于Python的策略开发与回测:均值回归 - 配对交易策略(附可视化代码)


1. 策略原理深度解析

核心逻辑
寻找两个具有长期均衡关系的资产(如可口可乐与百事可乐股票),当价差偏离历史均值时:

  • 做多低估资产 + 做空高估资产(价差扩大时)
  • 平仓回归(价差回归均值时)

数学基础

  1. 协整检验:通过ADF检验判断价差序列的平稳性(p值<0.05)
  2. 对冲比例:通过OLS回归确定资产配比 ( y = βx + ε )
  3. 交易信号:价差Z-score突破±1.5标准差触发交易

2. 完整策略实现代码

pip install matplotlib statsmodels pandas numpy

步骤1:生成协整资产模拟数据

import numpy as np  
import pandas as pd  
import statsmodels.api as sm  
from statsmodels.tsa.stattools import adfuller  

# 生成具有协整关系的价格序列  
np.random.seed(42)  
days = 1000  

# 基础资产X  
x = np.cumprod(1 + np.random.normal(0.0005, 0.02, days)) * 100  

# 协整资产Y = 0.8X + 噪声  
spread = np.random.normal(0, 2, days)  # 平稳价差  
y = 0.8 * x + spread  

data = pd.DataFrame({'Asset_X': x, 'Asset_Y': y},  
                   index=pd.date_range('2020-01-01', periods=days))  

步骤2:协整检验与对冲比例计算

# 协整检验  
def cointegration_test(series1, series2):  
    model = sm.OLS(series1, sm.add_constant(series2)).fit()  
    residuals = model.resid  
    adf_result = adfuller(residuals)  
    return adf_result[1]  # 返回p值  

p_value = cointegration_test(data['Asset_X'], data['Asset_Y'])  
print(f'ADF检验p值: {p_value:.4f}')  # 应小于0.05  

# 计算对冲比例  
model = sm.OLS(data['Asset_X'], sm.add_constant(data['Asset_Y'])).fit()  
beta = model.params[1]  
print(f'对冲比例: 1单位Y对应{beta:.2f}单位X')  

输出结果:

ADF检验p值: 0.0000
对冲比例: 1单位Y对应1.24单位X

步骤3:价差分析与信号生成

# 计算价差序列  
data['Spread'] = data['Asset_X'] - beta * data['Asset_Y']  

# 计算Z-score  
lookback = 60  
data['Mean'] = data['Spread'].rolling(lookback).mean()  
data['Std'] = data['Spread'].rolling(lookback).std()  
data['Zscore'] = (data['Spread'] - data['Mean']) / data['Std']  

# 生成交易信号  
data['Signal'] = 0  
data.loc[data['Zscore'] > 1.5, 'Signal'] = -1  # 做空价差(卖X买Y)  
data.loc[data['Zscore'] < -1.5, 'Signal'] = 1   # 做多价差(买X卖Y)  
data.loc[abs(data['Zscore']) < 0.5, 'Signal'] = 0  # 平仓  

3. 可视化代码与解析

可视化1:资产价格与价差关系

plt.figure(figsize=(14, 9))  

# 左轴绘制资产价格  
ax1 = plt.subplot(211)  
ax1.plot(data['Asset_X'], label='Asset X', color='#1f77b4')  
ax1.plot(data['Asset_Y'], label='Asset Y', color='#ff7f0e')  
ax1.set_title('Cointegrated Assets Price Movement', pad=15)  
ax1.legend(loc='upper left')  
ax1.grid(alpha=0.3)  

# 右轴绘制价差  
ax2 = plt.subplot(212)  
ax2.plot(data['Spread'], label='Spread (X - βY)', color='#2ca02c')  
ax2.axhline(data['Spread'].mean(), color='#d62728', linestyle='--', label='Mean')  
ax2.fill_between(data.index,   
                data['Mean'] + 1.5*data['Std'],  
                data['Mean'] - 1.5*data['Std'],  
                color='gray', alpha=0.2, label='±1.5σ')  
ax2.set_title('Spread Series with Mean Reversion Bands', pad=15)  
ax2.legend()  
ax2.grid(alpha=0.3)  

plt.tight_layout()  
plt.show()  

在这里插入图片描述

图表说明

  • 上图显示两个协整资产的价格走势
  • 下图展示价差序列的均值回归特性,灰色区域为交易触发带

可视化2:Z-score信号触发点

plt.figure(figsize=(12, 5))  

# 绘制Z-score  
plt.plot(data['Zscore'], color='#9467bd', label='Z-score')  
plt.axhline(1.5, color='red', linestyle='--', alpha=0.7, label='Upper Threshold')  
plt.axhline(-1.5, color='green', linestyle='--', alpha=0.7, label='Lower Threshold')  

# 标注交易信号  
buy_signals = data[data['Signal'] == 1]  
sell_signals = data[data['Signal'] == -1]  
plt.scatter(buy_signals.index, buy_signals['Zscore'],  
           marker='^', color='lime', s=100, edgecolors='black', label='Long Spread')  
plt.scatter(sell_signals.index, sell_signals['Zscore'],  
           marker='v', color='red', s=100, edgecolors='black', label='Short Spread')  

# 图表装饰  
plt.title('Z-score Signal Triggers', pad=15)  
plt.ylabel('Z-score')  
plt.legend(loc='upper left', ncol=2)  
plt.grid(alpha=0.3)  
plt.tight_layout()  
plt.show()  

在这里插入图片描述

图表说明

  • 紫色曲线为标准化后的价差Z-score
  • 红/绿虚线为交易阈值线
  • 绿色▲标记做多价差信号,红色▼标记做空价差信号

4. 关键风险控制

  1. 协整断裂监测
    # 滚动窗口协整检验  
    rolling_pvalues = [cointegration_test(data['Asset_X'].iloc[i-120:i],   
                      data['Asset_Y'].iloc[i-120:i])   
                    for i in range(120, len(data))]  
    
  2. 动态头寸调整:根据波动率调整仓位大小
  3. 交易成本模型
    commission = 0.001  # 单边千分之一手续费  
    slippage = 0.002    # 滑点成本  
    
Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐