第19章:基差回归中的风险管理:VaR计算、压力测试、止损策略设计
做基差交易,说白了就是吃价差回归的饭。但饭里有没有沙子?有。而且不少。
我见过太多人,模型跑得漂亮,回测曲线跟教科书一样完美。结果一上实盘,三天就被市场教育了。为什么?因为只盯着收益,忘了风险才是亲爹。
这一章,咱们就聊聊基差回归里的风险管理三板斧:VaR计算、压力测试、止损策略设计。这三样东西,缺一个,你的交易系统就是瘸腿的。
19.1 基差回归中的VaR计算
VaR,Value at Risk,风险价值。说白了就是:在给定的置信水平下,未来一段时间内,你的头寸最多可能亏多少钱。
举个例子:95%置信水平下,日VaR是10万。意思是,100天里,有95天亏损不会超过10万。剩下5天?嗯,那5天才是真正要命的。
19.1.1 参数法(方差-协方差法)
这个方法假设基差收益率服从正态分布。计算很简单:
VaR = Z_α × σ × √T × 头寸规模
其中:
- Z_α:置信水平对应的分位数(95%对应1.645,99%对应2.326)
- σ:基差收益率的标准差
- T:持有期天数
我在项目中遇到过一个问题:基差数据经常有尖峰厚尾特征。用正态分布去拟合,VaR会被严重低估。说白了,尾部风险比你想象的大得多。
19.1.2 历史模拟法
这个方法我比较喜欢。它不假设任何分布,直接用历史数据来算。
步骤很简单:
- 取过去N天的基差收益率数据(我一般用500天以上)
- 按收益率从小到大排序
- 取第5%分位数(95%置信水平)或第1%分位数(99%置信水平)
import numpy as np
def historical_var(basis_returns, confidence=0.95):
sorted_returns = np.sort(basis_returns)
index = int((1 - confidence) * len(sorted_returns))
return sorted_returns[index]
# 示例:过去500天的基差收益率
basis_returns = np.random.normal(0, 0.02, 500) # 模拟数据
var_95 = historical_var(basis_returns, 0.95)
print(f"95% VaR: {var_95:.4f}")
19.1.3 蒙特卡洛模拟法
这个方法最灵活,但计算量也最大。适合做复杂策略的VaR计算。
基本思路:
- 估计基差收益率的分布参数(均值、方差、偏度、峰度)
- 生成大量随机路径(我一般跑10000次以上)
- 统计每条路径的最终损益,取分位数
import numpy as np
def monte_carlo_var(mu, sigma, days=1, n_sim=10000, confidence=0.95):
# 模拟基差路径
returns = np.random.normal(mu, sigma, (n_sim, days))
# 计算累积收益
cum_returns = np.sum(returns, axis=1)
# 取分位数
var = np.percentile(cum_returns, (1 - confidence) * 100)
return var
# 示例
mu, sigma = 0.001, 0.03 # 日收益率均值和标准差
var_mc = monte_carlo_var(mu, sigma, days=5, n_sim=10000)
print(f"5天95% VaR (蒙特卡洛): {var_mc:.4f}")
19.2 压力测试:极端情况下的生存能力
VaR告诉你的是「正常情况」下的风险。但真正让你爆仓的,从来不是正常情况。
压力测试,就是模拟极端市场环境。比如:
- 2020年3月原油暴跌
- 2008年金融危机
- 2015年A股股灾
- 某个品种的逼仓行情
19.2.1 历史情景模拟
直接拿历史上最极端的基差波动来测试你的策略。
我曾经做过一个测试:把2015年股灾期间股指期货的基差数据,喂给一个看似完美的回归策略。结果呢?最大回撤直接翻了三倍。嗯,从那以后,我再也不信「历史回测完美」这种鬼话了。
def stress_test(strategy, historical_stress_data):
"""
压力测试函数
:param strategy: 策略函数
:param historical_stress_data: 历史极端行情数据
"""
pnl = []
for data_point in historical_stress_data:
# 模拟策略在极端行情下的表现
result = strategy(data_point)
pnl.append(result)
max_drawdown = np.min(pnl) - np.max(np.maximum.accumulate(pnl))
return {
'total_pnl': np.sum(pnl),
'max_drawdown': max_drawdown,
'sharpe_ratio': np.mean(pnl) / np.std(pnl) if np.std(pnl) > 0 else 0
}
19.2.2 假设情景模拟
历史不会完全重复。所以还需要自己构造极端情景:
- 基差瞬间扩大3倍标准差
- 流动性枯竭(滑点放大10倍)
- 多品种同时出现极端基差
19.3 止损策略设计
VaR和压力测试是「诊断」,止损是「治疗」。没有止损的交易,就像没有安全带的赛车。
19.3.1 基于基差绝对水平的止损
最简单粗暴的方法:设定一个基差阈值,到了就砍。
def absolute_basis_stop(current_basis, entry_basis, threshold=0.05):
"""
基于基差绝对水平的止损
:param current_basis: 当前基差
:param entry_basis: 开仓基差
:param threshold: 止损阈值(基差点数)
"""
deviation = abs(current_basis - entry_basis)
if deviation > threshold:
return True # 触发止损
return False
这个方法有个问题:不同品种的基差波动范围不一样。螺纹钢的基差可能就几十点,而铁矿石可能几百点。统一阈值不现实。
19.3.2 基于统计分布的止损
我比较推荐这个方法。用基差的历史分布来设定止损线。
比如:
- 以开仓时的基差为均值
- 用历史波动率计算标准差
- 设定止损在均值±3倍标准差的位置
def statistical_stop(current_basis, entry_basis, basis_std, z_score=3):
"""
基于统计分布的止损
:param current_basis: 当前基差
:param entry_basis: 开仓基差
:param basis_std: 基差历史标准差
:param z_score: 止损的Z值
"""
upper_bound = entry_basis + z_score * basis_std
lower_bound = entry_basis - z_score * basis_std
if current_basis > upper_bound or current_basis < lower_bound:
return True
return False
19.3.3 基于时间衰减的止损
基差回归是有时间成本的。如果持仓时间过长,基差还没回归,说明你的判断可能错了。
我常用的做法:
- 设定最大持仓天数(比如20个交易日)
- 每天检查基差是否向预期方向移动
- 如果超过最大天数还没回归,强制平仓
def time_decay_stop(holding_days, max_days=20, basis_progress=0):
"""
基于时间衰减的止损
:param holding_days: 已持仓天数
:param max_days: 最大持仓天数
:param basis_progress: 基差回归进度(0-1)
"""
if holding_days >= max_days:
return True # 超时止损
# 如果持仓时间过半,但回归进度不到30%,也考虑止损
if holding_days > max_days * 0.5 and basis_progress < 0.3:
return True
return False
19.4 综合风险管理框架
把上面三样东西整合起来,形成一个完整的风险管理流程:
19.5 实战中的几个坑
最后,分享几个我踩过的坑:
- 别把VaR当圣杯:VaR只是工具,不是真理。它假设市场是连续的、可预测的。但现实是,市场经常出现跳空。
- 压力测试要定期更新:市场结构在变,去年的极端行情,今年可能只是常态。我每季度会重新审视压力测试情景。
- 止损不是越紧越好:止损太紧,频繁被扫;止损太松,亏损太大。找到那个平衡点,需要不断回测和调整。
- 别忘了流动性风险:基差交易通常涉及两个品种。如果其中一个品种流动性枯竭,你的止损可能根本执行不了。
嗯,这一章的内容就到这里。记住,在基差交易这个行当里,活得久比赚得快更重要。
公众号:蓝海资料掘金营,微信deep3321