Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Functions
inflationvol.cpp File Reference
#include "toplevelfixture.hpp"
#include <boost/test/unit_test.hpp>
#include <ql/indexes/inflation/aucpi.hpp>
#include <ql/indexes/inflation/euhicp.hpp>
#include <ql/math/interpolations/bilinearinterpolation.hpp>
#include <ql/math/matrix.hpp>
#include <ql/pricingengines/blackcalculator.hpp>
#include <ql/termstructures/inflation/inflationhelpers.hpp>
#include <ql/termstructures/inflationtermstructure.hpp>
#include <ql/termstructures/volatility/inflation/cpivolatilitystructure.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/time/calendars/nullcalendar.hpp>
#include <ql/time/daycounters/actual365fixed.hpp>
#include <qle/pricingengines/cpiblackcapfloorengine.hpp>
#include <qle/termstructures/inflation/cpipricevolatilitysurface.hpp>
#include <qle/termstructures/inflation/piecewisezeroinflationcurve.hpp>
#include <qle/termstructures/interpolatedcpivolatilitysurface.hpp>
#include <qle/utilities/inflation.hpp>

Go to the source code of this file.

Functions

 BOOST_AUTO_TEST_CASE (testCPIVolatilitySurface)
 
 BOOST_AUTO_TEST_CASE (testCPIBlackCapFloorEngine)
 
 BOOST_AUTO_TEST_CASE (testCPIBlackCapFloorEngineSeasonedCapFloors)
 
 BOOST_AUTO_TEST_CASE (testCPIPriceVolSurface)
 
 BOOST_AUTO_TEST_CASE (testCPIPriceVolSurfaceMissingQuoteInterpolation)
 
 BOOST_AUTO_TEST_CASE (testCPIPriceVolSurfaceMissingQuoteExtrapolation)
 
 BOOST_AUTO_TEST_CASE (testCPIPriceVolSurfaceAllButOneQuoteMissing)
 
 BOOST_AUTO_TEST_CASE (testVolatiltiySurfaceWithStartDate)
 

Function Documentation

◆ BOOST_AUTO_TEST_CASE() [1/8]

BOOST_AUTO_TEST_CASE ( testCPIVolatilitySurface  )

Definition at line 232 of file inflationvol.cpp.

232 {
233 // Test case when the ZCIIS and Cap/Floors start today with using todays fixing
234 CommonData cd;
235
236 Date lastKnownFixing(1, Jul, 2022);
237 Date capFloorBaseDate(1, Jun, 2022); // first day of the month of today - 2M
238
239 auto index = buildIndexWithForwardTermStructure(cd);
240
241 Handle<ZeroInflationIndex> hindex(index);
242
243 BOOST_CHECK_EQUAL(index->zeroInflationTermStructure()->baseDate(), lastKnownFixing);
244
245 auto volSurface = buildVolSurface(cd, index);
246
247 // Expect the base fixing date of the cap/floor today - 2M
248 BOOST_CHECK_EQUAL(volSurface->baseDate(), capFloorBaseDate);
249
250 double baseCPI = index->fixing(volSurface->baseDate());
251
252 BOOST_CHECK_CLOSE(baseCPI, cd.cpiFixings[capFloorBaseDate], cd.tolerance);
253
254 for (size_t i = 0; i < cd.tenors.size(); ++i) {
255 auto fixingDate = volSurface->baseDate() + cd.tenors[i];
256 for (size_t j = 0; j < cd.strikes.size(); ++j) {
257 auto expectedVol = cd.vols[i][j]->value();
258 auto volByTenor = volSurface->volatility(cd.tenors[i], cd.strikes[j]);
259 auto volByFixingDate = volSurface->volatility(fixingDate, cd.strikes[j], 0 * Days);
260 BOOST_CHECK_CLOSE(volByTenor, expectedVol, cd.tolerance);
261 BOOST_CHECK_CLOSE(volByFixingDate, expectedVol, cd.tolerance);
262 }
263 }
264}
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
Definition: inflation.cpp:183

◆ BOOST_AUTO_TEST_CASE() [2/8]

BOOST_AUTO_TEST_CASE ( testCPIBlackCapFloorEngine  )

Definition at line 266 of file inflationvol.cpp.

