Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Functions
optionletstripper.cpp File Reference
#include "capfloormarketdata.hpp"
#include "yieldcurvemarketdata.hpp"
#include "toplevelfixture.hpp"
#include <boost/test/unit_test.hpp>
#include <qle/termstructures/optionletstripper1.hpp>
#include <qle/termstructures/optionletstripper2.hpp>
#include <ql/instruments/makecapfloor.hpp>
#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
#include <ql/termstructures/volatility/capfloor/capfloortermvolcurve.hpp>
#include <ql/termstructures/volatility/optionlet/strippedoptionletadapter.hpp>
#include <ql/termstructures/yield/zerospreadedtermstructure.hpp>

Go to the source code of this file.

Functions

 BOOST_AUTO_TEST_CASE (testUsualNormalStripping)
 
 BOOST_AUTO_TEST_CASE (testUsualShiftedLognormalStripping)
 
 BOOST_AUTO_TEST_CASE (testNormalToShiftedLognormalStripping)
 
 BOOST_AUTO_TEST_CASE (testShiftedLognormalToNormalStripping)
 
 BOOST_AUTO_TEST_CASE (testShiftedLognormalToShiftedLognormalStripping)
 
 BOOST_AUTO_TEST_CASE (testUsualNormalStrippingWithAtm)
 
 BOOST_AUTO_TEST_CASE (testUsualShiftedLognormalStrippingWithAtm)
 
 BOOST_AUTO_TEST_CASE (testNormalToShiftedLognormalStrippingWithAtm)
 
 BOOST_AUTO_TEST_CASE (testShiftedLognormalToNormalStrippingWithAtm)
 
 BOOST_AUTO_TEST_CASE (testShiftedLognormalToShiftedLognormalStrippingWithAtm)
 
 BOOST_AUTO_TEST_CASE (testNormalToLognormalGivesError)
 
 BOOST_AUTO_TEST_CASE (testNormalToLognormalModifiedGivesError)
 
 BOOST_AUTO_TEST_CASE (testNormalToLognormalWithPositiveForwards)
 

Function Documentation

◆ BOOST_AUTO_TEST_CASE() [1/13]

BOOST_AUTO_TEST_CASE ( testUsualNormalStripping  )

Definition at line 76 of file optionletstripper.cpp.

76 {
77 BOOST_TEST_MESSAGE("Testing standard stripping of normal capfloor vols...");
78
79 CommonVars vars;
80
81 // EUR cap floor normal volatility surface
82 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
83 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
84 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
85 vars.dayCounter);
86
87 // Create Normal stripped optionlet surface and Normal engine
88 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
89 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
90 vars.yieldCurves.discountEonia, Normal));
91 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
92 Handle<OptionletVolatilityStructure> ovs(adapter);
93 ovs->enableExtrapolation();
94 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> engine =
95 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
96
97 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
98 QuantLib::ext::shared_ptr<CapFloor> cap;
99
100 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
101 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
102 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
103
104 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
105 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
106 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
107 vars.settlementDays * Days);
108
109 cap->setPricingEngine(engine);
110 Real strippedPrice = cap->NPV();
111
112 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nVols[i][j]));
113 cap->setPricingEngine(flatEngine);
114 Real flatPrice = cap->NPV();
115
116 Real error = std::fabs(strippedPrice - flatPrice);
117 BOOST_CHECK_MESSAGE(error < vars.accuracy,
118 "\noption tenor: " << vars.vols.tenors[i]
119 << "\nstrike: " << io::rate(vars.vols.strikes[j])
120 << "\nstripped vol price: " << io::rate(strippedPrice)
121 << "\nconstant vol price: " << io::rate(flatPrice)
122 << "\nerror: " << io::rate(error)
123 << "\ntolerance: " << io::rate(vars.accuracy));
124 }
125 }
126}

◆ BOOST_AUTO_TEST_CASE() [2/13]

BOOST_AUTO_TEST_CASE ( testUsualShiftedLognormalStripping  )

Definition at line 128 of file optionletstripper.cpp.

