QuantLib Python中常数Vola与波动率曲面定价CAP的差异问题
Cap定价:常数波动率与波动率曲面结果不一致的原因及修正
概念解释
两种方法结果不同的核心原因是波动率的应用逻辑完全不同:
- 常数波动率方法:给Cap包含的所有单个Caplet(每个3M行权的期权)都直接赋予同一个0.0036的波动率,定价时每个Caplet都用该常数波动率计算。
- 原波动率曲面方法:你输入的是1-5年期的平值Cap期限波动率曲线(其中4年期为0.0036),通过
OptionletStripper1进行bootstrap剥离,目的是反推出每个Caplet的波动率,使得用这些Caplet波动率计算出的平值Cap价格和输入的期限波动率匹配。这个过程得到的是和期限波动率曲线一致的Caplet波动率序列,而非所有Caplet共用同一个常数波动率,因此结果必然不同。
你设置strikes=[0,1]仅保证了每个期限下不同行权价的波动率平坦,但无法改变bootstrap过程中基于期限波动率曲线反推Caplet波动率的逻辑。
修正方法
要让波动率曲面方法复现常数波动率的定价结果,有两种直接的实现方式:
方式一:直接构建常数期权let波动率结构
这是最直接的方式,直接定义一个在所有期限、所有行权价上都恒定为0.0036的期权let波动率曲面:
# 替换原波动率曲面构建代码 constant_optionlet_vol = ql.ConstantOptionletVolatility( 0, calendar, ql.ModifiedFollowing, 0.0036, day_count, ql.Normal ) optionlet_vols_handle = ql.OptionletVolatilityStructureHandle(constant_optionlet_vol) engine = ql.BachelierCapFloorEngine(curve_ois_handle, optionlet_vols_handle) cap.setPricingEngine(engine) print(cap.NPV())
方式二:统一所有期限的平值Cap波动率为0.0036
如果一定要通过CapFloor期限波动率曲面来实现,需要让所有期限的平值Cap波动率都等于0.0036,这样bootstrap剥离出的Caplet波动率也会是常数0.0036:
# 替换原波动率曲面构建代码 tenors_vola = [ql.Period('1Y'), ql.Period('2Y'), ql.Period('3Y'), ql.Period('4Y'), ql.Period('5Y')] rates_vola = [0.0036] * 5 # 所有期限的平值波动率都设为0.0036 strikes = [0, 1] rates_vola_new = [[item, item] for item in rates_vola] capfloor_vols = ql.CapFloorTermVolSurface(2, calendar, ql.ModifiedFollowing, tenors_vola, rates_vola_new) optionlet_surf = ql.OptionletStripper1(capfloor_vols, ql.Euribor3M(curve_3M_handle), type=ql.Normal) optionlet_vols = ql.StrippedOptionletAdapter(optionlet_surf) optionlet_vols_handle = ql.OptionletVolatilityStructureHandle(optionlet_vols) engine = ql.BachelierCapFloorEngine(curve_ois_handle, optionlet_vols_handle) cap.setPricingEngine(engine) print(cap.NPV())
验证
两种修正方法得到的NPV都会和你最初的常数波动率方法结果完全一致,因为本质上都是让所有Caplet使用同一个0.0036的波动率进行定价。
内容的提问来源于stack exchange,提问作者Maria Reinhardt
相关产品推荐
相关产品推荐