266 {
267 // Test case when the ZCIIS and Cap/Floors start today with using todays fixing
268 CommonData cd;
269 auto index = buildIndexWithForwardTermStructure(cd);
270
271 auto volSurface = buildVolSurface(cd, index);
272
273 double baseCPI = index->fixing(volSurface->baseDate());
274
275 auto priceData = pricesFromVolQuotes(cd, index, false, Date());
276
277 QuantLib::ext::shared_ptr<PricingEngine> engine = QuantLib::ext::make_shared<QuantExt::CPIBlackCapFloorEngine>(
278 cd.discountTS, Handle<CPIVolatilitySurface>(volSurface), true);
279
280 for (size_t i = 0; i < priceData.cStrikes.size(); ++i) {
281 double strike = priceData.cStrikes[i];
282 for (size_t j = 0; j < priceData.tenors.size(); ++j) {
283 Period tenor = priceData.tenors[j];
284 CPICapFloor cap(Option::Call, 1.0, cd.today, baseCPI, cd.today + tenor, cd.fixingCalendar, cd.bdc,
285 cd.fixingCalendar, cd.bdc, strike, index, cd.obsLag, CPI::Flat);
286 cap.setPricingEngine(engine);
287 BOOST_CHECK_CLOSE(cap.NPV(), priceData.cPrices[i][j], cd.tolerance);
288 }
289 }
290
291 for (size_t i = 0; i < priceData.fStrikes.size(); ++i) {
292 double strike = priceData.fStrikes[i];
293 for (size_t j = 0; j < priceData.tenors.size(); ++j) {
294 Period tenor = priceData.tenors[j];
295 CPICapFloor put(Option::Put, 1.0, cd.today, baseCPI, cd.today + tenor, cd.fixingCalendar, cd.bdc,
296 cd.fixingCalendar, cd.bdc, strike, index, cd.obsLag, CPI::Flat);
297 put.setPricingEngine(engine);
298 BOOST_CHECK_CLOSE(put.NPV(), priceData.fPrices[i][j], cd.tolerance);
299 }
300 }
301}

◆ BOOST_AUTO_TEST_CASE() [3/8]

BOOST_AUTO_TEST_CASE ( testCPIBlackCapFloorEngineSeasonedCapFloors  )

Definition at line 303 of file inflationvol.cpp.

303 {
304 // Pricing seasoned cap/floors
305 CommonData cd;
306
307 Date lastKnownFixing(1, Jul, 2022);
308 Date capFloorBaseDate(1, Jun, 2022); // first day of the month of today - 2M
309
310 auto index = buildIndexWithForwardTermStructure(cd);
311
312 auto volSurface = buildVolSurface(cd, index);
313
314 double baseCPI = index->fixing(volSurface->baseDate());
315
316 QuantLib::ext::shared_ptr<PricingEngine> engine = QuantLib::ext::make_shared<QuantExt::CPIBlackCapFloorEngine>(
317 cd.discountTS, Handle<CPIVolatilitySurface>(volSurface), true);
318
319 Date seasonedStartDate(15, Aug, 2021);
320 Date seasonedMaturity(15, Aug, 2024);
321 Date seasonedBaseFixingDate(1, Jun, 2021);
322 Date seasonedFixingDate(1, Jun, 2024);
323 double seasonedStrike = 0.03;
324 double seasonedBaseCPI = index->fixing(seasonedBaseFixingDate);
325
326 double K = pow(1 + seasonedStrike, cd.dayCounter.yearFraction(seasonedBaseFixingDate, seasonedFixingDate));
327 double atm = index->fixing(seasonedFixingDate) / seasonedBaseCPI;
328
329 double adjustedStrike = std::pow(K * seasonedBaseCPI / baseCPI,
330 1.0 / cd.dayCounter.yearFraction(volSurface->baseDate(), seasonedFixingDate)) -
331 1.0;
332
333 double volTimeFrom = cd.dayCounter.yearFraction(lastKnownFixing, seasonedFixingDate);
334 double vol = volSurface->volatility(seasonedFixingDate, adjustedStrike, 0 * Days, false);
335 double discountFactor = cd.discountTS->discount(seasonedMaturity);
336 QuantLib::BlackCalculator callPricer(Option::Call, K, atm, sqrt(volTimeFrom) * vol, discountFactor);
337
338 QuantLib::CPICapFloor cap(Option::Call, 1.0, seasonedStartDate, Null<double>(), seasonedMaturity, cd.fixingCalendar,
339 cd.bdc, cd.fixingCalendar, cd.bdc, seasonedStrike, index,
340 cd.obsLag, CPI::Flat);
341
342 cap.setPricingEngine(engine);
343
344 BOOST_CHECK_CLOSE(cap.NPV(), callPricer.value(), cd.tolerance);
345}
RandomVariable sqrt(RandomVariable x)
CompiledFormula pow(CompiledFormula x, const CompiledFormula &y)