128 {
129 BOOST_TEST_MESSAGE("Testing standard stripping of shifted lognormal capfloor vols...");
130
131 CommonVars vars;
132
133 // EUR cap floor shifted lognormal volatility surface
134 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
135 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
136 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_1,
137 vars.dayCounter);
138
139 // Create shifted lognormal stripped optionlet surface and shifted lognormal engine
140 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
141 new QuantExt::OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
142 vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_1));
143 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
144 Handle<OptionletVolatilityStructure> ovs(adapter);
145 ovs->enableExtrapolation();
146 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
147 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
148
149 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
150 QuantLib::ext::shared_ptr<CapFloor> cap;
151
152 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
153 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
154 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_1);
155
156 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
157 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
158 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
159 vars.settlementDays * Days);
160
161 cap->setPricingEngine(engine);
162 Real strippedPrice = cap->NPV();
163
164 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_1[i][j]));
165 cap->setPricingEngine(flatEngine);
166 Real flatPrice = cap->NPV();
167
168 Real error = std::fabs(strippedPrice - flatPrice);
169 BOOST_CHECK_MESSAGE(error < vars.accuracy,
170 "\noption tenor: " << vars.vols.tenors[i]
171 << "\nstrike: " << io::rate(vars.vols.strikes[j])
172 << "\nstripped vol price: " << io::rate(strippedPrice)
173 << "\nconstant vol price: " << io::rate(flatPrice)
174 << "\nerror: " << io::rate(error)
175 << "\ntolerance: " << io::rate(vars.accuracy));
176 }
177 }
178}

◆ BOOST_AUTO_TEST_CASE() [3/13]

BOOST_AUTO_TEST_CASE ( testNormalToShiftedLognormalStripping  )

Definition at line 180 of file optionletstripper.cpp.

180 {
181 BOOST_TEST_MESSAGE("Testing stripping of normal capfloor vols to give shifted lognormal optionlet vols...");
182
183 CommonVars vars;
184
185 // EUR cap floor normal volatility surface
186 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
187 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
188 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
189 vars.dayCounter);
190
191 // Create shifted lognormal stripped optionlet surface and Black engine
192 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
193 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
194 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, vars.vols.shift_1));
195 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
196 Handle<OptionletVolatilityStructure> ovs(adapter);
197 ovs->enableExtrapolation();
198 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
199 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
200
201 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
202 QuantLib::ext::shared_ptr<CapFloor> cap;
203
204 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
205 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
206 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
207
208 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
209 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
210 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
211 vars.settlementDays * Days);
212
213 cap->setPricingEngine(engine);
214 Real strippedPrice = cap->NPV();
215
216 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nVols[i][j]));
217 cap->setPricingEngine(flatEngine);
218 Real flatPrice = cap->NPV();
219
220 Real error = std::fabs(strippedPrice - flatPrice);
221 BOOST_CHECK_MESSAGE(error < vars.accuracy,
222 "\noption tenor: " << vars.vols.tenors[i]
223 << "\nstrike: " << io::rate(vars.vols.strikes[j])
224 << "\nstripped vol price: " << io::rate(strippedPrice)
225 << "\nconstant vol price: " << io::rate(flatPrice)
226 << "\nerror: " << io::rate(error)
227 << "\ntolerance: " << io::rate(vars.accuracy));
228 }
229 }
230}

◆ BOOST_AUTO_TEST_CASE() [4/13]

BOOST_AUTO_TEST_CASE ( testShiftedLognormalToNormalStripping  )

Definition at line 232 of file optionletstripper.cpp.

232 {
233 BOOST_TEST_MESSAGE("Testing stripping of shifted lognormal capfloor vols to give normal optionlet vols...");
234
235 CommonVars vars;
236
237 // EUR cap floor shifted lognormal volatility surface
238 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
239 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
240 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_2,
241 vars.dayCounter);
242
243 // Create normal stripped optionlet surface and Bachelier engine
244 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
245 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
246 vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_2, Normal, 0.0));
247 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
248 Handle<OptionletVolatilityStructure> ovs(adapter);
249 ovs->enableExtrapolation();
250 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> engine =
251 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
252
253 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
254 QuantLib::ext::shared_ptr<CapFloor> cap;
255
256 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
257 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
258 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_2);
259
260 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
261 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
262 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
263 vars.settlementDays * Days);
264
265 cap->setPricingEngine(engine);
266 Real strippedPrice = cap->NPV();
267
268 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_2[i][j]));
269 cap->setPricingEngine(flatEngine);
270 Real flatPrice = cap->NPV();
271
272 Real error = std::fabs(strippedPrice - flatPrice);
273 BOOST_CHECK_MESSAGE(error < vars.accuracy,
274 "\noption tenor: " << vars.vols.tenors[i]
275 << "\nstrike: " << io::rate(vars.vols.strikes[j])
276 << "\nstripped vol price: " << io::rate(strippedPrice)
277 << "\nconstant vol price: " << io::rate(flatPrice)
278 << "\nerror: " << io::rate(error)
279 << "\ntolerance: " << io::rate(vars.accuracy));
280 }
281 }
282}

