4、价差计算与标准化:价差公式、Z-score标准化、布林带、阈值设定
好,我们进入第四章。价差计算与标准化。
说实话,很多做跨品种套利的新手,上来就盯着价格差看。但价格本身是有量纲的,不同品种的价格水平可能差十倍甚至百倍。你拿螺纹钢和热卷比,一个三千多,一个四千多,直接算差值,那波动幅度完全被高价品种牵着鼻子走。所以,标准化处理是套利策略的基石。
4.1 价差公式:从原始数据到核心信号
价差,说白了就是两个相关品种价格之间的差值或比值。我个人习惯用差值,因为逻辑直观,回测时也容易理解。
最基本的价差公式:
# 假设我们有两个价格序列
price_a = [100, 102, 101, 103, ...]
price_b = [98, 99, 100, 101, ...]
# 简单价差
spread = price_a - price_b
但这里有个坑。如果两个品种的合约乘数不同,比如一手螺纹钢是10吨,一手热卷也是10吨,那直接减没问题。可要是做豆粕和豆油呢?合约乘数不一样,持仓比例就得调整。我在项目中遇到过这种情况,当时没注意,回测曲线漂亮得很,实盘一跑就亏。后来才发现,是价差计算时没考虑合约乘数。
带持仓比例的价差公式:
# 假设合约乘数:品种A为10,品种B为20
# 我们想构建一个市值中性的组合
ratio_a = 20 # 做多1手A,需要做空2手B来对冲
ratio_b = 10
# 调整后的价差
adjusted_spread = price_a * ratio_a - price_b * ratio_b
你想想看,如果不做这个调整,价差的波动范围会很大,而且没有经济意义。调整之后,价差就变成了一个围绕零轴波动的序列,方便我们后续做标准化。
4.2 Z-score标准化:让价差变得可比较
价差算出来了,但不同时间段的价差波动幅度可能不一样。牛市里价差波动大,熊市里波动小。直接用一个固定阈值去判断,肯定不行。
Z-score就是干这个的。它把价差序列转换成均值为0、标准差为1的标准正态分布。说白了,就是告诉你当前价差偏离均值多少个标准差。
Z-score公式:
z_score = (当前价差 - 价差均值) / 价差标准差
嗯,这里要注意。均值和标准差的计算窗口很关键。用太短的窗口,信号太敏感,容易频繁交易;用太长的窗口,反应迟钝,抓不住机会。
Python实现:
import pandas as pd
import numpy as np
def calculate_zscore(spread, window=60):
"""
计算价差的Z-score
"""
rolling_mean = spread.rolling(window=window).mean()
rolling_std = spread.rolling(window=window).std()
z_score = (spread - rolling_mean) / rolling_std
return z_score
# 示例
spread_series = pd.Series([...]) # 你的价差数据
z_scores = calculate_zscore(spread_series)
Z-score的好处是,它把不同时间段的价差都映射到同一个尺度上。这样我们就可以用统一的阈值来判断开仓和平仓了。
4.3 布林带:可视化价差的边界
布林带其实就是Z-score的图形化表达。它由三条线组成:中轨(均值)、上轨(均值+ k倍标准差)、下轨(均值- k倍标准差)。
为什么用布林带?因为直观。一眼就能看出当前价差处于什么位置。我个人习惯把k值设为2,也就是上下轨各偏离2个标准差。理论上,正态分布下,95%的数据会落在这个区间内。
布林带计算:
def bollinger_bands(spread, window=60, num_std=2):
"""
计算布林带
"""
rolling_mean = spread.rolling(window=window).mean()
rolling_std = spread.rolling(window=window).std()
upper_band = rolling_mean + num_std * rolling_std
lower_band = rolling_mean - num_std * rolling_std
return upper_band, rolling_mean, lower_band
upper, middle, lower = bollinger_bands(spread_series)
但这里有个问题。布林带的上下轨是固定的吗?不是。随着新数据的加入,均值和标准差会动态更新,布林带也会跟着移动。所以,它是一个自适应系统。
4.4 阈值设定:从统计到交易决策
阈值,就是决定什么时候开仓、什么时候平仓的那个数字。Z-score给了我们一个标准化的信号,布林带给了我们可视化的边界,但具体怎么用,还得靠阈值。
常见的阈值设定方法:
| 阈值类型 | 典型值 | 说明 |
|---|---|---|
| 开仓阈值 | Z-score > 2 或 < -2 | 价差偏离2个标准差以上时开仓 |
| 平仓阈值 | Z-score 回归到 0 附近 | 价差回归均值时平仓 |
| 止损阈值 | Z-score > 3 或 < -3 | 防止价差继续扩大造成巨大亏损 |
你可能会问,为什么开仓阈值是2,不是1.5或2.5?这其实取决于你的风险偏好和品种特性。我记得有一次做螺纹钢和热卷的套利,历史回测显示阈值设为1.8效果最好,但实盘时市场结构变了,阈值必须调到2.2才能避免频繁止损。
动态阈值调整:
# 基于历史分位数设定阈值
def dynamic_threshold(z_scores, percentile=95):
"""
根据历史分位数动态设定阈值
"""
upper_threshold = np.percentile(z_scores, percentile)
lower_threshold = np.percentile(z_scores, 100 - percentile)
return upper_threshold, lower_threshold
# 示例
upper_th, lower_th = dynamic_threshold(z_scores, percentile=95)
动态阈值的好处是,它能适应市场的变化。当波动率变大时,阈值会自动放宽;波动率变小时,阈值会收紧。这样就不会因为市场环境变化而频繁误判。
知识体系结构图
下面这张图,把价差计算到标准化再到阈值设定的整个流程串起来了。你可以把它当作一个检查清单,每次做策略时对照着走一遍。
嗯,到这里,价差计算与标准化的核心内容就讲完了。记住,标准化不是目的,目的是让价差信号变得稳定、可比较。布林带和阈值只是工具,真正决定策略好坏的是你对品种特性的理解和对参数的调试。