第3章 价差计算:价差公式、标准化处理与数据清洗
好,咱们进入实战环节的第一个硬核知识点——价差计算。
很多人觉得价差不就是两个价格相减吗?嗯,表面上看确实如此。但实际跑起来,你会发现事情没那么简单。我早年做第一个跨期套利策略时,就栽在价差计算上。数据没处理好,策略回测曲线漂亮得像假的一样,实盘一跑直接崩了。
今天咱们就把这块掰开揉碎了讲清楚。
3.1 价差的核心公式
先看最基础的定义。
跨期套利的价差,通常指同一品种、不同到期月份合约之间的价格差。公式很简单:
价差 = 近月合约价格 - 远月合约价格
或者反过来,取决于你的交易习惯。我个人习惯用近月减远月,这样价差为正时,说明近月比远月贵,市场处于“backwardation”(现货溢价)状态。
举个例子:
螺纹钢 RB2305(近月) = 4200 元/吨
螺纹钢 RB2310(远月) = 4100 元/吨
价差 = 4200 - 4100 = 100 元/吨
这个100就是价差。看起来简单吧?但实际数据里,你拿到的价格可能是这样的:
RB2305: 4200, 4201, 4198, 4203, NaN, 4202
RB2310: 4100, 4099, 4102, 4098, 4101, NaN
看到NaN了吗?这就是真实数据。缺失值、跳空、异常点,一个都不会少。
核心要点:价差公式本身不难,难的是你拿到的原始数据质量。数据清洗不到位,后面所有分析都是空中楼阁。
3.2 标准化处理:让不同品种的价差可比较
好,价差算出来了。但问题来了——螺纹钢的价差100,和铜的价差500,能直接比吗?不能。因为价格基数不同。
这就引出了标准化处理。
我常用的标准化方法有两种:
3.2.1 Z-score 标准化
说白了,就是把价差转换成“偏离均值多少个标准差”。公式:
Z = (当前价差 - 价差均值) / 价差标准差
这样做的好处是:不同品种的价差都变成了无量纲的数值,可以直接比较。比如Z=2,意味着当前价差偏离均值2个标准差,属于极端情况。
我在项目中遇到过一个问题:用全历史数据算均值和标准差,结果策略对近期行情反应迟钝。后来我改用滚动窗口,比如用过去60个交易日的数据算,效果就好多了。
实战技巧:滚动窗口的长度怎么选?我一般用20天(约1个月)到60天(约1个季度)。太短了噪声大,太长了反应慢。你可以根据品种的波动特性来调。
3.2.2 百分比标准化
另一种方法,是把价差除以某个基准价格,得到百分比:
标准化价差 = 价差 / 近月合约价格 × 100%
这样做的好处是直观。比如价差100,近月价格4200,标准化后就是2.38%。你一眼就能看出价差占价格的比重。
我个人更偏爱Z-score,因为它能直接反映价差的统计显著性。但百分比法在向非技术背景的同事解释时更好用。
3.3 数据清洗:实战中的那些坑
好,到了最关键的环节。数据清洗做不好,策略就是废的。我踩过的坑,今天全告诉你。
3.3.1 缺失值处理
真实数据里,缺失值太常见了。节假日、数据源故障、合约换月,都会导致缺失。
处理方法:
- 向前填充:用上一个有效值填充。适合短期缺失。
- 线性插值:用前后两个有效值做线性插值。适合价格连续的情况。
- 直接删除:如果缺失太多,干脆删掉那段时间的数据。
我曾经犯过一个错:用向前填充处理了连续5天的缺失数据,结果策略在回测里表现很好,实盘却连续触发错误信号。后来才发现,那5天市场发生了重大变化,向前填充完全扭曲了真实情况。
避坑指南:缺失值超过3个连续交易日,我建议直接删除,不要填充。宁可少做几笔交易,也不要基于错误数据做决策。
3.3.2 异常值检测
价差数据里经常出现“飞点”——比如正常价差在100左右波动,突然蹦出一个500。这通常是数据错误,不是市场机会。
我常用的方法:
# 基于3倍标准差剔除异常值
import numpy as np
def remove_outliers(spread, threshold=3):
mean = np.mean(spread)
std = np.std(spread)
z_scores = np.abs((spread - mean) / std)
return spread[z_scores < threshold]
但注意,这个方法有个问题:如果价差本身有趋势,用全局均值和标准差会误删正常数据。我建议先用差分或去趋势处理,再做异常值检测。
3.3.3 合约换月处理
这是跨期套利里最头疼的问题。合约到期了,你得换到下一个合约。但换月那天,价差会有一个“跳变”。
举个例子:
| 日期 | 近月合约 | 远月合约 | 价差 |
|---|---|---|---|
| 2023-05-10 | RB2305 | RB2310 | 100 |
| 2023-05-11 | RB2310 | RB2401 | 80 |
看到没?换月前后,价差从100变成了80。这不是市场变了,而是合约变了。如果你不做处理,策略会以为价差突然缩小了20点,可能触发错误的交易信号。
我的处理方式:
- 方法一:在换月日前后各留3天缓冲期,不交易。
- 方法二:用价差的变化量(差分)代替价差本身,这样跳变的影响就消除了。
- 方法三:构建连续价差序列,用换月日的价差差值做调整。
我个人推荐方法二。差分后的价差变化量,能更真实地反映市场动态。
3.4 实战代码示例
说了这么多,咱们直接上代码。这是一个完整的价差计算与清洗流程:
import pandas as pd
import numpy as np
def calculate_spread(near_price, far_price):
"""
计算价差,并做标准化处理
"""
# 1. 计算原始价差
spread = near_price - far_price
# 2. 删除缺失值(连续缺失超过3天则删除整段)
spread = spread.dropna()
# 3. 异常值检测(滚动窗口3倍标准差)
rolling_mean = spread.rolling(window=20).mean()
rolling_std = spread.rolling(window=20).std()
z_scores = np.abs((spread - rolling_mean) / rolling_std)
spread_clean = spread[z_scores < 3]
# 4. Z-score标准化
spread_z = (spread_clean - spread_clean.mean()) / spread_clean.std()
return spread_z
# 使用示例
near = pd.Series([4200, 4201, 4198, 4203, np.nan, 4202])
far = pd.Series([4100, 4099, 4102, 4098, 4101, np.nan])
spread_z = calculate_spread(near, far)
print(spread_z)
这段代码看起来简单,但包含了缺失值处理、异常值检测、标准化三个核心步骤。你可以在自己的数据上直接跑。
3.5 本章知识体系
下面这张图,帮你把本章的核心逻辑串起来:
这张图展示了从原始数据到标准化价差序列的完整流程。每一步都有坑,每一步都需要你根据实际情况调整参数。
总结一下:价差计算不是简单的减法。数据清洗和标准化处理,才是决定策略成败的关键。我见过太多人在这上面栽跟头,希望你能少走弯路。
好,这一章就到这里。记住:数据质量决定策略上限。把基础打牢,后面的路才能走稳。