◆ BOOST_AUTO_TEST_CASE() [5/13]

BOOST_AUTO_TEST_CASE ( testShiftedLognormalToShiftedLognormalStripping  )

Definition at line 284 of file optionletstripper.cpp.

284 {
285 BOOST_TEST_MESSAGE("Testing stripping with shifted lognormal vols with different shifts...");
286
287 CommonVars vars;
288
289 // EUR cap floor shifted lognormal volatility surface
290 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
291 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
292 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_2,
293 vars.dayCounter);
294
295 // Create shifted lognormal stripped optionlet surface and shifted lognormal engine with different shift
296 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper =
297 QuantLib::ext::shared_ptr<OptionletStripper1>(new OptionletStripper1(
298 volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter, vars.yieldCurves.discountEonia,
299 ShiftedLognormal, vars.vols.shift_2, ShiftedLognormal, vars.vols.shift_1));
300 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
301 Handle<OptionletVolatilityStructure> ovs(adapter);
302 ovs->enableExtrapolation();
303 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
304 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
305
306 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
307 QuantLib::ext::shared_ptr<CapFloor> cap;
308
309 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
310 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
311 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_2);
312
313 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
314 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
315 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
316 vars.settlementDays * Days);
317
318 cap->setPricingEngine(engine);
319 Real strippedPrice = cap->NPV();
320
321 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_2[i][j]));
322 cap->setPricingEngine(flatEngine);
323 Real flatPrice = cap->NPV();
324
325 Real error = std::fabs(strippedPrice - flatPrice);
326 BOOST_CHECK_MESSAGE(error < vars.accuracy,
327 "\noption tenor: " << vars.vols.tenors[i]
328 << "\nstrike: " << io::rate(vars.vols.strikes[j])
329 << "\nstripped vol price: " << io::rate(strippedPrice)
330 << "\nconstant vol price: " << io::rate(flatPrice)
331 << "\nerror: " << io::rate(error)
332 << "\ntolerance: " << io::rate(vars.accuracy));
333 }
334 }
335}

◆ BOOST_AUTO_TEST_CASE() [6/13]

BOOST_AUTO_TEST_CASE ( testUsualNormalStrippingWithAtm  )

Definition at line 337 of file optionletstripper.cpp.

