20#include <boost/test/unit_test.hpp>
22#include <ql/termstructures/inflationtermstructure.hpp>
25#include <ql/indexes/inflation/euhicp.hpp>
26#include <ql/termstructures/inflation/inflationhelpers.hpp>
27#include <ql/termstructures/yield/flatforward.hpp>
28#include <ql/time/calendars/nullcalendar.hpp>
29#include <ql/time/daycounters/actual365fixed.hpp>
32using namespace boost::unit_test_framework;
42 DayCounter dayCounter;
43 std::vector<Period> zeroCouponPillars;
44 std::vector<Rate> zeroCouponQuotes;
45 std::map<Date, Rate> cpiFixings;
49 : today(15, Aug, 2022), tolerance(1e-6), dayCounter(Actual365Fixed()),
50 zeroCouponPillars({1 * Years, 2 * Years, 3 * Years, 5 * Years}), zeroCouponQuotes({0.06, 0.04, 0.03, 0.02}),
51 cpiFixings({{Date(1, May, 2022), 98.}, {Date(1, June, 2022), 100.}, {Date(1, July, 2022), 104.}}),
55void addFixings(
const std::map<Date, Rate> fixings, ZeroInflationIndex& index) {
57 for (
const auto& fixing : fixings) {
58 index.addFixing(fixing.first, fixing.second,
true);
62QuantLib::ext::shared_ptr<Seasonality> buildSeasonalityCurve() {
63 std::vector<double> factors{0.99, 1.01, 0.98, 1.02, 0.97, 1.03, 0.96, 1.04, 0.95, 1.05, 0.94, 1.06};
64 Date seasonalityBaseDate(1, Jan, 2022);
65 return QuantLib::ext::make_shared<MultiplicativePriceSeasonality>(seasonalityBaseDate, Monthly, factors);
68QuantLib::ext::shared_ptr<ZeroInflationCurve>
69buildZeroInflationCurve(CommonData& cd,
bool useLastKnownFixing,
const QuantLib::ext::shared_ptr<ZeroInflationIndex>& index,
70 const bool isInterpolated,
const QuantLib::ext::shared_ptr<Seasonality>& seasonality =
nullptr) {
71 Date today = Settings::instance().evaluationDate();
72 QuantLib::ext::shared_ptr<SimpleQuote> flatZero = QuantLib::ext::make_shared<SimpleQuote>(0.01);
73 DayCounter dc = cd.dayCounter;
74 Calendar fixingCalendar = NullCalendar();
75 BusinessDayConvention bdc = ModifiedFollowing;
77 QuantLib::ext::shared_ptr<YieldTermStructure> discountTS =
78 QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), Handle<Quote>(flatZero), dc);
80 std::vector<QuantLib::ext::shared_ptr<QuantExt::ZeroInflationTraits::helper>> helpers;
81 for (
size_t i = 0; i < cd.zeroCouponQuotes.size(); ++i) {
82 Date maturity = today + cd.zeroCouponPillars[i];
83 Rate quote = cd.zeroCouponQuotes[i];
84 QuantLib::ext::shared_ptr<QuantExt::ZeroInflationTraits::helper> instrument =
85 QuantLib::ext::make_shared<ZeroCouponInflationSwapHelper>(
86 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(quote)), cd.obsLag, maturity, fixingCalendar, bdc, dc,
87 index, isInterpolated ? CPI::Linear : CPI::Flat, Handle<YieldTermStructure>(discountTS), today);
88 helpers.push_back(instrument);
91 cd.dayCounter, cd.obsLag, cd.zeroCouponQuotes[0],
92 cd.obsLag, cd.dayCounter, index, isInterpolated);
93 QuantLib::ext::shared_ptr<ZeroInflationCurve> curve = QuantLib::ext::make_shared<QuantExt::PiecewiseZeroInflationCurve<Linear>>(
94 today, fixingCalendar, dc, cd.obsLag, index->frequency(), baseRate, helpers, 1e-10, index, useLastKnownFixing);
96 curve->setSeasonality(seasonality);
105BOOST_AUTO_TEST_SUITE(InflationCurveTest)
110 Settings::instance().evaluationDate() = cd.today;
111 bool isInterpolated =
false;
112 bool useLastKnownFixingDateAsBaseDate =
false;
114 QuantLib::ext::shared_ptr<ZeroInflationIndex> curveBuildIndex = QuantLib::ext::make_shared<EUHICPXT>(
false);
115 addFixings(cd.cpiFixings, *curveBuildIndex);
116 auto curve = buildZeroInflationCurve(cd, useLastKnownFixingDateAsBaseDate, curveBuildIndex, isInterpolated);
118 BOOST_CHECK_NO_THROW(curve->zeroRate(1.0));
120 auto index = curveBuildIndex->clone(Handle<ZeroInflationTermStructure>(curve));
122 std::vector<Date> expectedPillarDates{Date(1, June, 2022), Date(1, June, 2023), Date(1, June, 2024),
123 Date(1, June, 2025), Date(1, June, 2027)};
125 std::vector<Real> expectedZeroRates{0.06, 0.06, 0.04, 0.03, 0.02};
127 std::vector<Real> expectedCPIs{100., 106., 108.171622850024, 109.281549591561, 110.414070537467};
129 BOOST_CHECK_EQUAL(curve->baseDate(), expectedPillarDates.front());
131 BOOST_CHECK_EQUAL(curve->dates().size(), expectedPillarDates.size());
133 for (
size_t i = 0; i < expectedPillarDates.size(); ++i) {
134 BOOST_CHECK_EQUAL(curve->dates().at(i), expectedPillarDates.at(i));
135 BOOST_CHECK_CLOSE(curve->zeroRate(curve->dates().at(i), 0 * Days), expectedZeroRates.at(i), cd.tolerance);
140 for (
size_t i = 0; i < expectedPillarDates.size(); ++i) {
141 auto forwardCPI = index->fixing(expectedPillarDates.at(i));
142 BOOST_CHECK_CLOSE(forwardCPI, expectedCPIs.at(i), cd.tolerance);
149 Settings::instance().evaluationDate() = cd.today;
150 bool isInterpolated =
false;
151 bool useLastKnownFixingDateAsBaseDate =
true;
153 QuantLib::ext::shared_ptr<ZeroInflationIndex> curveBuildIndex = QuantLib::ext::make_shared<EUHICPXT>(
false);
154 addFixings(cd.cpiFixings, *curveBuildIndex);
155 auto curve = buildZeroInflationCurve(cd, useLastKnownFixingDateAsBaseDate, curveBuildIndex, isInterpolated);
157 BOOST_CHECK_NO_THROW(curve->zeroRate(1.0));
159 auto index = curveBuildIndex->clone(Handle<ZeroInflationTermStructure>(curve));
161 std::vector<Date> expectedPillarDates{Date(1, July, 2022), Date(1, June, 2023), Date(1, June, 2024),
162 Date(1, June, 2025), Date(1, June, 2027)};
164 std::vector<Real> expectedZeroRates{0.02097086546, 0.02097086546, 0.02068868041, 0.01710609424437, 0.01223686945};
166 std::vector<Real> expectedCPIs{104, 106., 108.171622850024, 109.281549591561, 110.414070537467};
168 BOOST_CHECK_EQUAL(curve->baseDate(), expectedPillarDates.front());
170 BOOST_CHECK_EQUAL(curve->dates().size(), expectedPillarDates.size());
172 for (
size_t i = 0; i < expectedPillarDates.size(); ++i) {
173 BOOST_CHECK_EQUAL(curve->dates().at(i), expectedPillarDates.at(i));
174 BOOST_CHECK_CLOSE(curve->zeroRate(curve->dates().at(i), 0 * Days), expectedZeroRates.at(i), cd.tolerance);
179 for (
size_t i = 0; i < expectedPillarDates.size(); ++i) {
180 auto forwardCPI = index->fixing(expectedPillarDates.at(i));
181 BOOST_CHECK_CLOSE(forwardCPI, expectedCPIs.at(i), cd.tolerance);
188 Settings::instance().evaluationDate() = cd.today;
189 bool isInterpolated =
true;
190 bool useLastKnownFixingDateAsBaseDate =
true;
192 QuantLib::ext::shared_ptr<ZeroInflationIndex> curveBuildIndex = QuantLib::ext::make_shared<EUHICPXT>(
false);
193 addFixings(cd.cpiFixings, *curveBuildIndex);
194 auto curve = buildZeroInflationCurve(cd, useLastKnownFixingDateAsBaseDate, curveBuildIndex, isInterpolated);
196 BOOST_CHECK_NO_THROW(curve->zeroRate(1.0));
198 auto index = curveBuildIndex->clone(Handle<ZeroInflationTermStructure>(curve));
200 std::vector<Date> expectedPillarDates{Date(1, July, 2022), Date(1, July, 2023), Date(1, July, 2024),
201 Date(1, July, 2025), Date(1, July, 2027)};
203 std::vector<Real> expectedZeroRates{0.03945267289772, 0.03945267289772, 0.02921461897637, 0.02277721089513,
206 std::vector<Date> fixingDates{Date(15, June, 2022), Date(15, June, 2023), Date(15, June, 2024),
207 Date(15, June, 2025), Date(15, June, 2027)};
210 std::vector<Real> expectedCPIs{101.806451613, 107.914838710, 110.125690876, 111.255667907, 112.408647296};
212 BOOST_CHECK_EQUAL(curve->baseDate(), expectedPillarDates.front());
214 BOOST_CHECK_EQUAL(curve->dates().size(), expectedPillarDates.size());
216 for (
size_t i = 0; i < expectedPillarDates.size(); ++i) {
217 BOOST_CHECK_EQUAL(curve->dates().at(i), expectedPillarDates.at(i));
218 BOOST_CHECK_CLOSE(curve->zeroRate(curve->dates().at(i), 0 * Days), expectedZeroRates.at(i), 1e-6);
223 for (
size_t i = 0; i < fixingDates.size(); ++i) {
224 Date fixDate1 = inflationPeriod(fixingDates.at(i), index->frequency()).first;
225 Date fixDate2 = inflationPeriod(fixingDates.at(i), index->frequency()).second + 1 * Days;
226 Rate cpi1 = index->fixing(fixDate1);
227 Rate cpi2 = index->fixing(fixDate2);
228 auto forwardCPI = cpi1 + (cpi2 - cpi1) * 14 / 31;
229 BOOST_CHECK_CLOSE(forwardCPI, expectedCPIs.at(i), cd.tolerance);
236 Settings::instance().evaluationDate() = cd.today;
237 bool isInterpolated =
false;
238 bool useLastKnownFixingDateAsBaseDate =
false;
240 QuantLib::ext::shared_ptr<ZeroInflationIndex> curveBuildIndex = QuantLib::ext::make_shared<EUHICPXT>(
false);
241 addFixings(cd.cpiFixings, *curveBuildIndex);
242 auto seasonalityCurve = buildSeasonalityCurve();
243 auto curve = buildZeroInflationCurve(cd, useLastKnownFixingDateAsBaseDate, curveBuildIndex, isInterpolated,
245 BOOST_CHECK_NO_THROW(curve->zeroRate(1.0));
247 auto index = curveBuildIndex->clone(Handle<ZeroInflationTermStructure>(curve));
249 std::vector<Date> expectedPillarDates{Date(1, June, 2022), Date(1, June, 2023), Date(1, June, 2024),
250 Date(1, June, 2025), Date(1, June, 2027)};
252 std::vector<Real> expectedZeroRates{0.06, 0.06, 0.04, 0.03, 0.02};
254 std::vector<Real> expectedCPIs{100., 106., 108.171622850024, 109.281549591561, 110.414070537467};
256 BOOST_CHECK_EQUAL(curve->baseDate(), expectedPillarDates.front());
258 BOOST_CHECK_EQUAL(curve->dates().size(), expectedPillarDates.size());
260 for (
size_t i = 0; i < expectedPillarDates.size(); ++i) {
261 BOOST_CHECK_EQUAL(curve->dates().at(i), expectedPillarDates.at(i));
262 BOOST_CHECK_CLOSE(curve->zeroRate(curve->dates().at(i), 0 * Days), expectedZeroRates.at(i), cd.tolerance);
263 BOOST_CHECK_CLOSE(curve->data().at(i), expectedZeroRates.at(i), cd.tolerance);
268 for (
size_t i = 0; i < expectedPillarDates.size(); ++i) {
269 auto forwardCPI = index->fixing(expectedPillarDates.at(i));
270 BOOST_CHECK_CLOSE(forwardCPI, expectedCPIs.at(i), cd.tolerance);
277 Settings::instance().evaluationDate() = cd.today;
278 bool isInterpolated =
false;
279 bool useLastKnownFixingDateAsBaseDate =
true;
281 QuantLib::ext::shared_ptr<ZeroInflationIndex> curveBuildIndex = QuantLib::ext::make_shared<EUHICPXT>(
false);
282 addFixings(cd.cpiFixings, *curveBuildIndex);
283 auto seasonalityCurve = buildSeasonalityCurve();
284 auto curve = buildZeroInflationCurve(cd, useLastKnownFixingDateAsBaseDate, curveBuildIndex, isInterpolated,
287 BOOST_CHECK_NO_THROW(curve->zeroRate(1.0));
289 auto index = curveBuildIndex->clone(Handle<ZeroInflationTermStructure>(curve));
291 std::vector<Date> expectedPillarDates{Date(1, July, 2022), Date(1, June, 2023), Date(1, June, 2024),
292 Date(1, June, 2025), Date(1, June, 2027)};
294 std::vector<Real> expectedZeroRates{0.02097086546, 0.02097086546, 0.02068868041, 0.01710609424437, 0.01223686945};
295 std::vector<Real> expectedZeroRatesWithoutSeasonality{0.02097086546, -0.05439424967, -0.01603861959, -0.00711164972,
297 std::vector<Real> expectedCPIs{104, 106., 108.171622850024, 109.281549591561, 110.414070537467};
299 BOOST_CHECK_EQUAL(curve->baseDate(), expectedPillarDates.front());
301 BOOST_CHECK_EQUAL(curve->dates().size(), expectedPillarDates.size());
303 for (
size_t i = 0; i < expectedPillarDates.size(); ++i) {
304 BOOST_CHECK_EQUAL(curve->dates().at(i), expectedPillarDates.at(i));
305 BOOST_CHECK_CLOSE(curve->data().at(i), expectedZeroRatesWithoutSeasonality.at(i), cd.tolerance);
306 BOOST_CHECK_CLOSE(curve->zeroRate(curve->dates().at(i), 0 * Days), expectedZeroRates.at(i), cd.tolerance);
310 for (
size_t i = 0; i < expectedPillarDates.size(); ++i) {
311 auto forwardCPI = index->fixing(expectedPillarDates.at(i));
312 BOOST_CHECK_CLOSE(forwardCPI, expectedCPIs.at(i), cd.tolerance);
316BOOST_AUTO_TEST_SUITE_END()
318BOOST_AUTO_TEST_SUITE_END()
some inflation related utilities.
BOOST_AUTO_TEST_CASE(testZeroInflationCurveNonInterpolatedLastMonthFixingUnknown)
QuantLib::Rate guessCurveBaseRate(const bool baseDateLastKnownFixing, const QuantLib::Date &swapStart, const QuantLib::Date &asof, const QuantLib::Period &swapTenor, const QuantLib::DayCounter &swapZCLegDayCounter, const QuantLib::Period &swapObsLag, const QuantLib::Rate zeroCouponRate, const QuantLib::Period &curveObsLag, const QuantLib::DayCounter &curveDayCounter, const QuantLib::ext::shared_ptr< QuantLib::ZeroInflationIndex > &index, const bool interpolated, const QuantLib::ext::shared_ptr< QuantLib::Seasonality > &seasonality)
Piecewise interpolated zero inflation term structure.
Fixture that can be used at top level.