◆ BOOST_AUTO_TEST_CASE() [4/8]

BOOST_AUTO_TEST_CASE ( testCPIPriceVolSurface  )

Definition at line 347 of file inflationvol.cpp.

347 {
348 // Test case when the ZCIIS and Cap/Floors start today with using todays fixing
349 CommonData cd;
350 auto index = buildIndexWithForwardTermStructure(cd);
351
352 auto priceData = pricesFromVolQuotes(cd, index, false, Date());
353
354 auto volSurface = buildVolSurfaceFromPrices(cd, priceData, index, true, Date(), false);
355
356 for (size_t i = 0; i < cd.tenors.size(); ++i) {
357 auto fixingDate = volSurface->baseDate() + cd.tenors[i];
358 for (size_t j = 0; j < cd.strikes.size(); ++j) {
359 auto expectedVol = cd.vols[i][j]->value();
360 auto volByTenor = volSurface->volatility(cd.tenors[i], cd.strikes[j]);
361 auto volByFixingDate = volSurface->volatility(fixingDate, cd.strikes[j], 0 * Days);
362 BOOST_CHECK_CLOSE(volByTenor, expectedVol, cd.tolerance);
363 BOOST_CHECK_CLOSE(volByFixingDate, expectedVol, cd.tolerance);
364 }
365 }
366}

◆ BOOST_AUTO_TEST_CASE() [5/8]

BOOST_AUTO_TEST_CASE ( testCPIPriceVolSurfaceMissingQuoteInterpolation  )

Definition at line 368 of file inflationvol.cpp.

368 {
369 // Test case when the ZCIIS and Cap/Floors start today with using todays fixing
370 CommonData cd;
371 auto index = buildIndexWithForwardTermStructure(cd);
372
373 // Remove 1 Quote and check if its get interpolated
374 for (Size tenorIdx = 1; tenorIdx < cd.tenors.size() - 1; tenorIdx++) {
375 for (Size strikeIdx = 1; strikeIdx < cd.strikes.size() - 1; strikeIdx++) {
376 auto priceData = pricesFromVolQuotes(cd, index, false, Date());
377 priceData.cPrices[strikeIdx][tenorIdx] = Null<Real>();
378 priceData.fPrices[strikeIdx][tenorIdx] = Null<Real>();
379
380 auto volSurface = buildVolSurfaceFromPrices(cd, priceData, index, true, Date(), true);
381
382 double vol = volSurface->volatility(cd.tenors[tenorIdx], cd.strikes[strikeIdx]);
383
384 double expectedVol =
385 cd.vols[tenorIdx][strikeIdx - 1]->value() +
386 (cd.vols[tenorIdx][strikeIdx + 1]->value() - cd.vols[tenorIdx][strikeIdx - 1]->value()) *
387 (cd.strikes[strikeIdx] - cd.strikes[strikeIdx - 1]) /
388 (cd.strikes[strikeIdx + 1] - cd.strikes[strikeIdx - 1]);
389
390 BOOST_CHECK_CLOSE(vol, expectedVol, cd.tolerance);
391 }
392 }
393}

◆ BOOST_AUTO_TEST_CASE() [6/8]

BOOST_AUTO_TEST_CASE ( testCPIPriceVolSurfaceMissingQuoteExtrapolation  )

Definition at line 396 of file inflationvol.cpp.