337 {
338 BOOST_TEST_MESSAGE("Testing standard stripping of normal capfloor vols with overlayed ATM curve...");
339
340 CommonVars vars;
341
342 // EUR cap floor normal volatility surface
343 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
344 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
345 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
346 vars.dayCounter);
347
348 // EUR cap floor normal ATM curve
349 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
350 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.nAtmVols, vars.dayCounter));
351
352 // Create Normal stripped optionlet surface
353 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
354 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
355 vars.yieldCurves.discountEonia, Normal));
356
357 // Overlay normal ATM curve
358 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
359 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, Normal);
360
361 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
362 Handle<OptionletVolatilityStructure> ovs(adapter);
363 ovs->enableExtrapolation();
364
365 // Create engine
366 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> engine =
367 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
368
369 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
370 QuantLib::ext::shared_ptr<CapFloor> cap;
371
372 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
373 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
374 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
375
376 // Non-ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
377 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
378 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
379 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
380 vars.settlementDays * Days);
381
382 cap->setPricingEngine(engine);
383 Real strippedPrice = cap->NPV();
384
385 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nVols[i][j]));
386 cap->setPricingEngine(flatEngine);
387 Real flatPrice = cap->NPV();
388
389 Real error = std::fabs(strippedPrice - flatPrice);
390 BOOST_CHECK_MESSAGE(error < vars.accuracy,
391 "\noption tenor: " << vars.vols.tenors[i]
392 << "\nstrike: " << io::rate(vars.vols.strikes[j])
393 << "\nstripped vol price: " << io::rate(strippedPrice)
394 << "\nconstant vol price: " << io::rate(flatPrice)
395 << "\nerror: " << io::rate(error)
396 << "\ntolerance: " << io::rate(vars.accuracy));
397 }
398 }
399
400 // ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
401 Volatility dummyVol = 0.10;
402 QuantLib::ext::shared_ptr<BlackCapFloorEngine> tempEngine =
403 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, dummyVol);
404
405 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
406 // Null strike => cap is set up with ATM strike
407 // Need a temp BlackCapFloorEngine for ATM strike to be calculated (bad - should be fixed in QL)
408 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
409 vars.settlementDays * Days)
410 .withPricingEngine(tempEngine);
411
412 cap->setPricingEngine(engine);
413 Real strippedPrice = cap->NPV();
414
415 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nAtmVols[i]));
416 cap->setPricingEngine(flatEngine);
417 Real flatPrice = cap->NPV();
418
419 Real error = std::fabs(strippedPrice - flatPrice);
420 BOOST_CHECK_MESSAGE(error < vars.accuracy,
421 "\noption tenor: " << vars.vols.atmTenors[i] << "\natm strike: "
422 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
423 << "\nstripped vol price: " << io::rate(strippedPrice)
424 << "\nconstant vol price: " << io::rate(flatPrice)
425 << "\nerror: " << io::rate(error)
426 << "\ntolerance: " << io::rate(vars.accuracy));
427 }
428}

◆ BOOST_AUTO_TEST_CASE() [7/13]

BOOST_AUTO_TEST_CASE ( testUsualShiftedLognormalStrippingWithAtm  )

Definition at line 430 of file optionletstripper.cpp.

430 {
431 BOOST_TEST_MESSAGE("Testing standard stripping of shifted lognormal capfloor vols with overlayed ATM curve...");
432
433 CommonVars vars;
434
435 // EUR cap floor shifted lognormal volatility surface
436 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
437 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
438 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_2,
439 vars.dayCounter);
440
441 // EUR cap floor shifted lognormal ATM curve
442 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
443 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.slnAtmVols_2, vars.dayCounter));
444
445 // Create shifted lognormal stripped optionlet surface
446 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
447 new QuantExt::OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
448 vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_2));
449
450 // Overlay shifted lognormal ATM curve
451 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
452 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_2);
453
454 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
455 Handle<OptionletVolatilityStructure> ovs(adapter);
456 ovs->enableExtrapolation();
457
458 // Create engine
459 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
460 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
461
462 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
463 QuantLib::ext::shared_ptr<CapFloor> cap;
464
465 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
466 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
467 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_2);
468
469 // Non-ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
470 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
471 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
472 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
473 vars.settlementDays * Days);
474
475 cap->setPricingEngine(engine);
476 Real strippedPrice = cap->NPV();
477
478 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_2[i][j]));
479 cap->setPricingEngine(flatEngine);
480 Real flatPrice = cap->NPV();
481
482 Real error = std::fabs(strippedPrice - flatPrice);
483 BOOST_CHECK_MESSAGE(error < vars.accuracy,
484 "\noption tenor: " << vars.vols.tenors[i]
485 << "\nstrike: " << io::rate(vars.vols.strikes[j])
486 << "\nstripped vol price: " << io::rate(strippedPrice)
487 << "\nconstant vol price: " << io::rate(flatPrice)
488 << "\nerror: " << io::rate(error)
489 << "\ntolerance: " << io::rate(vars.accuracy));
490 }
491 }
492
493 // ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
494 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
495 // Null strike => cap is set up with ATM strike
496 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
497 vars.settlementDays * Days)
498 .withPricingEngine(engine);
499 Real strippedPrice = cap->NPV();
500
501 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnAtmVols_2[i]));
502 cap->setPricingEngine(flatEngine);
503 Real flatPrice = cap->NPV();
504
505 Real error = std::fabs(strippedPrice - flatPrice);
506 BOOST_CHECK_MESSAGE(error < vars.accuracy,
507 "\noption tenor: " << vars.vols.atmTenors[i] << "\natm strike: "
508 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
509 << "\nstripped vol price: " << io::rate(strippedPrice)
510 << "\nconstant vol price: " << io::rate(flatPrice)
511 << "\nerror: " << io::rate(error)
512 << "\ntolerance: " << io::rate(vars.accuracy));
513 }
514}

