44 {
45
46 try {
47
49
50 const QuantLib::ext::shared_ptr<Conventions>& conventions = InstrumentConventions::instance().conventions();
51 QuantLib::ext::shared_ptr<InflationSwapConvention> conv =
52 QuantLib::ext::dynamic_pointer_cast<InflationSwapConvention>(conventions->get(config->conventions()));
53
54 QL_REQUIRE(conv != nullptr, "convention " << config->conventions() << " could not be found.");
55
56 Handle<YieldTermStructure> nominalTs;
57 auto it = yieldCurves.find(config->nominalTermStructure());
58 if (it != yieldCurves.end()) {
59 nominalTs = it->second->handle();
60 } else {
61 QL_FAIL("The nominal term structure, " << config->nominalTermStructure()
62 << ", required in the building "
63 "of the curve, "
64 <<
spec.
name() <<
", was not found.");
65 }
66
67
68
69 const std::vector<string> strQuotes = config->swapQuotes();
70 std::vector<Handle<Quote>> quotes(strQuotes.size(), Handle<Quote>());
71 std::vector<Period> terms(strQuotes.size());
72 std::vector<bool> isZc(strQuotes.size(), true);
73
74 std::ostringstream ss1;
77 auto data1 = loader.
get(w1, asof);
78
79 std::ostringstream ss2;
82 auto data2 = loader.
get(w2, asof);
83
84 data1.merge(data2);
85
86 for (const auto& md : data1) {
87
88 QL_REQUIRE(md->asofDate() == asof, "MarketDatum asofDate '" << md->asofDate() << "' <> asof '" << asof << "'");
89
93
94 QuantLib::ext::shared_ptr<ZcInflationSwapQuote> q = QuantLib::ext::dynamic_pointer_cast<ZcInflationSwapQuote>(md);
95 if (q) {
96 auto it = std::find(strQuotes.begin(), strQuotes.end(), q->name());
97 if (it != strQuotes.end()) {
98 QL_REQUIRE(quotes[it - strQuotes.begin()].empty(), "duplicate quote " << q->name());
99 quotes[it - strQuotes.begin()] = q->quote();
100 terms[it - strQuotes.begin()] = q->term();
101 isZc[it - strQuotes.begin()] = true;
102 }
103 }
104
105 QuantLib::ext::shared_ptr<YoYInflationSwapQuote> q2 = QuantLib::ext::dynamic_pointer_cast<YoYInflationSwapQuote>(md);
106 if (q2) {
107 auto it = std::find(strQuotes.begin(), strQuotes.end(), q2->name());
108 if (it != strQuotes.end()) {
109 QL_REQUIRE(quotes[it - strQuotes.begin()].empty(), "duplicate quote " << q2->name());
110 quotes[it - strQuotes.begin()] = q2->quote();
111 terms[it - strQuotes.begin()] = q2->term();
112 isZc[it - strQuotes.begin()] = false;
113 }
114 }
115 }
116 }
117
118
119 for (Size i = 0; i < strQuotes.size(); ++i) {
120 QL_REQUIRE(!quotes[i].empty(), "quote " << strQuotes[i] << " not found in market data.");
121 QL_REQUIRE(isZc[i] == isZc[0], "mixed zc and yoy quotes");
122 }
124
125
126 QuantLib::ext::shared_ptr<Seasonality> seasonality;
127 if (config->seasonalityBaseDate() != Null<Date>()) {
128 if (config->overrideSeasonalityFactors().empty()) {
129 std::vector<string> strFactorIDs = config->seasonalityFactors();
130 std::vector<double> factors(strFactorIDs.size());
131 for (Size i = 0; i < strFactorIDs.size(); i++) {
132 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader.
get(strFactorIDs[i], asof);
133
134 if (marketQuote) {
136 "Market quote (" << marketQuote->name() << ") not of type seasonality.");
137
138 QL_REQUIRE(config->seasonalityFrequency() == Monthly && strFactorIDs.size() == 12,
139 "Only monthly seasonality with 12 factors is allowed. Provided "
140 << config->seasonalityFrequency() << " with " << strFactorIDs.size()
141 << " factors.");
142 QuantLib::ext::shared_ptr<SeasonalityQuote> sq =
143 QuantLib::ext::dynamic_pointer_cast<SeasonalityQuote>(marketQuote);
144 QL_REQUIRE(sq, "Could not cast to SeasonalityQuote, internal error.");
145 QL_REQUIRE(sq->type() == "MULT",
146 "Market quote (" << sq->name() << ") not of multiplicative type.");
147 Size seasBaseDateMonth = ((Size)config->seasonalityBaseDate().month());
148 int findex = sq->applyMonth() - seasBaseDateMonth;
149 if (findex < 0)
150 findex += 12;
151 QL_REQUIRE(findex >= 0 && findex < 12, "Unexpected seasonality index " << findex);
152 factors[findex] = sq->quote()->value();
153 } else {
154 QL_FAIL("Could not find quote for ID " << strFactorIDs[i] << " with as of date "
155 << io::iso_date(asof) << ".");
156 }
157 }
158 QL_REQUIRE(!factors.empty(), "no seasonality factors found");
159 seasonality = QuantLib::ext::make_shared<MultiplicativePriceSeasonality>(
160 config->seasonalityBaseDate(), config->seasonalityFrequency(), factors);
161 } else {
162
163 seasonality = QuantLib::ext::make_shared<MultiplicativePriceSeasonality>(config->seasonalityBaseDate(),
164 config->seasonalityFrequency(),
165 config->overrideSeasonalityFactors());
166 }
167 }
168
169
171 Date swapStart = p.first;
172 Period curveObsLag = p.second == Period() ? config->lag() : p.second;
173
174
175 std::vector<Date> pillarDates;
176
178 CPI::InterpolationType observationInterpolation =
interpolatedIndex_ ? CPI::Linear : CPI::Flat;
179 QuantLib::ext::shared_ptr<YoYInflationIndex> zc_to_yoy_conversion_index;
181
182 std::vector<QuantLib::ext::shared_ptr<QuantExt::ZeroInflationTraits::helper>> instruments;
183 QuantLib::ext::shared_ptr<ZeroInflationIndex> index = conv->index();
184 for (Size i = 0; i < strQuotes.size(); ++i) {
185
186 Date
maturity = swapStart + terms[i];
187 QuantLib::ext::shared_ptr<QuantExt::ZeroInflationTraits::helper> instrument =
188 QuantLib::ext::make_shared<ZeroCouponInflationSwapHelper>(
189 quotes[i], conv->observationLag(),
maturity, conv->fixCalendar(), conv->fixConvention(),
190 conv->dayCounter(), index, observationInterpolation, nominalTs, swapStart);
191
192
193
194 instrument->unregisterWith(Settings::instance().evaluationDate());
195 instruments.push_back(instrument);
196 }
197
198 Real baseRate = quotes[0]->value();
199 if (config->baseRate() != Null<Real>()) {
200 baseRate = config->baseRate();
201 } else if (index) {
202 try {
204 config->useLastAvailableFixingAsBaseDate(), swapStart, asof, terms[0], conv->dayCounter(),
205 conv->observationLag(), quotes[0]->value(), curveObsLag, config->dayCounter(), index,
207 } catch (const std::exception& e) {
208 WLOG(
"base rate estimation failed with " << e.what() <<
", fallback to use first quote");
209 baseRate = quotes[0]->value();
210 }
211 }
212 curve_ = QuantLib::ext::shared_ptr<QuantExt::PiecewiseZeroInflationCurve<Linear>>(
214 asof, config->calendar(), config->dayCounter(), curveObsLag, config->frequency(), baseRate,
215 instruments, config->tolerance(), index, config->useLastAvailableFixingAsBaseDate()));
216
217
218 QuantLib::ext::static_pointer_cast<QuantExt::PiecewiseZeroInflationCurve<Linear>>(
curve_)->zeroRate(QL_EPSILON);
219 if (derive_yoy_from_zc) {
220
221
222 zc_to_yoy_conversion_index = QuantLib::ext::make_shared<QuantExt::YoYInflationIndexWrapper>(
223 index->clone(Handle<ZeroInflationTermStructure>(
224 QuantLib::ext::dynamic_pointer_cast<ZeroInflationTermStructure>(
curve_))),
226 }
227 }
229
230 std::vector<QuantLib::ext::shared_ptr<YoYInflationTraits::helper>> instruments;
231 QuantLib::ext::shared_ptr<ZeroInflationIndex> zcindex = conv->index();
232 QuantLib::ext::shared_ptr<YoYInflationIndex> index =
233 QuantLib::ext::make_shared<QuantExt::YoYInflationIndexWrapper>(zcindex,
interpolatedIndex_);
234 QuantLib::ext::shared_ptr<InflationCouponPricer> yoyCpnPricer =
235 QuantLib::ext::make_shared<YoYInflationCouponPricer>(nominalTs);
236 for (Size i = 0; i < strQuotes.size(); ++i) {
237 Date
maturity = swapStart + terms[i];
238 Real effectiveQuote = quotes[i]->value();
239 if (derive_yoy_from_zc) {
240
241 Schedule schedule = MakeSchedule()
242 .from(swapStart)
244 .withTenor(1 * Years)
245 .withConvention(Unadjusted)
246 .withCalendar(conv->fixCalendar())
247 .backwards();
248 YearOnYearInflationSwap tmp(YearOnYearInflationSwap::Payer, 1000000.0, schedule, 0.02,
249 conv->dayCounter(), schedule, zc_to_yoy_conversion_index,
250 conv->observationLag(), 0.0, conv->dayCounter(), conv->fixCalendar(),
251 conv->fixConvention());
252 for (auto& c : tmp.yoyLeg()) {
253 auto cpn = QuantLib::ext::dynamic_pointer_cast<YoYInflationCoupon>(c);
254 QL_REQUIRE(cpn, "yoy inflation coupon expected, could not cast");
255 cpn->setPricer(yoyCpnPricer);
256 }
257 QuantLib::ext::shared_ptr<PricingEngine> engine =
258 QuantLib::ext::make_shared<QuantLib::DiscountingSwapEngine>(nominalTs);
259 tmp.setPricingEngine(engine);
260 effectiveQuote = tmp.fairRate();
261 DLOG(
"Derive " << terms[i] <<
" yoy quote " << effectiveQuote <<
" from zc quote "
262 << quotes[i]->
value());
263 }
264
265 QuantLib::ext::shared_ptr<YoYInflationTraits::helper> instrument =
266 QuantLib::ext::make_shared<YearOnYearInflationSwapHelper>(
267 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(effectiveQuote)), conv->observationLag(),
268 maturity, conv->fixCalendar(), conv->fixConvention(), conv->dayCounter(), index, nominalTs,
269 swapStart);
270 instrument->unregisterWith(Settings::instance().evaluationDate());
271 instruments.push_back(instrument);
272 pillarDates.push_back(instrument->pillarDate());
273 }
274
275 Real baseRate =
276 config->baseRate() != Null<Real>() ? config->baseRate() : instruments.front()->quote()->value();
277 curve_ = QuantLib::ext::shared_ptr<PiecewiseYoYInflationCurve<Linear>>(
new PiecewiseYoYInflationCurve<Linear>(
278 asof, config->calendar(), config->dayCounter(), curveObsLag, config->frequency(),
interpolatedIndex_,
279 baseRate, instruments, config->tolerance()));
280
281 QuantLib::ext::static_pointer_cast<PiecewiseYoYInflationCurve<Linear>>(
curve_)->yoyRate(QL_EPSILON);
282 }
283 if (seasonality != nullptr) {
284 curve_->setSeasonality(seasonality);
285 }
286 curve_->enableExtrapolation(config->extrapolate());
287 curve_->unregisterWith(Settings::instance().evaluationDate());
288
289
290
291 if (buildCalibrationInfo) {
292
293 if (pillarDates.empty()) {
294
295
296 Date
maturity = swapStart + terms.back();
297 for (Size i = 1; i < 60 * 12; ++i) {
298 Date current = inflationPeriod(
curve_->baseDate() + i * Months,
curve_->frequency()).first;
300 pillarDates.push_back(current);
301 else
302 break;
303 }
304 }
305
307 auto yoyCurve = QuantLib::ext::dynamic_pointer_cast<YoYInflationCurve>(
curve_);
308 QL_REQUIRE(yoyCurve, "internal error: expected YoYInflationCurve (inflation curve builder)");
309 auto calInfo = QuantLib::ext::make_shared<YoYInflationCurveCalibrationInfo>();
310 calInfo->dayCounter = config->dayCounter().name();
311 calInfo->calendar = config->calendar().empty() ? "null" : config->calendar().name();
312 calInfo->baseDate =
curve_->baseDate();
313 for (Size i = 0; i < pillarDates.size(); ++i) {
314 calInfo->pillarDates.push_back(pillarDates[i]);
315 calInfo->yoyRates.push_back(yoyCurve->yoyRate(pillarDates[i], 0 * Days));
316 calInfo->times.push_back(yoyCurve->timeFromReference(pillarDates[i]));
317 }
319 }
320
322 auto zcCurve = QuantLib::ext::dynamic_pointer_cast<ZeroInflationTermStructure>(
curve_);
323 QL_REQUIRE(zcCurve, "internal error: expected ZeroInflationCurve (inflation curve builder)");
324 auto zcIndex = conv->index()->clone(Handle<ZeroInflationTermStructure>(zcCurve));
325 auto calInfo = QuantLib::ext::make_shared<ZeroInflationCurveCalibrationInfo>();
326 calInfo->dayCounter = config->dayCounter().name();
327 calInfo->calendar = config->calendar().empty() ? "null" : config->calendar().name();
328 calInfo->baseDate =
curve_->baseDate();
329 auto lim = inflationPeriod(
curve_->baseDate(),
curve_->frequency());
330 try {
331 calInfo->baseCpi = conv->index()->fixing(lim.first);
332 } catch (...) {
333 }
334 for (Size i = 0; i < pillarDates.size(); ++i) {
335 calInfo->pillarDates.push_back(pillarDates[i]);
336 calInfo->zeroRates.push_back(zcCurve->zeroRate(pillarDates[i], 0 * Days));
337 calInfo->times.push_back(zcCurve->timeFromReference(pillarDates[i]));
338 Real cpi = 0.0;
339 try {
340 cpi = zcIndex->fixing(pillarDates[i]);
341 } catch (...) {
342 }
343 calInfo->forwardCpis.push_back(cpi);
344 }
346 }
347 }
348
349 } catch (std::exception& e) {
350 QL_FAIL("inflation curve building failed: " << e.what());
351 } catch (...) {
352 QL_FAIL("inflation curve building failed: unknown error");
353 }
354}
const std::string & curveConfigID() const
string name() const
returns the unique curve name
QuantLib::ext::shared_ptr< InflationCurveCalibrationInfo > calibrationInfo_
QuantLib::ext::shared_ptr< InflationTermStructure > curve_
const InflationCurveSpec & spec() const
getters
virtual QuantLib::ext::shared_ptr< MarketDatum > get(const std::string &name, const QuantLib::Date &d) const
get quote by its unique name, throws if not existent, override in derived classes for performance
SafeStack< ValueType > value
#define DLOG(text)
Logging Macro (Level = Debug)
#define WLOG(text)
Logging Macro (Level = Warning)
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 boost::shared_ptr< QuantLib::ZeroInflationIndex > &index, const bool interpolated, const boost::shared_ptr< QuantLib::Seasonality > &seasonality)
std::pair< QuantLib::Date, QuantLib::Period > getStartAndLag(const QuantLib::Date &asof, const InflationSwapConvention &conv)
vector< string > curveConfigs