396 {
397 // Test case when the ZCIIS and Cap/Floors start today with using todays fixing
398 CommonData cd;
399 auto index = buildIndexWithForwardTermStructure(cd);
400
401 // Remove 1 Quote and check if its get extrapolated
402 for (Size tenorIdx = 1; tenorIdx < cd.tenors.size() - 1; tenorIdx++) {
403
404 auto priceData = pricesFromVolQuotes(cd, index, false, Date());
405
406 priceData.cPrices[0][tenorIdx] = Null<Real>();
407 priceData.fPrices[0][tenorIdx] = Null<Real>();
408
409 auto volSurface = buildVolSurfaceFromPrices(cd, priceData, index, true, Date(), true);
410
411 double vol = volSurface->volatility(cd.tenors[tenorIdx], cd.strikes.front());
412
413 double expectedVol = cd.vols[tenorIdx][1]->value();
414
415 BOOST_CHECK_CLOSE(vol, expectedVol, cd.tolerance);
416 }
417
418 // Remove 1 Quote and check if its get extrapolated
419 for (Size tenorIdx = 1; tenorIdx < cd.tenors.size() - 1; tenorIdx++) {
420
421 auto priceData = pricesFromVolQuotes(cd, index, false, Date());
422
423 priceData.cPrices[cd.strikes.size()-1][tenorIdx] = Null<Real>();
424 priceData.fPrices[cd.strikes.size() - 1][tenorIdx] = Null<Real>();
425
426 auto volSurface = buildVolSurfaceFromPrices(cd, priceData, index, true, Date(), true);
427
428 double vol = volSurface->volatility(cd.tenors[tenorIdx], cd.strikes.back());
429
430 double expectedVol = cd.vols[tenorIdx][cd.strikes.size() - 2]->value();
431
432 BOOST_CHECK_CLOSE(vol, expectedVol, cd.tolerance);
433 }
434
435 // Remove 2 Quote and check if its get extrapolated
436 for (Size tenorIdx = 1; tenorIdx < cd.tenors.size() - 1; tenorIdx++) {
437
438 auto priceData = pricesFromVolQuotes(cd, index, false, Date());
439
440 priceData.cPrices[0][tenorIdx] = Null<Real>();
441 priceData.fPrices[0][tenorIdx] = Null<Real>();
442
443 priceData.cPrices[1][tenorIdx] = Null<Real>();
444 priceData.fPrices[1][tenorIdx] = Null<Real>();
445
446 auto volSurface = buildVolSurfaceFromPrices(cd, priceData, index, true, Date(), true);
447
448 double vol = volSurface->volatility(cd.tenors[tenorIdx], cd.strikes.front());
449
450 double expectedVol = cd.vols[tenorIdx][2]->value();
451
452 BOOST_CHECK_CLOSE(vol, expectedVol, cd.tolerance);
453 }
454}

◆ BOOST_AUTO_TEST_CASE() [7/8]

BOOST_AUTO_TEST_CASE ( testCPIPriceVolSurfaceAllButOneQuoteMissing  )

Definition at line 457 of file inflationvol.cpp.

457 {
458 // Test case when the ZCIIS and Cap/Floors start today with using todays fixing
459 CommonData cd;
460 auto index = buildIndexWithForwardTermStructure(cd);
461
462 // Remove all QUotes for a strike
463 auto priceDataAllQuotes = pricesFromVolQuotes(cd, index, false, Date());
464 auto priceData = pricesFromVolQuotes(cd, index, false, Date());
465 for (Size strikeIdx = 0; strikeIdx < cd.strikes.size(); strikeIdx++) {
466 priceData.cPrices[strikeIdx][0] = Null<Real>();
467 priceData.fPrices[strikeIdx][0] = Null<Real>();
468 }
469 priceData.cPrices[1][0] = priceDataAllQuotes.cPrices[1][0];
470 priceData.fPrices[1][0] = priceDataAllQuotes.fPrices[1][0];
471
472 auto volSurface = buildVolSurfaceFromPrices(cd, priceData, index, true, Date(), true);
473
474 double vol = volSurface->volatility(cd.tenors[0], cd.strikes[0]);
475 double expectedVol = cd.vols[0][1]->value();
476 BOOST_CHECK_CLOSE(vol, expectedVol, cd.tolerance);
477
478 for (Size tenorIdx = 0; tenorIdx < cd.tenors.size(); tenorIdx++) {
479 for (Size strikeIdx = 0; strikeIdx < cd.strikes.size(); strikeIdx++) {
480 priceData.cPrices[strikeIdx][tenorIdx] = Null<Real>();
481 priceData.fPrices[strikeIdx][tenorIdx] = Null<Real>();
482 }
483 priceData.cPrices[1][tenorIdx] = priceDataAllQuotes.cPrices[1][tenorIdx];
484 priceData.fPrices[1][tenorIdx] = priceDataAllQuotes.fPrices[1][tenorIdx];
485 }
486
487 volSurface = buildVolSurfaceFromPrices(cd, priceData, index, true, Date(), true);
488 for (Size tenorIdx = 0; tenorIdx < cd.tenors.size(); tenorIdx++) {
489 double vol = volSurface->volatility(cd.tenors[tenorIdx], cd.strikes[0]);
490 double expectedVol = cd.vols[tenorIdx][1]->value();
491 BOOST_CHECK_CLOSE(vol, expectedVol, cd.tolerance);
492 }
493}

◆ BOOST_AUTO_TEST_CASE() [8/8]

BOOST_AUTO_TEST_CASE ( testVolatiltiySurfaceWithStartDate  )

Definition at line 496 of file inflationvol.cpp.