◆ BOOST_AUTO_TEST_CASE() [8/13]

BOOST_AUTO_TEST_CASE ( testNormalToShiftedLognormalStrippingWithAtm  )

Definition at line 516 of file optionletstripper.cpp.

516 {
517 BOOST_TEST_MESSAGE("Testing stripping of normal capfloor vols with ATM to give shifted lognormal...");
518
519 CommonVars vars;
520
521 // EUR cap floor normal volatility surface
522 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
523 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
524 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
525 vars.dayCounter);
526
527 // EUR cap floor normal ATM curve
528 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
529 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.nAtmVols, vars.dayCounter));
530
531 // Create shifted lognormal stripped optionlet surface
532 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
533 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
534 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, vars.vols.shift_1));
535
536 // Overlay shifted lognormal ATM curve
537 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
538 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, Normal);
539
540 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
541 Handle<OptionletVolatilityStructure> ovs(adapter);
542 ovs->enableExtrapolation();
543
544 // Create engine
545 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
546 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
547
548 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
549 QuantLib::ext::shared_ptr<CapFloor> cap;
550
551 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
552 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
553 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
554
555 // Non-ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
556 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
557 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
558 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
559 vars.settlementDays * Days);
560
561 cap->setPricingEngine(engine);
562 Real strippedPrice = cap->NPV();
563
564 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nVols[i][j]));
565 cap->setPricingEngine(flatEngine);
566 Real flatPrice = cap->NPV();
567
568 Real error = std::fabs(strippedPrice - flatPrice);
569 BOOST_CHECK_MESSAGE(error < vars.accuracy,
570 "\noption tenor: " << vars.vols.tenors[i]
571 << "\nstrike: " << io::rate(vars.vols.strikes[j])
572 << "\nstripped vol price: " << io::rate(strippedPrice)
573 << "\nconstant vol price: " << io::rate(flatPrice)
574 << "\nerror: " << io::rate(error)
575 << "\ntolerance: " << io::rate(vars.accuracy));
576 }
577 }
578
579 // ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
580 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
581 // Null strike => cap is set up with ATM strike
582 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
583 vars.settlementDays * Days)
584 .withPricingEngine(engine);
585 Real strippedPrice = cap->NPV();
586
587 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.nAtmVols[i]));
588 cap->setPricingEngine(flatEngine);
589 Real flatPrice = cap->NPV();
590
591 Real error = std::fabs(strippedPrice - flatPrice);
592 BOOST_CHECK_MESSAGE(error < vars.accuracy,
593 "\noption tenor: " << vars.vols.atmTenors[i] << "\natm strike: "
594 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
595 << "\nstripped vol price: " << io::rate(strippedPrice)
596 << "\nconstant vol price: " << io::rate(flatPrice)
597 << "\nerror: " << io::rate(error)
598 << "\ntolerance: " << io::rate(vars.accuracy));
599 }
600}

◆ BOOST_AUTO_TEST_CASE() [9/13]

BOOST_AUTO_TEST_CASE ( testShiftedLognormalToNormalStrippingWithAtm  )

Definition at line 602 of file optionletstripper.cpp.

