第二章:价差分析工具——从数据到信号的完整链路

做跨期套利,核心就一句话:找到价差的规律,然后利用它赚钱

但价差不是凭空产生的。你得先把它算出来,然后分析它的脾气——均值多少、波动多大、有没有自相关性、是不是平稳的。这一套下来,你才能判断:这个价差能不能用来做策略?

我个人习惯把这套流程叫做「价差分析四步走」。今天咱们就一步步拆开来看。

2.1 价差序列的构建

说白了,价差就是两个合约价格之间的差值。但怎么算,有讲究。

最常见的构建方式:

  • 简单价差spread = P_near - P_far(近月减远月)
  • 对数价差spread = ln(P_near) - ln(P_far)(适合价格波动剧烈时)
  • 标准化价差spread = (P_near - P_far) / P_far(消除绝对价格影响)

我在项目中遇到过一个问题:用简单价差做螺纹钢的跨期套利,结果发现价差序列随着价格整体上涨而不断变大,策略频繁开仓。后来换成对数价差,这个问题就解决了。你想想看,对数价差天然消除了价格水平的影响,更适合做均值回归分析。

核心原则:价差构建方式取决于品种特性。股指期货用简单价差就够,商品期货建议用对数价差。

import numpy as np
import pandas as pd

# 构建价差序列
def build_spread(near_price, far_price, method='log'):
    if method == 'simple':
        spread = near_price - far_price
    elif method == 'log':
        spread = np.log(near_price) - np.log(far_price)
    elif method == 'normalized':
        spread = (near_price - far_price) / far_price
    else:
        raise ValueError("method must be 'simple', 'log', or 'normalized'")
    return spread

# 示例:螺纹钢近月与远月数据
near = pd.Series([3800, 3810, 3795, 3820, 3835])
far = pd.Series([3750, 3760, 3745, 3770, 3785])
spread_log = build_spread(near, far, method='log')
print(spread_log)

2.2 价差的统计特征

价差序列建好了,接下来就是「摸清它的底细」。我一般会看三个指标:均值、方差、自相关。

2.2.1 均值与方差

均值告诉你价差的中枢在哪里。方差告诉你价差波动的幅度有多大。

嗯,这里要注意:均值是会漂移的。我记得有一次做豆粕的跨期套利,用了过去半年的均值作为开仓依据,结果市场结构变了,均值从50块漂到了80块,策略直接亏了一周。后来我改用滚动窗口计算均值,才稳住。

# 计算滚动均值与标准差
window = 20
rolling_mean = spread.rolling(window=window).mean()
rolling_std = spread.rolling(window=window).std()

# 开仓信号:价差偏离均值2个标准差
z_score = (spread - rolling_mean) / rolling_std
signal = (z_score.abs() > 2).astype(int)

2.2.2 自相关

自相关衡量的是「今天的价差和昨天的价差有没有关系」。如果自相关系数很高,说明价差有趋势性,不适合做均值回归策略。

我曾经踩过一个坑:用铁矿石的价差做策略,自相关检验显示0.8,我心想「没关系,先跑跑看」。结果回测曲线一路向下,根本没法回归。后来才明白,自相关高的序列,你开仓后它还会继续往那个方向走,止损都来不及。

避坑指南:自相关系数超过0.5的价差序列,不建议直接用均值回归策略。可以考虑先做差分,或者改用趋势跟踪策略。

from statsmodels.tsa.stattools import acf

# 计算自相关系数
lag = 10
acf_values = acf(spread.dropna(), nlags=lag)
print(f"滞后1期自相关: {acf_values[1]:.3f}")
print(f"滞后5期自相关: {acf_values[5]:.3f}")

2.3 价差平稳性检验(ADF检验)

这是整个分析中最关键的一步。为什么?因为只有平稳的价差序列,才能用均值回归策略去套利。

平稳性说白了就是:价差的均值、方差不会随着时间发生系统性变化。如果价差不平稳,你设定的开仓阈值今天有效,明天可能就失效了。

ADF检验的判断逻辑:

  • 原假设H0:序列存在单位根(不平稳)
  • 备择假设H1:序列平稳
  • p值 < 0.05 → 拒绝H0 → 序列平稳

我个人经验:ADF检验的p值最好小于0.01才放心用。0.05的边缘情况,我建议你再做一次KPSS检验交叉验证。

from statsmodels.tsa.stattools import adfuller

def adf_test(series, name=''):
    result = adfuller(series.dropna(), autolag='AIC')
    print(f"{name} ADF检验结果:")
    print(f"  ADF统计量: {result[0]:.4f}")
    print(f"  p值: {result[1]:.4f}")
    print(f"  临界值(1%): {result[4]['1%']:.4f}")
    print(f"  临界值(5%): {result[4]['5%']:.4f}")
    if result[1] < 0.05:
        print("  → 结论:序列平稳,适合均值回归策略")
    else:
        print("  → 结论:序列不平稳,需要进一步处理")
    return result

# 测试价差序列
adf_test(spread_log, name='螺纹钢价差')

2.4 协整关系检验

有时候单个价差不平稳,但两个合约价格之间存在长期均衡关系——这就是协整。

协整检验回答的问题是:近月合约和远月合约的价格,是不是「手拉手」一起走的?如果是,那么即使价差暂时偏离,最终也会回归。

常用的协整检验方法:

  • Engle-Granger两步法:先回归,再检验残差平稳性
  • Johansen检验:可以检验多个协整关系

我记得有一次做原油的跨期套利,ADF检验显示价差不平稳,但协整检验通过。这说明近月和远月虽然各自在波动,但它们之间的差距是「有边界」的。这种情况下,用价差的Z-score做开仓信号,效果反而比直接用价差更好。

from statsmodels.tsa.stattools import coint

# Engle-Granger协整检验
def cointegration_test(y1, y2):
    score, pvalue, _ = coint(y1, y2)
    print(f"协整检验p值: {pvalue:.4f}")
    if pvalue < 0.05:
        print("→ 存在协整关系,可以构建配对交易策略")
    else:
        print("→ 不存在协整关系,不建议做配对交易")
    return pvalue

# 示例:近月与远月价格
cointegration_test(near, far)

2.5 知识体系总览

下面这张图,是我自己画的一个价差分析流程。每次做新品种之前,我都会按这个流程走一遍,确保没有遗漏。

价差分析工具 - 知识体系流程图 ① 价差序列构建 简单价差 / 对数价差 / 标准化 ② 统计特征分析 均值 / 方差 / 自相关 ③ 平稳性检验 ADF检验 / KPSS检验 ✅ 平稳 → 直接做均值回归 ❌ 不平稳 → 进入协整检验 或差分处理 ④ 协整关系检验 Engle-Granger / Johansen 🎯 构建配对交易策略 Z-score开仓 / 止损管理

我的小建议:刚开始做价差分析时,别急着上复杂模型。先把ADF检验和自相关这两个指标吃透,能解决80%的问题。协整检验是锦上添花,不是雪中送炭。

好了,价差分析工具的核心内容就这些。下一章我们会把这些工具串起来,构建一个完整的跨期套利策略框架。到时候你会看到,这些看似零散的分析步骤,是如何变成一套可执行的交易系统的。


公众号:蓝海资料掘金营,微信deep3321