25. 无套利约束:确保插值后的曲面不存在蝶式/日历套利
做波动率曲面插值,最怕什么?
不是精度不够,也不是速度慢。我最怕的是——插值出来的曲面,居然存在套利机会。
你想想看,一个交易员拿着你给的曲面去定价,结果发现能无风险赚钱。这问题就大了。轻则被风控叫去喝茶,重则整个模型被推翻重来。我在一家自营公司就遇到过这种事,那次之后,我把无套利约束写进了所有插值代码的基因里。
什么是蝶式套利?
说白了,蝶式套利就是利用同一到期日、不同行权价的期权价格关系。如果三个行权价K1 < K2 < K3,对应的隐含波动率算出来的价格,违反了凸性条件,就能套利。
数学上,蝶式套利对应的是:
∂²C/∂K² ≥ 0
这个二阶导数,其实就是风险中性密度。如果它为负,说明市场给了你一个免费午餐——买入两端、卖出中间,稳赚不赔。
我习惯用离散形式来检查:
Butterfly(K1, K2, K3) = C(K1) - 2*C(K2) + C(K3) ≥ 0
其中C是看涨期权价格。注意,这里用的是价格,不是波动率。很多人搞混这一点。
日历套利又是什么?
日历套利,也叫时间套利。它利用的是不同到期日之间的关系。
如果T1 < T2,那么看涨期权价格必须满足:
C(T2) ≥ C(T1)
为什么?因为时间越长,不确定性越大,期权应该越贵。如果T2的期权比T1还便宜,那就套利了——买入便宜的长期期权,卖出贵的短期期权,持有到期就能赚钱。
嗯,这里要注意。日历套利检查的是同一行权价下,不同到期日的价格单调性。插值时,如果时间维度处理不好,很容易出现这种问题。
如何确保无套利?
方法其实不复杂。我总结了三个步骤:
- 先检查原始数据——输入的数据本身不能有套利。如果原始数据就有问题,插值后只会更糟。
- 选择合适的插值方法——不是所有方法都适合。比如线性插值在总方差空间上做,天然满足无套利条件。
- 后处理修正——如果插值后出现了套利,用优化方法强制修正。
我个人最推荐的是在总方差空间做插值。总方差定义为:
w(K, T) = σ²(K, T) * T
在这个空间里,蝶式套利对应的是w对K的凸性,日历套利对应的是w对T的单调性。而且,总方差随到期日单调递增,这是个天然的好性质。
代码实现思路
下面是我常用的检查函数。它不复杂,但很实用:
def check_no_arbitrage(strikes, maturities, prices):
"""
检查期权价格是否存在套利
strikes: 行权价数组
maturities: 到期日数组
prices: 价格矩阵,shape=(len(maturities), len(strikes))
"""
# 检查蝶式套利
for t_idx in range(len(maturities)):
for k_idx in range(1, len(strikes)-1):
butterfly = (prices[t_idx, k_idx-1]
- 2*prices[t_idx, k_idx]
+ prices[t_idx, k_idx+1])
if butterfly < -1e-8: # 容忍小误差
return False, f"蝶式套利: T={maturities[t_idx]}, K={strikes[k_idx]}"
# 检查日历套利
for k_idx in range(len(strikes)):
for t_idx in range(1, len(maturities)):
if prices[t_idx, k_idx] < prices[t_idx-1, k_idx] - 1e-8:
return False, f"日历套利: K={strikes[k_idx]}, T={maturities[t_idx]}"
return True, "无套利"
插值后的修正方法
如果插值后发现了套利,怎么办?
我一般用约束优化来修正。目标是最小化修正后的曲面与原曲面的差异,同时满足无套利约束。
具体来说:
- 对于蝶式套利,约束是每个相邻三点的价格凸性非负
- 对于日历套利,约束是每个行权价上的价格随到期日单调递增
这个优化问题可以用二次规划求解。虽然计算量大了点,但换来的是曲面的可靠性——值了。
知识体系总览
下面这张图,把无套利约束的核心逻辑串起来了:
实战中的几点体会
做了这么多年,我总结了几条经验:
- 别等插值完了再检查——应该在每一步都做检查。原始数据、插值中间结果、最终曲面,层层把关。
- 总方差空间是好朋友——在这个空间做插值,很多套利问题自动就避免了。我建议你优先考虑。
- 容忍度要合理——太严了,市场微观结构噪声会让你误报;太松了,真套利会溜过去。1e-8到1e-6之间比较合适。
好了,关于无套利约束就聊这么多。记住,做金融模型,第一要务是逻辑自洽。套利检验,就是守住这条底线的最强武器。