602 {
603 BOOST_TEST_MESSAGE("Testing stripping of shifted lognormal capfloor vols with ATM to give normal...");
604
605 CommonVars vars;
606
607 // EUR cap floor shifted lognormal volatility surface
608 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
609 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
610 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_1,
611 vars.dayCounter);
612
613 // EUR cap floor shifted lognormal ATM curve
614 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
615 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.slnAtmVols_1, vars.dayCounter));
616
617 // Create normal stripped optionlet surface
618 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
619 new OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
620 vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_1, Normal, 0.0));
621
622 // Overlay shifted lognormal ATM curve
623 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
624 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_1);
625
626 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
627 Handle<OptionletVolatilityStructure> ovs(adapter);
628 ovs->enableExtrapolation();
629
630 // Create engine
631 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> engine =
632 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
633
634 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
635 QuantLib::ext::shared_ptr<CapFloor> cap;
636
637 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
638 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
639 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_1);
640
641 // Non-ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
642 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
643 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
644 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
645 vars.settlementDays * Days);
646
647 cap->setPricingEngine(engine);
648 Real strippedPrice = cap->NPV();
649
650 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_1[i][j]));
651 cap->setPricingEngine(flatEngine);
652 Real flatPrice = cap->NPV();
653
654 Real error = std::fabs(strippedPrice - flatPrice);
655 BOOST_CHECK_MESSAGE(error < vars.accuracy,
656 "\noption tenor: " << vars.vols.tenors[i]
657 << "\nstrike: " << io::rate(vars.vols.strikes[j])
658 << "\nstripped vol price: " << io::rate(strippedPrice)
659 << "\nconstant vol price: " << io::rate(flatPrice)
660 << "\nerror: " << io::rate(error)
661 << "\ntolerance: " << io::rate(vars.accuracy));
662 }
663 }
664
665 // ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
666 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
667 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnAtmVols_1[i]));
668 // Null strike => cap is set up with ATM strike
669 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
670 vars.settlementDays * Days)
671 .withPricingEngine(flatEngine);
672 Real flatPrice = cap->NPV();
673
674 cap->setPricingEngine(engine);
675 Real strippedPrice = cap->NPV();
676
677 Real error = std::fabs(strippedPrice - flatPrice);
678 BOOST_CHECK_MESSAGE(error < vars.accuracy,
679 "\noption tenor: " << vars.vols.atmTenors[i] << "\natm strike: "
680 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
681 << "\nstripped vol price: " << io::rate(strippedPrice)
682 << "\nconstant vol price: " << io::rate(flatPrice)
683 << "\nerror: " << io::rate(error)
684 << "\ntolerance: " << io::rate(vars.accuracy));
685 }
686}

◆ BOOST_AUTO_TEST_CASE() [10/13]

BOOST_AUTO_TEST_CASE ( testShiftedLognormalToShiftedLognormalStrippingWithAtm  )

Definition at line 688 of file optionletstripper.cpp.

