最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to get implied volatility of a cap Quantlib Python - Stack Overflow

programmeradmin1浏览0评论

I have the following code:

import QuantLib as ql
import numpy as np

reference_date = ql.Date(25,11,2019)
ql.Settings.instance().evaluationDate = reference_date

dates_3M = [ql.Date(25,11,2019), ql.Date(27,2,2020), ql.Date(27,3,2020), ql.Date(27,4,2020)]
rates_3M = [0.003, 0.0032, 0.0031, 0.0029]
dates_ois = [ql.Date(25,11,2019), ql.Date(26,11,2019), ql.Date(4,12,2019), ql.Date(11,12,2019), 
             ql.Date(18,12,2019), ql.Date(27,12,2019), ql.Date(27,1,2020), ql.Date(27,2,2020), 
             ql.Date(27,3,2020), ql.Date(27,4,2020)]
rates_ois = [0.0034, 0.0033, 0.0032, 0.0032, 0.0031, 0.003, 0.0029, 0.0031, 0.003, 0.0031]

calendar = ql.TARGET()
curve_ois = ql.ZeroCurve(dates_ois, rates_ois, ql.Actual360(), calendar, ql.Linear(), ql.Compounded, ql.Annual)
curve_ois_handle = ql.YieldTermStructureHandle(curve_ois)
curve_3M = ql.ZeroCurve(dates_3M, rates_3M, ql.Actual360(), calendar, ql.Linear(), ql.Compounded, ql.Annual)
curve_3M_handle = ql.YieldTermStructureHandle(curve_3M)

