25#include <ql/cashflows/couponpricer.hpp>
26#include <ql/cashflows/yoyinflationcoupon.hpp>
27#include <ql/pricingengines/swap/discountingswapengine.hpp>
28#include <ql/termstructures/inflation/piecewiseyoyinflationcurve.hpp>
30#include <ql/time/daycounters/actual365fixed.hpp>
43 map<
string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves,
44 const bool buildCalibrationInfo) {
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()));
54 QL_REQUIRE(conv !=
nullptr,
"convention " << config->conventions() <<
" could not be found.");
56 Handle<YieldTermStructure> nominalTs;
57 auto it = yieldCurves.find(config->nominalTermStructure());
58 if (it != yieldCurves.end()) {
59 nominalTs = it->second->handle();
61 QL_FAIL(
"The nominal term structure, " << config->nominalTermStructure()
62 <<
", required in the building "
64 <<
spec.
name() <<
", was not found.");
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);
74 std::ostringstream ss1;
77 auto data1 = loader.
get(w1, asof);
79 std::ostringstream ss2;
82 auto data2 = loader.
get(w2, asof);
86 for (
const auto& md : data1) {
88 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
94 QuantLib::ext::shared_ptr<ZcInflationSwapQuote> q = QuantLib::ext::dynamic_pointer_cast<ZcInflationSwapQuote>(md);
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;
105 QuantLib::ext::shared_ptr<YoYInflationSwapQuote> q2 = QuantLib::ext::dynamic_pointer_cast<YoYInflationSwapQuote>(md);
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;
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");
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);
136 "Market quote (" << marketQuote->name() <<
") not of type seasonality.");
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()
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;
151 QL_REQUIRE(findex >= 0 && findex < 12,
"Unexpected seasonality index " << findex);
152 factors[findex] = sq->quote()->value();
154 QL_FAIL(
"Could not find quote for ID " << strFactorIDs[i] <<
" with as of date "
155 << io::iso_date(asof) <<
".");
158 QL_REQUIRE(!factors.empty(),
"no seasonality factors found");
159 seasonality = QuantLib::ext::make_shared<MultiplicativePriceSeasonality>(
160 config->seasonalityBaseDate(), config->seasonalityFrequency(), factors);
163 seasonality = QuantLib::ext::make_shared<MultiplicativePriceSeasonality>(config->seasonalityBaseDate(),
164 config->seasonalityFrequency(),
165 config->overrideSeasonalityFactors());
171 Date swapStart = p.first;
172 Period curveObsLag = p.second == Period() ? config->lag() : p.second;
175 std::vector<Date> pillarDates;
178 CPI::InterpolationType observationInterpolation =
interpolatedIndex_ ? CPI::Linear : CPI::Flat;
179 QuantLib::ext::shared_ptr<YoYInflationIndex> zc_to_yoy_conversion_index;
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) {
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);
194 instrument->unregisterWith(Settings::instance().evaluationDate());
195 instruments.push_back(instrument);
198 Real baseRate = quotes[0]->value();
199 if (config->baseRate() != Null<Real>()) {
200 baseRate = config->baseRate();
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();
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()));
218 QuantLib::ext::static_pointer_cast<QuantExt::PiecewiseZeroInflationCurve<Linear>>(
curve_)->zeroRate(QL_EPSILON);
219 if (derive_yoy_from_zc) {
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_))),
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) {
241 Schedule schedule = MakeSchedule()
244 .withTenor(1 * Years)
245 .withConvention(Unadjusted)
246 .withCalendar(conv->fixCalendar())
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);
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());
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,
270 instrument->unregisterWith(Settings::instance().evaluationDate());
271 instruments.push_back(instrument);
272 pillarDates.push_back(instrument->pillarDate());
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()));
281 QuantLib::ext::static_pointer_cast<PiecewiseYoYInflationCurve<Linear>>(
curve_)->yoyRate(QL_EPSILON);
283 if (seasonality !=
nullptr) {
284 curve_->setSeasonality(seasonality);
286 curve_->enableExtrapolation(config->extrapolate());
287 curve_->unregisterWith(Settings::instance().evaluationDate());
291 if (buildCalibrationInfo) {
293 if (pillarDates.empty()) {
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);
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]));
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());
331 calInfo->baseCpi = conv->index()->fixing(lim.first);
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]));
340 cpi = zcIndex->fixing(pillarDates[i]);
343 calInfo->forwardCpis.push_back(cpi);
349 }
catch (std::exception& e) {
350 QL_FAIL(
"inflation curve building failed: " << e.what());
352 QL_FAIL(
"inflation curve building failed: unknown error");
Container class for all Curve Configurations.
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
Inflation curve description.
Market data loader base class.
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
Classes and functions for log message handling.
#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)
Serializable Credit Default Swap.
vector< string > curveConfigs