688 {
689 BOOST_TEST_MESSAGE("Testing stripping with shifted lognormal vols with ATM with different shifts...");
690
691 CommonVars vars;
692
693 // EUR cap floor shifted lognormal volatility surface
694 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
695 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
696 vars.vols.tenors, vars.vols.strikes, vars.vols.slnVols_1,
697 vars.dayCounter);
698
699 // EUR cap floor shifted lognormal ATM curve
700 Handle<CapFloorTermVolCurve> atmVolCurve(QuantLib::ext::make_shared<CapFloorTermVolCurve>(
701 vars.settlementDays, vars.calendar, vars.bdc, vars.vols.atmTenors, vars.vols.slnAtmVols_1, vars.dayCounter));
702
703 // Create shifted lognormal stripped optionlet surface
704 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper1> tempStripper =
705 QuantLib::ext::shared_ptr<OptionletStripper1>(new OptionletStripper1(
706 volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter, vars.yieldCurves.discountEonia,
707 ShiftedLognormal, vars.vols.shift_1, ShiftedLognormal, vars.vols.shift_2));
708
709 // Overlay shifted lognormal ATM curve
710 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::make_shared<QuantExt::OptionletStripper2>(
711 tempStripper, atmVolCurve, vars.yieldCurves.discountEonia, ShiftedLognormal, vars.vols.shift_1);
712
713 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
714 Handle<OptionletVolatilityStructure> ovs(adapter);
715 ovs->enableExtrapolation();
716
717 // Create engine
718 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
719 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
720
721 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
722 QuantLib::ext::shared_ptr<CapFloor> cap;
723
724 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
725 QuantLib::ext::shared_ptr<BlackCapFloorEngine> flatEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
726 vars.yieldCurves.discountEonia, quote, vars.dayCounter, vars.vols.shift_1);
727
728 // Non-ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
729 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
730 for (Size j = 0; j < vars.vols.strikes.size(); ++j) {
731 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], vars.iborIndex, vars.vols.strikes[j],
732 vars.settlementDays * Days);
733
734 cap->setPricingEngine(engine);
735 Real strippedPrice = cap->NPV();
736
737 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnVols_1[i][j]));
738 cap->setPricingEngine(flatEngine);
739 Real flatPrice = cap->NPV();
740
741 Real error = std::fabs(strippedPrice - flatPrice);
742 BOOST_CHECK_MESSAGE(error < vars.accuracy,
743 "\noption tenor: " << vars.vols.tenors[i]
744 << "\nstrike: " << io::rate(vars.vols.strikes[j])
745 << "\nstripped vol price: " << io::rate(strippedPrice)
746 << "\nconstant vol price: " << io::rate(flatPrice)
747 << "\nerror: " << io::rate(error)
748 << "\ntolerance: " << io::rate(vars.accuracy));
749 }
750 }
751
752 // ATM pillar points: check flat cap/floor surface price = stripped optionlet surface price
753 for (Size i = 0; i < vars.vols.atmTenors.size(); ++i) {
754 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vars.vols.slnAtmVols_1[i]));
755 // Null strike => cap is set up with ATM strike
756 cap = MakeCapFloor(CapFloor::Cap, vars.vols.atmTenors[i], vars.iborIndex, Null<Rate>(),
757 vars.settlementDays * Days)
758 .withPricingEngine(flatEngine);
759 Real flatPrice = cap->NPV();
760
761 cap->setPricingEngine(engine);
762 Real strippedPrice = cap->NPV();
763
764 Real error = std::fabs(strippedPrice - flatPrice);
765 BOOST_CHECK_MESSAGE(error < vars.accuracy,
766 "\noption tenor: " << vars.vols.atmTenors[i] << "\natm strike: "
767 << io::rate(cap->atmRate(**vars.yieldCurves.discountEonia))
768 << "\nstripped vol price: " << io::rate(strippedPrice)
769 << "\nconstant vol price: " << io::rate(flatPrice)
770 << "\nerror: " << io::rate(error)
771 << "\ntolerance: " << io::rate(vars.accuracy));
772 }
773}

◆ BOOST_AUTO_TEST_CASE() [11/13]

BOOST_AUTO_TEST_CASE ( testNormalToLognormalGivesError  )

Definition at line 775 of file optionletstripper.cpp.

775 {
776 BOOST_TEST_MESSAGE("Testing stripping of normal to give lognormal gives error (due to negative strike)...");
777
778 CommonVars vars;
779
780 // EUR cap floor normal volatility surface
781 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
782 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
783 vars.vols.tenors, vars.vols.strikes, vars.vols.nVols,
784 vars.dayCounter);
785
786 // Create shifted lognormal stripped optionlet surface and Black engine
787 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
788 new QuantExt::OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
789 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, 0.0));
790
791 // Error due to negative strike in input matrix
792 BOOST_CHECK_THROW(stripper->recalculate(), QuantLib::Error);
793}

◆ BOOST_AUTO_TEST_CASE() [12/13]

BOOST_AUTO_TEST_CASE ( testNormalToLognormalModifiedGivesError  )

Definition at line 795 of file optionletstripper.cpp.

795 {
796 BOOST_TEST_MESSAGE("Testing stripping of normal to give lognormal gives error (due to negative forward)...");
797
798 CommonVars vars;
799
800 // Create a reduced capfloor matrix with only positive strikes
801 vector<Volatility> strikes(vars.vols.strikes.begin() + 2, vars.vols.strikes.end());
802 Matrix vols(vars.vols.tenors.size(), strikes.size());
803 for (Size i = 0; i < vols.rows(); ++i)
804 for (Size j = 0; j < vols.columns(); ++j)
805 vols[i][j] = vars.vols.nVols[i][j + 2];
806
807 // EUR cap floor normal volatility surface
808 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
809 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
810 vars.vols.tenors, strikes, vols, vars.dayCounter);
811
812 // Create shifted lognormal stripped optionlet surface and Black engine
813 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
814 new QuantExt::OptionletStripper1(volSurface, vars.iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
815 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, 0.0));
816
817 // Error due to forward rates being negative
818 BOOST_CHECK_THROW(stripper->recalculate(), QuantLib::Error);
819}
vector< Real > strikes

