22#include <boost/test/unit_test.hpp>
23#include <ql/cashflows/cpicoupon.hpp>
24#include <ql/cashflows/cpicouponpricer.hpp>
25#include <ql/cashflows/indexedcashflow.hpp>
26#include <ql/experimental/inflation/cpicapfloorengines.hpp>
27#include <ql/experimental/inflation/cpicapfloortermpricesurface.hpp>
28#include <ql/indexes/ibor/gbplibor.hpp>
29#include <ql/indexes/inflation/euhicp.hpp>
30#include <ql/indexes/inflation/ukrpi.hpp>
31#include <ql/instruments/bonds/cpibond.hpp>
32#include <ql/instruments/cpicapfloor.hpp>
33#include <ql/instruments/cpiswap.hpp>
34#include <ql/instruments/zerocouponinflationswap.hpp>
35#include <ql/math/interpolations/bilinearinterpolation.hpp>
36#include <ql/pricingengines/blackformula.hpp>
37#include <ql/pricingengines/bond/discountingbondengine.hpp>
38#include <ql/pricingengines/swap/discountingswapengine.hpp>
39#include <ql/termstructures/bootstraphelper.hpp>
40#include <ql/termstructures/inflation/inflationhelpers.hpp>
41#include <ql/termstructures/inflation/piecewisezeroinflationcurve.hpp>
42#include <ql/termstructures/volatility/inflation/constantcpivolatility.hpp>
43#include <ql/termstructures/yield/flatforward.hpp>
44#include <ql/termstructures/yield/zerocurve.hpp>
45#include <ql/time/calendars/unitedkingdom.hpp>
46#include <ql/time/daycounters/actual365fixed.hpp>
47#include <ql/time/daycounters/actualactual.hpp>
48#include <ql/types.hpp>
55using namespace boost::unit_test_framework;
66BOOST_AUTO_TEST_SUITE(CPICapFloorTest)
68template <
class T,
class U,
class I>
69std::vector<QuantLib::ext::shared_ptr<BootstrapHelper<T> > > makeHelpers(Datum iiData[], Size N,
const QuantLib::ext::shared_ptr<I>& ii,
70 const Period& observationLag,
const Calendar& calendar,
71 const BusinessDayConvention& bdc,
const DayCounter& dc,
72 Handle<YieldTermStructure> yts) {
74 std::vector<QuantLib::ext::shared_ptr<BootstrapHelper<T> > > instruments;
75 for (Size i = 0; i < N; i++) {
76 Date maturity = iiData[i].date;
77 Handle<Quote> quote(QuantLib::ext::shared_ptr<Quote>(
new SimpleQuote(iiData[i].rate / 100.0)));
78 QuantLib::ext::shared_ptr<BootstrapHelper<T> > anInstrument(
79 new U(quote, observationLag, maturity, calendar, bdc, dc, ii, CPI::AsIndex, yts));
80 instruments.push_back(anInstrument);
97 std::vector<Real> nominals;
99 BusinessDayConvention convention;
102 Natural settlementDays;
104 Period observationLag, contractObservationLag;
105 CPI::InterpolationType contractObservationInterpolation;
106 DayCounter dcZCIIS, dcNominal;
107 std::vector<Date> zciisD;
108 std::vector<Rate> zciisR;
109 QuantLib::ext::shared_ptr<UKRPI> ii;
110 RelinkableHandle<ZeroInflationIndex> hii;
111 Size zciisDataLength;
113 RelinkableHandle<YieldTermStructure> nominalUK;
114 RelinkableHandle<ZeroInflationTermStructure> cpiUK;
115 RelinkableHandle<ZeroInflationTermStructure> hcpi;
117 vector<Rate> cStrikesUK;
118 vector<Rate> fStrikesUK;
119 vector<Period> cfMaturitiesUK;
120 QuantLib::ext::shared_ptr<Matrix> cPriceUK;
121 QuantLib::ext::shared_ptr<Matrix> fPriceUK;
123 QuantLib::ext::shared_ptr<CPICapFloorTermPriceSurface> cpiCFsurfUK;
127 SavedSettings backup;
130 CommonVars() : nominals(1, 1000000) {
136 calendar = UnitedKingdom();
137 convention = ModifiedFollowing;
138 Date today(1, June, 2010);
139 evaluationDate = calendar.adjust(today);
140 Settings::instance().evaluationDate() = evaluationDate;
143 settlement = calendar.advance(today, settlementDays, Days);
144 startDate = settlement;
145 dcZCIIS = ActualActual(ActualActual::ISDA);
146 dcNominal = ActualActual(ActualActual::ISDA);
150 Date from(1, July, 2007);
151 Date to(1, June, 2010);
152 Schedule rpiSchedule = MakeSchedule()
155 .withTenor(1 * Months)
156 .withCalendar(UnitedKingdom())
157 .withConvention(ModifiedFollowing);
158 Real fixData[] = { 206.1, 207.3, 208.0, 208.9, 209.7, 210.9, 209.8, 211.4, 212.1, 214.0, 215.1, 216.8,
159 216.5, 217.2, 218.4, 217.7, 216.0, 212.9, 210.1, 211.4, 211.3, 211.5, 212.8, 213.4,
160 213.4, 214.4, 215.3, 216.0, 216.6, 218.0, 217.9, 219.2, 220.7, 222.8, -999, -999,
164 ii = QuantLib::ext::make_shared<UKRPI>(hcpi);
165 for (Size i = 0; i < rpiSchedule.size(); i++) {
166 ii->addFixing(rpiSchedule[i], fixData[i],
true);
169 Datum nominalData[] = { { Date(2, June, 2010), 0.499997 },
170 { Date(3, June, 2010), 0.524992 },
171 { Date(8, June, 2010), 0.524974 },
172 { Date(15, June, 2010), 0.549942 },
173 { Date(22, June, 2010), 0.549913 },
174 { Date(1, July, 2010), 0.574864 },
175 { Date(2, August, 2010), 0.624668 },
176 { Date(1, September, 2010), 0.724338 },
177 { Date(16, September, 2010), 0.769461 },
178 { Date(1, December, 2010), 0.997501 },
180 { Date(17, March, 2011), 0.916996 },
181 { Date(16, June, 2011), 0.984339 },
182 { Date(22, September, 2011), 1.06085 },
183 { Date(22, December, 2011), 1.141788 },
184 { Date(1, June, 2012), 1.504426 },
185 { Date(3, June, 2013), 1.92064 },
186 { Date(2, June, 2014), 2.290824 },
187 { Date(1, June, 2015), 2.614394 },
188 { Date(1, June, 2016), 2.887445 },
189 { Date(1, June, 2017), 3.122128 },
190 { Date(1, June, 2018), 3.322511 },
191 { Date(3, June, 2019), 3.483997 },
192 { Date(1, June, 2020), 3.616896 },
193 { Date(1, June, 2022), 3.8281 },
194 { Date(2, June, 2025), 4.0341 },
195 { Date(3, June, 2030), 4.070854 },
196 { Date(1, June, 2035), 4.023202 },
197 { Date(1, June, 2040), 3.954748 },
198 { Date(1, June, 2050), 3.870953 },
199 { Date(1, June, 2060), 3.85298 },
200 { Date(2, June, 2070), 3.757542 },
201 { Date(3, June, 2080), 3.651379 } };
202 const Size nominalDataLength = 33 - 1;
204 std::vector<Date> nomD;
205 std::vector<Rate> nomR;
206 for (Size i = 0; i < nominalDataLength; i++) {
207 nomD.push_back(nominalData[i].date);
208 nomR.push_back(nominalData[i].rate / 100.0);
210 QuantLib::ext::shared_ptr<YieldTermStructure> nominalTS =
211 QuantLib::ext::make_shared<InterpolatedZeroCurve<Linear> >(nomD, nomR, dcNominal);
213 nominalUK.linkTo(nominalTS);
216 observationLag = Period(2, Months);
217 contractObservationLag = Period(3, Months);
218 contractObservationInterpolation = CPI::Flat;
220 Datum zciisData[] = {
221 { Date(1, June, 2011), 3.087 }, { Date(1, June, 2012), 3.12 }, { Date(1, June, 2013), 3.059 },
222 { Date(1, June, 2014), 3.11 }, { Date(1, June, 2015), 3.15 }, { Date(1, June, 2016), 3.207 },
223 { Date(1, June, 2017), 3.253 }, { Date(1, June, 2018), 3.288 }, { Date(1, June, 2019), 3.314 },
224 { Date(1, June, 2020), 3.401 }, { Date(1, June, 2022), 3.458 }, { Date(1, June, 2025), 3.52 },
225 { Date(1, June, 2030), 3.655 }, { Date(1, June, 2035), 3.668 }, { Date(1, June, 2040), 3.695 },
226 { Date(1, June, 2050), 3.634 }, { Date(1, June, 2060), 3.629 },
228 zciisDataLength = 17;
229 for (Size i = 0; i < zciisDataLength; i++) {
230 zciisD.push_back(zciisData[i].date);
231 zciisR.push_back(zciisData[i].rate);
235 std::vector<QuantLib::ext::shared_ptr<BootstrapHelper<ZeroInflationTermStructure> > > helpers =
236 makeHelpers<ZeroInflationTermStructure, ZeroCouponInflationSwapHelper, ZeroInflationIndex>(
237 zciisData, zciisDataLength, ii, observationLag, calendar, convention, dcZCIIS,
238 Handle<YieldTermStructure>(nominalTS));
242 baseZeroRate = zciisData[0].rate / 100.0;
243 QuantLib::ext::shared_ptr<PiecewiseZeroInflationCurve<Linear>> pCPIts(
244 new PiecewiseZeroInflationCurve<Linear>(evaluationDate, calendar, dcZCIIS, observationLag, ii->frequency(),
245 baseZeroRate, helpers));
246 pCPIts->recalculate();
247 cpiUK.linkTo(pCPIts);
254 Period cfMat[] = { 3 * Years, 5 * Years, 7 * Years, 10 * Years, 15 * Years, 20 * Years, 30 * Years };
255 Real cStrike[] = { 0.03, 0.04, 0.05, 0.06 };
256 Real fStrike[] = { -0.01, 0, 0.01, 0.02 };
257 Size ncStrikes = 4, nfStrikes = 4, ncfMaturities = 7;
259 Real cPrice[7][4] = { { 227.6, 100.27, 38.8, 14.94 }, { 345.32, 127.9, 40.59, 14.11 },
260 { 477.95, 170.19, 50.62, 16.88 }, { 757.81, 303.95, 107.62, 43.61 },
261 { 1140.73, 481.89, 168.4, 63.65 }, { 1537.6, 607.72, 172.27, 54.87 },
262 { 2211.67, 839.24, 184.75, 45.03 } };
263 Real fPrice[7][4] = { { 15.62, 28.38, 53.61, 104.6 }, { 21.45, 36.73, 66.66, 129.6 },
264 { 24.45, 42.08, 77.04, 152.24 }, { 39.25, 63.52, 109.2, 203.44 },
265 { 36.82, 63.62, 116.97, 232.73 }, { 39.7, 67.47, 121.79, 238.56 },
266 { 41.48, 73.9, 139.75, 286.75 } };
271 cfMaturitiesUK.clear();
272 for (Size i = 0; i < ncStrikes; i++)
273 cStrikesUK.push_back(cStrike[i]);
274 for (Size i = 0; i < nfStrikes; i++)
275 fStrikesUK.push_back(fStrike[i]);
276 for (Size i = 0; i < ncfMaturities; i++)
277 cfMaturitiesUK.push_back(cfMat[i]);
278 cPriceUK = QuantLib::ext::make_shared<Matrix>(ncStrikes, ncfMaturities);
279 fPriceUK = QuantLib::ext::make_shared<Matrix>(nfStrikes, ncfMaturities);
280 for (Size i = 0; i < ncStrikes; i++) {
281 for (Size j = 0; j < ncfMaturities; j++) {
282 (*cPriceUK)[i][j] = cPrice[j][i] / 10000.0;
285 for (Size i = 0; i < nfStrikes; i++) {
286 for (Size j = 0; j < ncfMaturities; j++) {
287 (*fPriceUK)[i][j] = fPrice[j][i] / 10000.0;
292 QuantLib::ext::shared_ptr<InterpolatedCPICapFloorTermPriceSurface<Bilinear> > intplCpiCFsurfUK(
293 new InterpolatedCPICapFloorTermPriceSurface<Bilinear>(
294 nominal, baseZeroRate, observationLag, calendar, convention, dcZCIIS, ii, CPI::AsIndex, nominalUK, cStrikesUK,
295 fStrikesUK, cfMaturitiesUK, *(cPriceUK), *(fPriceUK)));
297 cpiCFsurfUK = intplCpiCFsurfUK;
301class FlatZeroInflationTermStructure :
public ZeroInflationTermStructure {
303 FlatZeroInflationTermStructure(
const Date& referenceDate,
const Calendar& calendar,
const DayCounter& dayCounter,
304 Rate zeroRate,
const Period& observationLag, Frequency frequency,
bool indexIsInterp,
305 const Handle<YieldTermStructure>& ts)
306 : ZeroInflationTermStructure(referenceDate, calendar, dayCounter, zeroRate, observationLag, frequency),
307 zeroRate_(zeroRate), indexIsInterp_(indexIsInterp) {}
309 Date maxDate()
const override {
return Date::maxDate(); }
311 Date baseDate()
const override {
312 Date base = referenceDate() - observationLag();
313 if (!indexIsInterp_) {
314 std::pair<Date, Date> ips = inflationPeriod(base, frequency());
321 Rate zeroRateImpl(Time t)
const override {
return zeroRate_; }
333 Handle<YieldTermStructure> nominalTS = common.nominalUK;
336 QuantLib::ext::shared_ptr<QuantExt::CPIBlackCapFloorEngine> blackEngine =
337 QuantLib::ext::make_shared<QuantExt::CPIBlackCapFloorEngine>(nominalTS,
338 QuantLib::Handle<QuantLib::CPIVolatilitySurface>());
341 Handle<CPICapFloorTermPriceSurface> cpiPriceSurfaceHandle(common.cpiCFsurfUK);
345 QuantLib::ext::shared_ptr<QuantExt::StrippedCPIVolatilitySurface<QuantLib::Bilinear> > cpiVolSurface =
346 QuantLib::ext::make_shared<QuantExt::StrippedCPIVolatilitySurface<QuantLib::Bilinear> >(type, cpiPriceSurfaceHandle,
347 common.ii, blackEngine);
350 blackEngine->setVolatility(QuantLib::Handle<QuantLib::CPIVolatilitySurface>(cpiVolSurface));
356 Date startDate = Settings::instance().evaluationDate();
357 Calendar fixCalendar = UnitedKingdom(), payCalendar = UnitedKingdom();
358 BusinessDayConvention fixConvention(Unadjusted), payConvention(ModifiedFollowing);
359 Real baseCPI = common.hii->fixing(fixCalendar.adjust(startDate - common.observationLag, fixConvention));
360 CPI::InterpolationType observationInterpolation = CPI::AsIndex;
362 Handle<CPICapFloorTermPriceSurface> cpiCFsurfUKh(common.cpiCFsurfUK);
363 QuantLib::ext::shared_ptr<PricingEngine> engine(
new InterpolatingCPICapFloorEngine(cpiCFsurfUKh));
365 for (Size i = 0; i < common.cStrikesUK.size(); i++) {
366 Rate strike = common.cStrikesUK[i];
367 for (Size j = 0; j < common.cfMaturitiesUK.size(); j++) {
368 Period maturity = common.cfMaturitiesUK[j];
369 Date maturityDate = startDate + maturity;
371 CPICapFloor aCap(Option::Call, nominal, startDate, baseCPI, maturityDate, fixCalendar, fixConvention,
372 payCalendar, payConvention, strike, common.hii.currentLink(), common.observationLag,
373 observationInterpolation);
375 aCap.setPricingEngine(engine);
377 Real cached = (*common.cPriceUK)[i][j] * 10000;
378 Real npv1 = aCap.NPV() * 10000;
382 BOOST_CHECK_SMALL(fabs(cached - npv1), 1e-10);
384 aCap.setPricingEngine(blackEngine);
385 Real npv2 = aCap.NPV() * 10000;
388 BOOST_CHECK_SMALL(fabs(cached - npv2), 1e-5);
390 BOOST_TEST_MESSAGE(
"Cap " << fixed << std::setprecision(2) << strike <<
" " << setw(3) << maturity
391 <<
": cached " << setw(7) << cached <<
" QL " << setw(8) << npv1 <<
" QLE "
392 << setw(8) << npv2 <<
" diff " << setw(8) << npv2 - npv1);
396 for (Size i = 0; i < common.fStrikesUK.size(); i++) {
397 Rate strike = common.fStrikesUK[i];
399 for (Size j = 0; j < common.cfMaturitiesUK.size(); j++) {
400 Period maturity = common.cfMaturitiesUK[j];
401 Date maturityDate = startDate + maturity;
403 CPICapFloor aFloor(Option::Put, nominal, startDate, baseCPI, maturityDate, fixCalendar, fixConvention,
404 payCalendar, payConvention, strike, common.hii.currentLink(), common.observationLag,
405 observationInterpolation);
407 aFloor.setPricingEngine(engine);
409 Real cached = (*common.fPriceUK)[i][j] * 10000;
410 Real npv1 = aFloor.NPV() * 10000;
414 BOOST_CHECK_SMALL(fabs(cached - npv1), 1e-10);
416 aFloor.setPricingEngine(blackEngine);
417 Real npv2 = aFloor.NPV() * 10000;
420 BOOST_CHECK_SMALL(fabs(cached - npv2), 1e-5);
422 BOOST_TEST_MESSAGE(
"Floor " << fixed << std::setprecision(2) << strike <<
" " << setw(3) << maturity
423 <<
": cached " << setw(7) << cached <<
" QL " << setw(8) << npv1 <<
" QLE "
424 << setw(8) << npv2 <<
" diff " << setw(8) << npv2 - npv1);
433 Handle<YieldTermStructure> nominalTS = common.nominalUK;
436 QuantLib::ext::shared_ptr<QuantExt::CPIBlackCapFloorEngine> blackEngine =
437 QuantLib::ext::make_shared<QuantExt::CPIBlackCapFloorEngine>(nominalTS,
438 QuantLib::Handle<QuantLib::CPIVolatilitySurface>());
441 Handle<CPICapFloorTermPriceSurface> cpiPriceSurfaceHandle(common.cpiCFsurfUK);
445 QuantLib::ext::shared_ptr<QuantExt::StrippedCPIVolatilitySurface<QuantLib::Bilinear> > cpiVolSurface =
446 QuantLib::ext::make_shared<QuantExt::StrippedCPIVolatilitySurface<QuantLib::Bilinear> >(type, cpiPriceSurfaceHandle,
447 common.ii, blackEngine);
450 blackEngine->setVolatility(QuantLib::Handle<QuantLib::CPIVolatilitySurface>(cpiVolSurface));
452 Period mat[] = { 3 * Years, 4 * Years, 5 * Years, 6 * Years, 7 * Years, 8 * Years, 9 * Years,
453 10 * Years, 12 * Years, 15 * Years, 20 * Years, 25 * Years, 30 * Years };
456 Real strike[] = { 0.0, 0.005, 0.01, 0.015, 0.02, 0.025, 0.03, 0.035, 0.04, 0.045, 0.05 };
461 Date startDate = Settings::instance().evaluationDate();
462 Calendar fixCalendar = UnitedKingdom(), payCalendar = UnitedKingdom();
463 BusinessDayConvention fixConvention(Unadjusted), payConvention(ModifiedFollowing);
464 Real baseCPI = common.hii->fixing(fixCalendar.adjust(startDate - common.observationLag, fixConvention));
465 CPI::InterpolationType observationInterpolation = CPI::AsIndex;
467 Date effectiveStart = startDate - common.observationLag;
468 std::pair<Date, Date> ips = inflationPeriod(effectiveStart, common.ii->frequency());
469 effectiveStart = ips.first;
471 for (Size i = 0; i < nStrike; ++i) {
473 for (Size j = 0; j < nMat; ++j) {
474 Date maturityDate = startDate + mat[j];
476 CPICapFloor aCap(Option::Call, nominal, startDate, baseCPI, maturityDate, fixCalendar, fixConvention,
477 payCalendar, payConvention, strike[i], common.hii.currentLink(), common.observationLag,
478 observationInterpolation);
479 aCap.setPricingEngine(blackEngine);
481 CPICapFloor aFloor(Option::Put, nominal, startDate, baseCPI, maturityDate, fixCalendar, fixConvention,
482 payCalendar, payConvention, strike[i], common.hii.currentLink(), common.observationLag,
483 observationInterpolation);
484 aFloor.setPricingEngine(blackEngine);
486 Real capPrice = aCap.NPV() * 10000;
487 Real floorPrice = aFloor.NPV() * 10000;
490 Date
fixingDate = maturityDate - common.observationLag;
493 common.ii->zeroInflationTermStructure()->dayCounter().yearFraction(effectiveStart, effectiveMaturity);
494 Real K =
pow(1.0 + strike[i], timeFromStart);
495 Real F = common.ii->fixing(effectiveMaturity) / baseCPI;
496 DiscountFactor disc = nominalTS->discount(maturityDate);
497 Real cpilegPrice = disc * (F - K) * 10000;
499 Real parity = capPrice - floorPrice - cpilegPrice;
500 BOOST_TEST_MESSAGE(std::setprecision(3) << fixed <<
"strike=" << strike[i] <<
" mat=" << mat[j] <<
" cap="
501 << capPrice <<
" floor=" << floorPrice <<
" cpileg=" << cpilegPrice
502 <<
" parity=cap-floor-cpileg=" << parity);
505 BOOST_CHECK_SMALL(fabs(parity), 1.1);
513 Handle<YieldTermStructure> nominalTS = common.nominalUK;
516 QuantLib::ext::shared_ptr<QuantExt::CPIBlackCapFloorEngine> blackEngine =
517 QuantLib::ext::make_shared<QuantExt::CPIBlackCapFloorEngine>(nominalTS,
518 QuantLib::Handle<QuantLib::CPIVolatilitySurface>());
521 Handle<CPICapFloorTermPriceSurface> cpiPriceSurfaceHandle(common.cpiCFsurfUK);
525 QuantLib::ext::shared_ptr<QuantExt::StrippedCPIVolatilitySurface<QuantLib::Bilinear> > cpiVolSurface =
526 QuantLib::ext::make_shared<QuantExt::StrippedCPIVolatilitySurface<QuantLib::Bilinear> >(type, cpiPriceSurfaceHandle,
527 common.ii, blackEngine);
529 std::vector<Period> optionTenors = cpiVolSurface->maturities();
530 std::vector<Real>
strikes = cpiVolSurface->strikes();
531 vector<vector<Handle<Quote> > > quotes(optionTenors.size(),
532 vector<Handle<Quote> >(
strikes.size(), Handle<Quote>()));
533 for (Size i = 0; i < optionTenors.size(); ++i) {
534 for (Size j = 0; j <
strikes.size(); ++j) {
535 Real vol = cpiVolSurface->volatility(optionTenors[i],
strikes[j]);
536 QuantLib::ext::shared_ptr<SimpleQuote> q(
new SimpleQuote(vol));
537 quotes[i][j] = Handle<Quote>(q);
540 QuantLib::ext::shared_ptr<QuantExt::InterpolatedCPIVolatilitySurface<Bilinear> > interpolatedCpiVol =
541 QuantLib::ext::make_shared<QuantExt::InterpolatedCPIVolatilitySurface<Bilinear> >(
542 optionTenors,
strikes, quotes, common.hii.currentLink(), cpiVolSurface->settlementDays(),
543 cpiVolSurface->calendar(), cpiVolSurface->businessDayConvention(), cpiVolSurface->dayCounter(),
544 cpiVolSurface->observationLag());
546 for (Size i = 0; i < optionTenors.size(); ++i) {
547 Date d = cpiVolSurface->optionDateFromTenor(optionTenors[i]);
548 for (Size j = 0; j <
strikes.size(); ++j) {
549 Real vol1 = cpiVolSurface->volatility(d,
strikes[j]);
550 Real vol2 = interpolatedCpiVol->volatility(d,
strikes[j]);
551 BOOST_CHECK_SMALL(fabs(vol1 - vol2), 1.0e-10);
560 Real inflationRate = 0.02;
561 Real inflationBlackVol = 0.05;
562 BusinessDayConvention bdc = Unadjusted;
563 DayCounter dc = ActualActual(ActualActual::ISDA);
564 Period observationLag = 3 * Months;
565 Handle<YieldTermStructure> discountCurve(QuantLib::ext::make_shared<FlatForward>(common.evaluationDate, rate, dc));
566 RelinkableHandle<ZeroInflationTermStructure> inflationCurve;
567 Handle<ZeroInflationIndex> index(QuantLib::ext::make_shared<EUHICPXT>(inflationCurve));
571 QuantLib::ext::shared_ptr<ZeroInflationTermStructure> inflationCurvePtr =
572 QuantLib::ext::make_shared<FlatZeroInflationTermStructure>(common.evaluationDate, index->fixingCalendar(), dc,
573 inflationRate, observationLag, index->frequency(),
574 false, discountCurve);
575 inflationCurve.linkTo(inflationCurvePtr);
579 Handle<CPIVolatilitySurface> inflationVol(QuantLib::ext::make_shared<ConstantCPIVolatility>(
580 inflationBlackVol, 0, inflationCurve->calendar(), bdc, dc, inflationCurve->observationLag(),
581 inflationCurve->frequency(),
false));
583 QuantLib::ext::shared_ptr<PricingEngine> engine =
584 QuantLib::ext::make_shared<QuantExt::CPIBlackCapFloorEngine>(discountCurve, inflationVol);
586 Real nominal = 10000.0;
587 Date start = common.evaluationDate;
588 Date end = start + 10 * Years;
589 Real baseCPI = 100.0;
590 Calendar fixCalendar = index->fixingCalendar();
591 Calendar payCalendar = index->fixingCalendar();
593 Rate capStrike = 0.03;
594 CPICapFloor atmCap(Option::Call, nominal, start, baseCPI, end, fixCalendar, bdc, payCalendar, bdc, inflationRate,
595 index.currentLink(), observationLag);
596 atmCap.setPricingEngine(engine);
597 CPICapFloor cap(Option::Call, nominal, start, baseCPI, end, fixCalendar, bdc, payCalendar, bdc, capStrike,
598 index.currentLink(), observationLag);
599 cap.setPricingEngine(engine);
601 Rate floorStrike = 0.01;
602 CPICapFloor atmFloor(Option::Put, nominal, start, baseCPI, end, fixCalendar, bdc, fixCalendar, bdc, inflationRate,
603 index.currentLink(), observationLag);
604 atmFloor.setPricingEngine(engine);
605 CPICapFloor floor(Option::Put, nominal, start, baseCPI, end, fixCalendar, bdc, fixCalendar, bdc, floorStrike,
606 index.currentLink(), observationLag);
607 floor.setPricingEngine(engine);
612 index->addFixing(inflationCurve->baseDate(), baseCPI);
615 BOOST_CHECK_CLOSE(atmCap.NPV(), atmFloor.NPV(), 1e-8);
619 Real stdDev = inflationBlackVol *
sqrt(t);
620 Real forward =
pow(1.0 + inflationRate, t);
621 Real floorStrikePrice =
pow(1.0 + floorStrike, t);
622 Real capStrikePrice =
pow(1.0 + capStrike, t);
623 Real discount =
exp(-rate * t);
624 Real expectedCapNPV = nominal * blackFormula(Option::Call, capStrikePrice, forward, stdDev, discount);
625 Real expectedFloorNPV = nominal * blackFormula(Option::Put, floorStrikePrice, forward, stdDev, discount);
627 BOOST_TEST_MESSAGE(
"CPI Cap NPV " << cap.NPV() <<
" " << expectedCapNPV);
628 BOOST_TEST_MESSAGE(
"CPI Floor NPV " << floor.NPV() <<
" " << expectedFloorNPV);
630 BOOST_CHECK_CLOSE(cap.NPV(), expectedCapNPV, 0.01);
631 BOOST_CHECK_CLOSE(floor.NPV(), expectedFloorNPV, 0.01);
634BOOST_AUTO_TEST_SUITE_END()
636BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(testForwardEvaluation)
Year counter for when we want a whole number year fraction.
CPI cap/floor engine using the Black pricing formula and interpreting the volatility data as lognorma...
zero inflation volatility structure interpolated on a expiry/strike matrix of quotes
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
RandomVariable sqrt(RandomVariable x)
CompiledFormula exp(CompiledFormula x)
CompiledFormula pow(CompiledFormula x, const CompiledFormula &y)
zero inflation volatility structure implied from a cpi cap/floor price surface
Fixture that can be used at top level.
helper macros and methods for tests
day counter that returns the nearest integer yearfraction