You can subscribe to this list here.
| 2000 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
(60) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2001 |
Jan
(18) |
Feb
(4) |
Mar
(6) |
Apr
(2) |
May
|
Jun
(12) |
Jul
(48) |
Aug
(6) |
Sep
(3) |
Oct
(24) |
Nov
(15) |
Dec
(18) |
| 2002 |
Jan
(39) |
Feb
(12) |
Mar
(80) |
Apr
(72) |
May
(46) |
Jun
(27) |
Jul
(23) |
Aug
(34) |
Sep
(65) |
Oct
(71) |
Nov
(19) |
Dec
(14) |
| 2003 |
Jan
(44) |
Feb
(59) |
Mar
(18) |
Apr
(62) |
May
(54) |
Jun
(27) |
Jul
(46) |
Aug
(15) |
Sep
(44) |
Oct
(36) |
Nov
(19) |
Dec
(12) |
| 2004 |
Jan
(26) |
Feb
(33) |
Mar
(47) |
Apr
(63) |
May
(36) |
Jun
(65) |
Jul
(80) |
Aug
(163) |
Sep
(65) |
Oct
(39) |
Nov
(36) |
Dec
(39) |
| 2005 |
Jan
(97) |
Feb
(78) |
Mar
(64) |
Apr
(64) |
May
(48) |
Jun
(55) |
Jul
(89) |
Aug
(57) |
Sep
(51) |
Oct
(111) |
Nov
(86) |
Dec
(76) |
| 2006 |
Jan
(84) |
Feb
(103) |
Mar
(143) |
Apr
(92) |
May
(55) |
Jun
(58) |
Jul
(71) |
Aug
(57) |
Sep
(74) |
Oct
(59) |
Nov
(8) |
Dec
(32) |
| 2007 |
Jan
(60) |
Feb
(40) |
Mar
(50) |
Apr
(26) |
May
(61) |
Jun
(120) |
Jul
(119) |
Aug
(48) |
Sep
(121) |
Oct
(66) |
Nov
(103) |
Dec
(43) |
| 2008 |
Jan
(60) |
Feb
(109) |
Mar
(92) |
Apr
(106) |
May
(82) |
Jun
(59) |
Jul
(67) |
Aug
(118) |
Sep
(131) |
Oct
(56) |
Nov
(37) |
Dec
(69) |
| 2009 |
Jan
(75) |
Feb
(76) |
Mar
(103) |
Apr
(78) |
May
(61) |
Jun
(35) |
Jul
(66) |
Aug
(69) |
Sep
(166) |
Oct
(46) |
Nov
(72) |
Dec
(65) |
| 2010 |
Jan
(48) |
Feb
(57) |
Mar
(93) |
Apr
(85) |
May
(123) |
Jun
(82) |
Jul
(98) |
Aug
(121) |
Sep
(146) |
Oct
(86) |
Nov
(72) |
Dec
(34) |
| 2011 |
Jan
(96) |
Feb
(55) |
Mar
(73) |
Apr
(57) |
May
(33) |
Jun
(74) |
Jul
(89) |
Aug
(71) |
Sep
(103) |
Oct
(76) |
Nov
(52) |
Dec
(61) |
| 2012 |
Jan
(48) |
Feb
(54) |
Mar
(78) |
Apr
(60) |
May
(75) |
Jun
(59) |
Jul
(33) |
Aug
(66) |
Sep
(43) |
Oct
(46) |
Nov
(75) |
Dec
(51) |
| 2013 |
Jan
(112) |
Feb
(72) |
Mar
(49) |
Apr
(48) |
May
(42) |
Jun
(44) |
Jul
(80) |
Aug
(19) |
Sep
(33) |
Oct
(37) |
Nov
(38) |
Dec
(98) |
| 2014 |
Jan
(113) |
Feb
(93) |
Mar
(49) |
Apr
(106) |
May
(97) |
Jun
(155) |
Jul
(87) |
Aug
(127) |
Sep
(85) |
Oct
(48) |
Nov
(41) |
Dec
(37) |
| 2015 |
Jan
(34) |
Feb
(50) |
Mar
(104) |
Apr
(80) |
May
(82) |
Jun
(66) |
Jul
(41) |
Aug
(84) |
Sep
(37) |
Oct
(65) |
Nov
(83) |
Dec
(52) |
| 2016 |
Jan
(68) |
Feb
(35) |
Mar
(42) |
Apr
(35) |
May
(54) |
Jun
(75) |
Jul
(45) |
Aug
(52) |
Sep
(60) |
Oct
(52) |
Nov
(36) |
Dec
(64) |
| 2017 |
Jan
(92) |
Feb
(59) |
Mar
(35) |
Apr
(53) |
May
(83) |
Jun
(43) |
Jul
(65) |
Aug
(68) |
Sep
(46) |
Oct
(75) |
Nov
(40) |
Dec
(49) |
| 2018 |
Jan
(68) |
Feb
(54) |
Mar
(48) |
Apr
(58) |
May
(51) |
Jun
(44) |
Jul
(40) |
Aug
(68) |
Sep
(35) |
Oct
(15) |
Nov
(7) |
Dec
(37) |
| 2019 |
Jan
(43) |
Feb
(7) |
Mar
(22) |
Apr
(21) |
May
(31) |
Jun
(39) |
Jul
(73) |
Aug
(45) |
Sep
(47) |
Oct
(89) |
Nov
(19) |
Dec
(69) |
| 2020 |
Jan
(52) |
Feb
(63) |
Mar
(45) |
Apr
(59) |
May
(42) |
Jun
(57) |
Jul
(30) |
Aug
(29) |
Sep
(75) |
Oct
(64) |
Nov
(96) |
Dec
(22) |
| 2021 |
Jan
(14) |
Feb
(24) |
Mar
(35) |
Apr
(58) |
May
(36) |
Jun
(15) |
Jul
(18) |
Aug
(31) |
Sep
(30) |
Oct
(33) |
Nov
(27) |
Dec
(16) |
| 2022 |
Jan
(35) |
Feb
(22) |
Mar
(14) |
Apr
(20) |
May
(44) |
Jun
(53) |
Jul
(25) |
Aug
(56) |
Sep
(11) |
Oct
(47) |
Nov
(22) |
Dec
(36) |
| 2023 |
Jan
(30) |
Feb
(17) |
Mar
(31) |
Apr
(48) |
May
(31) |
Jun
(7) |
Jul
(25) |
Aug
(26) |
Sep
(61) |
Oct
(66) |
Nov
(19) |
Dec
(21) |
| 2024 |
Jan
(37) |
Feb
(29) |
Mar
(26) |
Apr
(26) |
May
(34) |
Jun
(9) |
Jul
(27) |
Aug
(13) |
Sep
(15) |
Oct
(25) |
Nov
(13) |
Dec
(8) |
| 2025 |
Jan
(13) |
Feb
(1) |
Mar
(16) |
Apr
(17) |
May
(8) |
Jun
(6) |
Jul
(9) |
Aug
|
Sep
(6) |
Oct
(15) |
Nov
(6) |
Dec
|
| 2026 |
Jan
(6) |
Feb
(4) |
Mar
(20) |
Apr
(2) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
|
From: Peter C. <pca...@gm...> - 2022-08-24 13:37:03
|
Hi Daniel yes you'd change T in the ctor call ql.Gsr(...). 1. T is the maturity of the zero bond P(0, T) used as a numeraire in the T-Forward measure 2. years 3. slightly higher than 60 I'd think to capture day counter / convention / lag effects, maybe 61 is sufficient already Thanks Peter On Wed, 24 Aug 2022 at 11:47, Daniel <gr...@gm...> wrote: > > Peter, > Thank you for your reply. > Just want to make sure overwriting T means I should change below statement > model = ql.Gsr(term_structure,stepDates, sigmas,reversions,T=60); > to use some other T value instead, am I right? > Two questions: > 1. What is the meaning of T here? > 2. What is the unit for T (month, year)? > 3. If my calibrated swaption has a maximum 30Y x 30Y matrix, what is the T value I should use? > > Thanks, > Mark > > > On Tue, Aug 23, 2022 at 1:30 PM Peter Caspers <pca...@gm...> wrote: >> >> Hi >> >> the GSR model operates in the T-forward measure with T=60 by default. You can overwrite T in the constructor if longer horizons are needed (as it appears to be the case in your setup). >> >> Best >> Peter >> >> >> Daniel <gr...@gm...> schrieb am Mo. 22. Aug. 2022 um 21:25: >>> >>> Thank you Dmitri for your information. Actually I increased the vol surface number to more than to be calibration volatility number, now I can solve for mean reversion and volatility now. >>> >>> But experience another error when I tried to change the yield curve and volatility surface to some other type, I got below error. >>> >>> ---> 14 model.calibrate(swaptions, optimization_method, end_criteria) >>> >>> ~\.conda\envs\tf-gpu\lib\site-packages\QuantLib\QuantLib.py in calibrate(self, *args) >>> >>> def calibrate(self, *args): >>> > return _QuantLib.Gsr_calibrate(self, *args) >>> >>> def setParams(self, params): >>> >>> RuntimeError: G(t,w) should be called with (t,w)=(29.9985,60.0055) in Range [0,60]. >>> >>> >>> What does this runtimeError mean? >>> >>> >>> Thanks, >>> >>> Mark >>> >>> >>> >>> >>> >>> >>> On Mon, Aug 22, 2022 at 12:50 PM Dmitri Goloubentsev <dm...@ma...> wrote: >>>> >>>> Hi Mark, >>>> >>>> >>>> The error is coming from LM optimiser. You should reduce number of points in your vol surface or add more calibration instruments or add some sort of regularisation. >>>> >>>> Kind regards, >>>> Dmitri. >>>> >>>> On Mon, 22 Aug 2022, 17:26 Daniel, <gr...@gm...> wrote: >>>>> >>>>> All, >>>>> >>>>> Please forgive me if someone has answered a similar question before. >>>>> >>>>> I am trying to calibrate interest rate vol surface using GSR (gaussian short rate model) based on this post >>>>> http://gouthamanbalaraman.com/blog/short-interest-rate-model-calibration-quantlib.html >>>>> >>>>> and I found a python example for GSR model calibration. >>>>> https://github.com/mlungwitz/notebooks/blob/master/GSR_Example.ipynb >>>>> >>>>> What I tried to do is to calibrate the same european swaption vol surface in the 1st python example based on 2nd example gsr model specification, what I added is: >>>>> gsr = ql.Gsr(term_structure,stepDates, sigmas,reversions); >>>>> engine = ql.Gaussian1dSwaptionEngine(gsr, 64, 7.0, True, False, term_structure) >>>>> swaptions = create_swaption_helpers(data, index, term_structure, engine) >>>>> >>>>> optimization_method = ql.LevenbergMarquardt(1.0e-8,1.0e-8,1.0e-8) >>>>> end_criteria = ql.EndCriteria(1000, 100, 1e-6, 1e-8, 1e-8) >>>>> model.calibrate(swaptions, optimization_method, end_criteria) >>>>> >>>>> But this will give me this error: >>>>> >>>>> RuntimeError: less functions (5) than available variables (12) >>>>> >>>>> Is the error caused by Gaussian1dSwaptionEngine? Should I proceed with Gaussian1dNonstandardSwaptionEngine? What change should I do to make it work ? >>>>> >>>>> >>>>> Thanks, >>>>> >>>>> Mark >>>>> >>>>> _______________________________________________ >>>>> QuantLib-users mailing list >>>>> Qua...@li... >>>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users >>> >>> _______________________________________________ >>> QuantLib-users mailing list >>> Qua...@li... >>> https://lists.sourceforge.net/lists/listinfo/quantlib-users |
|
From: Daniel <gr...@gm...> - 2022-08-24 09:48:06
|
Peter,
Thank you for your reply.
Just want to make sure overwriting T means I should change below statement
model = ql.Gsr(term_structure,stepDates, sigmas,reversions,T=60);
to use some other T value instead, am I right?
Two questions:
1. What is the meaning of T here?
2. What is the unit for T (month, year)?
3. If my calibrated swaption has a maximum 30Y x 30Y matrix, what is the T
value I should use?
Thanks,
Mark
On Tue, Aug 23, 2022 at 1:30 PM Peter Caspers <pca...@gm...>
wrote:
> Hi
>
> the GSR model operates in the T-forward measure with T=60 by default. You
> can overwrite T in the constructor if longer horizons are needed (as it
> appears to be the case in your setup).
>
> Best
> Peter
>
>
> Daniel <gr...@gm...> schrieb am Mo. 22. Aug. 2022 um 21:25:
>
>> Thank you Dmitri for your information. Actually I increased the vol
>> surface number to more than to be calibration volatility number, now I can
>> solve for mean reversion and volatility now.
>>
>> But experience another error when I tried to change the yield curve and
>> volatility surface to some other type, I got below error.
>>
>> ---> 14 model.calibrate(swaptions, optimization_method, end_criteria)
>>
>> ~\.conda\envs\tf-gpu\lib\site-packages\QuantLib\QuantLib.py in calibrate(self, *args) def calibrate(self, *args):> return _QuantLib.Gsr_calibrate(self, *args) def setParams(self, params):
>> RuntimeError: G(t,w) should be called with (t,w)=(29.9985,60.0055) in Range [0,60].
>>
>>
>> What does this runtimeError mean?
>>
>>
>> Thanks,
>>
>> Mark
>>
>>
>>
>>
>>
>>
>> On Mon, Aug 22, 2022 at 12:50 PM Dmitri Goloubentsev <
>> dm...@ma...> wrote:
>>
>>> Hi Mark,
>>>
>>>
>>> The error is coming from LM optimiser. You should reduce number of
>>> points in your vol surface or add more calibration instruments or add some
>>> sort of regularisation.
>>>
>>> Kind regards,
>>> Dmitri.
>>>
>>> On Mon, 22 Aug 2022, 17:26 Daniel, <gr...@gm...> wrote:
>>>
>>>> All,
>>>>
>>>> Please forgive me if someone has answered a similar question before.
>>>>
>>>> I am trying to calibrate interest rate vol surface using GSR (gaussian
>>>> short rate model) based on this post
>>>>
>>>> http://gouthamanbalaraman.com/blog/short-interest-rate-model-calibration-quantlib.html
>>>>
>>>> and I found a python example for GSR model calibration.
>>>> https://github.com/mlungwitz/notebooks/blob/master/GSR_Example.ipynb
>>>>
>>>> What I tried to do is to calibrate the same european swaption vol
>>>> surface in the 1st python example based on 2nd example gsr model
>>>> specification, what I added is:
>>>> gsr = ql.Gsr(term_structure,stepDates, sigmas,reversions);
>>>> engine = ql.Gaussian1dSwaptionEngine(gsr, 64, 7.0, True, False,
>>>> term_structure)
>>>> swaptions = create_swaption_helpers(data, index, term_structure, engine)
>>>>
>>>> optimization_method = ql.LevenbergMarquardt(1.0e-8,1.0e-8,1.0e-8)
>>>> end_criteria = ql.EndCriteria(1000, 100, 1e-6, 1e-8, 1e-8)
>>>> model.calibrate(swaptions, optimization_method, end_criteria)
>>>>
>>>> But this will give me this error:
>>>>
>>>> RuntimeError: less functions (5) than available variables (12)
>>>>
>>>> Is the error caused by Gaussian1dSwaptionEngine? Should I proceed with Gaussian1dNonstandardSwaptionEngine? What change should I do to make it work ?
>>>>
>>>>
>>>> Thanks,
>>>>
>>>> Mark
>>>>
>>>> _______________________________________________
>>>> QuantLib-users mailing list
>>>> Qua...@li...
>>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>>>>
>>> _______________________________________________
>> QuantLib-users mailing list
>> Qua...@li...
>> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>>
>
|
|
From: Aditya G. <adi...@co...> - 2022-08-24 02:53:23
|
Hi Peter,
The index fixings are very close (within say 1bp). If we use a mean reversion of 5.03... the rate is very similar too, but changes for other values of the mean reversion. Has Quantlib factored in the convexity? And if so, how is it reflected?
print('Price:', instrument.price(yts))
print('Rate:', instrument.rate())
print('ATM Strike: ', volCube.atmStrike(startDate, ql.Period('1y')))
print('Adjusted Fixing:', instrument.adjustedFixing())
print('Index Fixing:', instrument.indexFixing())
print('Undiscounted amount:', instrument.amount())
print('Cap:', instrument.effectiveCap())
print('Floor:', instrument.effectiveFloor())
print('Spread:', instrument.spread())
print('Convexity Adjustment:', instrument.convexityAdjustment()
print('Fixing Date:', instrument.fixingDate())
print(instrument.index().name())
print('Accrual Days:', instrument.accrualDays())
print('Accrual Start:', instrument.accrualStartDate())
print('Accrual End:', instrument.accrualEndDate())
print('Accrued Amount:', instrument.accruedAmount(today))
Price: -1537877.96969176
Rate: -0.0015730095034598435
ATM Strike: 0.03253706033376852
Adjusted Fixing: -0.0015730095034598435
Index Fixing: -0.0011397955055623271
Undiscounted amount: -1594856.8576745635
Cap: -0.0008
Floor: 3.4028234663852886e+38
Spread: 0.0
Convexity Adjustment: 253.58685952193696
Fixing Date: September 26th, 2022
UsdLiborSwapIsdaFixAm30Y 30/360 (Bond Basis)(1.0000) + UsdLiborSwapIsdaFixAm10Y 30/360 (Bond Basis)(-1.0000)
Accrual Days: 365
Accrual Start: September 26th, 2022
Accrual End: September 26th, 2023
Accrued Amount: 0.0
-----Original Message-----
From: Peter Caspers <pca...@gm...>
Sent: Friday, August 19, 2022 5:26 PM
To: Aditya Gupta <adi...@co...>
Cc: qua...@li...
Subject: Re: [Quantlib-users] CMS Spread Cap Pricing
CAUTION: EXTERNAL Sender
Hi Aditya,
did you check whether the forward swap rates estimated purely on the curve are matching? I.e. excluding the convexity?
Thanks
Peter
On Fri, 19 Aug 2022 at 07:19, Aditya Gupta <adi...@co...> wrote:
>
> Hi Peter,
>
> The strike is in % and it is the difference between the two forward CMS rates with included convexity. What is the unit for the QL atmStrike and how can we convert between the two to benchmark? For now, we can stick to the Linearly interpolated cube (as I would like to use normal volatilities) and try and see the errors there.
>
> For constant volatility, the error is around 10%. Would it be possible to reduce this error further, even though BBG is most likely using a different pricing engine?
>
> Thanks,
> Aditya
> ________________________________
> From: Peter Caspers <pca...@gm...>
> Sent: Thursday, August 18, 2022 2:49 PM
> To: Aditya Gupta <adi...@co...>
> Cc: qua...@li...
> <qua...@li...>
> Subject: Re: [Quantlib-users] CMS Spread Cap Pricing
>
> CAUTION: EXTERNAL Sender
>
>
> Hi Aditya,
>
> I suppose the BBG atmStrike is in percent, i.e. -0.000759 in unit
> scale? What is that exactly, one of the forward CMS rates or their
> difference? Including or excluding convexity?
>
> Since you are using normal vols, you can't use SwaptionVolCube1 which
> only supports lognormal and shifted lognormal input vols at the
> moment. You could use the interpolated cube and feed a wider range of
> strikes if you can get these values from BBG.
>
> Thanks
> Peter
>
> Peter
>
>
> On Tue, 16 Aug 2022 at 08:11, Aditya Gupta <adi...@co...> wrote:
> >
> > Hi Peter,
> >
> >
> >
> > When I use a constant volatility, the error is within say 10%, and can be attributed to the pricing engine (maybe there are other errors, but it is not the current focus). With the linear volatility cube, the atmStrike(startDate, ql.Period(‘1y’)) is returning 0.03253 but BBG has an atmStrike of -0.0759. I tried to implement a Sabr cube and got the following error:
> >
> >
> >
> > guess = nRows * [0]
> >
> > for n in range(nRows):
> >
> > guess[n] = (ql.QuoteHandle(ql.SimpleQuote(0.1)),
> >
> > ql.QuoteHandle(ql.SimpleQuote(0.1)),
> >
> > ql.QuoteHandle(ql.SimpleQuote(0.1)),
> >
> > ql.QuoteHandle(ql.SimpleQuote(0.1)))
> >
> >
> >
> > isParameterFixed = [False,False,False,False]
> >
> > volCubeSabr =
> > ql.SwaptionVolCube1(swaptionVolMatrixHandle,optionTenors,
> >
> > swapTenors, strikeSpreads, volSpreads,
> >
> > swapIndex1, swapIndex2,
> > vegaWeightedSmileFit,
> >
> > guess, isParameterFixed, True)
> >
> >
> >
> > RuntimeError: global swaptions calibration failed: error tolerance exceeded:
> >
> > using rmsError tolerance 0.01,
> >
> > option maturity = October 25th, 2022,
> >
> > swap tenor = 1Y,
> >
> > rms error = 19.629123 %,
> >
> > max error = 32.809999 %,
> >
> > alpha = 1.70317n beta = 0.130399
> >
> > nu = 82.568
> >
> > rho = 0.222489
> >
> >
> >
> > I tried to look at LognormalCmsSpreadPricer.cpp and all the related source code files, but cannot figure out how atmStrike is calculated and how to debug this. It seems that there might be a more grave underlying error in my code if the atmStrike is nowhere near the expected value. Do let me know what you think of this, and if you need me to provide any more information. Again, thank you so much for your input.
> >
> >
> > Regards,
> >
> > Aditya Gupta.
> >
> > ________________________________
> > Complus Asset Management Limited ("CAML") is licensed by the Securities and Futures Commission of Hong Kong (“SFC”) for the Regulated Activities of Advising on Securities and Asset Management with CE number AWX256. CAML is subject to certain SFC codes and regulations, details of which can be found on the SFC's website at http://www.sfc.hk. CAML is a company incorporated in Hong Kong with its registered office and principal place of business as indicated above.
> >
> > Unless specifically indicated, this message should not be construed as an offer to sell or solicitation to purchase any securities, financial products or services or the giving of investment advice within or outside Hong Kong. This message is intended solely for the person(s) to whom it is addressed. If you receive this message in error, please immediately delete it and all copies of it from your computer network or hard copies, and notify the sender.
|
|
From: Peter C. <pca...@gm...> - 2022-08-23 17:31:14
|
Hi the GSR model operates in the T-forward measure with T=60 by default. You can overwrite T in the constructor if longer horizons are needed (as it appears to be the case in your setup). Best Peter Daniel <gr...@gm...> schrieb am Mo. 22. Aug. 2022 um 21:25: > Thank you Dmitri for your information. Actually I increased the vol > surface number to more than to be calibration volatility number, now I can > solve for mean reversion and volatility now. > > But experience another error when I tried to change the yield curve and > volatility surface to some other type, I got below error. > > ---> 14 model.calibrate(swaptions, optimization_method, end_criteria) > > ~\.conda\envs\tf-gpu\lib\site-packages\QuantLib\QuantLib.py in calibrate(self, *args) def calibrate(self, *args):> return _QuantLib.Gsr_calibrate(self, *args) def setParams(self, params): > RuntimeError: G(t,w) should be called with (t,w)=(29.9985,60.0055) in Range [0,60]. > > > What does this runtimeError mean? > > > Thanks, > > Mark > > > > > > > On Mon, Aug 22, 2022 at 12:50 PM Dmitri Goloubentsev <dm...@ma...> > wrote: > >> Hi Mark, >> >> >> The error is coming from LM optimiser. You should reduce number of points >> in your vol surface or add more calibration instruments or add some sort of >> regularisation. >> >> Kind regards, >> Dmitri. >> >> On Mon, 22 Aug 2022, 17:26 Daniel, <gr...@gm...> wrote: >> >>> All, >>> >>> Please forgive me if someone has answered a similar question before. >>> >>> I am trying to calibrate interest rate vol surface using GSR (gaussian >>> short rate model) based on this post >>> >>> http://gouthamanbalaraman.com/blog/short-interest-rate-model-calibration-quantlib.html >>> >>> and I found a python example for GSR model calibration. >>> https://github.com/mlungwitz/notebooks/blob/master/GSR_Example.ipynb >>> >>> What I tried to do is to calibrate the same european swaption vol >>> surface in the 1st python example based on 2nd example gsr model >>> specification, what I added is: >>> gsr = ql.Gsr(term_structure,stepDates, sigmas,reversions); >>> engine = ql.Gaussian1dSwaptionEngine(gsr, 64, 7.0, True, False, >>> term_structure) >>> swaptions = create_swaption_helpers(data, index, term_structure, engine) >>> >>> optimization_method = ql.LevenbergMarquardt(1.0e-8,1.0e-8,1.0e-8) >>> end_criteria = ql.EndCriteria(1000, 100, 1e-6, 1e-8, 1e-8) >>> model.calibrate(swaptions, optimization_method, end_criteria) >>> >>> But this will give me this error: >>> >>> RuntimeError: less functions (5) than available variables (12) >>> >>> Is the error caused by Gaussian1dSwaptionEngine? Should I proceed with Gaussian1dNonstandardSwaptionEngine? What change should I do to make it work ? >>> >>> >>> Thanks, >>> >>> Mark >>> >>> _______________________________________________ >>> QuantLib-users mailing list >>> Qua...@li... >>> https://lists.sourceforge.net/lists/listinfo/quantlib-users >>> >> _______________________________________________ > QuantLib-users mailing list > Qua...@li... > https://lists.sourceforge.net/lists/listinfo/quantlib-users > |
|
From: Keithan S. <smi...@gm...> - 2022-08-23 10:03:01
|
Hello everyone, We have successfully solved the below query, regarding building a new class in the quantlib library for use in Python via a wrapper. Currently working on transforming swap index for use with CDS index. Sincerely, Keithan Smith. > On Aug 22, 2022, at 15:00, Keithan Smith <smi...@gm...> wrote: > > Hi can I develop my own class inside of quantlib solution to be wrapped to python? If so, how? > > Also, Is there currently functionality for CDSindex pricing? i see single default swaps but can find for the index > > Sincerely, > Keithan Smith. |
|
From: Daniel <gr...@gm...> - 2022-08-22 19:24:56
|
Thank you Dmitri for your information. Actually I increased the vol surface number to more than to be calibration volatility number, now I can solve for mean reversion and volatility now. But experience another error when I tried to change the yield curve and volatility surface to some other type, I got below error. ---> 14 model.calibrate(swaptions, optimization_method, end_criteria) ~\.conda\envs\tf-gpu\lib\site-packages\QuantLib\QuantLib.py in calibrate(self, *args) def calibrate(self, *args):> return _QuantLib.Gsr_calibrate(self, *args) def setParams(self, params): RuntimeError: G(t,w) should be called with (t,w)=(29.9985,60.0055) in Range [0,60]. What does this runtimeError mean? Thanks, Mark On Mon, Aug 22, 2022 at 12:50 PM Dmitri Goloubentsev <dm...@ma...> wrote: > Hi Mark, > > > The error is coming from LM optimiser. You should reduce number of points > in your vol surface or add more calibration instruments or add some sort of > regularisation. > > Kind regards, > Dmitri. > > On Mon, 22 Aug 2022, 17:26 Daniel, <gr...@gm...> wrote: > >> All, >> >> Please forgive me if someone has answered a similar question before. >> >> I am trying to calibrate interest rate vol surface using GSR (gaussian >> short rate model) based on this post >> >> http://gouthamanbalaraman.com/blog/short-interest-rate-model-calibration-quantlib.html >> >> and I found a python example for GSR model calibration. >> https://github.com/mlungwitz/notebooks/blob/master/GSR_Example.ipynb >> >> What I tried to do is to calibrate the same european swaption vol surface >> in the 1st python example based on 2nd example gsr model specification, >> what I added is: >> gsr = ql.Gsr(term_structure,stepDates, sigmas,reversions); >> engine = ql.Gaussian1dSwaptionEngine(gsr, 64, 7.0, True, False, >> term_structure) >> swaptions = create_swaption_helpers(data, index, term_structure, engine) >> >> optimization_method = ql.LevenbergMarquardt(1.0e-8,1.0e-8,1.0e-8) >> end_criteria = ql.EndCriteria(1000, 100, 1e-6, 1e-8, 1e-8) >> model.calibrate(swaptions, optimization_method, end_criteria) >> >> But this will give me this error: >> >> RuntimeError: less functions (5) than available variables (12) >> >> Is the error caused by Gaussian1dSwaptionEngine? Should I proceed with Gaussian1dNonstandardSwaptionEngine? What change should I do to make it work ? >> >> >> Thanks, >> >> Mark >> >> _______________________________________________ >> QuantLib-users mailing list >> Qua...@li... >> https://lists.sourceforge.net/lists/listinfo/quantlib-users >> > |
|
From: Dmitri G. <dm...@ma...> - 2022-08-22 17:20:25
|
Hi Mark, The error is coming from LM optimiser. You should reduce number of points in your vol surface or add more calibration instruments or add some sort of regularisation. Kind regards, Dmitri. On Mon, 22 Aug 2022, 17:26 Daniel, <gr...@gm...> wrote: > All, > > Please forgive me if someone has answered a similar question before. > > I am trying to calibrate interest rate vol surface using GSR (gaussian > short rate model) based on this post > > http://gouthamanbalaraman.com/blog/short-interest-rate-model-calibration-quantlib.html > > and I found a python example for GSR model calibration. > https://github.com/mlungwitz/notebooks/blob/master/GSR_Example.ipynb > > What I tried to do is to calibrate the same european swaption vol surface > in the 1st python example based on 2nd example gsr model specification, > what I added is: > gsr = ql.Gsr(term_structure,stepDates, sigmas,reversions); > engine = ql.Gaussian1dSwaptionEngine(gsr, 64, 7.0, True, False, > term_structure) > swaptions = create_swaption_helpers(data, index, term_structure, engine) > > optimization_method = ql.LevenbergMarquardt(1.0e-8,1.0e-8,1.0e-8) > end_criteria = ql.EndCriteria(1000, 100, 1e-6, 1e-8, 1e-8) > model.calibrate(swaptions, optimization_method, end_criteria) > > But this will give me this error: > > RuntimeError: less functions (5) than available variables (12) > > Is the error caused by Gaussian1dSwaptionEngine? Should I proceed with Gaussian1dNonstandardSwaptionEngine? What change should I do to make it work ? > > > Thanks, > > Mark > > _______________________________________________ > QuantLib-users mailing list > Qua...@li... > https://lists.sourceforge.net/lists/listinfo/quantlib-users > |
|
From: Daniel <gr...@gm...> - 2022-08-22 16:23:20
|
All, Please forgive me if someone has answered a similar question before. I am trying to calibrate interest rate vol surface using GSR (gaussian short rate model) based on this post http://gouthamanbalaraman.com/blog/short-interest-rate-model-calibration-quantlib.html and I found a python example for GSR model calibration. https://github.com/mlungwitz/notebooks/blob/master/GSR_Example.ipynb What I tried to do is to calibrate the same european swaption vol surface in the 1st python example based on 2nd example gsr model specification, what I added is: gsr = ql.Gsr(term_structure,stepDates, sigmas,reversions); engine = ql.Gaussian1dSwaptionEngine(gsr, 64, 7.0, True, False, term_structure) swaptions = create_swaption_helpers(data, index, term_structure, engine) optimization_method = ql.LevenbergMarquardt(1.0e-8,1.0e-8,1.0e-8) end_criteria = ql.EndCriteria(1000, 100, 1e-6, 1e-8, 1e-8) model.calibrate(swaptions, optimization_method, end_criteria) But this will give me this error: RuntimeError: less functions (5) than available variables (12) Is the error caused by Gaussian1dSwaptionEngine? Should I proceed with Gaussian1dNonstandardSwaptionEngine? What change should I do to make it work ? Thanks, Mark |
|
From: Keithan S. <smi...@gm...> - 2022-08-22 14:00:29
|
Hi can I develop my own class inside of quantlib solution to be wrapped to python? If so, how? Also, Is there currently functionality for CDSindex pricing? i see single default swaps but can find for the index Sincerely, Keithan Smith. |
|
From: Peter C. <pca...@gm...> - 2022-08-19 09:26:03
|
Hi Aditya,
did you check whether the forward swap rates estimated purely on the
curve are matching? I.e. excluding the convexity?
Thanks
Peter
On Fri, 19 Aug 2022 at 07:19, Aditya Gupta <adi...@co...> wrote:
>
> Hi Peter,
>
> The strike is in % and it is the difference between the two forward CMS rates with included convexity. What is the unit for the QL atmStrike and how can we convert between the two to benchmark? For now, we can stick to the Linearly interpolated cube (as I would like to use normal volatilities) and try and see the errors there.
>
> For constant volatility, the error is around 10%. Would it be possible to reduce this error further, even though BBG is most likely using a different pricing engine?
>
> Thanks,
> Aditya
> ________________________________
> From: Peter Caspers <pca...@gm...>
> Sent: Thursday, August 18, 2022 2:49 PM
> To: Aditya Gupta <adi...@co...>
> Cc: qua...@li... <qua...@li...>
> Subject: Re: [Quantlib-users] CMS Spread Cap Pricing
>
> CAUTION: EXTERNAL Sender
>
>
> Hi Aditya,
>
> I suppose the BBG atmStrike is in percent, i.e. -0.000759 in unit
> scale? What is that exactly, one of the forward CMS rates or their
> difference? Including or excluding convexity?
>
> Since you are using normal vols, you can't use SwaptionVolCube1 which
> only supports lognormal and shifted lognormal input vols at the
> moment. You could use the interpolated cube and feed a wider range of
> strikes if you can get these values from BBG.
>
> Thanks
> Peter
>
> Peter
>
>
> On Tue, 16 Aug 2022 at 08:11, Aditya Gupta <adi...@co...> wrote:
> >
> > Hi Peter,
> >
> >
> >
> > When I use a constant volatility, the error is within say 10%, and can be attributed to the pricing engine (maybe there are other errors, but it is not the current focus). With the linear volatility cube, the atmStrike(startDate, ql.Period(‘1y’)) is returning 0.03253 but BBG has an atmStrike of -0.0759. I tried to implement a Sabr cube and got the following error:
> >
> >
> >
> > guess = nRows * [0]
> >
> > for n in range(nRows):
> >
> > guess[n] = (ql.QuoteHandle(ql.SimpleQuote(0.1)),
> >
> > ql.QuoteHandle(ql.SimpleQuote(0.1)),
> >
> > ql.QuoteHandle(ql.SimpleQuote(0.1)),
> >
> > ql.QuoteHandle(ql.SimpleQuote(0.1)))
> >
> >
> >
> > isParameterFixed = [False,False,False,False]
> >
> > volCubeSabr = ql.SwaptionVolCube1(swaptionVolMatrixHandle,optionTenors,
> >
> > swapTenors, strikeSpreads, volSpreads,
> >
> > swapIndex1, swapIndex2, vegaWeightedSmileFit,
> >
> > guess, isParameterFixed, True)
> >
> >
> >
> > RuntimeError: global swaptions calibration failed: error tolerance exceeded:
> >
> > using rmsError tolerance 0.01,
> >
> > option maturity = October 25th, 2022,
> >
> > swap tenor = 1Y,
> >
> > rms error = 19.629123 %,
> >
> > max error = 32.809999 %,
> >
> > alpha = 1.70317n beta = 0.130399
> >
> > nu = 82.568
> >
> > rho = 0.222489
> >
> >
> >
> > I tried to look at LognormalCmsSpreadPricer.cpp and all the related source code files, but cannot figure out how atmStrike is calculated and how to debug this. It seems that there might be a more grave underlying error in my code if the atmStrike is nowhere near the expected value. Do let me know what you think of this, and if you need me to provide any more information. Again, thank you so much for your input.
> >
> >
> > Regards,
> >
> > Aditya Gupta.
> >
> > ________________________________
> > Complus Asset Management Limited ("CAML") is licensed by the Securities and Futures Commission of Hong Kong (“SFC”) for the Regulated Activities of Advising on Securities and Asset Management with CE number AWX256. CAML is subject to certain SFC codes and regulations, details of which can be found on the SFC's website at http://www.sfc.hk. CAML is a company incorporated in Hong Kong with its registered office and principal place of business as indicated above.
> >
> > Unless specifically indicated, this message should not be construed as an offer to sell or solicitation to purchase any securities, financial products or services or the giving of investment advice within or outside Hong Kong. This message is intended solely for the person(s) to whom it is addressed. If you receive this message in error, please immediately delete it and all copies of it from your computer network or hard copies, and notify the sender.
|
|
From: Aditya G. <adi...@co...> - 2022-08-19 05:19:33
|
Hi Peter,
The strike is in % and it is the difference between the two forward CMS rates with included convexity. What is the unit for the QL atmStrike and how can we convert between the two to benchmark? For now, we can stick to the Linearly interpolated cube (as I would like to use normal volatilities) and try and see the errors there.
For constant volatility, the error is around 10%. Would it be possible to reduce this error further, even though BBG is most likely using a different pricing engine?
Thanks,
Aditya
________________________________
From: Peter Caspers <pca...@gm...>
Sent: Thursday, August 18, 2022 2:49 PM
To: Aditya Gupta <adi...@co...>
Cc: qua...@li... <qua...@li...>
Subject: Re: [Quantlib-users] CMS Spread Cap Pricing
CAUTION: EXTERNAL Sender
Hi Aditya,
I suppose the BBG atmStrike is in percent, i.e. -0.000759 in unit
scale? What is that exactly, one of the forward CMS rates or their
difference? Including or excluding convexity?
Since you are using normal vols, you can't use SwaptionVolCube1 which
only supports lognormal and shifted lognormal input vols at the
moment. You could use the interpolated cube and feed a wider range of
strikes if you can get these values from BBG.
Thanks
Peter
Peter
On Tue, 16 Aug 2022 at 08:11, Aditya Gupta <adi...@co...> wrote:
>
> Hi Peter,
>
>
>
> When I use a constant volatility, the error is within say 10%, and can be attributed to the pricing engine (maybe there are other errors, but it is not the current focus). With the linear volatility cube, the atmStrike(startDate, ql.Period(‘1y’)) is returning 0.03253 but BBG has an atmStrike of -0.0759. I tried to implement a Sabr cube and got the following error:
>
>
>
> guess = nRows * [0]
>
> for n in range(nRows):
>
> guess[n] = (ql.QuoteHandle(ql.SimpleQuote(0.1)),
>
> ql.QuoteHandle(ql.SimpleQuote(0.1)),
>
> ql.QuoteHandle(ql.SimpleQuote(0.1)),
>
> ql.QuoteHandle(ql.SimpleQuote(0.1)))
>
>
>
> isParameterFixed = [False,False,False,False]
>
> volCubeSabr = ql.SwaptionVolCube1(swaptionVolMatrixHandle,optionTenors,
>
> swapTenors, strikeSpreads, volSpreads,
>
> swapIndex1, swapIndex2, vegaWeightedSmileFit,
>
> guess, isParameterFixed, True)
>
>
>
> RuntimeError: global swaptions calibration failed: error tolerance exceeded:
>
> using rmsError tolerance 0.01,
>
> option maturity = October 25th, 2022,
>
> swap tenor = 1Y,
>
> rms error = 19.629123 %,
>
> max error = 32.809999 %,
>
> alpha = 1.70317n beta = 0.130399
>
> nu = 82.568
>
> rho = 0.222489
>
>
>
> I tried to look at LognormalCmsSpreadPricer.cpp and all the related source code files, but cannot figure out how atmStrike is calculated and how to debug this. It seems that there might be a more grave underlying error in my code if the atmStrike is nowhere near the expected value. Do let me know what you think of this, and if you need me to provide any more information. Again, thank you so much for your input.
>
>
> Regards,
>
> Aditya Gupta.
>
> ________________________________
> Complus Asset Management Limited ("CAML") is licensed by the Securities and Futures Commission of Hong Kong (“SFC”) for the Regulated Activities of Advising on Securities and Asset Management with CE number AWX256. CAML is subject to certain SFC codes and regulations, details of which can be found on the SFC's website at http://www.sfc.hk. CAML is a company incorporated in Hong Kong with its registered office and principal place of business as indicated above.
>
> Unless specifically indicated, this message should not be construed as an offer to sell or solicitation to purchase any securities, financial products or services or the giving of investment advice within or outside Hong Kong. This message is intended solely for the person(s) to whom it is addressed. If you receive this message in error, please immediately delete it and all copies of it from your computer network or hard copies, and notify the sender.
|
|
From: Peter C. <pca...@gm...> - 2022-08-18 06:50:01
|
Hi Aditya,
I suppose the BBG atmStrike is in percent, i.e. -0.000759 in unit
scale? What is that exactly, one of the forward CMS rates or their
difference? Including or excluding convexity?
Since you are using normal vols, you can't use SwaptionVolCube1 which
only supports lognormal and shifted lognormal input vols at the
moment. You could use the interpolated cube and feed a wider range of
strikes if you can get these values from BBG.
Thanks
Peter
Peter
On Tue, 16 Aug 2022 at 08:11, Aditya Gupta <adi...@co...> wrote:
>
> Hi Peter,
>
>
>
> When I use a constant volatility, the error is within say 10%, and can be attributed to the pricing engine (maybe there are other errors, but it is not the current focus). With the linear volatility cube, the atmStrike(startDate, ql.Period(‘1y’)) is returning 0.03253 but BBG has an atmStrike of -0.0759. I tried to implement a Sabr cube and got the following error:
>
>
>
> guess = nRows * [0]
>
> for n in range(nRows):
>
> guess[n] = (ql.QuoteHandle(ql.SimpleQuote(0.1)),
>
> ql.QuoteHandle(ql.SimpleQuote(0.1)),
>
> ql.QuoteHandle(ql.SimpleQuote(0.1)),
>
> ql.QuoteHandle(ql.SimpleQuote(0.1)))
>
>
>
> isParameterFixed = [False,False,False,False]
>
> volCubeSabr = ql.SwaptionVolCube1(swaptionVolMatrixHandle,optionTenors,
>
> swapTenors, strikeSpreads, volSpreads,
>
> swapIndex1, swapIndex2, vegaWeightedSmileFit,
>
> guess, isParameterFixed, True)
>
>
>
> RuntimeError: global swaptions calibration failed: error tolerance exceeded:
>
> using rmsError tolerance 0.01,
>
> option maturity = October 25th, 2022,
>
> swap tenor = 1Y,
>
> rms error = 19.629123 %,
>
> max error = 32.809999 %,
>
> alpha = 1.70317n beta = 0.130399
>
> nu = 82.568
>
> rho = 0.222489
>
>
>
> I tried to look at LognormalCmsSpreadPricer.cpp and all the related source code files, but cannot figure out how atmStrike is calculated and how to debug this. It seems that there might be a more grave underlying error in my code if the atmStrike is nowhere near the expected value. Do let me know what you think of this, and if you need me to provide any more information. Again, thank you so much for your input.
>
>
> Regards,
>
> Aditya Gupta.
>
> ________________________________
> Complus Asset Management Limited ("CAML") is licensed by the Securities and Futures Commission of Hong Kong (“SFC”) for the Regulated Activities of Advising on Securities and Asset Management with CE number AWX256. CAML is subject to certain SFC codes and regulations, details of which can be found on the SFC's website at http://www.sfc.hk. CAML is a company incorporated in Hong Kong with its registered office and principal place of business as indicated above.
>
> Unless specifically indicated, this message should not be construed as an offer to sell or solicitation to purchase any securities, financial products or services or the giving of investment advice within or outside Hong Kong. This message is intended solely for the person(s) to whom it is addressed. If you receive this message in error, please immediately delete it and all copies of it from your computer network or hard copies, and notify the sender.
|
|
From: Aditya G. <adi...@co...> - 2022-08-16 06:26:01
|
Hi Peter,
When I use a constant volatility, the error is within say 10%, and can be attributed to the pricing engine (maybe there are other errors, but it is not the current focus). With the linear volatility cube, the atmStrike(startDate, ql.Period('1y')) is returning 0.03253 but BBG has an atmStrike of -0.0759. I tried to implement a Sabr cube and got the following error:
guess = nRows * [0]
for n in range(nRows):
guess[n] = (ql.QuoteHandle(ql.SimpleQuote(0.1)),
ql.QuoteHandle(ql.SimpleQuote(0.1)),
ql.QuoteHandle(ql.SimpleQuote(0.1)),
ql.QuoteHandle(ql.SimpleQuote(0.1)))
isParameterFixed = [False,False,False,False]
volCubeSabr = ql.SwaptionVolCube1(swaptionVolMatrixHandle,optionTenors,
swapTenors, strikeSpreads, volSpreads,
swapIndex1, swapIndex2, vegaWeightedSmileFit,
guess, isParameterFixed, True)
RuntimeError: global swaptions calibration failed: error tolerance exceeded:
using rmsError tolerance 0.01,
option maturity = October 25th, 2022,
swap tenor = 1Y,
rms error = 19.629123 %,
max error = 32.809999 %,
alpha = 1.70317n beta = 0.130399
nu = 82.568
rho = 0.222489
I tried to look at LognormalCmsSpreadPricer.cpp and all the related source code files, but cannot figure out how atmStrike is calculated and how to debug this. It seems that there might be a more grave underlying error in my code if the atmStrike is nowhere near the expected value. Do let me know what you think of this, and if you need me to provide any more information. Again, thank you so much for your input.
Regards,
Aditya Gupta.
________________________________
Complus Asset Management Limited ("CAML") is licensed by the Securities and Futures Commission of Hong Kong ("SFC") for the Regulated Activities of Advising on Securities and Asset Management with CE number AWX256. CAML is subject to certain SFC codes and regulations, details of which can be found on the SFC's website at http://www.sfc.hk<http://www.sfc.hk/>. CAML is a company incorporated in Hong Kong with its registered office and principal place of business as indicated above.
Unless specifically indicated, this message should not be construed as an offer to sell or solicitation to purchase any securities, financial products or services or the giving of investment advice within or outside Hong Kong. This message is intended solely for the person(s) to whom it is addressed. If you receive this message in error, please immediately delete it and all copies of it from your computer network or hard copies, and notify the sender.
|
|
From: Peter C. <pca...@gm...> - 2022-08-15 18:18:47
|
Hi Aditya, if I knew of an error in the code I would have fixed it already :-) As a first step I'd suggest to compare the adjusted CMS rates and compare to BBG if possible to see if we match their ATM level. As I said the CMS adjustment is dependent on the whole vol smile and if BBG uses a SABR model for vol extrapolation you won't match their vols outside ATM+-200 with a linearly interpolated cube. Thanks Peter On Mon, 15 Aug 2022 at 05:40, Aditya Gupta <adi...@co...> wrote: > Hi Peter, > > Regarding Bloomberg's model, it seems that it isn't very clear in their > documentation what engine is being used, but the Sabr cube is used for the > volatilities. I will try to contact support and ask them for more details. > > In any case, it seems that with a constant or non constant volatility, the > pricing is not accurate. What could be the error in the code? If Bloomberg > has a different model to the Quantlib one, the Quantlib model should still > be able to price within some reasonable error percentage. Since this is not > the case, where in the code could the mistake be? > > I am happy to send any updated code or fill in the missing lines. Thank > you for your time. > > Regards, > Aditya Gupta. > > On Aug 12, 2022 8:22 PM, Peter Caspers <pca...@gm...> wrote: > You don't often get email from pca...@gm.... Learn why this is > important <https://aka.ms/LearnAboutSenderIdentification> > CAUTION: EXTERNAL Sender > > Hi Aditya > > 2 the error shouldn't show up unless you pass a volatilityType to the > pricer. Might have to do with how SWIG calls the constructor, not sure at > the moment. > > 3 what I meant was: the cms price depends on the volatilities outside the > quoted part ATM-200 / ATM+200 as well, so maybe BBG uses different > extrapolated vols than you do. And as I said, the model itself (and the > mean reversion value) have a big impact, so you might first want check what > BBG is doing > > Thanks > Peter > > > > On Fri, 12 Aug 2022 at 10:43, Aditya Gupta <adi...@co...> > wrote: > >> Hi Peter, >> >> >> >> For point 2) the LinearTsrPricer can take the input as >> ql.ConstantSwaptionVolatilityStructure, but the Lognormal pricer complains >> by saying: " if only an atm surface is given, the volatility type must be >> inherited" So I guess it is not possible. To create a ‘constant volatility >> cube’, I instantiated an atmMatrix with a constant value, but all the OTM >> and ITM swaptions had a volatility of 0. This way, for every triplet >> (strike, expiry, tenor), QL would return a constant volatility. It is a >> workaround, but it sufficed for my purpose. >> >> >> >> For 3) The code and data for the extrapolation is below. Some notes: >> >> >> >> volHandle at the end is passed into the pricer. The data is not complete >> but should be enough for debugging purposes. The BBG/QL volatility cubes >> seem to match to a decent degree (i.e. I don’t believe it is the source of >> the error). The volatilities are within 10 bps when testing random data >> points. However, the atm strike does not change (even if the volatility is >> constant) and does not agree with Bloomberg. >> >> >> >> normalVolCube = >> >> Expiry >> >> 1Yr >> >> 2Yr >> >> 3Yr >> >> 4Yr >> >> 5Yr >> >> 7Yr >> >> 10Yr >> >> 12Yr >> >> 15Yr >> >> 20Yr >> >> 25Yr >> >> 30Yr >> >> 1Mo >> >> 153.65 >> >> 160.51 >> >> 151.81 >> >> 143.8 >> >> 141.03 >> >> 129.88 >> >> 123.16 >> >> 118.88 >> >> 110.61 >> >> 108.9 >> >> 104.21 >> >> 101.91 >> >> 3Mo >> >> 150.06 >> >> 152.12 >> >> 149.09 >> >> 140.08 >> >> 134.79 >> >> 125.74 >> >> 118.65 >> >> 115.21 >> >> 112.26 >> >> 107.13 >> >> 103.41 >> >> 99.43 >> >> 6Mo >> >> 154.30 >> >> 150.74 >> >> 143.9 >> >> 137.09 >> >> 132.84 >> >> 123.31 >> >> 115.03 >> >> 111.31 >> >> 107.88 >> >> 103.41 >> >> 100.5 >> >> 96.31 >> >> 9Mo >> >> 155.29 >> >> 149.86 >> >> 142.87 >> >> 135.17 >> >> 130.97 >> >> 121.33 >> >> 112.06 >> >> 108.57 >> >> 105.04 >> >> 99.95 >> >> 97.12 >> >> 93.19 >> >> 1Yr >> >> 155.98 >> >> 149.11 >> >> 140.78 >> >> 133.39 >> >> 127.82 >> >> 118.13 >> >> 109.42 >> >> 106.32 >> >> 102.06 >> >> 97.15 >> >> 94.05 >> >> 90.8 >> >> 2Yr >> >> 142.61 >> >> 135.71 >> >> 129.64 >> >> 123.34 >> >> 118.42 >> >> 110 >> >> 102.05 >> >> 99.13 >> >> 95.18 >> >> 89.95 >> >> 86.93 >> >> 84.38 >> >> 3Yr >> >> 130.05 >> >> 124.03 >> >> 117.71 >> >> 113.72 >> >> 109.87 >> >> 102.87 >> >> 96.37 >> >> 93.46 >> >> 89.5 >> >> 84.21 >> >> 81.15 >> >> 79.07 >> >> 4Yr >> >> 118.85 >> >> 113.22 >> >> 108.13 >> >> 104.87 >> >> 102.41 >> >> 97.49 >> >> 91.33 >> >> 88.78 >> >> 85.31 >> >> 79.42 >> >> 77.09 >> >> 74.93 >> >> 5Yr >> >> 109.73 >> >> 104.94 >> >> 102.2 >> >> 99.09 >> >> 96.85 >> >> 92.53 >> >> 87.38 >> >> 84.84 >> >> 81.37 >> >> 76.05 >> >> 73.36 >> >> 72.24 >> >> 6Yr >> >> 102.35 >> >> 99.42 >> >> 97.38 >> >> 94.42 >> >> 92.18 >> >> 88.36 >> >> 83.64 >> >> 81.19 >> >> 77.88 >> >> 72.88 >> >> 70.54 >> >> 69.33 >> >> 7Yr >> >> 96.52 >> >> 93.55 >> >> 91.44 >> >> 89.36 >> >> 87.41 >> >> 84.15 >> >> 79.99 >> >> 77.68 >> >> 74.58 >> >> 70.13 >> >> 68.25 >> >> 66.85 >> >> 8Yr >> >> 91.55 >> >> 89.03 >> >> 87.38 >> >> 85.58 >> >> 83.78 >> >> 80.83 >> >> 77.08 >> >> 74.82 >> >> 71.81 >> >> 67.42 >> >> 65.22 >> >> 64.37 >> >> 9Yr >> >> 86.91 >> >> 85.11 >> >> 83.57 >> >> 81.89 >> >> 80.19 >> >> 77.48 >> >> 74.07 >> >> 71.89 >> >> 68.99 >> >> 64.76 >> >> 62.82 >> >> 62.05 >> >> 10Yr >> >> 83.39 >> >> 81.29 >> >> 79.55 >> >> 77.84 >> >> 76.4 >> >> 74.03 >> >> 71.19 >> >> 69.2 >> >> 66.54 >> >> 62.56 >> >> 60.74 >> >> 60.1 >> >> 12Yr >> >> 77.24 >> >> 75.44 >> >> 74 >> >> 72.38 >> >> 70.97 >> >> 69.1 >> >> 66.47 >> >> 64.68 >> >> 62.27 >> >> 58.88 >> >> 57.3 >> >> 56.85 >> >> 15Yr >> >> 68.34 >> >> 67.24 >> >> 65.61 >> >> 64.58 >> >> 64.34 >> >> 62.11 >> >> 60.25 >> >> 58.73 >> >> 56.7 >> >> 53.89 >> >> 52.72 >> >> 52.28 >> >> 20Yr >> >> 60.48 >> >> 60.35 >> >> 59.15 >> >> 58.46 >> >> 57.75 >> >> 56.64 >> >> 55.07 >> >> 53.74 >> >> 51.97 >> >> 49.87 >> >> 48.96 >> >> 48.63 >> >> 25Yr >> >> 58.04 >> >> 56.62 >> >> 55.74 >> >> 55.16 >> >> 54.62 >> >> 53.59 >> >> 52.25 >> >> 51.07 >> >> 49.5 >> >> 47.49 >> >> 46.72 >> >> 46.08 >> >> 30Yr >> >> 54.83 >> >> 53.31 >> >> 52.73 >> >> 52.34 >> >> 51.96 >> >> 51.06 >> >> 49.7 >> >> 48.76 >> >> 47.52 >> >> 45.21 >> >> 44.11 >> >> 44.1 >> >> >> >> >> >> strikeSpreadData Snippet: >> >> Term x Tenor >> >> -200bps >> >> -100bps >> >> -50bps >> >> -25bps >> >> ATM >> >> 25bps >> >> 50bps >> >> 100bps >> >> 200bps >> >> 3Mo X 1Yr >> >> 163.06 >> >> 147.61 >> >> 145.65 >> >> 147.02 >> >> 150.06 >> >> 154.64 >> >> 160.47 >> >> 174.74 >> >> 208.13 >> >> 3Mo X 2Yr >> >> 168.32 >> >> 152.63 >> >> 149.59 >> >> 150.1 >> >> 152.12 >> >> 155.6 >> >> 160.36 >> >> 172.72 >> >> 203.32 >> >> 3Mo X 3Yr >> >> 165.94 >> >> 150.14 >> >> 146.87 >> >> 147.22 >> >> 149.09 >> >> 152.42 >> >> 157.05 >> >> 169.18 >> >> 199.38 >> >> 3Mo X 5Yr >> >> 154.44 >> >> 137.68 >> >> 133.49 >> >> 133.36 >> >> 134.79 >> >> 137.76 >> >> 142.09 >> >> 153.73 >> >> 182.89 >> >> 3Mo X 7Yr >> >> 145.98 >> >> 128.92 >> >> 124.5 >> >> 124.31 >> >> 125.74 >> >> 128.76 >> >> 133.16 >> >> 144.93 >> >> 173.96 >> >> 3Mo X 10Yr >> >> 141.64 >> >> 123.6 >> >> 118.33 >> >> 117.66 >> >> 118.65 >> >> 121.3 >> >> 125.42 >> >> 136.78 >> >> 165.14 >> >> 3Mo X 12Yr >> >> 124.87 >> >> 115.74 >> >> 113.96 >> >> 114.17 >> >> 115.21 >> >> 117.06 >> >> 119.67 >> >> 126.74 >> >> 145.33 >> >> 3Mo X 15Yr >> >> 137.53 >> >> 118.56 >> >> 112.58 >> >> 111.56 >> >> 112.26 >> >> 114.72 >> >> 118.73 >> >> 130 >> >> 158.17 >> >> 3Mo X 20Yr >> >> 133.96 >> >> 114.4 >> >> 107.91 >> >> 106.63 >> >> 107.13 >> >> 109.46 >> >> 113.42 >> >> 124.65 >> >> 152.72 >> >> 3Mo X 25Yr >> >> 130.47 >> >> 110.79 >> >> 104.19 >> >> 102.88 >> >> 103.41 >> >> 105.83 >> >> 109.9 >> >> 121.34 >> >> 149.56 >> >> 3Mo X 30Yr >> >> 126.66 >> >> 106.9 >> >> 100.19 >> >> 98.87 >> >> 99.43 >> >> 101.94 >> >> 106.13 >> >> 117.77 >> >> 146.12 >> >> 6Mo X 1Yr >> >> 153.08 >> >> 147.26 >> >> 148.88 >> >> 151.13 >> >> 154.3 >> >> 158.3 >> >> 163.02 >> >> 174.1 >> >> 199.93 >> >> 6Mo X 2Yr >> >> 155.61 >> >> 147.49 >> >> 147.3 >> >> 148.56 >> >> 150.74 >> >> 153.82 >> >> 157.69 >> >> 167.34 >> >> 191.18 >> >> 6Mo X 3Yr >> >> 151.41 >> >> 141.74 >> >> 140.85 >> >> 141.85 >> >> 143.9 >> >> 146.93 >> >> 150.84 >> >> 160.73 >> >> 185.27 >> >> 6Mo X 5Yr >> >> 144.96 >> >> 132.89 >> >> 130.7 >> >> 131.19 >> >> 132.84 >> >> 135.64 >> >> 139.46 >> >> 149.43 >> >> 174.44 >> >> 6Mo X 7Yr >> >> 137.51 >> >> 124.48 >> >> 121.67 >> >> 121.87 >> >> 123.31 >> >> 125.95 >> >> 129.67 >> >> 139.51 >> >> 164.24 >> >> 6Mo X 10Yr >> >> 131.51 >> >> 117.49 >> >> 113.98 >> >> 113.86 >> >> 115.03 >> >> 117.47 >> >> 121.05 >> >> 130.73 >> >> 155.14 >> >> 6Mo X 12Yr >> >> 119.63 >> >> 111.38 >> >> 109.93 >> >> 110.24 >> >> 111.31 >> >> 113.15 >> >> 115.68 >> >> 122.45 >> >> 140.14 >> >> 6Mo X 15Yr >> >> 126.39 >> >> 111.49 >> >> 107.36 >> >> 106.95 >> >> 107.88 >> >> 110.16 >> >> 113.64 >> >> 123.22 >> >> 147.39 >> >> 6Mo X 20Yr >> >> 123.32 >> >> 107.85 >> >> 103.29 >> >> 102.66 >> >> 103.41 >> >> 105.58 >> >> 109 >> >> 118.53 >> >> 142.61 >> >> 6Mo X 25Yr >> >> 120.86 >> >> 105.2 >> >> 100.48 >> >> 99.78 >> >> 100.5 >> >> 102.67 >> >> 106.1 >> >> 115.7 >> >> 139.82 >> >> >> >> >> >> # ATM Volatility matrix >> >> volType = ql.Normal >> >> flatExtrapolation = False >> >> >> >> atmSwapTenors = [ql.Period(tenor[:-1]) for tenor in >> normalVolCube.columns[1:]] >> >> atmOptionTenors = [ql.Period(tenor[:-1]) for tenor in >> normalVolCube['Expiry']] >> >> >> >> normalVols = normalVolCube[normalVolCube.columns[1:]].apply( >> >> lambda x: x * 1e-4).values.tolist() >> >> >> >> swaptionVolMatrix = ql.SwaptionVolatilityMatrix(calendar, convention, >> >> atmOptionTenors, >> atmSwapTenors, >> >> ql.Matrix(normalVols), >> >> dayCounter, >> flatExtrapolation, >> >> volType) >> >> >> >> swaptionVolMatrixHandle = ql.SwaptionVolatilityStructureHandle( >> >> swaptionVolMatrix) >> >> >> >> strikeSpreads = [-200, -100, -50, -25, 0, 25, 50, 100, 200] >> >> strikeSpreads = [x * 1e-4 for x in strikeSpreads] >> >> >> >> optionTenors = [ >> >> ql.Period(3, ql.Months), >> >> ql.Period(6, ql.Months), >> >> ql.Period(9, ql.Months), >> >> ql.Period(1, ql.Years), >> >> ql.Period(3, ql.Years), >> >> ql.Period(5, ql.Years), >> >> ql.Period(7, ql.Years), >> >> ql.Period(10, ql.Years), >> >> ql.Period(15, ql.Years), >> >> ql.Period(20, ql.Years), >> >> ql.Period(30, ql.Years) >> >> ] >> >> >> >> swapTenors = [ >> >> ql.Period(1, ql.Years), >> >> ql.Period(2, ql.Years), >> >> ql.Period(3, ql.Years), >> >> ql.Period(5, ql.Years), >> >> ql.Period(7, ql.Years), >> >> ql.Period(10, ql.Years), >> >> ql.Period(12, ql.Years), >> >> ql.Period(15, ql.Years), >> >> ql.Period(20, ql.Years), >> >> ql.Period(25, ql.Years), >> >> ql.Period(30, ql.Years) >> >> ] >> >> >> >> nRows = len(optionTenors) * len(swapTenors) >> >> nCols = len(strikeSpreads) >> >> >> >> volSpreadsMatrix = strikeSpreadData[strikeSpreadData.columns[1:]].apply( >> >> lambda x: x).values.tolist() >> >> >> >> volSpreads = [] >> >> >> >> for i in range(nRows): >> >> volSpreadsRow = [] >> >> for j in range(nCols): >> >> volSpreadsRow.append( >> >> ql.QuoteHandle(ql.SimpleQuote(volSpreadsMatrix[i][j]))) >> >> >> >> volSpreads.append(volSpreadsRow) >> >> >> >> vegaWeightedSmileFit = False >> >> >> >> volCube = ql.SwaptionVolCube2(swaptionVolMatrixHandle, optionTenors, >> >> swapTenors, strikeSpreads, volSpreads, >> >> swapIndex1, swapIndex2, >> vegaWeightedSmileFit) >> >> >> >> volHandle = ql.SwaptionVolatilityStructureHandle(volCube) >> >> volHandle.enableExtrapolation() >> >> >> >> Thank you! >> >> >> >> -----Original Message----- >> From: Peter Caspers <pca...@gm...> >> Sent: Friday, August 12, 2022 4:20 PM >> To: Aditya Gupta <adi...@co...> >> Cc: qua...@li... >> Subject: Re: [Quantlib-users] CMS Spread Cap Pricing >> >> >> >> [You don't often get email from pca...@gm.... Learn why this >> is important at https://aka.ms/LearnAboutSenderIdentification ] >> >> >> >> CAUTION: EXTERNAL Sender >> >> >> >> >> >> Hi Aditya >> >> >> >> 1. correct >> >> 2. the pricer takes any SwaptionVolatilityStructure, i.e. a >> ConstantSwaptionVolatilityStructure should work. Is that what you mean? >> >> 3. this might be related to smile extrapolation, which strike range do >> you cover in your swaption cube and how do you set up the extrapolation? >> The pricer uses an integration over [-200%, +200%] by default. >> >> 4. I agree 500% is much to high, let's discuss further once 3 is >> resolved, i.e. QL / BBG matches >> >> >> >> In addition, the linear TSR model might not work well when the time to >> expiry is large. Do you have more info on which model BBG uses? >> >> >> >> Thank you >> >> Peter >> >> >> >> On Fri, 12 Aug 2022 at 09:36, Aditya Gupta <adi...@co...> >> wrote: >> >> > >> >> > Hi, >> >> > >> >> > >> >> > >> >> > I am trying to price a CMS Spread Cap using Quantlib. I am using data >> from Bloomberg, and I am using the following classes: >> >> > >> >> > >> >> > >> >> > yts is a bootstrapped Sofr curve >> >> > >> >> > Instrument = ql.CappedFlooredCmsSpreadCoupon() >> >> > >> >> > volCube = ql.SwaptionVolCube2() with the correct data and volType = >> >> > ql.Normal >> >> > >> >> > cmsPricer = ql.LinearTSRPricer(volHandle, meanReversion, yts) >> >> > >> >> > pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts, 16) >> >> > >> >> > >> >> > >> >> > I have a few questions. >> >> > >> >> > >> >> > >> >> > It seems to me that to price a ql. CappedFlooredCmsSpreadCoupon, the >> only option is to use a ql.LognormalCmsSpreadPricer, and hence if we use >> Normal volatility, the input cmsPricer must be the LinearTSRPricer (Hagan >> cannot be used). Is my assessment correct? >> >> > In order to check the pricing, I used a ‘constant’ volatility cube (set >> the atm matrix to a constant, rest to 0) and tested the pricing. The >> instrument was priced correctly (within the error from the discount curve). >> Can the pricer accept a constant volatility otherwise? >> >> > The volCube seems to be returning the correct volatilities (within >> reason) when compared to Bloomberg, and is being passed to the pricer >> without error. But for some reason, the price is completely different >> (about half of what it should be). What could be the cause for the error, >> or how could I go about debugging this? >> >> > If I use scipy.optimize to ‘artificially’ optimize the price as a >> function of the mean reversion, for a value of around 5 (which is way too >> high for the mean correlation), I was able to bring the error down to less >> than 1 dollar. Clearly this approach is not sensible. So I used the >> HullWhite model on the swaption matrix, and calibrated the mean reversion. >> But the error for this value of the mean reversion is much too high. How >> can I determine a good value for the mean reversion? >> >> > >> >> > >> >> > >> >> > Right now I am content with there being some error compared to >> Bloomberg, but I am unable to understand the blackbox behind the pricing >> engine and the mean reversion calibration, and if there is any way to solve >> these issues. Any and all help would be greatly appreciated. Thank you. >> >> > >> >> > >> >> > >> >> > Some of the code and data is as follows: >> >> > >> >> > >> >> > >> >> > today = ql.Date(25, 7, 2022) >> >> > >> >> > ql.Settings.instance().evaluationDate = today >> >> > >> >> > >> >> > >> >> > #Global variables >> >> > >> >> > calendar = ql.UnitedStates(ql.UnitedStates.FederalReserve) >> >> > >> >> > convention = ql.ModifiedFollowing >> >> > >> >> > endOfMonth = False >> >> > >> >> > dayCounter = ql.Actual360() >> >> > >> >> > fixingDays = 0 >> >> > >> >> > compounding = ql.Compounded >> >> > >> >> > compoundingFrequency = ql.Annual >> >> > >> >> > >> >> > >> >> > yts = ql.RelinkableYieldTermStructureHandle() >> >> > >> >> > index = ql.Sofr(yts) >> >> > >> >> > >> >> > >> >> > helper = [] >> >> > >> >> > >> >> > >> >> > # sofrData are just the rates from a dataframe >> >> > >> >> > >> >> > >> >> > helper += [ >> >> > >> >> > ql.DepositRateHelper( >> >> > >> >> > ql.QuoteHandle(ql.SimpleQuote(sofrData['Shifted Rate'][0] / >> >> > 100.0)), >> >> > >> >> > index) >> >> > >> >> > ] >> >> > >> >> > >> >> > >> >> > helper += [ >> >> > >> >> > ql.OISRateHelper(0, >> >> > >> >> > ql.Period(expiration), >> >> > >> >> > ql.QuoteHandle(ql.SimpleQuote(rate / 100.0)), >> >> > >> >> > index, >> >> > >> >> > paymentLag=0, >> >> > >> >> > paymentConvention=convention, >> >> > >> >> > paymentFrequency=ql.Annual, >> >> > >> >> > paymentCalendar=calendar, >> >> > >> >> > averagingMethod=ql.RateAveraging.Compound) for >> >> > rate, >> >> > >> >> > expiration in zip(sofrData['Shifted Rate'][1:], >> >> > sofrData["Term"][1:]) >> >> > >> >> > ] >> >> > >> >> > >> >> > >> >> > curve = ql.PiecewiseSplineCubicDiscount(today, helper, ql.Actual360()) >> >> > >> >> > curve.enableExtrapolation() >> >> > >> >> > yts.linkTo(curve) >> >> > >> >> > >> >> > >> >> > meanReversion = ql.QuoteHandle(ql.SimpleQuote(5.037702498779181)) >> >> > #this is the ‘optimised’ value but I need further help >> >> > >> >> > correlation = ql.QuoteHandle(ql.SimpleQuote(36.6 * 1e-4)) >> >> > >> >> > >> >> > >> >> > nominal = 1e9 >> >> > >> >> > gearing = 1.0 >> >> > >> >> > spread = 0.0 >> >> > >> >> > cap = -8 * 1e-4 >> >> > >> >> > floor = ql.nullDouble() #infinite floor >> >> > >> >> > >> >> > >> >> > startDate = ql.Date(26, 9, 2022) >> >> > >> >> > endDate = ql.Date(26, 9, 2023) >> >> > >> >> > paymentDate = ql.Date(26, 9, 2023) >> >> > >> >> > >> >> > >> >> > isInArrears = False >> >> > >> >> > exCouponDate = ql.Date() >> >> > >> >> > >> >> > >> >> > fixingDays = 0 >> >> > >> >> > >> >> > >> >> > swapIndex1 = ql.UsdLiborSwapIsdaFixAm(ql.Period('30y'), yts, yts) >> >> > >> >> > ql.IndexManager.instance().clearHistory(swapIndex1.name()) >> >> > >> >> > >> >> > >> >> > swapIndex2 = ql.UsdLiborSwapIsdaFixAm(ql.Period('10y'), yts, yts) >> >> > >> >> > ql.IndexManager.instance().clearHistory(swapIndex2.name()) >> >> > >> >> > >> >> > >> >> > spreadIndex = ql.SwapSpreadIndex("Joint Index", swapIndex1, >> >> > swapIndex2) >> >> > >> >> > >> >> > >> >> > instrument = ql.CappedFlooredCmsSpreadCoupon(paymentDate, nominal, >> >> > startDate, >> >> > >> >> > endDate, fixingDays, >> >> > spreadIndex, >> >> > >> >> > gearing, spread, cap, >> >> > floor, >> >> > >> >> > ql.Date(), ql.Date(), >> >> > dayCounter, >> >> > >> >> > isInArrears, >> >> > exCouponDate) >> >> > >> >> > >> >> > >> >> > cmsPricer = ql.LinearTsrPricer(volHandle, meanReversion, yts) >> >> > >> >> > >> >> > >> >> > # cmsPricer = ql.NumericHaganPricer(volHandle, >> >> > ql.GFunctionFactory.ParallelShifts , meanReversion)#, Rate >> >> > lowerLimit=0.0, Rate upperLimit=1.0, Real precision=1.0e-6, Real >> >> > hardUpperLimit=QL_MAX_REAL) >> >> > >> >> > >> >> > >> >> > integrationPoints = 16 >> >> > >> >> > >> >> > >> >> > pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts, >> >> > >> >> > integrationPoints) >> >> > >> >> > >> >> > >> >> > instrument.setPricer(pricer) >> >> > >> >> > >> >> > >> >> > Regards, >> >> > >> >> > Aditya Gupta. >> >> > >> >> > >> >> > >> >> > ________________________________ >> >> > Complus Asset Management Limited ("CAML") is licensed by the Securities >> and Futures Commission of Hong Kong (“SFC”) for the Regulated Activities of >> Advising on Securities and Asset Management with CE number AWX256. CAML is >> subject to certain SFC codes and regulations, details of which can be found >> on the SFC's website at http://www.sfc.hk. CAML is a company >> incorporated in Hong Kong with its registered office and principal place of >> business as indicated above. >> >> > >> >> > Unless specifically indicated, this message should not be construed as >> an offer to sell or solicitation to purchase any securities, financial >> products or services or the giving of investment advice within or outside >> Hong Kong. This message is intended solely for the person(s) to whom it is >> addressed. If you receive this message in error, please immediately delete >> it and all copies of it from your computer network or hard copies, and >> notify the sender. >> >> > _______________________________________________ >> >> > QuantLib-users mailing list >> >> > Qua...@li... >> >> > https://lists.sourceforge.net/lists/listinfo/quantlib-users >> > |
|
From: Aditya G. <adi...@co...> - 2022-08-15 03:40:28
|
Hi Peter, Regarding Bloomberg's model, it seems that it isn't very clear in their documentation what engine is being used, but the Sabr cube is used for the volatilities. I will try to contact support and ask them for more details. In any case, it seems that with a constant or non constant volatility, the pricing is not accurate. What could be the error in the code? If Bloomberg has a different model to the Quantlib one, the Quantlib model should still be able to price within some reasonable error percentage. Since this is not the case, where in the code could the mistake be? I am happy to send any updated code or fill in the missing lines. Thank you for your time. Regards, Aditya Gupta. On Aug 12, 2022 8:22 PM, Peter Caspers <pca...@gm...> wrote: You don't often get email from pca...@gm.... Learn why this is important<https://aka.ms/LearnAboutSenderIdentification> CAUTION: EXTERNAL Sender Hi Aditya 2 the error shouldn't show up unless you pass a volatilityType to the pricer. Might have to do with how SWIG calls the constructor, not sure at the moment. 3 what I meant was: the cms price depends on the volatilities outside the quoted part ATM-200 / ATM+200 as well, so maybe BBG uses different extrapolated vols than you do. And as I said, the model itself (and the mean reversion value) have a big impact, so you might first want check what BBG is doing Thanks Peter On Fri, 12 Aug 2022 at 10:43, Aditya Gupta <adi...@co...<mailto:adi...@co...>> wrote: Hi Peter, For point 2) the LinearTsrPricer can take the input as ql.ConstantSwaptionVolatilityStructure, but the Lognormal pricer complains by saying: " if only an atm surface is given, the volatility type must be inherited" So I guess it is not possible. To create a ‘constant volatility cube’, I instantiated an atmMatrix with a constant value, but all the OTM and ITM swaptions had a volatility of 0. This way, for every triplet (strike, expiry, tenor), QL would return a constant volatility. It is a workaround, but it sufficed for my purpose. For 3) The code and data for the extrapolation is below. Some notes: volHandle at the end is passed into the pricer. The data is not complete but should be enough for debugging purposes. The BBG/QL volatility cubes seem to match to a decent degree (i.e. I don’t believe it is the source of the error). The volatilities are within 10 bps when testing random data points. However, the atm strike does not change (even if the volatility is constant) and does not agree with Bloomberg. normalVolCube = Expiry 1Yr 2Yr 3Yr 4Yr 5Yr 7Yr 10Yr 12Yr 15Yr 20Yr 25Yr 30Yr 1Mo 153.65 160.51 151.81 143.8 141.03 129.88 123.16 118.88 110.61 108.9 104.21 101.91 3Mo 150.06 152.12 149.09 140.08 134.79 125.74 118.65 115.21 112.26 107.13 103.41 99.43 6Mo 154.30 150.74 143.9 137.09 132.84 123.31 115.03 111.31 107.88 103.41 100.5 96.31 9Mo 155.29 149.86 142.87 135.17 130.97 121.33 112.06 108.57 105.04 99.95 97.12 93.19 1Yr 155.98 149.11 140.78 133.39 127.82 118.13 109.42 106.32 102.06 97.15 94.05 90.8 2Yr 142.61 135.71 129.64 123.34 118.42 110 102.05 99.13 95.18 89.95 86.93 84.38 3Yr 130.05 124.03 117.71 113.72 109.87 102.87 96.37 93.46 89.5 84.21 81.15 79.07 4Yr 118.85 113.22 108.13 104.87 102.41 97.49 91.33 88.78 85.31 79.42 77.09 74.93 5Yr 109.73 104.94 102.2 99.09 96.85 92.53 87.38 84.84 81.37 76.05 73.36 72.24 6Yr 102.35 99.42 97.38 94.42 92.18 88.36 83.64 81.19 77.88 72.88 70.54 69.33 7Yr 96.52 93.55 91.44 89.36 87.41 84.15 79.99 77.68 74.58 70.13 68.25 66.85 8Yr 91.55 89.03 87.38 85.58 83.78 80.83 77.08 74.82 71.81 67.42 65.22 64.37 9Yr 86.91 85.11 83.57 81.89 80.19 77.48 74.07 71.89 68.99 64.76 62.82 62.05 10Yr 83.39 81.29 79.55 77.84 76.4 74.03 71.19 69.2 66.54 62.56 60.74 60.1 12Yr 77.24 75.44 74 72.38 70.97 69.1 66.47 64.68 62.27 58.88 57.3 56.85 15Yr 68.34 67.24 65.61 64.58 64.34 62.11 60.25 58.73 56.7 53.89 52.72 52.28 20Yr 60.48 60.35 59.15 58.46 57.75 56.64 55.07 53.74 51.97 49.87 48.96 48.63 25Yr 58.04 56.62 55.74 55.16 54.62 53.59 52.25 51.07 49.5 47.49 46.72 46.08 30Yr 54.83 53.31 52.73 52.34 51.96 51.06 49.7 48.76 47.52 45.21 44.11 44.1 strikeSpreadData Snippet: Term x Tenor -200bps -100bps -50bps -25bps ATM 25bps 50bps 100bps 200bps 3Mo X 1Yr 163.06 147.61 145.65 147.02 150.06 154.64 160.47 174.74 208.13 3Mo X 2Yr 168.32 152.63 149.59 150.1 152.12 155.6 160.36 172.72 203.32 3Mo X 3Yr 165.94 150.14 146.87 147.22 149.09 152.42 157.05 169.18 199.38 3Mo X 5Yr 154.44 137.68 133.49 133.36 134.79 137.76 142.09 153.73 182.89 3Mo X 7Yr 145.98 128.92 124.5 124.31 125.74 128.76 133.16 144.93 173.96 3Mo X 10Yr 141.64 123.6 118.33 117.66 118.65 121.3 125.42 136.78 165.14 3Mo X 12Yr 124.87 115.74 113.96 114.17 115.21 117.06 119.67 126.74 145.33 3Mo X 15Yr 137.53 118.56 112.58 111.56 112.26 114.72 118.73 130 158.17 3Mo X 20Yr 133.96 114.4 107.91 106.63 107.13 109.46 113.42 124.65 152.72 3Mo X 25Yr 130.47 110.79 104.19 102.88 103.41 105.83 109.9 121.34 149.56 3Mo X 30Yr 126.66 106.9 100.19 98.87 99.43 101.94 106.13 117.77 146.12 6Mo X 1Yr 153.08 147.26 148.88 151.13 154.3 158.3 163.02 174.1 199.93 6Mo X 2Yr 155.61 147.49 147.3 148.56 150.74 153.82 157.69 167.34 191.18 6Mo X 3Yr 151.41 141.74 140.85 141.85 143.9 146.93 150.84 160.73 185.27 6Mo X 5Yr 144.96 132.89 130.7 131.19 132.84 135.64 139.46 149.43 174.44 6Mo X 7Yr 137.51 124.48 121.67 121.87 123.31 125.95 129.67 139.51 164.24 6Mo X 10Yr 131.51 117.49 113.98 113.86 115.03 117.47 121.05 130.73 155.14 6Mo X 12Yr 119.63 111.38 109.93 110.24 111.31 113.15 115.68 122.45 140.14 6Mo X 15Yr 126.39 111.49 107.36 106.95 107.88 110.16 113.64 123.22 147.39 6Mo X 20Yr 123.32 107.85 103.29 102.66 103.41 105.58 109 118.53 142.61 6Mo X 25Yr 120.86 105.2 100.48 99.78 100.5 102.67 106.1 115.7 139.82 # ATM Volatility matrix volType = ql.Normal flatExtrapolation = False atmSwapTenors = [ql.Period(tenor[:-1]) for tenor in normalVolCube.columns[1:]] atmOptionTenors = [ql.Period(tenor[:-1]) for tenor in normalVolCube['Expiry']] normalVols = normalVolCube[normalVolCube.columns[1:]].apply( lambda x: x * 1e-4).values.tolist() swaptionVolMatrix = ql.SwaptionVolatilityMatrix(calendar, convention, atmOptionTenors, atmSwapTenors, ql.Matrix(normalVols), dayCounter, flatExtrapolation, volType) swaptionVolMatrixHandle = ql.SwaptionVolatilityStructureHandle( swaptionVolMatrix) strikeSpreads = [-200, -100, -50, -25, 0, 25, 50, 100, 200] strikeSpreads = [x * 1e-4 for x in strikeSpreads] optionTenors = [ ql.Period(3, ql.Months), ql.Period(6, ql.Months), ql.Period(9, ql.Months), ql.Period(1, ql.Years), ql.Period(3, ql.Years), ql.Period(5, ql.Years), ql.Period(7, ql.Years), ql.Period(10, ql.Years), ql.Period(15, ql.Years), ql.Period(20, ql.Years), ql.Period(30, ql.Years) ] swapTenors = [ ql.Period(1, ql.Years), ql.Period(2, ql.Years), ql.Period(3, ql.Years), ql.Period(5, ql.Years), ql.Period(7, ql.Years), ql.Period(10, ql.Years), ql.Period(12, ql.Years), ql.Period(15, ql.Years), ql.Period(20, ql.Years), ql.Period(25, ql.Years), ql.Period(30, ql.Years) ] nRows = len(optionTenors) * len(swapTenors) nCols = len(strikeSpreads) volSpreadsMatrix = strikeSpreadData[strikeSpreadData.columns[1:]].apply( lambda x: x).values.tolist() volSpreads = [] for i in range(nRows): volSpreadsRow = [] for j in range(nCols): volSpreadsRow.append( ql.QuoteHandle(ql.SimpleQuote(volSpreadsMatrix[i][j]))) volSpreads.append(volSpreadsRow) vegaWeightedSmileFit = False volCube = ql.SwaptionVolCube2(swaptionVolMatrixHandle, optionTenors, swapTenors, strikeSpreads, volSpreads, swapIndex1, swapIndex2, vegaWeightedSmileFit) volHandle = ql.SwaptionVolatilityStructureHandle(volCube) volHandle.enableExtrapolation() Thank you! -----Original Message----- From: Peter Caspers <pca...@gm...<mailto:pca...@gm...>> Sent: Friday, August 12, 2022 4:20 PM To: Aditya Gupta <adi...@co...<mailto:adi...@co...>> Cc: qua...@li...<mailto:qua...@li...> Subject: Re: [Quantlib-users] CMS Spread Cap Pricing [You don't often get email from pca...@gm...<mailto:pca...@gm...>. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] CAUTION: EXTERNAL Sender Hi Aditya 1. correct 2. the pricer takes any SwaptionVolatilityStructure, i.e. a ConstantSwaptionVolatilityStructure should work. Is that what you mean? 3. this might be related to smile extrapolation, which strike range do you cover in your swaption cube and how do you set up the extrapolation? The pricer uses an integration over [-200%, +200%] by default. 4. I agree 500% is much to high, let's discuss further once 3 is resolved, i.e. QL / BBG matches In addition, the linear TSR model might not work well when the time to expiry is large. Do you have more info on which model BBG uses? Thank you Peter On Fri, 12 Aug 2022 at 09:36, Aditya Gupta <adi...@co...<mailto:adi...@co...>> wrote: > > Hi, > > > > I am trying to price a CMS Spread Cap using Quantlib. I am using data from Bloomberg, and I am using the following classes: > > > > yts is a bootstrapped Sofr curve > > Instrument = ql.CappedFlooredCmsSpreadCoupon() > > volCube = ql.SwaptionVolCube2() with the correct data and volType = > ql.Normal > > cmsPricer = ql.LinearTSRPricer(volHandle, meanReversion, yts) > > pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts, 16) > > > > I have a few questions. > > > > It seems to me that to price a ql. CappedFlooredCmsSpreadCoupon, the only option is to use a ql.LognormalCmsSpreadPricer, and hence if we use Normal volatility, the input cmsPricer must be the LinearTSRPricer (Hagan cannot be used). Is my assessment correct? > In order to check the pricing, I used a ‘constant’ volatility cube (set the atm matrix to a constant, rest to 0) and tested the pricing. The instrument was priced correctly (within the error from the discount curve). Can the pricer accept a constant volatility otherwise? > The volCube seems to be returning the correct volatilities (within reason) when compared to Bloomberg, and is being passed to the pricer without error. But for some reason, the price is completely different (about half of what it should be). What could be the cause for the error, or how could I go about debugging this? > If I use scipy.optimize to ‘artificially’ optimize the price as a function of the mean reversion, for a value of around 5 (which is way too high for the mean correlation), I was able to bring the error down to less than 1 dollar. Clearly this approach is not sensible. So I used the HullWhite model on the swaption matrix, and calibrated the mean reversion. But the error for this value of the mean reversion is much too high. How can I determine a good value for the mean reversion? > > > > Right now I am content with there being some error compared to Bloomberg, but I am unable to understand the blackbox behind the pricing engine and the mean reversion calibration, and if there is any way to solve these issues. Any and all help would be greatly appreciated. Thank you. > > > > Some of the code and data is as follows: > > > > today = ql.Date(25, 7, 2022) > > ql.Settings.instance().evaluationDate = today > > > > #Global variables > > calendar = ql.UnitedStates(ql.UnitedStates.FederalReserve) > > convention = ql.ModifiedFollowing > > endOfMonth = False > > dayCounter = ql.Actual360() > > fixingDays = 0 > > compounding = ql.Compounded > > compoundingFrequency = ql.Annual > > > > yts = ql.RelinkableYieldTermStructureHandle() > > index = ql.Sofr(yts) > > > > helper = [] > > > > # sofrData are just the rates from a dataframe > > > > helper += [ > > ql.DepositRateHelper( > > ql.QuoteHandle(ql.SimpleQuote(sofrData['Shifted Rate'][0] / > 100.0)), > > index) > > ] > > > > helper += [ > > ql.OISRateHelper(0, > > ql.Period(expiration), > > ql.QuoteHandle(ql.SimpleQuote(rate / 100.0)), > > index, > > paymentLag=0, > > paymentConvention=convention, > > paymentFrequency=ql.Annual, > > paymentCalendar=calendar, > > averagingMethod=ql.RateAveraging.Compound) for > rate, > > expiration in zip(sofrData['Shifted Rate'][1:], > sofrData["Term"][1:]) > > ] > > > > curve = ql.PiecewiseSplineCubicDiscount(today, helper, ql.Actual360()) > > curve.enableExtrapolation() > > yts.linkTo(curve) > > > > meanReversion = ql.QuoteHandle(ql.SimpleQuote(5.037702498779181)) > #this is the ‘optimised’ value but I need further help > > correlation = ql.QuoteHandle(ql.SimpleQuote(36.6 * 1e-4)) > > > > nominal = 1e9 > > gearing = 1.0 > > spread = 0.0 > > cap = -8 * 1e-4 > > floor = ql.nullDouble() #infinite floor > > > > startDate = ql.Date(26, 9, 2022) > > endDate = ql.Date(26, 9, 2023) > > paymentDate = ql.Date(26, 9, 2023) > > > > isInArrears = False > > exCouponDate = ql.Date() > > > > fixingDays = 0 > > > > swapIndex1 = ql.UsdLiborSwapIsdaFixAm(ql.Period('30y'), yts, yts) > > ql.IndexManager.instance().clearHistory(swapIndex1.name()) > > > > swapIndex2 = ql.UsdLiborSwapIsdaFixAm(ql.Period('10y'), yts, yts) > > ql.IndexManager.instance().clearHistory(swapIndex2.name()) > > > > spreadIndex = ql.SwapSpreadIndex("Joint Index", swapIndex1, > swapIndex2) > > > > instrument = ql.CappedFlooredCmsSpreadCoupon(paymentDate, nominal, > startDate, > > endDate, fixingDays, > spreadIndex, > > gearing, spread, cap, > floor, > > ql.Date(), ql.Date(), > dayCounter, > > isInArrears, > exCouponDate) > > > > cmsPricer = ql.LinearTsrPricer(volHandle, meanReversion, yts) > > > > # cmsPricer = ql.NumericHaganPricer(volHandle, > ql.GFunctionFactory.ParallelShifts , meanReversion)#, Rate > lowerLimit=0.0, Rate upperLimit=1.0, Real precision=1.0e-6, Real > hardUpperLimit=QL_MAX_REAL) > > > > integrationPoints = 16 > > > > pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts, > > integrationPoints) > > > > instrument.setPricer(pricer) > > > > Regards, > > Aditya Gupta. > > > > ________________________________ > Complus Asset Management Limited ("CAML") is licensed by the Securities and Futures Commission of Hong Kong (“SFC”) for the Regulated Activities of Advising on Securities and Asset Management with CE number AWX256. CAML is subject to certain SFC codes and regulations, details of which can be found on the SFC's website at http://www.sfc.hk. CAML is a company incorporated in Hong Kong with its registered office and principal place of business as indicated above. > > Unless specifically indicated, this message should not be construed as an offer to sell or solicitation to purchase any securities, financial products or services or the giving of investment advice within or outside Hong Kong. This message is intended solely for the person(s) to whom it is addressed. If you receive this message in error, please immediately delete it and all copies of it from your computer network or hard copies, and notify the sender. > _______________________________________________ > QuantLib-users mailing list > Qua...@li...<mailto:Qua...@li...> > https://lists.sourceforge.net/lists/listinfo/quantlib-users |
|
From: Peter C. <pca...@gm...> - 2022-08-12 12:22:49
|
Hi Aditya 2 the error shouldn't show up unless you pass a volatilityType to the pricer. Might have to do with how SWIG calls the constructor, not sure at the moment. 3 what I meant was: the cms price depends on the volatilities outside the quoted part ATM-200 / ATM+200 as well, so maybe BBG uses different extrapolated vols than you do. And as I said, the model itself (and the mean reversion value) have a big impact, so you might first want check what BBG is doing Thanks Peter On Fri, 12 Aug 2022 at 10:43, Aditya Gupta <adi...@co...> wrote: > Hi Peter, > > > > For point 2) the LinearTsrPricer can take the input as > ql.ConstantSwaptionVolatilityStructure, but the Lognormal pricer complains > by saying: " if only an atm surface is given, the volatility type must be > inherited" So I guess it is not possible. To create a ‘constant volatility > cube’, I instantiated an atmMatrix with a constant value, but all the OTM > and ITM swaptions had a volatility of 0. This way, for every triplet > (strike, expiry, tenor), QL would return a constant volatility. It is a > workaround, but it sufficed for my purpose. > > > > For 3) The code and data for the extrapolation is below. Some notes: > > > > volHandle at the end is passed into the pricer. The data is not complete > but should be enough for debugging purposes. The BBG/QL volatility cubes > seem to match to a decent degree (i.e. I don’t believe it is the source of > the error). The volatilities are within 10 bps when testing random data > points. However, the atm strike does not change (even if the volatility is > constant) and does not agree with Bloomberg. > > > > normalVolCube = > > Expiry > > 1Yr > > 2Yr > > 3Yr > > 4Yr > > 5Yr > > 7Yr > > 10Yr > > 12Yr > > 15Yr > > 20Yr > > 25Yr > > 30Yr > > 1Mo > > 153.65 > > 160.51 > > 151.81 > > 143.8 > > 141.03 > > 129.88 > > 123.16 > > 118.88 > > 110.61 > > 108.9 > > 104.21 > > 101.91 > > 3Mo > > 150.06 > > 152.12 > > 149.09 > > 140.08 > > 134.79 > > 125.74 > > 118.65 > > 115.21 > > 112.26 > > 107.13 > > 103.41 > > 99.43 > > 6Mo > > 154.30 > > 150.74 > > 143.9 > > 137.09 > > 132.84 > > 123.31 > > 115.03 > > 111.31 > > 107.88 > > 103.41 > > 100.5 > > 96.31 > > 9Mo > > 155.29 > > 149.86 > > 142.87 > > 135.17 > > 130.97 > > 121.33 > > 112.06 > > 108.57 > > 105.04 > > 99.95 > > 97.12 > > 93.19 > > 1Yr > > 155.98 > > 149.11 > > 140.78 > > 133.39 > > 127.82 > > 118.13 > > 109.42 > > 106.32 > > 102.06 > > 97.15 > > 94.05 > > 90.8 > > 2Yr > > 142.61 > > 135.71 > > 129.64 > > 123.34 > > 118.42 > > 110 > > 102.05 > > 99.13 > > 95.18 > > 89.95 > > 86.93 > > 84.38 > > 3Yr > > 130.05 > > 124.03 > > 117.71 > > 113.72 > > 109.87 > > 102.87 > > 96.37 > > 93.46 > > 89.5 > > 84.21 > > 81.15 > > 79.07 > > 4Yr > > 118.85 > > 113.22 > > 108.13 > > 104.87 > > 102.41 > > 97.49 > > 91.33 > > 88.78 > > 85.31 > > 79.42 > > 77.09 > > 74.93 > > 5Yr > > 109.73 > > 104.94 > > 102.2 > > 99.09 > > 96.85 > > 92.53 > > 87.38 > > 84.84 > > 81.37 > > 76.05 > > 73.36 > > 72.24 > > 6Yr > > 102.35 > > 99.42 > > 97.38 > > 94.42 > > 92.18 > > 88.36 > > 83.64 > > 81.19 > > 77.88 > > 72.88 > > 70.54 > > 69.33 > > 7Yr > > 96.52 > > 93.55 > > 91.44 > > 89.36 > > 87.41 > > 84.15 > > 79.99 > > 77.68 > > 74.58 > > 70.13 > > 68.25 > > 66.85 > > 8Yr > > 91.55 > > 89.03 > > 87.38 > > 85.58 > > 83.78 > > 80.83 > > 77.08 > > 74.82 > > 71.81 > > 67.42 > > 65.22 > > 64.37 > > 9Yr > > 86.91 > > 85.11 > > 83.57 > > 81.89 > > 80.19 > > 77.48 > > 74.07 > > 71.89 > > 68.99 > > 64.76 > > 62.82 > > 62.05 > > 10Yr > > 83.39 > > 81.29 > > 79.55 > > 77.84 > > 76.4 > > 74.03 > > 71.19 > > 69.2 > > 66.54 > > 62.56 > > 60.74 > > 60.1 > > 12Yr > > 77.24 > > 75.44 > > 74 > > 72.38 > > 70.97 > > 69.1 > > 66.47 > > 64.68 > > 62.27 > > 58.88 > > 57.3 > > 56.85 > > 15Yr > > 68.34 > > 67.24 > > 65.61 > > 64.58 > > 64.34 > > 62.11 > > 60.25 > > 58.73 > > 56.7 > > 53.89 > > 52.72 > > 52.28 > > 20Yr > > 60.48 > > 60.35 > > 59.15 > > 58.46 > > 57.75 > > 56.64 > > 55.07 > > 53.74 > > 51.97 > > 49.87 > > 48.96 > > 48.63 > > 25Yr > > 58.04 > > 56.62 > > 55.74 > > 55.16 > > 54.62 > > 53.59 > > 52.25 > > 51.07 > > 49.5 > > 47.49 > > 46.72 > > 46.08 > > 30Yr > > 54.83 > > 53.31 > > 52.73 > > 52.34 > > 51.96 > > 51.06 > > 49.7 > > 48.76 > > 47.52 > > 45.21 > > 44.11 > > 44.1 > > > > > > strikeSpreadData Snippet: > > Term x Tenor > > -200bps > > -100bps > > -50bps > > -25bps > > ATM > > 25bps > > 50bps > > 100bps > > 200bps > > 3Mo X 1Yr > > 163.06 > > 147.61 > > 145.65 > > 147.02 > > 150.06 > > 154.64 > > 160.47 > > 174.74 > > 208.13 > > 3Mo X 2Yr > > 168.32 > > 152.63 > > 149.59 > > 150.1 > > 152.12 > > 155.6 > > 160.36 > > 172.72 > > 203.32 > > 3Mo X 3Yr > > 165.94 > > 150.14 > > 146.87 > > 147.22 > > 149.09 > > 152.42 > > 157.05 > > 169.18 > > 199.38 > > 3Mo X 5Yr > > 154.44 > > 137.68 > > 133.49 > > 133.36 > > 134.79 > > 137.76 > > 142.09 > > 153.73 > > 182.89 > > 3Mo X 7Yr > > 145.98 > > 128.92 > > 124.5 > > 124.31 > > 125.74 > > 128.76 > > 133.16 > > 144.93 > > 173.96 > > 3Mo X 10Yr > > 141.64 > > 123.6 > > 118.33 > > 117.66 > > 118.65 > > 121.3 > > 125.42 > > 136.78 > > 165.14 > > 3Mo X 12Yr > > 124.87 > > 115.74 > > 113.96 > > 114.17 > > 115.21 > > 117.06 > > 119.67 > > 126.74 > > 145.33 > > 3Mo X 15Yr > > 137.53 > > 118.56 > > 112.58 > > 111.56 > > 112.26 > > 114.72 > > 118.73 > > 130 > > 158.17 > > 3Mo X 20Yr > > 133.96 > > 114.4 > > 107.91 > > 106.63 > > 107.13 > > 109.46 > > 113.42 > > 124.65 > > 152.72 > > 3Mo X 25Yr > > 130.47 > > 110.79 > > 104.19 > > 102.88 > > 103.41 > > 105.83 > > 109.9 > > 121.34 > > 149.56 > > 3Mo X 30Yr > > 126.66 > > 106.9 > > 100.19 > > 98.87 > > 99.43 > > 101.94 > > 106.13 > > 117.77 > > 146.12 > > 6Mo X 1Yr > > 153.08 > > 147.26 > > 148.88 > > 151.13 > > 154.3 > > 158.3 > > 163.02 > > 174.1 > > 199.93 > > 6Mo X 2Yr > > 155.61 > > 147.49 > > 147.3 > > 148.56 > > 150.74 > > 153.82 > > 157.69 > > 167.34 > > 191.18 > > 6Mo X 3Yr > > 151.41 > > 141.74 > > 140.85 > > 141.85 > > 143.9 > > 146.93 > > 150.84 > > 160.73 > > 185.27 > > 6Mo X 5Yr > > 144.96 > > 132.89 > > 130.7 > > 131.19 > > 132.84 > > 135.64 > > 139.46 > > 149.43 > > 174.44 > > 6Mo X 7Yr > > 137.51 > > 124.48 > > 121.67 > > 121.87 > > 123.31 > > 125.95 > > 129.67 > > 139.51 > > 164.24 > > 6Mo X 10Yr > > 131.51 > > 117.49 > > 113.98 > > 113.86 > > 115.03 > > 117.47 > > 121.05 > > 130.73 > > 155.14 > > 6Mo X 12Yr > > 119.63 > > 111.38 > > 109.93 > > 110.24 > > 111.31 > > 113.15 > > 115.68 > > 122.45 > > 140.14 > > 6Mo X 15Yr > > 126.39 > > 111.49 > > 107.36 > > 106.95 > > 107.88 > > 110.16 > > 113.64 > > 123.22 > > 147.39 > > 6Mo X 20Yr > > 123.32 > > 107.85 > > 103.29 > > 102.66 > > 103.41 > > 105.58 > > 109 > > 118.53 > > 142.61 > > 6Mo X 25Yr > > 120.86 > > 105.2 > > 100.48 > > 99.78 > > 100.5 > > 102.67 > > 106.1 > > 115.7 > > 139.82 > > > > > > # ATM Volatility matrix > > volType = ql.Normal > > flatExtrapolation = False > > > > atmSwapTenors = [ql.Period(tenor[:-1]) for tenor in > normalVolCube.columns[1:]] > > atmOptionTenors = [ql.Period(tenor[:-1]) for tenor in > normalVolCube['Expiry']] > > > > normalVols = normalVolCube[normalVolCube.columns[1:]].apply( > > lambda x: x * 1e-4).values.tolist() > > > > swaptionVolMatrix = ql.SwaptionVolatilityMatrix(calendar, convention, > > atmOptionTenors, > atmSwapTenors, > > ql.Matrix(normalVols), > > dayCounter, > flatExtrapolation, > > volType) > > > > swaptionVolMatrixHandle = ql.SwaptionVolatilityStructureHandle( > > swaptionVolMatrix) > > > > strikeSpreads = [-200, -100, -50, -25, 0, 25, 50, 100, 200] > > strikeSpreads = [x * 1e-4 for x in strikeSpreads] > > > > optionTenors = [ > > ql.Period(3, ql.Months), > > ql.Period(6, ql.Months), > > ql.Period(9, ql.Months), > > ql.Period(1, ql.Years), > > ql.Period(3, ql.Years), > > ql.Period(5, ql.Years), > > ql.Period(7, ql.Years), > > ql.Period(10, ql.Years), > > ql.Period(15, ql.Years), > > ql.Period(20, ql.Years), > > ql.Period(30, ql.Years) > > ] > > > > swapTenors = [ > > ql.Period(1, ql.Years), > > ql.Period(2, ql.Years), > > ql.Period(3, ql.Years), > > ql.Period(5, ql.Years), > > ql.Period(7, ql.Years), > > ql.Period(10, ql.Years), > > ql.Period(12, ql.Years), > > ql.Period(15, ql.Years), > > ql.Period(20, ql.Years), > > ql.Period(25, ql.Years), > > ql.Period(30, ql.Years) > > ] > > > > nRows = len(optionTenors) * len(swapTenors) > > nCols = len(strikeSpreads) > > > > volSpreadsMatrix = strikeSpreadData[strikeSpreadData.columns[1:]].apply( > > lambda x: x).values.tolist() > > > > volSpreads = [] > > > > for i in range(nRows): > > volSpreadsRow = [] > > for j in range(nCols): > > volSpreadsRow.append( > > ql.QuoteHandle(ql.SimpleQuote(volSpreadsMatrix[i][j]))) > > > > volSpreads.append(volSpreadsRow) > > > > vegaWeightedSmileFit = False > > > > volCube = ql.SwaptionVolCube2(swaptionVolMatrixHandle, optionTenors, > > swapTenors, strikeSpreads, volSpreads, > > swapIndex1, swapIndex2, vegaWeightedSmileFit) > > > > volHandle = ql.SwaptionVolatilityStructureHandle(volCube) > > volHandle.enableExtrapolation() > > > > Thank you! > > > > -----Original Message----- > From: Peter Caspers <pca...@gm...> > Sent: Friday, August 12, 2022 4:20 PM > To: Aditya Gupta <adi...@co...> > Cc: qua...@li... > Subject: Re: [Quantlib-users] CMS Spread Cap Pricing > > > > [You don't often get email from pca...@gm.... Learn why this is > important at https://aka.ms/LearnAboutSenderIdentification ] > > > > CAUTION: EXTERNAL Sender > > > > > > Hi Aditya > > > > 1. correct > > 2. the pricer takes any SwaptionVolatilityStructure, i.e. a > ConstantSwaptionVolatilityStructure should work. Is that what you mean? > > 3. this might be related to smile extrapolation, which strike range do you > cover in your swaption cube and how do you set up the extrapolation? The > pricer uses an integration over [-200%, +200%] by default. > > 4. I agree 500% is much to high, let's discuss further once 3 is resolved, > i.e. QL / BBG matches > > > > In addition, the linear TSR model might not work well when the time to > expiry is large. Do you have more info on which model BBG uses? > > > > Thank you > > Peter > > > > On Fri, 12 Aug 2022 at 09:36, Aditya Gupta <adi...@co...> > wrote: > > > > > > Hi, > > > > > > > > > > > > I am trying to price a CMS Spread Cap using Quantlib. I am using data > from Bloomberg, and I am using the following classes: > > > > > > > > > > > > yts is a bootstrapped Sofr curve > > > > > > Instrument = ql.CappedFlooredCmsSpreadCoupon() > > > > > > volCube = ql.SwaptionVolCube2() with the correct data and volType = > > > ql.Normal > > > > > > cmsPricer = ql.LinearTSRPricer(volHandle, meanReversion, yts) > > > > > > pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts, 16) > > > > > > > > > > > > I have a few questions. > > > > > > > > > > > > It seems to me that to price a ql. CappedFlooredCmsSpreadCoupon, the > only option is to use a ql.LognormalCmsSpreadPricer, and hence if we use > Normal volatility, the input cmsPricer must be the LinearTSRPricer (Hagan > cannot be used). Is my assessment correct? > > > In order to check the pricing, I used a ‘constant’ volatility cube (set > the atm matrix to a constant, rest to 0) and tested the pricing. The > instrument was priced correctly (within the error from the discount curve). > Can the pricer accept a constant volatility otherwise? > > > The volCube seems to be returning the correct volatilities (within > reason) when compared to Bloomberg, and is being passed to the pricer > without error. But for some reason, the price is completely different > (about half of what it should be). What could be the cause for the error, > or how could I go about debugging this? > > > If I use scipy.optimize to ‘artificially’ optimize the price as a > function of the mean reversion, for a value of around 5 (which is way too > high for the mean correlation), I was able to bring the error down to less > than 1 dollar. Clearly this approach is not sensible. So I used the > HullWhite model on the swaption matrix, and calibrated the mean reversion. > But the error for this value of the mean reversion is much too high. How > can I determine a good value for the mean reversion? > > > > > > > > > > > > Right now I am content with there being some error compared to > Bloomberg, but I am unable to understand the blackbox behind the pricing > engine and the mean reversion calibration, and if there is any way to solve > these issues. Any and all help would be greatly appreciated. Thank you. > > > > > > > > > > > > Some of the code and data is as follows: > > > > > > > > > > > > today = ql.Date(25, 7, 2022) > > > > > > ql.Settings.instance().evaluationDate = today > > > > > > > > > > > > #Global variables > > > > > > calendar = ql.UnitedStates(ql.UnitedStates.FederalReserve) > > > > > > convention = ql.ModifiedFollowing > > > > > > endOfMonth = False > > > > > > dayCounter = ql.Actual360() > > > > > > fixingDays = 0 > > > > > > compounding = ql.Compounded > > > > > > compoundingFrequency = ql.Annual > > > > > > > > > > > > yts = ql.RelinkableYieldTermStructureHandle() > > > > > > index = ql.Sofr(yts) > > > > > > > > > > > > helper = [] > > > > > > > > > > > > # sofrData are just the rates from a dataframe > > > > > > > > > > > > helper += [ > > > > > > ql.DepositRateHelper( > > > > > > ql.QuoteHandle(ql.SimpleQuote(sofrData['Shifted Rate'][0] / > > > 100.0)), > > > > > > index) > > > > > > ] > > > > > > > > > > > > helper += [ > > > > > > ql.OISRateHelper(0, > > > > > > ql.Period(expiration), > > > > > > ql.QuoteHandle(ql.SimpleQuote(rate / 100.0)), > > > > > > index, > > > > > > paymentLag=0, > > > > > > paymentConvention=convention, > > > > > > paymentFrequency=ql.Annual, > > > > > > paymentCalendar=calendar, > > > > > > averagingMethod=ql.RateAveraging.Compound) for > > > rate, > > > > > > expiration in zip(sofrData['Shifted Rate'][1:], > > > sofrData["Term"][1:]) > > > > > > ] > > > > > > > > > > > > curve = ql.PiecewiseSplineCubicDiscount(today, helper, ql.Actual360()) > > > > > > curve.enableExtrapolation() > > > > > > yts.linkTo(curve) > > > > > > > > > > > > meanReversion = ql.QuoteHandle(ql.SimpleQuote(5.037702498779181)) > > > #this is the ‘optimised’ value but I need further help > > > > > > correlation = ql.QuoteHandle(ql.SimpleQuote(36.6 * 1e-4)) > > > > > > > > > > > > nominal = 1e9 > > > > > > gearing = 1.0 > > > > > > spread = 0.0 > > > > > > cap = -8 * 1e-4 > > > > > > floor = ql.nullDouble() #infinite floor > > > > > > > > > > > > startDate = ql.Date(26, 9, 2022) > > > > > > endDate = ql.Date(26, 9, 2023) > > > > > > paymentDate = ql.Date(26, 9, 2023) > > > > > > > > > > > > isInArrears = False > > > > > > exCouponDate = ql.Date() > > > > > > > > > > > > fixingDays = 0 > > > > > > > > > > > > swapIndex1 = ql.UsdLiborSwapIsdaFixAm(ql.Period('30y'), yts, yts) > > > > > > ql.IndexManager.instance().clearHistory(swapIndex1.name()) > > > > > > > > > > > > swapIndex2 = ql.UsdLiborSwapIsdaFixAm(ql.Period('10y'), yts, yts) > > > > > > ql.IndexManager.instance().clearHistory(swapIndex2.name()) > > > > > > > > > > > > spreadIndex = ql.SwapSpreadIndex("Joint Index", swapIndex1, > > > swapIndex2) > > > > > > > > > > > > instrument = ql.CappedFlooredCmsSpreadCoupon(paymentDate, nominal, > > > startDate, > > > > > > endDate, fixingDays, > > > spreadIndex, > > > > > > gearing, spread, cap, > > > floor, > > > > > > ql.Date(), ql.Date(), > > > dayCounter, > > > > > > isInArrears, > > > exCouponDate) > > > > > > > > > > > > cmsPricer = ql.LinearTsrPricer(volHandle, meanReversion, yts) > > > > > > > > > > > > # cmsPricer = ql.NumericHaganPricer(volHandle, > > > ql.GFunctionFactory.ParallelShifts , meanReversion)#, Rate > > > lowerLimit=0.0, Rate upperLimit=1.0, Real precision=1.0e-6, Real > > > hardUpperLimit=QL_MAX_REAL) > > > > > > > > > > > > integrationPoints = 16 > > > > > > > > > > > > pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts, > > > > > > integrationPoints) > > > > > > > > > > > > instrument.setPricer(pricer) > > > > > > > > > > > > Regards, > > > > > > Aditya Gupta. > > > > > > > > > > > > ________________________________ > > > Complus Asset Management Limited ("CAML") is licensed by the Securities > and Futures Commission of Hong Kong (“SFC”) for the Regulated Activities of > Advising on Securities and Asset Management with CE number AWX256. CAML is > subject to certain SFC codes and regulations, details of which can be found > on the SFC's website at http://www.sfc.hk. CAML is a company > incorporated in Hong Kong with its registered office and principal place of > business as indicated above. > > > > > > Unless specifically indicated, this message should not be construed as > an offer to sell or solicitation to purchase any securities, financial > products or services or the giving of investment advice within or outside > Hong Kong. This message is intended solely for the person(s) to whom it is > addressed. If you receive this message in error, please immediately delete > it and all copies of it from your computer network or hard copies, and > notify the sender. > > > _______________________________________________ > > > QuantLib-users mailing list > > > Qua...@li... > > > https://lists.sourceforge.net/lists/listinfo/quantlib-users > |
|
From: Aditya G. <adi...@co...> - 2022-08-12 08:58:36
|
Hi Peter,
For point 2) the LinearTsrPricer can take the input as ql.ConstantSwaptionVolatilityStructure, but the Lognormal pricer complains by saying: " if only an atm surface is given, the volatility type must be inherited" So I guess it is not possible. To create a ‘constant volatility cube’, I instantiated an atmMatrix with a constant value, but all the OTM and ITM swaptions had a volatility of 0. This way, for every triplet (strike, expiry, tenor), QL would return a constant volatility. It is a workaround, but it sufficed for my purpose.
For 3) The code and data for the extrapolation is below. Some notes:
volHandle at the end is passed into the pricer. The data is not complete but should be enough for debugging purposes. The BBG/QL volatility cubes seem to match to a decent degree (i.e. I don’t believe it is the source of the error). The volatilities are within 10 bps when testing random data points. However, the atm strike does not change (even if the volatility is constant) and does not agree with Bloomberg.
normalVolCube =
Expiry
1Yr
2Yr
3Yr
4Yr
5Yr
7Yr
10Yr
12Yr
15Yr
20Yr
25Yr
30Yr
1Mo
153.65
160.51
151.81
143.8
141.03
129.88
123.16
118.88
110.61
108.9
104.21
101.91
3Mo
150.06
152.12
149.09
140.08
134.79
125.74
118.65
115.21
112.26
107.13
103.41
99.43
6Mo
154.30
150.74
143.9
137.09
132.84
123.31
115.03
111.31
107.88
103.41
100.5
96.31
9Mo
155.29
149.86
142.87
135.17
130.97
121.33
112.06
108.57
105.04
99.95
97.12
93.19
1Yr
155.98
149.11
140.78
133.39
127.82
118.13
109.42
106.32
102.06
97.15
94.05
90.8
2Yr
142.61
135.71
129.64
123.34
118.42
110
102.05
99.13
95.18
89.95
86.93
84.38
3Yr
130.05
124.03
117.71
113.72
109.87
102.87
96.37
93.46
89.5
84.21
81.15
79.07
4Yr
118.85
113.22
108.13
104.87
102.41
97.49
91.33
88.78
85.31
79.42
77.09
74.93
5Yr
109.73
104.94
102.2
99.09
96.85
92.53
87.38
84.84
81.37
76.05
73.36
72.24
6Yr
102.35
99.42
97.38
94.42
92.18
88.36
83.64
81.19
77.88
72.88
70.54
69.33
7Yr
96.52
93.55
91.44
89.36
87.41
84.15
79.99
77.68
74.58
70.13
68.25
66.85
8Yr
91.55
89.03
87.38
85.58
83.78
80.83
77.08
74.82
71.81
67.42
65.22
64.37
9Yr
86.91
85.11
83.57
81.89
80.19
77.48
74.07
71.89
68.99
64.76
62.82
62.05
10Yr
83.39
81.29
79.55
77.84
76.4
74.03
71.19
69.2
66.54
62.56
60.74
60.1
12Yr
77.24
75.44
74
72.38
70.97
69.1
66.47
64.68
62.27
58.88
57.3
56.85
15Yr
68.34
67.24
65.61
64.58
64.34
62.11
60.25
58.73
56.7
53.89
52.72
52.28
20Yr
60.48
60.35
59.15
58.46
57.75
56.64
55.07
53.74
51.97
49.87
48.96
48.63
25Yr
58.04
56.62
55.74
55.16
54.62
53.59
52.25
51.07
49.5
47.49
46.72
46.08
30Yr
54.83
53.31
52.73
52.34
51.96
51.06
49.7
48.76
47.52
45.21
44.11
44.1
strikeSpreadData Snippet:
Term x Tenor
-200bps
-100bps
-50bps
-25bps
ATM
25bps
50bps
100bps
200bps
3Mo X 1Yr
163.06
147.61
145.65
147.02
150.06
154.64
160.47
174.74
208.13
3Mo X 2Yr
168.32
152.63
149.59
150.1
152.12
155.6
160.36
172.72
203.32
3Mo X 3Yr
165.94
150.14
146.87
147.22
149.09
152.42
157.05
169.18
199.38
3Mo X 5Yr
154.44
137.68
133.49
133.36
134.79
137.76
142.09
153.73
182.89
3Mo X 7Yr
145.98
128.92
124.5
124.31
125.74
128.76
133.16
144.93
173.96
3Mo X 10Yr
141.64
123.6
118.33
117.66
118.65
121.3
125.42
136.78
165.14
3Mo X 12Yr
124.87
115.74
113.96
114.17
115.21
117.06
119.67
126.74
145.33
3Mo X 15Yr
137.53
118.56
112.58
111.56
112.26
114.72
118.73
130
158.17
3Mo X 20Yr
133.96
114.4
107.91
106.63
107.13
109.46
113.42
124.65
152.72
3Mo X 25Yr
130.47
110.79
104.19
102.88
103.41
105.83
109.9
121.34
149.56
3Mo X 30Yr
126.66
106.9
100.19
98.87
99.43
101.94
106.13
117.77
146.12
6Mo X 1Yr
153.08
147.26
148.88
151.13
154.3
158.3
163.02
174.1
199.93
6Mo X 2Yr
155.61
147.49
147.3
148.56
150.74
153.82
157.69
167.34
191.18
6Mo X 3Yr
151.41
141.74
140.85
141.85
143.9
146.93
150.84
160.73
185.27
6Mo X 5Yr
144.96
132.89
130.7
131.19
132.84
135.64
139.46
149.43
174.44
6Mo X 7Yr
137.51
124.48
121.67
121.87
123.31
125.95
129.67
139.51
164.24
6Mo X 10Yr
131.51
117.49
113.98
113.86
115.03
117.47
121.05
130.73
155.14
6Mo X 12Yr
119.63
111.38
109.93
110.24
111.31
113.15
115.68
122.45
140.14
6Mo X 15Yr
126.39
111.49
107.36
106.95
107.88
110.16
113.64
123.22
147.39
6Mo X 20Yr
123.32
107.85
103.29
102.66
103.41
105.58
109
118.53
142.61
6Mo X 25Yr
120.86
105.2
100.48
99.78
100.5
102.67
106.1
115.7
139.82
# ATM Volatility matrix
volType = ql.Normal
flatExtrapolation = False
atmSwapTenors = [ql.Period(tenor[:-1]) for tenor in normalVolCube.columns[1:]]
atmOptionTenors = [ql.Period(tenor[:-1]) for tenor in normalVolCube['Expiry']]
normalVols = normalVolCube[normalVolCube.columns[1:]].apply(
lambda x: x * 1e-4).values.tolist()
swaptionVolMatrix = ql.SwaptionVolatilityMatrix(calendar, convention,
atmOptionTenors, atmSwapTenors,
ql.Matrix(normalVols),
dayCounter, flatExtrapolation,
volType)
swaptionVolMatrixHandle = ql.SwaptionVolatilityStructureHandle(
swaptionVolMatrix)
strikeSpreads = [-200, -100, -50, -25, 0, 25, 50, 100, 200]
strikeSpreads = [x * 1e-4 for x in strikeSpreads]
optionTenors = [
ql.Period(3, ql.Months),
ql.Period(6, ql.Months),
ql.Period(9, ql.Months),
ql.Period(1, ql.Years),
ql.Period(3, ql.Years),
ql.Period(5, ql.Years),
ql.Period(7, ql.Years),
ql.Period(10, ql.Years),
ql.Period(15, ql.Years),
ql.Period(20, ql.Years),
ql.Period(30, ql.Years)
]
swapTenors = [
ql.Period(1, ql.Years),
ql.Period(2, ql.Years),
ql.Period(3, ql.Years),
ql.Period(5, ql.Years),
ql.Period(7, ql.Years),
ql.Period(10, ql.Years),
ql.Period(12, ql.Years),
ql.Period(15, ql.Years),
ql.Period(20, ql.Years),
ql.Period(25, ql.Years),
ql.Period(30, ql.Years)
]
nRows = len(optionTenors) * len(swapTenors)
nCols = len(strikeSpreads)
volSpreadsMatrix = strikeSpreadData[strikeSpreadData.columns[1:]].apply(
lambda x: x).values.tolist()
volSpreads = []
for i in range(nRows):
volSpreadsRow = []
for j in range(nCols):
volSpreadsRow.append(
ql.QuoteHandle(ql.SimpleQuote(volSpreadsMatrix[i][j])))
volSpreads.append(volSpreadsRow)
vegaWeightedSmileFit = False
volCube = ql.SwaptionVolCube2(swaptionVolMatrixHandle, optionTenors,
swapTenors, strikeSpreads, volSpreads,
swapIndex1, swapIndex2, vegaWeightedSmileFit)
volHandle = ql.SwaptionVolatilityStructureHandle(volCube)
volHandle.enableExtrapolation()
Thank you!
-----Original Message-----
From: Peter Caspers <pca...@gm...>
Sent: Friday, August 12, 2022 4:20 PM
To: Aditya Gupta <adi...@co...>
Cc: qua...@li...
Subject: Re: [Quantlib-users] CMS Spread Cap Pricing
[You don't often get email from pca...@gm...<mailto:pca...@gm...>. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
CAUTION: EXTERNAL Sender
Hi Aditya
1. correct
2. the pricer takes any SwaptionVolatilityStructure, i.e. a ConstantSwaptionVolatilityStructure should work. Is that what you mean?
3. this might be related to smile extrapolation, which strike range do you cover in your swaption cube and how do you set up the extrapolation? The pricer uses an integration over [-200%, +200%] by default.
4. I agree 500% is much to high, let's discuss further once 3 is resolved, i.e. QL / BBG matches
In addition, the linear TSR model might not work well when the time to expiry is large. Do you have more info on which model BBG uses?
Thank you
Peter
On Fri, 12 Aug 2022 at 09:36, Aditya Gupta <adi...@co...<mailto:adi...@co...>> wrote:
>
> Hi,
>
>
>
> I am trying to price a CMS Spread Cap using Quantlib. I am using data from Bloomberg, and I am using the following classes:
>
>
>
> yts is a bootstrapped Sofr curve
>
> Instrument = ql.CappedFlooredCmsSpreadCoupon()
>
> volCube = ql.SwaptionVolCube2() with the correct data and volType =
> ql.Normal
>
> cmsPricer = ql.LinearTSRPricer(volHandle, meanReversion, yts)
>
> pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts, 16)
>
>
>
> I have a few questions.
>
>
>
> It seems to me that to price a ql. CappedFlooredCmsSpreadCoupon, the only option is to use a ql.LognormalCmsSpreadPricer, and hence if we use Normal volatility, the input cmsPricer must be the LinearTSRPricer (Hagan cannot be used). Is my assessment correct?
> In order to check the pricing, I used a ‘constant’ volatility cube (set the atm matrix to a constant, rest to 0) and tested the pricing. The instrument was priced correctly (within the error from the discount curve). Can the pricer accept a constant volatility otherwise?
> The volCube seems to be returning the correct volatilities (within reason) when compared to Bloomberg, and is being passed to the pricer without error. But for some reason, the price is completely different (about half of what it should be). What could be the cause for the error, or how could I go about debugging this?
> If I use scipy.optimize to ‘artificially’ optimize the price as a function of the mean reversion, for a value of around 5 (which is way too high for the mean correlation), I was able to bring the error down to less than 1 dollar. Clearly this approach is not sensible. So I used the HullWhite model on the swaption matrix, and calibrated the mean reversion. But the error for this value of the mean reversion is much too high. How can I determine a good value for the mean reversion?
>
>
>
> Right now I am content with there being some error compared to Bloomberg, but I am unable to understand the blackbox behind the pricing engine and the mean reversion calibration, and if there is any way to solve these issues. Any and all help would be greatly appreciated. Thank you.
>
>
>
> Some of the code and data is as follows:
>
>
>
> today = ql.Date(25, 7, 2022)
>
> ql.Settings.instance().evaluationDate = today
>
>
>
> #Global variables
>
> calendar = ql.UnitedStates(ql.UnitedStates.FederalReserve)
>
> convention = ql.ModifiedFollowing
>
> endOfMonth = False
>
> dayCounter = ql.Actual360()
>
> fixingDays = 0
>
> compounding = ql.Compounded
>
> compoundingFrequency = ql.Annual
>
>
>
> yts = ql.RelinkableYieldTermStructureHandle()
>
> index = ql.Sofr(yts)
>
>
>
> helper = []
>
>
>
> # sofrData are just the rates from a dataframe
>
>
>
> helper += [
>
> ql.DepositRateHelper(
>
> ql.QuoteHandle(ql.SimpleQuote(sofrData['Shifted Rate'][0] /
> 100.0)),
>
> index)
>
> ]
>
>
>
> helper += [
>
> ql.OISRateHelper(0,
>
> ql.Period(expiration),
>
> ql.QuoteHandle(ql.SimpleQuote(rate / 100.0)),
>
> index,
>
> paymentLag=0,
>
> paymentConvention=convention,
>
> paymentFrequency=ql.Annual,
>
> paymentCalendar=calendar,
>
> averagingMethod=ql.RateAveraging.Compound) for
> rate,
>
> expiration in zip(sofrData['Shifted Rate'][1:],
> sofrData["Term"][1:])
>
> ]
>
>
>
> curve = ql.PiecewiseSplineCubicDiscount(today, helper, ql.Actual360())
>
> curve.enableExtrapolation()
>
> yts.linkTo(curve)
>
>
>
> meanReversion = ql.QuoteHandle(ql.SimpleQuote(5.037702498779181))
> #this is the ‘optimised’ value but I need further help
>
> correlation = ql.QuoteHandle(ql.SimpleQuote(36.6 * 1e-4))
>
>
>
> nominal = 1e9
>
> gearing = 1.0
>
> spread = 0.0
>
> cap = -8 * 1e-4
>
> floor = ql.nullDouble() #infinite floor
>
>
>
> startDate = ql.Date(26, 9, 2022)
>
> endDate = ql.Date(26, 9, 2023)
>
> paymentDate = ql.Date(26, 9, 2023)
>
>
>
> isInArrears = False
>
> exCouponDate = ql.Date()
>
>
>
> fixingDays = 0
>
>
>
> swapIndex1 = ql.UsdLiborSwapIsdaFixAm(ql.Period('30y'), yts, yts)
>
> ql.IndexManager.instance().clearHistory(swapIndex1.name())
>
>
>
> swapIndex2 = ql.UsdLiborSwapIsdaFixAm(ql.Period('10y'), yts, yts)
>
> ql.IndexManager.instance().clearHistory(swapIndex2.name())
>
>
>
> spreadIndex = ql.SwapSpreadIndex("Joint Index", swapIndex1,
> swapIndex2)
>
>
>
> instrument = ql.CappedFlooredCmsSpreadCoupon(paymentDate, nominal,
> startDate,
>
> endDate, fixingDays,
> spreadIndex,
>
> gearing, spread, cap,
> floor,
>
> ql.Date(), ql.Date(),
> dayCounter,
>
> isInArrears,
> exCouponDate)
>
>
>
> cmsPricer = ql.LinearTsrPricer(volHandle, meanReversion, yts)
>
>
>
> # cmsPricer = ql.NumericHaganPricer(volHandle,
> ql.GFunctionFactory.ParallelShifts , meanReversion)#, Rate
> lowerLimit=0.0, Rate upperLimit=1.0, Real precision=1.0e-6, Real
> hardUpperLimit=QL_MAX_REAL)
>
>
>
> integrationPoints = 16
>
>
>
> pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts,
>
> integrationPoints)
>
>
>
> instrument.setPricer(pricer)
>
>
>
> Regards,
>
> Aditya Gupta.
>
>
>
> ________________________________
> Complus Asset Management Limited ("CAML") is licensed by the Securities and Futures Commission of Hong Kong (“SFC”) for the Regulated Activities of Advising on Securities and Asset Management with CE number AWX256. CAML is subject to certain SFC codes and regulations, details of which can be found on the SFC's website at http://www.sfc.hk. CAML is a company incorporated in Hong Kong with its registered office and principal place of business as indicated above.
>
> Unless specifically indicated, this message should not be construed as an offer to sell or solicitation to purchase any securities, financial products or services or the giving of investment advice within or outside Hong Kong. This message is intended solely for the person(s) to whom it is addressed. If you receive this message in error, please immediately delete it and all copies of it from your computer network or hard copies, and notify the sender.
> _______________________________________________
> QuantLib-users mailing list
> Qua...@li...<mailto:Qua...@li...>
> https://lists.sourceforge.net/lists/listinfo/quantlib-users
|
|
From: Peter C. <pca...@gm...> - 2022-08-12 08:20:05
|
Hi Aditya
1. correct
2. the pricer takes any SwaptionVolatilityStructure, i.e. a
ConstantSwaptionVolatilityStructure should work. Is that what you
mean?
3. this might be related to smile extrapolation, which strike range do
you cover in your swaption cube and how do you set up the
extrapolation? The pricer uses an integration over [-200%, +200%] by
default.
4. I agree 500% is much to high, let's discuss further once 3 is
resolved, i.e. QL / BBG matches
In addition, the linear TSR model might not work well when the time to
expiry is large. Do you have more info on which model BBG uses?
Thank you
Peter
On Fri, 12 Aug 2022 at 09:36, Aditya Gupta <adi...@co...> wrote:
>
> Hi,
>
>
>
> I am trying to price a CMS Spread Cap using Quantlib. I am using data from Bloomberg, and I am using the following classes:
>
>
>
> yts is a bootstrapped Sofr curve
>
> Instrument = ql.CappedFlooredCmsSpreadCoupon()
>
> volCube = ql.SwaptionVolCube2() with the correct data and volType = ql.Normal
>
> cmsPricer = ql.LinearTSRPricer(volHandle, meanReversion, yts)
>
> pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts, 16)
>
>
>
> I have a few questions.
>
>
>
> It seems to me that to price a ql. CappedFlooredCmsSpreadCoupon, the only option is to use a ql.LognormalCmsSpreadPricer, and hence if we use Normal volatility, the input cmsPricer must be the LinearTSRPricer (Hagan cannot be used). Is my assessment correct?
> In order to check the pricing, I used a ‘constant’ volatility cube (set the atm matrix to a constant, rest to 0) and tested the pricing. The instrument was priced correctly (within the error from the discount curve). Can the pricer accept a constant volatility otherwise?
> The volCube seems to be returning the correct volatilities (within reason) when compared to Bloomberg, and is being passed to the pricer without error. But for some reason, the price is completely different (about half of what it should be). What could be the cause for the error, or how could I go about debugging this?
> If I use scipy.optimize to ‘artificially’ optimize the price as a function of the mean reversion, for a value of around 5 (which is way too high for the mean correlation), I was able to bring the error down to less than 1 dollar. Clearly this approach is not sensible. So I used the HullWhite model on the swaption matrix, and calibrated the mean reversion. But the error for this value of the mean reversion is much too high. How can I determine a good value for the mean reversion?
>
>
>
> Right now I am content with there being some error compared to Bloomberg, but I am unable to understand the blackbox behind the pricing engine and the mean reversion calibration, and if there is any way to solve these issues. Any and all help would be greatly appreciated. Thank you.
>
>
>
> Some of the code and data is as follows:
>
>
>
> today = ql.Date(25, 7, 2022)
>
> ql.Settings.instance().evaluationDate = today
>
>
>
> #Global variables
>
> calendar = ql.UnitedStates(ql.UnitedStates.FederalReserve)
>
> convention = ql.ModifiedFollowing
>
> endOfMonth = False
>
> dayCounter = ql.Actual360()
>
> fixingDays = 0
>
> compounding = ql.Compounded
>
> compoundingFrequency = ql.Annual
>
>
>
> yts = ql.RelinkableYieldTermStructureHandle()
>
> index = ql.Sofr(yts)
>
>
>
> helper = []
>
>
>
> # sofrData are just the rates from a dataframe
>
>
>
> helper += [
>
> ql.DepositRateHelper(
>
> ql.QuoteHandle(ql.SimpleQuote(sofrData['Shifted Rate'][0] / 100.0)),
>
> index)
>
> ]
>
>
>
> helper += [
>
> ql.OISRateHelper(0,
>
> ql.Period(expiration),
>
> ql.QuoteHandle(ql.SimpleQuote(rate / 100.0)),
>
> index,
>
> paymentLag=0,
>
> paymentConvention=convention,
>
> paymentFrequency=ql.Annual,
>
> paymentCalendar=calendar,
>
> averagingMethod=ql.RateAveraging.Compound) for rate,
>
> expiration in zip(sofrData['Shifted Rate'][1:], sofrData["Term"][1:])
>
> ]
>
>
>
> curve = ql.PiecewiseSplineCubicDiscount(today, helper, ql.Actual360())
>
> curve.enableExtrapolation()
>
> yts.linkTo(curve)
>
>
>
> meanReversion = ql.QuoteHandle(ql.SimpleQuote(5.037702498779181)) #this is the ‘optimised’ value but I need further help
>
> correlation = ql.QuoteHandle(ql.SimpleQuote(36.6 * 1e-4))
>
>
>
> nominal = 1e9
>
> gearing = 1.0
>
> spread = 0.0
>
> cap = -8 * 1e-4
>
> floor = ql.nullDouble() #infinite floor
>
>
>
> startDate = ql.Date(26, 9, 2022)
>
> endDate = ql.Date(26, 9, 2023)
>
> paymentDate = ql.Date(26, 9, 2023)
>
>
>
> isInArrears = False
>
> exCouponDate = ql.Date()
>
>
>
> fixingDays = 0
>
>
>
> swapIndex1 = ql.UsdLiborSwapIsdaFixAm(ql.Period('30y'), yts, yts)
>
> ql.IndexManager.instance().clearHistory(swapIndex1.name())
>
>
>
> swapIndex2 = ql.UsdLiborSwapIsdaFixAm(ql.Period('10y'), yts, yts)
>
> ql.IndexManager.instance().clearHistory(swapIndex2.name())
>
>
>
> spreadIndex = ql.SwapSpreadIndex("Joint Index", swapIndex1, swapIndex2)
>
>
>
> instrument = ql.CappedFlooredCmsSpreadCoupon(paymentDate, nominal, startDate,
>
> endDate, fixingDays, spreadIndex,
>
> gearing, spread, cap, floor,
>
> ql.Date(), ql.Date(), dayCounter,
>
> isInArrears, exCouponDate)
>
>
>
> cmsPricer = ql.LinearTsrPricer(volHandle, meanReversion, yts)
>
>
>
> # cmsPricer = ql.NumericHaganPricer(volHandle, ql.GFunctionFactory.ParallelShifts , meanReversion)#, Rate lowerLimit=0.0, Rate upperLimit=1.0, Real precision=1.0e-6, Real hardUpperLimit=QL_MAX_REAL)
>
>
>
> integrationPoints = 16
>
>
>
> pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts,
>
> integrationPoints)
>
>
>
> instrument.setPricer(pricer)
>
>
>
> Regards,
>
> Aditya Gupta.
>
>
>
> ________________________________
> Complus Asset Management Limited ("CAML") is licensed by the Securities and Futures Commission of Hong Kong (“SFC”) for the Regulated Activities of Advising on Securities and Asset Management with CE number AWX256. CAML is subject to certain SFC codes and regulations, details of which can be found on the SFC's website at http://www.sfc.hk. CAML is a company incorporated in Hong Kong with its registered office and principal place of business as indicated above.
>
> Unless specifically indicated, this message should not be construed as an offer to sell or solicitation to purchase any securities, financial products or services or the giving of investment advice within or outside Hong Kong. This message is intended solely for the person(s) to whom it is addressed. If you receive this message in error, please immediately delete it and all copies of it from your computer network or hard copies, and notify the sender.
> _______________________________________________
> QuantLib-users mailing list
> Qua...@li...
> https://lists.sourceforge.net/lists/listinfo/quantlib-users
|
|
From: Aditya G. <adi...@co...> - 2022-08-12 07:35:40
|
Hi,
I am trying to price a CMS Spread Cap using Quantlib. I am using data from Bloomberg, and I am using the following classes:
yts is a bootstrapped Sofr curve
Instrument = ql.CappedFlooredCmsSpreadCoupon()
volCube = ql.SwaptionVolCube2() with the correct data and volType = ql.Normal
cmsPricer = ql.LinearTSRPricer(volHandle, meanReversion, yts)
pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts, 16)
I have a few questions.
1. It seems to me that to price a ql. CappedFlooredCmsSpreadCoupon, the only option is to use a ql.LognormalCmsSpreadPricer, and hence if we use Normal volatility, the input cmsPricer must be the LinearTSRPricer (Hagan cannot be used). Is my assessment correct?
2. In order to check the pricing, I used a 'constant' volatility cube (set the atm matrix to a constant, rest to 0) and tested the pricing. The instrument was priced correctly (within the error from the discount curve). Can the pricer accept a constant volatility otherwise?
3. The volCube seems to be returning the correct volatilities (within reason) when compared to Bloomberg, and is being passed to the pricer without error. But for some reason, the price is completely different (about half of what it should be). What could be the cause for the error, or how could I go about debugging this?
4. If I use scipy.optimize to 'artificially' optimize the price as a function of the mean reversion, for a value of around 5 (which is way too high for the mean correlation), I was able to bring the error down to less than 1 dollar. Clearly this approach is not sensible. So I used the HullWhite model on the swaption matrix, and calibrated the mean reversion. But the error for this value of the mean reversion is much too high. How can I determine a good value for the mean reversion?
Right now I am content with there being some error compared to Bloomberg, but I am unable to understand the blackbox behind the pricing engine and the mean reversion calibration, and if there is any way to solve these issues. Any and all help would be greatly appreciated. Thank you.
Some of the code and data is as follows:
today = ql.Date(25, 7, 2022)
ql.Settings.instance().evaluationDate = today
#Global variables
calendar = ql.UnitedStates(ql.UnitedStates.FederalReserve)
convention = ql.ModifiedFollowing
endOfMonth = False
dayCounter = ql.Actual360()
fixingDays = 0
compounding = ql.Compounded
compoundingFrequency = ql.Annual
yts = ql.RelinkableYieldTermStructureHandle()
index = ql.Sofr(yts)
helper = []
# sofrData are just the rates from a dataframe
helper += [
ql.DepositRateHelper(
ql.QuoteHandle(ql.SimpleQuote(sofrData['Shifted Rate'][0] / 100.0)),
index)
]
helper += [
ql.OISRateHelper(0,
ql.Period(expiration),
ql.QuoteHandle(ql.SimpleQuote(rate / 100.0)),
index,
paymentLag=0,
paymentConvention=convention,
paymentFrequency=ql.Annual,
paymentCalendar=calendar,
averagingMethod=ql.RateAveraging.Compound) for rate,
expiration in zip(sofrData['Shifted Rate'][1:], sofrData["Term"][1:])
]
curve = ql.PiecewiseSplineCubicDiscount(today, helper, ql.Actual360())
curve.enableExtrapolation()
yts.linkTo(curve)
meanReversion = ql.QuoteHandle(ql.SimpleQuote(5.037702498779181)) #this is the 'optimised' value but I need further help
correlation = ql.QuoteHandle(ql.SimpleQuote(36.6 * 1e-4))
nominal = 1e9
gearing = 1.0
spread = 0.0
cap = -8 * 1e-4
floor = ql.nullDouble() #infinite floor
startDate = ql.Date(26, 9, 2022)
endDate = ql.Date(26, 9, 2023)
paymentDate = ql.Date(26, 9, 2023)
isInArrears = False
exCouponDate = ql.Date()
fixingDays = 0
swapIndex1 = ql.UsdLiborSwapIsdaFixAm(ql.Period('30y'), yts, yts)
ql.IndexManager.instance().clearHistory(swapIndex1.name())
swapIndex2 = ql.UsdLiborSwapIsdaFixAm(ql.Period('10y'), yts, yts)
ql.IndexManager.instance().clearHistory(swapIndex2.name())
spreadIndex = ql.SwapSpreadIndex("Joint Index", swapIndex1, swapIndex2)
instrument = ql.CappedFlooredCmsSpreadCoupon(paymentDate, nominal, startDate,
endDate, fixingDays, spreadIndex,
gearing, spread, cap, floor,
ql.Date(), ql.Date(), dayCounter,
isInArrears, exCouponDate)
cmsPricer = ql.LinearTsrPricer(volHandle, meanReversion, yts)
# cmsPricer = ql.NumericHaganPricer(volHandle, ql.GFunctionFactory.ParallelShifts , meanReversion)#, Rate lowerLimit=0.0, Rate upperLimit=1.0, Real precision=1.0e-6, Real hardUpperLimit=QL_MAX_REAL)
integrationPoints = 16
pricer = ql.LognormalCmsSpreadPricer(cmsPricer, correlation, yts,
integrationPoints)
instrument.setPricer(pricer)
Regards,
Aditya Gupta.
________________________________
Complus Asset Management Limited ("CAML") is licensed by the Securities and Futures Commission of Hong Kong ("SFC") for the Regulated Activities of Advising on Securities and Asset Management with CE number AWX256. CAML is subject to certain SFC codes and regulations, details of which can be found on the SFC's website at http://www.sfc.hk<http://www.sfc.hk/>. CAML is a company incorporated in Hong Kong with its registered office and principal place of business as indicated above.
Unless specifically indicated, this message should not be construed as an offer to sell or solicitation to purchase any securities, financial products or services or the giving of investment advice within or outside Hong Kong. This message is intended solely for the person(s) to whom it is addressed. If you receive this message in error, please immediately delete it and all copies of it from your computer network or hard copies, and notify the sender.
|
|
From: Evgenia V. <ge...@gm...> - 2022-08-12 05:42:52
|
Hi Ashish, Tank you! Evgenia ср, 10 авг. 2022 г., 20:47 Ashish Bansal <ash...@gm...>: > For vanilla swap you can refer to following: > http://gouthamanbalaraman.com/blog/interest-rate-swap-quantlib-python.html > > http://billiontrader.com/interest-rate-swaps-valuation-with-quantlib/ > > Try to find changes related to OIS to this. I have not used it for swap > yet not sure. > > Regards > Ashish > > On Wed, Aug 10, 2022, 12:21 PM Evgenia Vass <ge...@gm...> wrote: > >> Dear Quantlib users, >> I need to calculate the net present value for Overnight Indexed Swap. >> Since this is my first time doing this, please advise me where I can get >> acquainted with the basic algorithm for evaluating such swaps. Are there >> any typical examples? I will be grateful for any help and hints. >> Thanks! >> Jenn Vass >> _______________________________________________ >> QuantLib-users mailing list >> Qua...@li... >> https://lists.sourceforge.net/lists/listinfo/quantlib-users >> > |
|
From: Shashank C. <sha...@gm...> - 2022-08-11 17:40:29
|
Thank you :) On Thu, Aug 11, 2022 at 11:09 AM Mike DelMedico <mik...@gm...> wrote: > Minimize = scipy.minimize > > https://docs.scipy.org/doc/scipy/reference/optimize.html > > > On Thu, Aug 11, 2022 at 09:03 Shashank Choudhary <sha...@gm...> > wrote: > >> Hi jack , >> >> Thank you for your help. I really appreciate it. >> I am not sure what library does the “minimize” function belongs to ? >> I am not a Python programmer so I am trying to translate this code to C++. >> >> Regards >> Shashank >> >> On Thu, Aug 11, 2022 at 9:24 AM Jack G <jac...@gm...> wrote: >> >>> Shashank, >>> >>> Can I recommend this excellent answer by David Duarte that I think will >>> do exactly what you want? >>> >>> >>> https://quant.stackexchange.com/questions/57786/sabr-model-pricing-engine-in-python-quantlib >>> >>> Best, >>> Jack >>> >>> On Tue, Aug 9, 2022 at 11:17 AM Shashank Choudhary < >>> sha...@gm...> wrote: >>> >>>> Hi Jack , >>>> >>>> I am trying to find the values of Rho and Nu which minimize the mean >>>> squared error between market volatility and volatility computed using the >>>> sabr model formula for a given set of parameters (alpha, rho, nu , beta). I >>>> can assume Beta to be 0.5 while alpha can be ATM volatility. >>>> Now I need to set up the calibration as explained in the MATLAB version >>>> below >>>> >>>> https://www.mathworks.com/help/fininst/calibrating-the-sabr-model.html >>>> >>>> Now quantlib has the sabr model class in C++ with the formula to >>>> compute the volatility, but I need to calibrate the parameters rho and Nu >>>> that minimises the error function. >>>> >>>> Hope this makes sense :) >>>> >>>> regards >>>> Shashank >>>> >>>> >>>> >>>> On Mon, Aug 8, 2022 at 7:14 PM Jack G <jac...@gm...> wrote: >>>> >>>>> Hi Shashank, >>>>> >>>>> What exactly are you trying to do here? SABR is a funny one because >>>>> although it is a "model", it usually isn't used as such and is just used >>>>> for smile interpolation at a given tenor (with different SABR "models" >>>>> fitted to each tenor). Quite a lot of the art is about how to choose these >>>>> adjacent smile fits. >>>>> >>>>> I'm not aware of an inbuilt calibrator but it should be easy enough to >>>>> calibrate to a given vol smile. How are you interacting with QuantLib? >>>>> There are some exams of calibrators in the cookbook I believe, and I have >>>>> some python code for QuantLib that will do this for SABR as well if it >>>>> help,. >>>>> >>>>> >>>>> Best, >>>>> Jack >>>>> >>>>> >>>>> On Tue, 9 Aug 2022, 06:01 Shashank Choudhary, < >>>>> sha...@gm...> wrote: >>>>> >>>>>> Hi , >>>>>> >>>>>> How do I use the calibration engine for the SABR model parameters? I >>>>>> can access the sabr model class and find implied volatilities for a given >>>>>> set of parameters but I can’t locate and use the calibration engine, if >>>>>> there’s one. >>>>>> >>>>>> Any help would be much appreciated :) >>>>>> >>>>>> Regards >>>>>> Shashank >>>>>> _______________________________________________ >>>>>> QuantLib-users mailing list >>>>>> Qua...@li... >>>>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users >>>>>> >>>>>> _______________________________________________ >> QuantLib-users mailing list >> Qua...@li... >> https://lists.sourceforge.net/lists/listinfo/quantlib-users >> > |
|
From: Roberto C. <rob...@so...> - 2022-08-11 15:28:22
|
Thanks Ben & Tom seems there is a reason why QL did not implement the interpolation for swaps. As always, QL is a great community; thanks to all. Roberto ps. Would love to hear some contrarian, though. From: "Ben Watson" <ben...@ma...> To: "Tom Anderson" <tw...@ur...>, "QuantLib Users" <Qua...@li...> Sent: Thursday, 11 August, 2022 16:03:04 Subject: Re: [Quantlib-users] Rate curves with Nelson Siegel Svensson My 2c worth NSS is not designed to reprice the instruments exactly - it is basically a 3 factor model that just finds the curve of best fit as a proxy for 'fair' value. Typically used for relative value rich cheap type of analysis. The reason for this is that that bonds are not really homogeneous in their characteristics - coupons and maturities differ greatly adding a duration element to the tenor structure . The curve is typically not viewed in forward space there is no real market for forward start bonds. Bootstrapped splines are designed to reprice exactly all instruments. Swaps are homogeneous product. The splines are fantastic for pricing. Typically a trader will uses them to create a sooth forward curve (and that is what they are really trading). They are actually lousy for risk management however - you get what is called delta bleed. Regards Ben -----Original Message----- From: Tom Anderson <tw...@ur...> Sent: Thursday, 11 August 2022 8:45 PM To: QuantLib Users <Qua...@li...> Subject: Re: [Quantlib-users] Rate curves with Nelson Siegel Svensson Hi Roberto, I was not involved in this decision. But in my limited experience, it is rare for people trading rates derivatives to use a parametric curve like Nelson-Siegel-Svensson; rather, we use interpolated curves, like splines and so on. Parametric curves are more commonly used with bonds. Why is this? Assuming it is not just a random historical accident, i think this is because rate derivatives have a greater degree of fungibility with each other than bonds do. In very loose terms, rate derivatives are various slices of some consistent underlying term structure, and have potentially infinite supply. Whereas bonds are individual things that trade according to their own particular dynamics, and have fixed supply. Both derive their value from discounted future cashflows, but there are idiosyncractic supply and demand effects that can push bond prices away from the "true" value of their cashflows. That means that trying to draw a spline through bond yields (or implied forwards etc) makes no sense. There is no reason to think that all bond yields sit exactly on some smooth, meaningful term structure. So, instead, we fit a parametric model, which tries to extract such a term structure from the soup of idiosyncratic pricing noise. We could also do this for rate derivatives. But since we have a lot more confidence in the consistency of their pricing, there's no point. We can just use a spline. I could be completely wrong about this, though. I have never seen this discussed explicitly. Regards, tom On Wed, 10 Aug 2022, Roberto Cocchi wrote: > Dear all > > I'm working w Claudio on this topic. I am asking myself (and anyone > that sees fit to have an opinion) why does fitting (NSS, etc) been > implemented only for Govt Bonds? Is it that it does not make sense in > the case of pure yield bearing instruments, such as swaps, FRA's, MM? > Else, if it made sense, it would have been implemented time ago. > > Your thoughts? > > Roberto > Sent: Thursday, 4 August, 2022 13:05:08 > Subject: Re: [Quantlib-users] Rate curves with Nelson Siegel Svensson > > Hello Claudio, > you can modify the FittedBondDiscountCurve class so that it can use generic RateHelper instances, but it will miss some features. Also, do keep into account that while PiecewiseYieldCurve creates a curve that reproduces the inputs exactly, FittedBondDiscountCurve doesn't, so I would check if that's acceptable for you. > > This said: you can replace BondHelper with RateHelper in the FittedBondDiscountCurve class, but you'll have to comment out some code that would not compile. One such place is < [ https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuant Lib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffittedbonddiscountcurve. cpp%23L98-L110&e=8b2395da&h=278c8320&f=y&p=n | https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuantLib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffi&e=8b2395da&h=47826e49&f=y&p=n ttedbonddiscountcurve.cpp#L98-L110 ] >; these are a few checks and you can live without them. Another place is < [ https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuant Lib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffittedbonddiscountcurve. cpp%23L98-L110&e=8b2395da&h=278c8320&f=y&p=n | https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuantLib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffi&e=8b2395da&h=47826e49&f=y&p=n ttedbonddiscountcurve.cpp#L98-L110 ] >: commenting this out means that you can't rely on the curve for calculating the calibration weights and you'll have to pass them in explicitly. > > Hope this helps, > Luigi > > > On Tue, Aug 2, 2022 at 3:39 PM Claudio D'Angelo < [ mailto:cla...@so... | cla...@so... ] > wrote: > > > Hello all, > > let me introduce myself, I'm a java developer, I need to modify an > application that is usimg quantlib (via SWIG). > > Currently this application is creating curves using the class > PiecewiseYieldCurve and passing rates like RfaRate and DepositRate, > both using a SimpleQuote containing the yield value of the rate. > > Now I would interpolate/fit the curve using the NSS, checking the API > seems the only class that can implement this fitting is the > FittedBondDiscountCurve. Unfortunately this class wants in input a > BondHelper or a Fixed RateBondHelper, both of them work using bond > data and the price. > > Is it possible to implement a curve using the NSS but with rates with > only yield values (naturally with other data but not a bond and a > price as input). > > I don't know if I was clear, unfortunately I don't have experience > with quant, quantlib and C++ and I need help in order to understand > better and resolve my issue. > > Thank you very much in advance to all. > > > Claudio > > > > > _______________________________________________ > QuantLib-users mailing list > [ mailto:Qua...@li... | > Qua...@li... ] [ > https://urlsand.esvalabs.com/?u=https%3A%2F%2Flists.sourceforge.net%2F > lists%2Flistinfo%2Fquantlib-users&e=8b2395da&h=5e0eb166&f=y&p=n | > https://urlsand.esvalabs.com/?u=https%3A%2F%2Flists.sourceforge.net%2Flists%2Flistinfo%2Fquantlib-users&e=8b2395da&h=5e0eb166&f=y&p=n ] > > > > > > > -- There's no future. _______________________________________________ QuantLib-users mailing list Qua...@li... https://urlsand.esvalabs.com/?u=https%3A%2F%2Flists.sourceforge.net%2Flists%2Flistinfo%2Fquantlib-users&e=8b2395da&h=5e0eb166&f=y&p=n -- EXTERNAL E-MAIL. Message from outside of SoftSolutions! Do not click links or download/open attachments unless you trust the sender and believe the content is safe. |
|
From: Mike D. <mik...@gm...> - 2022-08-11 15:09:40
|
Minimize = scipy.minimize https://docs.scipy.org/doc/scipy/reference/optimize.html On Thu, Aug 11, 2022 at 09:03 Shashank Choudhary <sha...@gm...> wrote: > Hi jack , > > Thank you for your help. I really appreciate it. > I am not sure what library does the “minimize” function belongs to ? > I am not a Python programmer so I am trying to translate this code to C++. > > Regards > Shashank > > On Thu, Aug 11, 2022 at 9:24 AM Jack G <jac...@gm...> wrote: > >> Shashank, >> >> Can I recommend this excellent answer by David Duarte that I think will >> do exactly what you want? >> >> >> https://quant.stackexchange.com/questions/57786/sabr-model-pricing-engine-in-python-quantlib >> >> Best, >> Jack >> >> On Tue, Aug 9, 2022 at 11:17 AM Shashank Choudhary < >> sha...@gm...> wrote: >> >>> Hi Jack , >>> >>> I am trying to find the values of Rho and Nu which minimize the mean >>> squared error between market volatility and volatility computed using the >>> sabr model formula for a given set of parameters (alpha, rho, nu , beta). I >>> can assume Beta to be 0.5 while alpha can be ATM volatility. >>> Now I need to set up the calibration as explained in the MATLAB version >>> below >>> >>> https://www.mathworks.com/help/fininst/calibrating-the-sabr-model.html >>> >>> Now quantlib has the sabr model class in C++ with the formula to compute >>> the volatility, but I need to calibrate the parameters rho and Nu >>> that minimises the error function. >>> >>> Hope this makes sense :) >>> >>> regards >>> Shashank >>> >>> >>> >>> On Mon, Aug 8, 2022 at 7:14 PM Jack G <jac...@gm...> wrote: >>> >>>> Hi Shashank, >>>> >>>> What exactly are you trying to do here? SABR is a funny one because >>>> although it is a "model", it usually isn't used as such and is just used >>>> for smile interpolation at a given tenor (with different SABR "models" >>>> fitted to each tenor). Quite a lot of the art is about how to choose these >>>> adjacent smile fits. >>>> >>>> I'm not aware of an inbuilt calibrator but it should be easy enough to >>>> calibrate to a given vol smile. How are you interacting with QuantLib? >>>> There are some exams of calibrators in the cookbook I believe, and I have >>>> some python code for QuantLib that will do this for SABR as well if it >>>> help,. >>>> >>>> >>>> Best, >>>> Jack >>>> >>>> >>>> On Tue, 9 Aug 2022, 06:01 Shashank Choudhary, <sha...@gm...> >>>> wrote: >>>> >>>>> Hi , >>>>> >>>>> How do I use the calibration engine for the SABR model parameters? I >>>>> can access the sabr model class and find implied volatilities for a given >>>>> set of parameters but I can’t locate and use the calibration engine, if >>>>> there’s one. >>>>> >>>>> Any help would be much appreciated :) >>>>> >>>>> Regards >>>>> Shashank >>>>> _______________________________________________ >>>>> QuantLib-users mailing list >>>>> Qua...@li... >>>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users >>>>> >>>>> _______________________________________________ > QuantLib-users mailing list > Qua...@li... > https://lists.sourceforge.net/lists/listinfo/quantlib-users > |
|
From: Ben W. <ben...@ma...> - 2022-08-11 14:25:23
|
My 2c worth NSS is not designed to reprice the instruments exactly - it is basically a 3 factor model that just finds the curve of best fit as a proxy for 'fair' value. Typically used for relative value rich cheap type of analysis. The reason for this is that that bonds are not really homogeneous in their characteristics - coupons and maturities differ greatly adding a duration element to the tenor structure . The curve is typically not viewed in forward space there is no real market for forward start bonds. Bootstrapped splines are designed to reprice exactly all instruments. Swaps are homogeneous product. The splines are fantastic for pricing. Typically a trader will uses them to create a sooth forward curve (and that is what they are really trading). They are actually lousy for risk management however - you get what is called delta bleed. Regards Ben -----Original Message----- From: Tom Anderson <tw...@ur...> Sent: Thursday, 11 August 2022 8:45 PM To: QuantLib Users <Qua...@li...> Subject: Re: [Quantlib-users] Rate curves with Nelson Siegel Svensson Hi Roberto, I was not involved in this decision. But in my limited experience, it is rare for people trading rates derivatives to use a parametric curve like Nelson-Siegel-Svensson; rather, we use interpolated curves, like splines and so on. Parametric curves are more commonly used with bonds. Why is this? Assuming it is not just a random historical accident, i think this is because rate derivatives have a greater degree of fungibility with each other than bonds do. In very loose terms, rate derivatives are various slices of some consistent underlying term structure, and have potentially infinite supply. Whereas bonds are individual things that trade according to their own particular dynamics, and have fixed supply. Both derive their value from discounted future cashflows, but there are idiosyncractic supply and demand effects that can push bond prices away from the "true" value of their cashflows. That means that trying to draw a spline through bond yields (or implied forwards etc) makes no sense. There is no reason to think that all bond yields sit exactly on some smooth, meaningful term structure. So, instead, we fit a parametric model, which tries to extract such a term structure from the soup of idiosyncratic pricing noise. We could also do this for rate derivatives. But since we have a lot more confidence in the consistency of their pricing, there's no point. We can just use a spline. I could be completely wrong about this, though. I have never seen this discussed explicitly. Regards, tom On Wed, 10 Aug 2022, Roberto Cocchi wrote: > Dear all > > I'm working w Claudio on this topic. I am asking myself (and anyone > that sees fit to have an opinion) why does fitting (NSS, etc) been > implemented only for Govt Bonds? Is it that it does not make sense in > the case of pure yield bearing instruments, such as swaps, FRA's, MM? > Else, if it made sense, it would have been implemented time ago. > > Your thoughts? > > Roberto > Sent: Thursday, 4 August, 2022 13:05:08 > Subject: Re: [Quantlib-users] Rate curves with Nelson Siegel Svensson > > Hello Claudio, > you can modify the FittedBondDiscountCurve class so that it can use generic RateHelper instances, but it will miss some features. Also, do keep into account that while PiecewiseYieldCurve creates a curve that reproduces the inputs exactly, FittedBondDiscountCurve doesn't, so I would check if that's acceptable for you. > > This said: you can replace BondHelper with RateHelper in the FittedBondDiscountCurve class, but you'll have to comment out some code that would not compile. One such place is < [ https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuant Lib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffittedbonddiscountcurve. cpp%23L98-L110&e=8b2395da&h=278c8320&f=y&p=n | https://github.com/lballabio/QuantLib/blob/master/ql/termstructures/yield/fi ttedbonddiscountcurve.cpp#L98-L110 ] >; these are a few checks and you can live without them. Another place is < [ https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuant Lib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffittedbonddiscountcurve. cpp%23L98-L110&e=8b2395da&h=278c8320&f=y&p=n | https://github.com/lballabio/QuantLib/blob/master/ql/termstructures/yield/fi ttedbonddiscountcurve.cpp#L98-L110 ] >: commenting this out means that you can't rely on the curve for calculating the calibration weights and you'll have to pass them in explicitly. > > Hope this helps, > Luigi > > > On Tue, Aug 2, 2022 at 3:39 PM Claudio D'Angelo < [ mailto:cla...@so... | cla...@so... ] > wrote: > > > Hello all, > > let me introduce myself, I'm a java developer, I need to modify an > application that is usimg quantlib (via SWIG). > > Currently this application is creating curves using the class > PiecewiseYieldCurve and passing rates like RfaRate and DepositRate, > both using a SimpleQuote containing the yield value of the rate. > > Now I would interpolate/fit the curve using the NSS, checking the API > seems the only class that can implement this fitting is the > FittedBondDiscountCurve. Unfortunately this class wants in input a > BondHelper or a Fixed RateBondHelper, both of them work using bond > data and the price. > > Is it possible to implement a curve using the NSS but with rates with > only yield values (naturally with other data but not a bond and a > price as input). > > I don't know if I was clear, unfortunately I don't have experience > with quant, quantlib and C++ and I need help in order to understand > better and resolve my issue. > > Thank you very much in advance to all. > > > Claudio > > > > > _______________________________________________ > QuantLib-users mailing list > [ mailto:Qua...@li... | > Qua...@li... ] [ > https://urlsand.esvalabs.com/?u=https%3A%2F%2Flists.sourceforge.net%2F > lists%2Flistinfo%2Fquantlib-users&e=8b2395da&h=5e0eb166&f=y&p=n | > https://lists.sourceforge.net/lists/listinfo/quantlib-users ] > > > > > > > -- There's no future. |
|
From: Simone C. <Sim...@ri...> - 2022-08-11 14:22:00
|
Hi Roberto and Tom, I recall this being discussed in the 2013 paper by Bianchetti and Ametrano "Everything You Always Wanted to Know About Multiple Interest Rate Curve Bootstrapping but Were Afraid to Ask" (link: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2219548). See in particular this excerpt from the introduction section: [cid:image002.jpg@01D8AD93.E1D54D80] Hope this helps! Best regards, Simone From: Tom Anderson <tw...@ur...> Sent: 11 August 2022 11:45 To: QuantLib Users <Qua...@li...> Subject: Re: [Quantlib-users] Rate curves with Nelson Siegel Svensson Hi Roberto, I was not involved in this decision. But in my limited experience, it is rare for people trading rates derivatives to use a parametric curve like Nelson-Siegel-Svensson; rather, we use interpolated curves, like splines and so on. Parametric curves are more commonly used with bonds. Why is this? Assuming it is not just a random historical accident, i think this is because rate derivatives have a greater degree of fungibility with each other than bonds do. In very loose terms, rate derivatives are various slices of some consistent underlying term structure, and have potentially infinite supply. Whereas bonds are individual things that trade according to their own particular dynamics, and have fixed supply. Both derive their value from discounted future cashflows, but there are idiosyncractic supply and demand effects that can push bond prices away from the "true" value of their cashflows. That means that trying to draw a spline through bond yields (or implied forwards etc) makes no sense. There is no reason to think that all bond yields sit exactly on some smooth, meaningful term structure. So, instead, we fit a parametric model, which tries to extract such a term structure from the soup of idiosyncratic pricing noise. We could also do this for rate derivatives. But since we have a lot more confidence in the consistency of their pricing, there's no point. We can just use a spline. I could be completely wrong about this, though. I have never seen this discussed explicitly. Regards, tom On Wed, 10 Aug 2022, Roberto Cocchi wrote: > Dear all > > I'm working w Claudio on this topic. I am asking myself (and anyone that > sees fit to have an opinion) why does fitting (NSS, etc) been > implemented only for Govt Bonds? Is it that it does not make sense in > the case of pure yield bearing instruments, such as swaps, FRA's, MM? > Else, if it made sense, it would have been implemented time ago. > > Your thoughts? > > Roberto > Sent: Thursday, 4 August, 2022 13:05:08 > Subject: Re: [Quantlib-users] Rate curves with Nelson Siegel Svensson > > Hello Claudio, > you can modify the FittedBondDiscountCurve class so that it can use generic RateHelper instances, but it will miss some features. Also, do keep into account that while PiecewiseYieldCurve creates a curve that reproduces the inputs exactly, FittedBondDiscountCurve doesn't, so I would check if that's acceptable for you. > > This said: you can replace BondHelper with RateHelper in the FittedBondDiscountCurve class, but you'll have to comment out some code that would not compile. One such place is < [ https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuantLib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffittedbonddiscountcurve.cpp%23L98-L110&e=8b2395da&h=278c8320&f=y&p=n<https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuantLib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffittedbonddiscountcurve.cpp%23L98-L110&e=8b2395da&h=278c8320&f=y&p=n> | https://github.com/lballabio/QuantLib/blob/master/ql/termstructures/yield/fittedbonddiscountcurve.cpp#L98-L110<https://github.com/lballabio/QuantLib/blob/master/ql/termstructures/yield/fittedbonddiscountcurve.cpp#L98-L110> ] >; these are a few checks and you can live without them. Another place is < [ https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuantLib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffittedbonddiscountcurve.cpp%23L98-L110&e=8b2395da&h=278c8320&f=y&p=n<https://urlsand.esvalabs.com/?u=https%3A%2F%2Fgithub.com%2Flballabio%2FQuantLib%2Fblob%2Fmaster%2Fql%2Ftermstructures%2Fyield%2Ffittedbonddiscountcurve.cpp%23L98-L110&e=8b2395da&h=278c8320&f=y&p=n> | https://github.com/lballabio/QuantLib/blob/master/ql/termstructures/yield/fittedbonddiscountcurve.cpp#L98-L110<https://github.com/lballabio/QuantLib/blob/master/ql/termstructures/yield/fittedbonddiscountcurve.cpp#L98-L110> ] >: commenting this out means that you can't rely on the curve for calculating the calibration weights and you'll have to pass them in explicitly. > > Hope this helps, > Luigi > > > On Tue, Aug 2, 2022 at 3:39 PM Claudio D'Angelo < [ mailto:cla...@so... | cla...@so...<mailto:cla...@so...> ] > wrote: > > > Hello all, > > let me introduce myself, I'm a java developer, I need to modify an > application that is usimg quantlib (via SWIG). > > Currently this application is creating curves using the class > PiecewiseYieldCurve and passing rates like RfaRate and DepositRate, both > using a SimpleQuote containing the yield value of the rate. > > Now I would interpolate/fit the curve using the NSS, checking the API > seems the only class that can implement this fitting is the > FittedBondDiscountCurve. Unfortunately this class wants in input a > BondHelper or a Fixed RateBondHelper, both of them work using bond data > and the price. > > Is it possible to implement a curve using the NSS but with rates with > only yield values (naturally with other data but not a bond and a price > as input). > > I don't know if I was clear, unfortunately I don't have experience with > quant, quantlib and C++ and I need help in order to understand better > and resolve my issue. > > Thank you very much in advance to all. > > > Claudio > > > > > _______________________________________________ > QuantLib-users mailing list > [ mailto:Qua...@li... | Qua...@li...<mailto:Qua...@li...> ] > [ https://urlsand.esvalabs.com/?u=https%3A%2F%2Flists.sourceforge.net%2Flists%2Flistinfo%2Fquantlib-users&e=8b2395da&h=5e0eb166&f=y&p=n<https://urlsand.esvalabs.com/?u=https%3A%2F%2Flists.sourceforge.net%2Flists%2Flistinfo%2Fquantlib-users&e=8b2395da&h=5e0eb166&f=y&p=n> | https://lists.sourceforge.net/lists/listinfo/quantlib-users<https://lists.sourceforge.net/lists/listinfo/quantlib-users> ] > > > > > > > -- There's no future. |