◆ BOOST_AUTO_TEST_CASE() [13/13]

BOOST_AUTO_TEST_CASE ( testNormalToLognormalWithPositiveForwards  )

Definition at line 821 of file optionletstripper.cpp.

821 {
822 BOOST_TEST_MESSAGE("Testing stripping of normal to give lognormal when forwards are positive...");
823
824 CommonVars vars;
825
826 // Create a reduced capfloor matrix with only positive strikes
827 vector<Volatility> strikes(vars.vols.strikes.begin() + 2, vars.vols.strikes.end());
828 Matrix vols(vars.vols.tenors.size(), strikes.size());
829 for (Size i = 0; i < vols.rows(); ++i)
830 for (Size j = 0; j < vols.columns(); ++j)
831 vols[i][j] = vars.vols.nVols[i][j + 2];
832
833 // Link ibor index to shifted forward curve
834 Handle<Quote> spread(QuantLib::ext::make_shared<SimpleQuote>(0.015));
835 Handle<YieldTermStructure> shiftedForward(
836 QuantLib::ext::make_shared<ZeroSpreadedTermStructure>(vars.yieldCurves.forward6M, spread));
837 QuantLib::ext::shared_ptr<IborIndex> iborIndex = vars.iborIndex->clone(shiftedForward);
838
839 // EUR cap floor normal volatility surface
840 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> volSurface =
841 QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(vars.settlementDays, vars.calendar, vars.bdc,
842 vars.vols.tenors, strikes, vols, vars.dayCounter);
843
844 // Create shifted lognormal stripped optionlet surface and Black engine
845 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> stripper = QuantLib::ext::shared_ptr<OptionletStripper1>(
846 new QuantExt::OptionletStripper1(volSurface, iborIndex, Null<Rate>(), vars.accuracy, vars.maxIter,
847 vars.yieldCurves.discountEonia, Normal, 0.0, ShiftedLognormal, 0.0));
848 QuantLib::ext::shared_ptr<StrippedOptionletAdapter> adapter = QuantLib::ext::make_shared<StrippedOptionletAdapter>(stripper);
849 Handle<OptionletVolatilityStructure> ovs(adapter);
850 ovs->enableExtrapolation();
851 QuantLib::ext::shared_ptr<BlackCapFloorEngine> engine =
852 QuantLib::ext::make_shared<BlackCapFloorEngine>(vars.yieldCurves.discountEonia, ovs);
853
854 // Price a cap at each pillar point with flat cap/floor surface and stripped optionlet surface and compare price
855 QuantLib::ext::shared_ptr<CapFloor> cap;
856
857 RelinkableHandle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
858 QuantLib::ext::shared_ptr<BachelierCapFloorEngine> flatEngine =
859 QuantLib::ext::make_shared<BachelierCapFloorEngine>(vars.yieldCurves.discountEonia, quote, vars.dayCounter);
860
861 for (Size i = 0; i < vars.vols.tenors.size(); ++i) {
862 for (Size j = 0; j < strikes.size(); ++j) {
863 cap = MakeCapFloor(CapFloor::Cap, vars.vols.tenors[i], iborIndex, strikes[j], vars.settlementDays * Days);
864
865 cap->setPricingEngine(engine);
866 Real strippedPrice = cap->NPV();
867
868 quote.linkTo(QuantLib::ext::make_shared<SimpleQuote>(vols[i][j]));
869 cap->setPricingEngine(flatEngine);
870 Real flatPrice = cap->NPV();
871
872 Real error = std::fabs(strippedPrice - flatPrice);
873 BOOST_CHECK_MESSAGE(error < vars.accuracy,
874 "\noption tenor: " << vars.vols.tenors[i]
875 << "\nstrike: " << io::rate(strikes[j])
876 << "\nstripped vol price: " << io::rate(strippedPrice)
877 << "\nconstant vol price: " << io::rate(flatPrice)
878 << "\nerror: " << io::rate(error)
879 << "\ntolerance: " << io::rate(vars.accuracy));
880 }
881 }
882}