startDate = reference_date + 2
endDate = calendar.advance(startDate, ql.Period('5M')
schedule = ql.Schedule(startDate, endDate, ql.Period(ql.Quarterly), calendar, ql.ModifiedFollowing, ql.ModifiedFollowing, ql.DateGeneration.Forward, False)
nom = 1000000
capRate = 0.01

cap = ql.Cap(ql.IborLeg([nominal], schedule, ql.Euribor3M(curve_3M_handle)), [capRate])
vola = ql.QuoteHandle(ql.SimpleQuote(0.3064))
engine = ql.BachelierCapFloorEngine(curve_ois_handle, vola)
cap.setPricingEngine(engine)
cap_npv = cap.NPV()

This gives me the output 9596.7536... Now I am trying

cap.impliedVolatility(9596.7536, curve_ois_handle, 0.2)

hoping I would get the original vola 0.3064. However, now I don't get the error as before, but the error "root not bracketed f[1e-07,4] -> [-9,596735e+03, -9.4199483+03]"

Now let's say instead I try the following:

tenors_vola = [ql.Period('2M'), ql.Period('4M'), ql.Period('6M'), ql.Period('8M')]
rates_vola = [0.16, 0.2, 0.22, 0.25]
strikes = [0,1]
rates_vola_new = [[item, item] for item in rates_vola]

vola_surf = ql.CapFloorTermVolSurface(2, calendar, ql.ModifiedFollowing, tenors_vola, 
                                      strikes, rates_vola_new)

tmp1 = ql.OptionletStripper1(vola_surf, ql.Euribor3M(curve_3M_handle), type=ql.Normal)
tmp2 = ql.StrippedOptionletAdapter(tmp1)
vola_handle = ql.OptionletVolatilityStructureHandle(tmp2)
engine = ql.BachelierCapFloorEngine(curve_ois_handle, vola_handle)
cap.setPricingEngine(engine)
print(cap.NPV())

I have vola date for different tenors but not for different strikes, so I assume my vola surface to be constant in the strike dimension. Now in this specific example I don't get this code to run because I get the error "not enough points to interpolate: at least 2 required, 1 provided." However in my actual code it works and there the only difference is, that my ois, euribor and vola data is much longer for many more years. But I cannot copy that data here. Could someone please adjust my code such that it is running, including in the end the

cap.impliedVolatility(price, curve_ois_handle, vola_guess)

And how would I interpret the resulting implied volatility in this case? Because the NPV is computed depending on all the volas in the curve. Would the result of cap.impliedVolatility(...) be some kind of average?

I have the following code:

import QuantLib as ql
import numpy as np

reference_date = ql.Date(25,11,2019)
ql.Settings.instance().evaluationDate = reference_date

dates_3M = [ql.Date(25,11,2019), ql.Date(27,2,2020), ql.Date(27,3,2020), ql.Date(27,4,2020)]
rates_3M = [0.003, 0.0032, 0.0031, 0.0029]
dates_ois = [ql.Date(25,11,2019), ql.Date(26,11,2019), ql.Date(4,12,2019), ql.Date(11,12,2019), 
             ql.Date(18,12,2019), ql.Date(27,12,2019), ql.Date(27,1,2020), ql.Date(27,2,2020), 
             ql.Date(27,3,2020), ql.Date(27,4,2020)]
rates_ois = [0.0034, 0.0033, 0.0032, 0.0032, 0.0031, 0.003, 0.0029, 0.0031, 0.003, 0.0031]

calendar = ql.TARGET()
curve_ois = ql.ZeroCurve(dates_ois, rates_ois, ql.Actual360(), calendar, ql.Linear(), ql.Compounded, ql.Annual)
curve_ois_handle = ql.YieldTermStructureHandle(curve_ois)
curve_3M = ql.ZeroCurve(dates_3M, rates_3M, ql.Actual360(), calendar, ql.Linear(), ql.Compounded, ql.Annual)
curve_3M_handle = ql.YieldTermStructureHandle(curve_3M)

startDate = reference_date + 2
endDate = calendar.advance(startDate, ql.Period('5M')
schedule = ql.Schedule(startDate, endDate, ql.Period(ql.Quarterly), calendar, ql.ModifiedFollowing, ql.ModifiedFollowing, ql.DateGeneration.Forward, False)
nom = 1000000
capRate = 0.01

cap = ql.Cap(ql.IborLeg([nominal], schedule, ql.Euribor3M(curve_3M_handle)), [capRate])
vola = ql.QuoteHandle(ql.SimpleQuote(0.3064))
engine = ql.BachelierCapFloorEngine(curve_ois_handle, vola)
cap.setPricingEngine(engine)
cap_npv = cap.NPV()

This gives me the output 9596.7536... Now I am trying

cap.impliedVolatility(9596.7536, curve_ois_handle, 0.2)

hoping I would get the original vola 0.3064. However, now I don't get the error as before, but the error "root not bracketed f[1e-07,4] -> [-9,596735e+03, -9.4199483+03]"

Now let's say instead I try the following:

tenors_vola = [ql.Period('2M'), ql.Period('4M'), ql.Period('6M'), ql.Period('8M')]
rates_vola = [0.16, 0.2, 0.22, 0.25]
strikes = [0,1]
rates_vola_new = [[item, item] for item in rates_vola]

vola_surf = ql.CapFloorTermVolSurface(2, calendar, ql.ModifiedFollowing, tenors_vola, 
                                      strikes, rates_vola_new)

tmp1 = ql.OptionletStripper1(vola_surf, ql.Euribor3M(curve_3M_handle), type=ql.Normal)
tmp2 = ql.StrippedOptionletAdapter(tmp1)
vola_handle = ql.OptionletVolatilityStructureHandle(tmp2)
engine = ql.BachelierCapFloorEngine(curve_ois_handle, vola_handle)
cap.setPricingEngine(engine)
print(cap.NPV())

I have vola date for different tenors but not for different strikes, so I assume my vola surface to be constant in the strike dimension. Now in this specific example I don't get this code to run because I get the error "not enough points to interpolate: at least 2 required, 1 provided." However in my actual code it works and there the only difference is, that my ois, euribor and vola data is much longer for many more years. But I cannot copy that data here. Could someone please adjust my code such that it is running, including in the end the

cap.impliedVolatility(price, curve_ois_handle, vola_guess)

And how would I interpret the resulting implied volatility in this case? Because the NPV is computed depending on all the volas in the curve. Would the result of cap.impliedVolatility(...) be some kind of average?

Share Improve this question edited Mar 12 at 14:57 Maria Reinhardt asked Mar 11 at 8:50 Maria ReinhardtMaria Reinhardt 591 silver badge7 bronze badges 4
  • Volatility is an alias for a real. – Luigi Ballabio Commented Mar 11 at 14:30
  • I tried with just the real number, but got the same error. – Maria Reinhardt Commented Mar 11 at 17:03
  • Please post runnable code so we can check. The one above misses data and has syntax errors. – Luigi Ballabio Commented Mar 11 at 18:24
  • I just edited my entire post, including example lists now with which the code runs for a constant vola. I do not get it to run with the vola curve, although it was running in my actual code where I use the real data, which I cannot provide here. In none of the 2 cases I manage to compute the implied Volatility, however. – Maria Reinhardt Commented Mar 12 at 14:59
Add a comment  | 

1 Answer 1

Reset to default 1

In your first example you're using the Bachelier engine, which means you're using normal volatility. When you're calling impliedVolatility, though, it defaults to using lognormal (Black) volatility (you can see its declaration and its default parameters here). The inconsistency causes the error. If you specify that you want a normal implied volatility, as in

cap.impliedVolatility(cap_npv, curve_ois_handle, guess=0.2, type=ql.Normal)

you'll get back your input volatility.

In your second example, I'm not sure what should be adjusted since you say that it works with your data. In any case, the call to impliedVolatility will give you the volatility that would give you the same price when used as a constant vol for all the caplets in the cap.

发布评论

评论列表(0)

  1. 暂无评论