第四章:基差统计特征——均值回归、波动率、分布与季节性
各位交易员朋友,大家好。今天我们来聊聊基差的“脾气”。
做基差交易,说白了就是跟统计规律打交道。你想想看,如果基差完全随机游走,那我们还分析个啥?直接抛硬币得了。但现实不是这样。基差有它自己的性格——它会回归、会波动、会呈现特定的分布形态,还会跟着季节走。
我个人习惯,拿到一个品种的基差数据,第一件事就是做四件事:测回归速度、算波动率、看分布形态、找季节性规律。这四板斧砍下去,基差的底裤基本就扒干净了。
4.1 基差均值回归特性
均值回归,是基差交易最核心的信仰。说白了,就是基差不会永远偏离一个合理范围,它迟早会“回家”。
我在项目中遇到过最典型的例子是螺纹钢。2018年那会儿,螺纹钢基差一度拉到800块以上,市场一片恐慌。但我当时看了历史数据,这种极端偏离平均能持续多久?平均回归周期是多久?心里有数了,反而敢逆势建仓。
核心指标:
- 半衰期(Half-life):基差偏离均值后,回到一半所需的时间。半衰期越短,回归越快。
- 均值回复速度(Mean Reversion Speed):通常用Ornstein-Uhlenbeck过程建模,速度参数θ越大,回归越猛。
- 回归区间:基差在±1倍标准差内震荡的概率约68%,±2倍约95%。
怎么算半衰期?我一般用这个简单方法:
import numpy as np
import statsmodels.api as sm
# 假设基差序列 basis_series
def half_life(basis_series):
# 计算滞后一期
lag = basis_series.shift(1).dropna()
current = basis_series.iloc[1:]
# 回归:Δbasis = α + β * basis_lag
X = sm.add_constant(lag)
model = sm.OLS(current - lag, X).fit()
beta = model.params.iloc[1]
# 半衰期 = -ln(2) / ln(1 + β)
hl = -np.log(2) / np.log(1 + beta)
return hl
# 举个例子
hl_days = half_life(basis_series)
print(f"基差半衰期:{hl_days:.1f} 天")
避坑指南:我曾经犯过一个错误——直接用日频数据算半衰期,结果发现不同品种差异巨大。后来我意识到,数据频率要跟交易周期匹配。你做日内交易,就用分钟级数据;做月频套利,用日频就够了。
4.2 基差波动率
波动率,是基差交易的“油门”。波动率太低,没利润空间;波动率太高,容易翻车。
我习惯把基差波动率分成两类:
- 历史波动率(HV):过去N天的实际波动。我一般用20天、60天两个窗口。
- 隐含波动率(IV):从期权价格反推出来的市场预期。这个对基差交易特别重要——如果IV远高于HV,说明市场在恐慌,这时候做回归策略要小心。
计算历史波动率的代码很简单:
def basis_volatility(basis_series, window=20):
# 计算对数收益率
log_returns = np.log(basis_series / basis_series.shift(1))
# 滚动标准差,年化
vol = log_returns.rolling(window).std() * np.sqrt(252)
return vol
# 看看当前波动率处于什么水平
current_vol = basis_volatility(basis_series, 20).iloc[-1]
historical_median = basis_volatility(basis_series, 20).median()
print(f"当前波动率:{current_vol:.2%},历史中位数:{historical_median:.2%}")
注意:基差波动率有“聚集效应”——大波动后面往往跟着大波动。我见过有人看到波动率低就重仓,结果第二天一个黑天鹅直接爆仓。所以,波动率低的时候,反而要警惕“波动率爆发”。
4.3 基差分布形态
基差的分布形态,决定了你的止损线该设在哪里。
我刚开始做基差交易时,天真地以为基差是正态分布。结果被市场狠狠教育了一顿——基差分布往往有厚尾特征。什么意思?就是极端值出现的概率,比正态分布预测的要高得多。
| 分布特征 | 含义 | 对交易的影响 |
|---|---|---|
| 偏度(Skewness) | 正偏:右尾更长;负偏:左尾更长 | 正偏时,基差容易突然拉大,做空基差要小心 |
| 峰度(Kurtosis) | 高峰度:厚尾,极端值多 | 止损要设得更宽,否则容易被假突破打掉 |
| Jarque-Bera检验 | 检验是否正态分布 | 如果p值小于0.05,别用正态假设做风控 |
怎么快速看分布?我一般画个直方图加QQ图:
import matplotlib.pyplot as plt
import scipy.stats as stats
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
# 直方图
axes[0].hist(basis_series, bins=50, density=True, alpha=0.6)
axes[0].set_title('基差分布直方图')
# QQ图
stats.probplot(basis_series, dist="norm", plot=axes[1])
axes[1].set_title('QQ图 - 正态性检验')
plt.tight_layout()
plt.show()
个人经验:我见过最极端的例子是动力煤。2021年那波行情,基差分布峰度超过10,意味着极端值出现的概率是正态分布的几十倍。如果你用正态分布设止损,一天能被打穿三次。所以,厚尾品种,止损要放大到3倍标准差以上。
4.4 基差季节性规律
季节性,是基差交易里最“稳”的规律。为什么?因为很多商品的供需本身就跟着季节走。
举个例子:
- 农产品:收获季节,现货供应充足,基差往往走弱;青黄不接时,基差走强。
- 能源品:冬季取暖需求,天然气基差通常走强;夏季需求淡季,基差走弱。
- 黑色系:春节前后工地停工,螺纹钢基差容易走弱;3月复工后,基差走强。
我一般用“季节性指数”来量化这个规律:
def seasonal_index(basis_series, period=252):
# 计算每年同一天的均值
daily_avg = basis_series.groupby(basis_series.index.dayofyear).mean()
# 计算整体均值
overall_avg = basis_series.mean()
# 季节性指数 = 日均值 / 整体均值
seasonal = daily_avg / overall_avg
return seasonal
# 看看当前处于什么季节位置
today = basis_series.index[-1].dayofyear
current_seasonal = seasonal_index(basis_series).loc[today]
print(f"当前季节性指数:{current_seasonal:.2f}(大于1表示偏强,小于1表示偏弱)")
核心逻辑:季节性规律不是“一定会发生”,而是“概率上更可能发生”。我见过有人把季节性当圣杯,结果遇到极端天气或政策干预,季节性完全失效。所以,季节性只能作为辅助判断,不能作为唯一依据。
知识体系总览
下面这张图,是我自己整理的四维分析框架。每次做基差交易前,我都会过一遍:
嗯,这四个维度,说白了就是一套“体检报告”。均值回归看“健康度”,波动率看“风险度”,分布形态看“极端风险”,季节性看“周期规律”。把这四个维度吃透了,基差交易就不再是玄学,而是有据可依的系统工程。
我个人习惯,每周五收盘后,把这四个指标跑一遍,更新到我的交易日志里。时间长了,你会发现每个品种都有自己的“性格”——有的回归快,有的波动大,有的季节性特别强。摸透了这些性格,交易起来就顺手多了。
公众号:蓝海资料掘金营,微信deep3321