496 {
497 // Test case when the ZCIIS and Cap/Floors don't start today
498 // but depend on the publishing schedule of the fixings
499 CommonData cd;
500 Date today(15, July, 2022);
501 cd.today = today;
502 cd.obsLag = 3 * Months;
503 Settings::instance().evaluationDate() = today;
504 std::map<Date, double> fixings{{Date(1, Mar, 2022), 100.0}};
505 // the Q2 fixing not published yet, the zciis swaps and caps start on 15th Jun and
506 // reference on the Q1 fixing
507 Date startDate(15, Jun, 2022);
508 Date lastKnownFixing(1, Jan, 2022);
509
510 QuantLib::ext::shared_ptr<ZeroInflationIndex> curveBuildIndex = QuantLib::ext::make_shared<QuantLib::AUCPI>(Quarterly, true, false);
511 for (const auto& [date, fixing] : fixings) {
512 curveBuildIndex->addFixing(date, fixing);
513 }
514
515 auto curve = buildZeroInflationCurve(cd, true, curveBuildIndex, false, nullptr, startDate);
516
517 auto index = curveBuildIndex->clone(Handle<ZeroInflationTermStructure>(curve));
518
519 BOOST_CHECK_EQUAL(curve->baseDate(), lastKnownFixing);
520
521 BOOST_CHECK_EQUAL(curve->dates()[1], Date(1, Jan, 2023));
522 BOOST_CHECK_CLOSE(curve->data()[0], cd.zeroCouponQuotes[0], cd.tolerance);
523 BOOST_CHECK_CLOSE(curve->data()[1], cd.zeroCouponQuotes[0], cd.tolerance);
524 BOOST_CHECK_CLOSE(curve->data()[2], cd.zeroCouponQuotes[1], cd.tolerance);
525
526 auto volSurface = buildVolSurface(cd, index, startDate);
527
528 BOOST_CHECK_EQUAL(volSurface->baseDate(), Date(1, Jan, 2022));
529
530 double baseCPI = index->fixing(volSurface->baseDate());
531
532 BOOST_CHECK_CLOSE(baseCPI, 100.0, cd.tolerance);
533
534 Matrix cPrices(cd.strikes.size(), cd.tenors.size(), 0.0);
535 Matrix fPrices(cd.strikes.size(), cd.tenors.size(), 0.0);
536
537 for (size_t i = 0; i < cd.strikes.size(); ++i) {
538 for (size_t j = 0; j < cd.tenors.size(); ++j) {
539 double expectedVol = cd.vols[j][i]->value();
540 Date optionFixingDate = volSurface->baseDate() + cd.tenors[j];
541 Date optionPaymentDate = startDate + cd.tenors[j];
542
543 double vol = volSurface->volatility(optionFixingDate, cd.strikes[i], 0 * Days, false);
544 BOOST_CHECK_CLOSE(vol, expectedVol, cd.tolerance);
545 double ttm = cd.dayCounter.yearFraction(volSurface->baseDate(), optionFixingDate);
546 double atmf = index->fixing(optionFixingDate) / baseCPI;
547 double strike = std::pow(1 + cd.strikes[i], ttm);
548 double discountFactor = cd.discountTS->discount(optionPaymentDate);
549 double volTimeFrom = cd.dayCounter.yearFraction(lastKnownFixing, optionFixingDate);
550 QuantLib::BlackCalculator callPricer(Option::Call, strike, atmf, sqrt(volTimeFrom) * vol, discountFactor);
551 QuantLib::BlackCalculator putPricer(Option::Put, strike, atmf, sqrt(volTimeFrom) * vol, discountFactor);
552
553 cPrices[i][j] = callPricer.value();
554 fPrices[i][j] = putPricer.value();
555 }
556 }
557
558 CPICapFloorPriceData priceData;
559 priceData.tenors = cd.tenors;
560 priceData.cPrices = cPrices;
561 priceData.fPrices = fPrices;
562 priceData.cStrikes = cd.strikes;
563 priceData.fStrikes = cd.strikes;
564
565 auto priceSurface = buildVolSurfaceFromPrices(cd, priceData, index, true, startDate);
566
567 for (size_t i = 0; i < cd.strikes.size(); ++i) {
568 for (size_t j = 0; j < cd.tenors.size(); ++j) {
569 double expectedVol = cd.vols[j][i]->value();
570 Date optionFixingDate = priceSurface->baseDate() + cd.tenors[j];
571 double vol = priceSurface->volatility(optionFixingDate, cd.strikes[i], 0 * Days, false);
572 BOOST_CHECK_CLOSE(vol, expectedVol, cd.tolerance);
573 }
574 }
575
576
577
578}