| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2011 Chris Kenyon |
| 5 | |
| 6 | |
| 7 | This file is part of QuantLib, a free-software/open-source library |
| 8 | for financial quantitative analysts and developers - http://quantlib.org/ |
| 9 | |
| 10 | QuantLib is free software: you can redistribute it and/or modify it |
| 11 | under the terms of the QuantLib license. You should have received a |
| 12 | copy of the license along with this program; if not, please email |
| 13 | <quantlib-dev@lists.sf.net>. The license is also available online at |
| 14 | <http://quantlib.org/license.shtml>. |
| 15 | |
| 16 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 17 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 18 | FOR A PARTICULAR PURPOSE. See the license for more details. |
| 19 | */ |
| 20 | |
| 21 | |
| 22 | #include "inflationcpicapfloor.hpp" |
| 23 | #include "utilities.hpp" |
| 24 | #include <ql/types.hpp> |
| 25 | #include <ql/indexes/inflation/ukrpi.hpp> |
| 26 | #include <ql/termstructures/bootstraphelper.hpp> |
| 27 | #include <ql/time/calendars/unitedkingdom.hpp> |
| 28 | #include <ql/time/daycounters/actualactual.hpp> |
| 29 | #include <ql/time/daycounters/actual365fixed.hpp> |
| 30 | #include <ql/termstructures/yield/zerocurve.hpp> |
| 31 | #include <ql/indexes/ibor/gbplibor.hpp> |
| 32 | #include <ql/termstructures/inflation/inflationhelpers.hpp> |
| 33 | #include <ql/termstructures/inflation/piecewisezeroinflationcurve.hpp> |
| 34 | #include <ql/cashflows/indexedcashflow.hpp> |
| 35 | #include <ql/pricingengines/swap/discountingswapengine.hpp> |
| 36 | #include <ql/instruments/zerocouponinflationswap.hpp> |
| 37 | #include <ql/pricingengines/bond/discountingbondengine.hpp> |
| 38 | #include <ql/math/interpolations/bilinearinterpolation.hpp> |
| 39 | #include <ql/cashflows/cpicoupon.hpp> |
| 40 | #include <ql/cashflows/cpicouponpricer.hpp> |
| 41 | #include <ql/instruments/cpiswap.hpp> |
| 42 | #include <ql/instruments/bonds/cpibond.hpp> |
| 43 | #include <ql/instruments/cpicapfloor.hpp> |
| 44 | #include <ql/experimental/inflation/cpicapfloortermpricesurface.hpp> |
| 45 | #include <ql/experimental/inflation/cpicapfloorengines.hpp> |
| 46 | |
| 47 | #include <iostream> |
| 48 | |
| 49 | |
| 50 | using namespace QuantLib; |
| 51 | using namespace boost::unit_test_framework; |
| 52 | |
| 53 | |
| 54 | namespace inflation_cpi_capfloor_test { |
| 55 | struct Datum { |
| 56 | Date date; |
| 57 | Rate rate; |
| 58 | }; |
| 59 | |
| 60 | template <class T, class U, class I> |
| 61 | std::vector<ext::shared_ptr<BootstrapHelper<T> > > makeHelpers( |
| 62 | Datum iiData[], Size N, |
| 63 | const ext::shared_ptr<I> &ii, const Period &observationLag, |
| 64 | const Calendar &calendar, |
| 65 | const BusinessDayConvention &bdc, |
| 66 | const DayCounter &dc, |
| 67 | const Handle<YieldTermStructure>& discountCurve) { |
| 68 | |
| 69 | std::vector<ext::shared_ptr<BootstrapHelper<T> > > instruments; |
| 70 | for (Size i=0; i<N; i++) { |
| 71 | Date maturity = iiData[i].date; |
| 72 | Handle<Quote> quote(ext::shared_ptr<Quote>( |
| 73 | new SimpleQuote(iiData[i].rate/100.0))); |
| 74 | ext::shared_ptr<BootstrapHelper<T> > anInstrument(new U(quote, observationLag, maturity, |
| 75 | calendar, bdc, dc, ii, |
| 76 | CPI::AsIndex, discountCurve)); |
| 77 | instruments.push_back(anInstrument); |
| 78 | } |
| 79 | |
| 80 | return instruments; |
| 81 | } |
| 82 | |
| 83 | |
| 84 | struct CommonVars { |
| 85 | |
| 86 | // common data |
| 87 | |
| 88 | Size length; |
| 89 | Date startDate; |
| 90 | Rate baseZeroRate; |
| 91 | Real volatility; |
| 92 | |
| 93 | Frequency frequency; |
| 94 | std::vector<Real> nominals; |
| 95 | Calendar calendar; |
| 96 | BusinessDayConvention convention; |
| 97 | Natural fixingDays; |
| 98 | Date evaluationDate; |
| 99 | Natural settlementDays; |
| 100 | Date settlement; |
| 101 | Period observationLag, contractObservationLag; |
| 102 | CPI::InterpolationType contractObservationInterpolation; |
| 103 | DayCounter dcZCIIS,dcNominal; |
| 104 | std::vector<Date> zciisD; |
| 105 | std::vector<Rate> zciisR; |
| 106 | ext::shared_ptr<UKRPI> ii; |
| 107 | Size zciisDataLength; |
| 108 | |
| 109 | RelinkableHandle<YieldTermStructure> nominalUK; |
| 110 | RelinkableHandle<ZeroInflationTermStructure> cpiUK; |
| 111 | RelinkableHandle<ZeroInflationTermStructure> hcpi; |
| 112 | |
| 113 | std::vector<Rate> cStrikesUK; |
| 114 | std::vector<Rate> fStrikesUK; |
| 115 | std::vector<Period> cfMaturitiesUK; |
| 116 | ext::shared_ptr<Matrix> cPriceUK; |
| 117 | ext::shared_ptr<Matrix> fPriceUK; |
| 118 | |
| 119 | ext::shared_ptr<CPICapFloorTermPriceSurface> cpiCFsurfUK; |
| 120 | |
| 121 | // setup |
| 122 | CommonVars() |
| 123 | : nominals(1,1000000) { |
| 124 | //std::cout <<"CommonVars" << std::endl; |
| 125 | // option variables |
| 126 | frequency = Annual; |
| 127 | // usual setup |
| 128 | volatility = 0.01; |
| 129 | length = 7; |
| 130 | calendar = UnitedKingdom(); |
| 131 | convention = ModifiedFollowing; |
| 132 | Date today(1, June, 2010); |
| 133 | evaluationDate = calendar.adjust(today); |
| 134 | Settings::instance().evaluationDate() = evaluationDate; |
| 135 | settlementDays = 0; |
| 136 | fixingDays = 0; |
| 137 | settlement = calendar.advance(today,n: settlementDays,unit: Days); |
| 138 | startDate = settlement; |
| 139 | dcZCIIS = ActualActual(ActualActual::ISDA); |
| 140 | dcNominal = ActualActual(ActualActual::ISDA); |
| 141 | |
| 142 | // uk rpi index |
| 143 | // fixing data |
| 144 | Date from(1, July, 2007); |
| 145 | Date to(1, June, 2010); |
| 146 | Schedule rpiSchedule = MakeSchedule().from(effectiveDate: from).to(terminationDate: to) |
| 147 | .withTenor(1*Months) |
| 148 | .withCalendar(UnitedKingdom()) |
| 149 | .withConvention(ModifiedFollowing); |
| 150 | Real fixData[] = { |
| 151 | 206.1, 207.3, 208.0, 208.9, 209.7, 210.9, |
| 152 | 209.8, 211.4, 212.1, 214.0, 215.1, 216.8, // 2008 |
| 153 | 216.5, 217.2, 218.4, 217.7, 216.0, 212.9, |
| 154 | 210.1, 211.4, 211.3, 211.5, 212.8, 213.4, // 2009 |
| 155 | 213.4, 214.4, 215.3, 216.0, 216.6, 218.0, |
| 156 | 217.9, 219.2, 220.7, 222.8, -999, -999, // 2010 |
| 157 | -999}; |
| 158 | |
| 159 | // link from cpi index to cpi TS |
| 160 | ii = ext::make_shared<UKRPI>(args&: hcpi); |
| 161 | for (Size i=0; i<rpiSchedule.size();i++) { |
| 162 | ii->addFixing(fixingDate: rpiSchedule[i], fixing: fixData[i], forceOverwrite: true);// force overwrite in case multiple use |
| 163 | }; |
| 164 | |
| 165 | |
| 166 | Datum nominalData[] = { |
| 167 | { .date: Date( 2, June, 2010), .rate: 0.499997 }, |
| 168 | { .date: Date( 3, June, 2010), .rate: 0.524992 }, |
| 169 | { .date: Date( 8, June, 2010), .rate: 0.524974 }, |
| 170 | { .date: Date( 15, June, 2010), .rate: 0.549942 }, |
| 171 | { .date: Date( 22, June, 2010), .rate: 0.549913 }, |
| 172 | { .date: Date( 1, July, 2010), .rate: 0.574864 }, |
| 173 | { .date: Date( 2, August, 2010), .rate: 0.624668 }, |
| 174 | { .date: Date( 1, September, 2010), .rate: 0.724338 }, |
| 175 | { .date: Date( 16, September, 2010), .rate: 0.769461 }, |
| 176 | { .date: Date( 1, December, 2010), .rate: 0.997501 }, |
| 177 | //{ Date( 16, December, 2010), 0.838164 }, |
| 178 | { .date: Date( 17, March, 2011), .rate: 0.916996 }, |
| 179 | { .date: Date( 16, June, 2011), .rate: 0.984339 }, |
| 180 | { .date: Date( 22, September, 2011), .rate: 1.06085 }, |
| 181 | { .date: Date( 22, December, 2011), .rate: 1.141788 }, |
| 182 | { .date: Date( 1, June, 2012), .rate: 1.504426 }, |
| 183 | { .date: Date( 3, June, 2013), .rate: 1.92064 }, |
| 184 | { .date: Date( 2, June, 2014), .rate: 2.290824 }, |
| 185 | { .date: Date( 1, June, 2015), .rate: 2.614394 }, |
| 186 | { .date: Date( 1, June, 2016), .rate: 2.887445 }, |
| 187 | { .date: Date( 1, June, 2017), .rate: 3.122128 }, |
| 188 | { .date: Date( 1, June, 2018), .rate: 3.322511 }, |
| 189 | { .date: Date( 3, June, 2019), .rate: 3.483997 }, |
| 190 | { .date: Date( 1, June, 2020), .rate: 3.616896 }, |
| 191 | { .date: Date( 1, June, 2022), .rate: 3.8281 }, |
| 192 | { .date: Date( 2, June, 2025), .rate: 4.0341 }, |
| 193 | { .date: Date( 3, June, 2030), .rate: 4.070854 }, |
| 194 | { .date: Date( 1, June, 2035), .rate: 4.023202 }, |
| 195 | { .date: Date( 1, June, 2040), .rate: 3.954748 }, |
| 196 | { .date: Date( 1, June, 2050), .rate: 3.870953 }, |
| 197 | { .date: Date( 1, June, 2060), .rate: 3.85298 }, |
| 198 | { .date: Date( 2, June, 2070), .rate: 3.757542 }, |
| 199 | { .date: Date( 3, June, 2080), .rate: 3.651379 } |
| 200 | }; |
| 201 | |
| 202 | std::vector<Date> nomD; |
| 203 | std::vector<Rate> nomR; |
| 204 | for (auto& i : nominalData) { |
| 205 | nomD.push_back(x: i.date); |
| 206 | nomR.push_back(x: i.rate / 100.0); |
| 207 | } |
| 208 | ext::shared_ptr<YieldTermStructure> nominalTS = |
| 209 | ext::make_shared<InterpolatedZeroCurve<Linear>>(args&: nomD,args&: nomR,args&: dcNominal); |
| 210 | |
| 211 | nominalUK.linkTo(h: nominalTS); |
| 212 | |
| 213 | |
| 214 | // now build the zero inflation curve |
| 215 | observationLag = Period(2,Months); |
| 216 | contractObservationLag = Period(3,Months); |
| 217 | contractObservationInterpolation = CPI::Flat; |
| 218 | |
| 219 | Datum zciisData[] = { |
| 220 | { .date: Date(1, June, 2011), .rate: 3.087 }, |
| 221 | { .date: Date(1, June, 2012), .rate: 3.12 }, |
| 222 | { .date: Date(1, June, 2013), .rate: 3.059 }, |
| 223 | { .date: Date(1, June, 2014), .rate: 3.11 }, |
| 224 | { .date: Date(1, June, 2015), .rate: 3.15 }, |
| 225 | { .date: Date(1, June, 2016), .rate: 3.207 }, |
| 226 | { .date: Date(1, June, 2017), .rate: 3.253 }, |
| 227 | { .date: Date(1, June, 2018), .rate: 3.288 }, |
| 228 | { .date: Date(1, June, 2019), .rate: 3.314 }, |
| 229 | { .date: Date(1, June, 2020), .rate: 3.401 }, |
| 230 | { .date: Date(1, June, 2022), .rate: 3.458 }, |
| 231 | { .date: Date(1, June, 2025), .rate: 3.52 }, |
| 232 | { .date: Date(1, June, 2030), .rate: 3.655 }, |
| 233 | { .date: Date(1, June, 2035), .rate: 3.668 }, |
| 234 | { .date: Date(1, June, 2040), .rate: 3.695 }, |
| 235 | { .date: Date(1, June, 2050), .rate: 3.634 }, |
| 236 | { .date: Date(1, June, 2060), .rate: 3.629 }, |
| 237 | }; |
| 238 | zciisDataLength = 17; |
| 239 | for (Size i = 0; i < zciisDataLength; i++) { |
| 240 | zciisD.push_back(x: zciisData[i].date); |
| 241 | zciisR.push_back(x: zciisData[i].rate); |
| 242 | } |
| 243 | |
| 244 | // now build the helpers ... |
| 245 | std::vector<ext::shared_ptr<BootstrapHelper<ZeroInflationTermStructure> > > helpers = |
| 246 | makeHelpers<ZeroInflationTermStructure,ZeroCouponInflationSwapHelper, |
| 247 | ZeroInflationIndex>(iiData: zciisData, N: zciisDataLength, ii, |
| 248 | observationLag, |
| 249 | calendar, bdc: convention, dc: dcZCIIS, |
| 250 | discountCurve: Handle<YieldTermStructure>(nominalTS)); |
| 251 | |
| 252 | // we can use historical or first ZCIIS for this |
| 253 | // we know historical is WAY off market-implied, so use market implied flat. |
| 254 | baseZeroRate = zciisData[0].rate/100.0; |
| 255 | ext::shared_ptr<PiecewiseZeroInflationCurve<Linear> > pCPIts( |
| 256 | new PiecewiseZeroInflationCurve<Linear>( |
| 257 | evaluationDate, calendar, dcZCIIS, observationLag, |
| 258 | ii->frequency(), baseZeroRate, helpers)); |
| 259 | pCPIts->recalculate(); |
| 260 | cpiUK.linkTo(h: pCPIts); |
| 261 | |
| 262 | // make sure that the index has the latest zero inflation term structure |
| 263 | hcpi.linkTo(h: pCPIts); |
| 264 | |
| 265 | // cpi CF price surf data |
| 266 | Period cfMat[] = {3*Years, 5*Years, 7*Years, 10*Years, 15*Years, 20*Years, 30*Years}; |
| 267 | Real cStrike[] = {0.03, 0.04, 0.05, 0.06}; |
| 268 | Real fStrike[] = {-0.01, 0, 0.01, 0.02}; |
| 269 | Size ncStrikes = 4, nfStrikes = 4, ncfMaturities = 7; |
| 270 | |
| 271 | Real cPrice[7][4] = { |
| 272 | {227.6, 100.27, 38.8, 14.94}, |
| 273 | {345.32, 127.9, 40.59, 14.11}, |
| 274 | {477.95, 170.19, 50.62, 16.88}, |
| 275 | {757.81, 303.95, 107.62, 43.61}, |
| 276 | {1140.73, 481.89, 168.4, 63.65}, |
| 277 | {1537.6, 607.72, 172.27, 54.87}, |
| 278 | {2211.67, 839.24, 184.75, 45.03}}; |
| 279 | Real fPrice[7][4] = { |
| 280 | {15.62, 28.38, 53.61, 104.6}, |
| 281 | {21.45, 36.73, 66.66, 129.6}, |
| 282 | {24.45, 42.08, 77.04, 152.24}, |
| 283 | {39.25, 63.52, 109.2, 203.44}, |
| 284 | {36.82, 63.62, 116.97, 232.73}, |
| 285 | {39.7, 67.47, 121.79, 238.56}, |
| 286 | {41.48, 73.9, 139.75, 286.75}}; |
| 287 | |
| 288 | // now load the data into vector and Matrix classes |
| 289 | cStrikesUK.clear(); |
| 290 | fStrikesUK.clear(); |
| 291 | cfMaturitiesUK.clear(); |
| 292 | for(Size i = 0; i < ncStrikes; i++) cStrikesUK.push_back(x: cStrike[i]); |
| 293 | for(Size i = 0; i < nfStrikes; i++) fStrikesUK.push_back(x: fStrike[i]); |
| 294 | for(Size i = 0; i < ncfMaturities; i++) cfMaturitiesUK.push_back(x: cfMat[i]); |
| 295 | cPriceUK = ext::make_shared<Matrix>(args&: ncStrikes, args&: ncfMaturities); |
| 296 | fPriceUK = ext::make_shared<Matrix>(args&: nfStrikes, args&: ncfMaturities); |
| 297 | for(Size i = 0; i < ncStrikes; i++) { |
| 298 | for(Size j = 0; j < ncfMaturities; j++) { |
| 299 | (*cPriceUK)[i][j] = cPrice[j][i]/10000.0; |
| 300 | } |
| 301 | } |
| 302 | for(Size i = 0; i < nfStrikes; i++) { |
| 303 | for(Size j = 0; j < ncfMaturities; j++) { |
| 304 | (*fPriceUK)[i][j] = fPrice[j][i]/10000.0; |
| 305 | } |
| 306 | } |
| 307 | } |
| 308 | }; |
| 309 | |
| 310 | } |
| 311 | |
| 312 | |
| 313 | |
| 314 | void InflationCPICapFloorTest::cpicapfloorpricesurface() { |
| 315 | BOOST_TEST_MESSAGE("Checking CPI cap/floor against price surface..." ); |
| 316 | |
| 317 | using namespace inflation_cpi_capfloor_test; |
| 318 | |
| 319 | CommonVars common; |
| 320 | |
| 321 | Real nominal = 1.0; |
| 322 | InterpolatedCPICapFloorTermPriceSurface |
| 323 | <Bilinear> cpiSurf(nominal, |
| 324 | common.baseZeroRate, |
| 325 | common.observationLag, |
| 326 | common.calendar, |
| 327 | common.convention, |
| 328 | common.dcZCIIS, |
| 329 | common.ii, |
| 330 | CPI::Flat, |
| 331 | common.nominalUK, |
| 332 | common.cStrikesUK, |
| 333 | common.fStrikesUK, |
| 334 | common.cfMaturitiesUK, |
| 335 | *(common.cPriceUK), |
| 336 | *(common.fPriceUK)); |
| 337 | |
| 338 | // test code - note order of indices |
| 339 | for (Size i =0; i<common.fStrikesUK.size(); i++){ |
| 340 | |
| 341 | Real qK = common.fStrikesUK[i]; |
| 342 | Size nMat = common.cfMaturitiesUK.size(); |
| 343 | for (Size j=0; j<nMat; j++) { |
| 344 | Period t = common.cfMaturitiesUK[j]; |
| 345 | Real a = (*(common.fPriceUK))[i][j]; |
| 346 | Real b = cpiSurf.floorPrice(d: t,k: qK); |
| 347 | |
| 348 | QL_REQUIRE(fabs(a-b)<1e-7,"cannot reproduce cpi floor data from surface: " |
| 349 | << a << " vs constructed = " << b); |
| 350 | } |
| 351 | |
| 352 | } |
| 353 | |
| 354 | for (Size i =0; i<common.cStrikesUK.size(); i++){ |
| 355 | |
| 356 | Real qK = common.cStrikesUK[i]; |
| 357 | Size nMat = common.cfMaturitiesUK.size(); |
| 358 | for (Size j=0; j<nMat; j++) { |
| 359 | Period t = common.cfMaturitiesUK[j]; |
| 360 | Real a = (*(common.cPriceUK))[i][j]; |
| 361 | Real b = cpiSurf.capPrice(d: t,k: qK); |
| 362 | |
| 363 | QL_REQUIRE(fabs(a-b)<1e-7,"cannot reproduce cpi cap data from surface: " |
| 364 | << a << " vs constructed = " << b); |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | // Test the price method also i.e. does it pick out the correct premium? |
| 369 | // Look up premium from surface at 3 years and strike of 1% |
| 370 | // Expect, as 1% < ATM, to get back floor premium at 1% i.e. 53.61 bps |
| 371 | Real premium = cpiSurf.price(d: 3 * Years, k: 0.01); |
| 372 | Real expPremium = (*common.fPriceUK)[2][0]; |
| 373 | if (fabs(x: premium - expPremium) > 1e-12) { |
| 374 | BOOST_ERROR("The requested premium, " << premium |
| 375 | << ", does not equal the expected premium, " << expPremium << "." ); |
| 376 | } |
| 377 | |
| 378 | // remove circular refernce |
| 379 | common.hcpi.linkTo(h: ext::shared_ptr<ZeroInflationTermStructure>()); |
| 380 | } |
| 381 | |
| 382 | |
| 383 | void InflationCPICapFloorTest::cpicapfloorpricer() { |
| 384 | BOOST_TEST_MESSAGE("Checking CPI cap/floor pricer..." ); |
| 385 | |
| 386 | using namespace inflation_cpi_capfloor_test; |
| 387 | |
| 388 | CommonVars common; |
| 389 | Real nominal = 1.0; |
| 390 | ext::shared_ptr<CPICapFloorTermPriceSurface> cpiCFpriceSurf(new InterpolatedCPICapFloorTermPriceSurface |
| 391 | <Bilinear>(nominal, |
| 392 | common.baseZeroRate, |
| 393 | common.observationLag, |
| 394 | common.calendar, |
| 395 | common.convention, |
| 396 | common.dcZCIIS, |
| 397 | common.ii, |
| 398 | CPI::Flat, |
| 399 | common.nominalUK, |
| 400 | common.cStrikesUK, |
| 401 | common.fStrikesUK, |
| 402 | common.cfMaturitiesUK, |
| 403 | *(common.cPriceUK), |
| 404 | *(common.fPriceUK))); |
| 405 | |
| 406 | common.cpiCFsurfUK = cpiCFpriceSurf; |
| 407 | |
| 408 | // interpolation pricer first |
| 409 | // N.B. no new instrument required but we do need a new pricer |
| 410 | |
| 411 | Date startDate = Settings::instance().evaluationDate(); |
| 412 | Date maturity(startDate + Period(3,Years)); |
| 413 | Calendar fixCalendar = UnitedKingdom(), payCalendar = UnitedKingdom(); |
| 414 | BusinessDayConvention fixConvention(Unadjusted), payConvention(ModifiedFollowing); |
| 415 | Rate strike(0.03); |
| 416 | Real baseCPI = common.ii->fixing(fixingDate: fixCalendar.adjust(startDate-common.observationLag,convention: fixConvention)); |
| 417 | CPI::InterpolationType observationInterpolation = CPI::AsIndex; |
| 418 | CPICapFloor aCap(Option::Call, |
| 419 | nominal, |
| 420 | startDate, // start date of contract (only) |
| 421 | baseCPI, |
| 422 | maturity, // this is pre-adjustment! |
| 423 | fixCalendar, |
| 424 | fixConvention, |
| 425 | payCalendar, |
| 426 | payConvention, |
| 427 | strike, |
| 428 | common.ii, |
| 429 | common.observationLag, |
| 430 | observationInterpolation); |
| 431 | |
| 432 | Handle<CPICapFloorTermPriceSurface> cpiCFsurfUKh(common.cpiCFsurfUK); |
| 433 | ext::shared_ptr<PricingEngine>engine(new InterpolatingCPICapFloorEngine(cpiCFsurfUKh)); |
| 434 | |
| 435 | aCap.setPricingEngine(engine); |
| 436 | |
| 437 | // We should get back the cap premium at strike 0.03 i.e. 227.6 bps |
| 438 | Real cached = (*common.cPriceUK)[0][0]; |
| 439 | |
| 440 | QL_REQUIRE(fabs(cached - aCap.NPV())<1e-10,"InterpolatingCPICapFloorEngine does not reproduce cached price: " |
| 441 | << cached << " vs " << aCap.NPV()); |
| 442 | |
| 443 | // remove circular refernce |
| 444 | common.hcpi.linkTo(h: ext::shared_ptr<ZeroInflationTermStructure>()); |
| 445 | } |
| 446 | |
| 447 | |
| 448 | |
| 449 | |
| 450 | test_suite* InflationCPICapFloorTest::suite() { |
| 451 | auto* suite = BOOST_TEST_SUITE("CPIswaption tests" ); |
| 452 | |
| 453 | suite->add(QUANTLIB_TEST_CASE(&InflationCPICapFloorTest::cpicapfloorpricesurface)); |
| 454 | suite->add(QUANTLIB_TEST_CASE(&InflationCPICapFloorTest::cpicapfloorpricer)); |
| 455 | |
| 456 | return suite; |
| 457 | } |
| 458 | |
| 459 | |