3、价差计算与标准化:价差的计算方法(价格差、对数价差)、Z-score标准化

好,咱们进入价差回归策略的核心环节——价差怎么算,以及算完之后怎么让它变得“好用”。

说实话,我见过不少新手,上来就把两个价格一减,然后直接往里冲。结果呢?回测曲线漂亮得不行,实盘一跑就崩。为什么?因为价差的计算和标准化,远没有表面看起来那么简单。

3.1 价差的计算方法

价差,说白了就是两个品种之间的“距离”。但这个距离怎么量,学问很大。

3.1.1 价格差(Price Spread)

最直观的方法,就是直接相减:

spread = price_A - price_B

举个例子,如果A是300元,B是280元,价差就是20元。

优点:简单、直观,谁都能看懂。

缺点:价格水平不同时,没法比较。比如A从300涨到600,B从280涨到560,价差从20变成了40。但你能说套利机会变多了吗?不一定,因为比例关系没变。

⚠️ 注意:价格差对价格水平敏感。如果你用价格差做历史回测,不同时间段的价差均值可能完全不同,导致信号不稳定。

我在项目中遇到过这种情况:用价格差做螺纹钢和热卷的套利,结果发现价差的均值一直在漂移。后来一查,是因为两个品种的绝对价格都在涨,但价差本身并没有明显的回归特性。

3.1.2 对数价差(Log Spread)

这个是我个人比较喜欢的方法。它计算的是价格比值的对数:

log_spread = log(price_A / price_B)

还是刚才的例子,A=300,B=280,对数价差就是 log(300/280) ≈ 0.069。

当A变成600,B变成560时,对数价差还是 log(600/560) ≈ 0.069。看到了吗?它不随价格水平变化!

为什么对数价差更好?

  • 尺度不变:价格翻倍,对数价差不变。这很重要,因为很多金融时间序列是“乘法”的,不是“加法”的。
  • 近似收益率:当价差很小时,log(price_A/price_B) ≈ (price_A - price_B)/price_B,相当于一个百分比差异。
  • 统计性质更好:对数变换往往能让数据更接近正态分布,这对后续的统计检验有好处。
💡 我的习惯:除非有特殊理由,否则我默认用对数价差。尤其是做股指期货、商品期货这种价格会长期上涨或下跌的品种,对数价差能避免很多坑。

3.2 Z-score 标准化

好,价差算出来了。但问题来了——这个价差什么时候算“大”,什么时候算“小”?

你想想看,如果价差均值是0.05,标准差是0.02,那0.09就算很大了。但如果均值是0.5,标准差是0.3,那0.09其实很小。

所以,我们需要一个统一的标准——Z-score。

3.2.1 什么是 Z-score?

Z-score 告诉你:当前价差偏离均值多少个标准差。

z_score = (当前价差 - 历史均值) / 历史标准差

说白了,就是把价差“归一化”了。Z-score = 2,意味着当前价差比均值高2个标准差,属于比较极端的情况。

3.2.2 为什么需要 Z-score?

我举个例子你就明白了。

假设你同时监控三组价差:

品种对 当前价差 历史均值 历史标准差 Z-score
A-B 0.12 0.05 0.02 3.5
C-D 0.50 0.30 0.10 2.0
E-F 1.20 1.00 0.50 0.4

只看价差,A-B只有0.12,E-F有1.20,你会觉得E-F更极端。但Z-score告诉你,A-B的3.5才是真正的极端情况,而E-F的0.4其实很普通。

这就是标准化的力量——它让你能在同一把尺子上比较不同的价差。

3.2.3 滚动窗口 vs 固定窗口

这里有个关键问题:均值和标准差怎么算?

两种主流做法:

  • 固定窗口:用过去N天的数据算均值和标准差。比如用过去60天的数据。
  • 滚动窗口:每天更新,用最新的N天数据重新计算。

我个人建议用滚动窗口。为什么?因为市场在变,价差的均值和波动率也在变。用滚动窗口能自适应地捕捉这些变化。

# Python 示例:滚动窗口 Z-score
import pandas as pd
import numpy as np

def calculate_zscore(spread, window=60):
    """
    计算滚动 Z-score
    spread: 价差序列(可以是价格差或对数价差)
    window: 滚动窗口大小
    """
    rolling_mean = spread.rolling(window=window).mean()
    rolling_std = spread.rolling(window=window).std()
    zscore = (spread - rolling_mean) / rolling_std
    return zscore

# 使用示例
log_spread = np.log(price_A / price_B)
zscore = calculate_zscore(log_spread, window=60)
⚠️ 避坑指南:我曾经犯过一个错误——窗口选得太小。比如用20天的窗口,结果Z-score频繁触发信号,但大部分都是假信号。后来我改成60天,信号质量明显提升。窗口大小取决于你的交易频率,一般日频交易建议60-120天。

3.3 知识体系总览

下面这张图,把价差计算与标准化的核心逻辑串起来了。你可以把它当作本章的“地图”。

价差计算与标准化知识体系 原始价格数据 price_A, price_B 价差计算 价格差 / 对数价差 Z-score 标准化 (当前值 - 均值) / 标准差 价格差 price_A - price_B 对数价差 log(A/B) 价格差:简单直观,但对价格水平敏感 对数价差:尺度不变,统计性质更好 ← 推荐 滚动窗口计算 窗口大小:60~120天 标准化后的价差序列 Z-score:可比较、可设定阈值、可回测 核心逻辑:原始价格 → 价差计算(推荐对数价差)→ Z-score标准化 → 可交易的信号 滚动窗口自适应,避免参数过拟合

3.4 实际应用中的注意事项

嗯,这里要注意几个实操中的细节:

  1. 数据频率要一致:价差计算时,两个品种的时间戳必须对齐。别拿A的收盘价和B的开盘价比,那会出大问题。
  2. 异常值处理:如果某天数据缺失或出现极端值,Z-score会变得很大。我一般会先做一次中位数过滤,把明显的异常点剔除。
  3. 重新计算频率:Z-score的均值和标准差不是算一次就完事了。每天收盘后,我都会重新计算一次,确保参数是最新的。

📌 核心要点

  • 对数价差优于价格差,因为它尺度不变、统计性质更好
  • Z-score 让不同价差可以在同一标准下比较
  • 滚动窗口(60-120天)比固定窗口更适应市场变化
  • 标准化后的价差,才能用于设定统一的交易阈值

好了,价差的计算和标准化就讲到这里。记住一句话:没有标准化的价差,就像没有刻度的尺子——你没法判断它到底有多“大”


专注资料整理