4. 价差计算与标准化:价差序列构建、Z-score标准化、滚动均值与标准差、阈值设定

好,咱们进入正题。跨品种套利的核心,说白了就是盯着两个相关品种的价差。价差不正常了,我们就进场;价差回归了,我们就离场。但这里有个关键问题——你怎么定义“不正常”?

我个人习惯,先把价差算出来,然后把它变成一个“标准分”,也就是Z-score。这样不管价差是100还是1000,我们都能用同一个尺子去量它。嗯,今天我们就来聊聊这个尺子怎么做。

4.1 价差序列构建:从原始价格到价差

价差计算本身不复杂。最常见的就是两种方式:

  • 简单价差:P1 - P2
  • 比例价差:P1 / P2 或 ln(P1) - ln(P2)

我在项目中遇到过一个问题:如果两个品种价格量级差太多,比如一个3000块,一个30块,简单价差几乎完全被高价品种牵着走。这时候比例价差或者对数价差会更靠谱。

举个例子,螺纹钢和热卷:

import pandas as pd
import numpy as np

# 假设我们有螺纹钢和热卷的日线数据
df['spread_simple'] = df['rebar'] - df['hot_coil']
df['spread_log'] = np.log(df['rebar']) - np.log(df['hot_coil'])

你想想看,对数价差还有个好处——它天然就是平稳的。很多套利策略都假设价差是均值回归的,对数价差更符合这个假设。

我的小技巧:构建价差序列时,先看一眼两个品种的价格走势图。如果它们长期走势高度一致,但短期有偏离,那这个价差就值得做。

4.2 Z-score标准化:把价差变成标准分

价差算出来了,但问题来了——价差100算大还是小?这得看历史。如果历史均值是50,标准差是10,那100就很大。如果历史均值是90,标准差是5,那100也就那样。

Z-score就是解决这个问题的。它的公式很简单:

Z = (当前价差 - 均值) / 标准差

Z-score告诉你:当前价差偏离均值多少个标准差。Z=2,意味着偏离了2个标准差。这在统计学上是个小概率事件,值得关注。

# 计算Z-score
mean = df['spread_log'].rolling(window=60).mean()
std = df['spread_log'].rolling(window=60).std()
df['z_score'] = (df['spread_log'] - mean) / std

说白了,Z-score就是把不同品种的价差拉到同一个坐标系里。不管你是做螺纹热卷,还是做豆油棕榈油,Z-score的阈值都可以统一用±2、±2.5这样的标准。

核心要点:Z-score标准化后,价差变成了一个无量纲的数值。这样我们就能用统一的规则去判断开仓和平仓时机。

4.3 滚动均值与标准差:动态更新参数

这里有个坑,我踩过。如果你用全量历史数据算均值和标准差,那参数是固定的。但市场是会变的。两年前螺纹钢和热卷的价差均值是50,今年可能变成了80。你用老参数去套新数据,会一直误判。

所以要用滚动窗口。我一般用60天或90天的滚动窗口。窗口太小,参数波动太大,容易频繁交易;窗口太大,参数反应太慢,跟不上市场变化。

# 滚动窗口参数设置
window = 60  # 我个人习惯用60个交易日,大约一个季度
df['rolling_mean'] = df['spread_log'].rolling(window=window).mean()
df['rolling_std'] = df['spread_log'].rolling(window=window).std()

为什么会这样?因为市场结构会变。比如某个品种的供需关系发生了变化,或者交易所调整了交割规则,价差的均值就会漂移。滚动窗口能自动适应这种变化。

避坑指南:我曾经用30天的滚动窗口做回测,结果策略表现很好,但实盘一跑就崩。后来发现30天窗口太敏感,一个极端行情就把参数拉偏了。建议至少用60天以上。

4.4 阈值设定:开仓与平仓的触发条件

阈值怎么设?这取决于你的风险偏好和交易频率。常见的做法是:

  • 开仓阈值:Z-score超过±2或±2.5时开仓
  • 平仓阈值:Z-score回归到±0.5或0时平仓
  • 止损阈值:Z-score达到±3或±3.5时止损

我个人的经验是,阈值不能太死板。比如在波动率高的市场里,±2可能经常被突破,导致频繁交易。这时候可以适当放宽到±2.5。

# 阈值设定示例
entry_threshold = 2.0   # 开仓阈值
exit_threshold = 0.5    # 平仓阈值
stop_threshold = 3.0    # 止损阈值

# 生成交易信号
df['signal'] = 0
df.loc[df['z_score'] > entry_threshold, 'signal'] = -1  # 做空价差
df.loc[df['z_score'] < -entry_threshold, 'signal'] = 1  # 做多价差
df.loc[abs(df['z_score']) < exit_threshold, 'signal'] = 0  # 平仓

你想想看,阈值设得太小,交易次数多但每笔赚得少;阈值设得太大,交易次数少但可能错过机会。这是个权衡。

我的建议:先用历史数据做回测,找到最优阈值。但别过度优化。我见过有人把阈值优化到2.13这种精度,实盘一跑就废。差不多就行,留点余量。

4.5 知识体系总览

为了让你更直观地理解这一整套流程,我画了张图。从原始价格到最终交易信号,每一步都有它的意义。

价差计算与标准化流程 原始价格数据 品种A & 品种B 价差序列构建 简单价差 / 对数价差 Z-score标准化 (价差 - 均值) / 标准差 阈值设定 开仓/平仓/止损 滚动均值与标准差 窗口大小:60天 / 90天 生成交易信号 做多 / 做空 / 平仓 核心逻辑:将原始价格转化为标准化的Z-score,再用阈值判断交易时机 滚动窗口确保参数能自适应市场变化

这张图把整个流程串起来了。从原始价格到价差,再到Z-score,最后用阈值触发交易信号。滚动均值与标准差是中间那个关键的支撑环节。

实战建议:刚开始做套利时,别急着上复杂模型。先把价差计算和Z-score标准化这步做扎实。我见过太多人一上来就用机器学习,结果基础数据都没处理好。基础打牢,策略才能稳。

好了,价差计算与标准化的核心内容就这些。记住:价差是基础,Z-score是尺子,滚动参数是自适应,阈值是触发器。把这四步走稳,你的套利策略就有了坚实的根基。

专注资料整理