Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
yieldcurve.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 Quaternion Risk Management Ltd
3 Copyright (C) 2021 Skandinaviska Enskilda Banken AB (publ)
4 All rights reserved.
5
6 This file is part of ORE, a free-software/open-source library
7 for transparent pricing and risk analysis - http://opensourcerisk.org
8
9 ORE is free software: you can redistribute it and/or modify it
10 under the terms of the Modified BSD License. You should have received a
11 copy of the license along with this program.
12 The license is also available online at <http://opensourcerisk.org>
13
14 This program is distributed on the basis that it will form a useful
15 contribution to risk analytics and model standardisation, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include <ql/currencies/exchangeratemanager.hpp>
21#include <ql/math/functional.hpp>
22#include <ql/math/randomnumbers/haltonrsg.hpp>
23#include <ql/pricingengines/bond/bondfunctions.hpp>
24#include <ql/pricingengines/bond/discountingbondengine.hpp>
25#include <ql/quotes/derivedquote.hpp>
26#include <ql/termstructures/yield/bondhelpers.hpp>
27#include <ql/termstructures/yield/flatforward.hpp>
28#include <ql/termstructures/yield/nonlinearfittingmethods.hpp>
29#include <ql/termstructures/yield/oisratehelper.hpp>
30#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
31#include <ql/termstructures/yield/piecewisezerospreadedtermstructure.hpp>
32#include <ql/termstructures/yield/ratehelpers.hpp>
33#include <ql/termstructures/yield/overnightindexfutureratehelper.hpp>
34#include <ql/time/daycounters/actual360.hpp>
35#include <ql/time/daycounters/actualactual.hpp>
36#include <ql/time/imm.hpp>
37
38#include <ql/indexes/ibor/all.hpp>
39#include <ql/math/interpolations/convexmonotoneinterpolation.hpp>
40#include <ql/math/interpolations/mixedinterpolation.hpp>
62
75
76using namespace QuantLib;
77using namespace QuantExt;
78using namespace std;
79
80namespace {
81/* Helper function to return the key required to look up the map in the YieldCurve ctor */
82string yieldCurveKey(const Currency& curveCcy, const string& curveID, const Date&) {
83 ore::data::YieldCurveSpec tempSpec(curveCcy.code(), curveID);
84 return tempSpec.name();
85}
86} // namespace
87
88namespace ore {
89namespace data {
90
91template <template <class> class CurveType>
92QuantLib::ext::shared_ptr<YieldTermStructure> buildYieldCurve(const vector<Date>& dates, const vector<QuantLib::Real>& rates,
93 const DayCounter& dayCounter,
94 YieldCurve::InterpolationMethod interpolationMethod, Size n) {
95
96 QuantLib::ext::shared_ptr<YieldTermStructure> yieldts;
97 switch (interpolationMethod) {
99 yieldts.reset(new CurveType<QuantLib::Linear>(dates, rates, dayCounter, QuantLib::Linear()));
100 break;
102 yieldts.reset(new CurveType<QuantLib::LogLinear>(dates, rates, dayCounter, QuantLib::LogLinear()));
103 break;
105 yieldts.reset(new CurveType<QuantLib::Cubic>(dates, rates, dayCounter,
106 QuantLib::Cubic(CubicInterpolation::Kruger, true)));
107 break;
109 yieldts.reset(new CurveType<QuantLib::Cubic>(dates, rates, dayCounter,
110 QuantLib::Cubic(CubicInterpolation::Kruger, true,
111 CubicInterpolation::SecondDerivative, 0.0,
112 CubicInterpolation::FirstDerivative)));
113 break;
115 yieldts.reset(
116 new CurveType<QuantLib::ConvexMonotone>(dates, rates, dayCounter, Calendar(), {}, {}, QuantLib::ConvexMonotone()));
117 break;
119 yieldts.reset(new CurveType<QuantExt::Quadratic>(dates, rates, dayCounter, QuantExt::Quadratic(1, 0, 1, 0, 1)));
120 break;
122 yieldts.reset(
123 new CurveType<QuantExt::LogQuadratic>(dates, rates, dayCounter, QuantExt::LogQuadratic(1, 0, -1, 0, 1)));
124 break;
126 yieldts.reset(new CurveType<QuantLib::Cubic>(dates, rates, dayCounter, Cubic(CubicInterpolation::Parabolic)));
127 break;
129 yieldts.reset(new CurveType<QuantLib::Cubic>(dates, rates, dayCounter,
130 Cubic(CubicInterpolation::Spline, false,
131 CubicInterpolation::SecondDerivative, 0.0,
132 CubicInterpolation::SecondDerivative, 0.0)));
133 break;
135 yieldts.reset(
136 new CurveType<DefaultLogMixedLinearCubic>(dates, rates, dayCounter, DefaultLogMixedLinearCubic(n)));
137 break;
139 yieldts.reset(
140 new CurveType<MonotonicLogMixedLinearCubic>(dates, rates, dayCounter, MonotonicLogMixedLinearCubic(n)));
141 break;
143 yieldts.reset(
144 new CurveType<KrugerLogMixedLinearCubic>(dates, rates, dayCounter, KrugerLogMixedLinearCubic(n)));
145 break;
147 yieldts.reset(new CurveType<LogMixedLinearCubic>(
148 dates, rates, dayCounter,
149 LogMixedLinearCubic(n, MixedInterpolation::ShareRanges, CubicInterpolation::Spline, false,
150 CubicInterpolation::SecondDerivative, 0.0, CubicInterpolation::SecondDerivative,
151 0.0)));
152 break;
153
154 default:
155 QL_FAIL("Interpolation method '" << interpolationMethod << "' not recognised.");
156 }
157 return yieldts;
158}
159
160QuantLib::ext::shared_ptr<YieldTermStructure> zerocurve(const vector<Date>& dates, const vector<Rate>& yields,
161 const DayCounter& dayCounter,
162 YieldCurve::InterpolationMethod interpolationMethod, Size n) {
163 return buildYieldCurve<InterpolatedZeroCurve>(dates, yields, dayCounter, interpolationMethod, n);
164}
165
166QuantLib::ext::shared_ptr<YieldTermStructure> discountcurve(const vector<Date>& dates, const vector<DiscountFactor>& dfs,
167 const DayCounter& dayCounter,
168 YieldCurve::InterpolationMethod interpolationMethod, Size n) {
169 return buildYieldCurve<InterpolatedDiscountCurve>(dates, dfs, dayCounter, interpolationMethod, n);
170}
171
172QuantLib::ext::shared_ptr<YieldTermStructure> forwardcurve(const vector<Date>& dates, const vector<Rate>& forwards,
173 const DayCounter& dayCounter,
174 YieldCurve::InterpolationMethod interpolationMethod, Size n) {
175 return buildYieldCurve<InterpolatedForwardCurve>(dates, forwards, dayCounter, interpolationMethod, n);
176}
177
178/* Helper functions
179 */
181 if (s == "Linear")
183 else if (s == "LogLinear")
185 else if (s == "NaturalCubic")
187 else if (s == "FinancialCubic")
189 else if (s == "ConvexMonotone")
191 else if (s == "ExponentialSplines")
193 else if (s == "Quadratic")
195 else if (s == "LogQuadratic")
197 else if (s == "LogNaturalCubic")
199 else if (s == "LogFinancialCubic")
201 else if (s == "LogCubicSpline")
203 else if (s == "Hermite")
205 else if (s == "CubicSpline")
207 else if (s == "DefaultLogMixedLinearCubic")
209 else if (s == "MonotonicLogMixedLinearCubic")
211 else if (s == "KrugerLogMixedLinearCubic")
213 else if (s == "LogMixedLinearCubicNaturalSpline")
215 else if (s == "NelsonSiegel")
217 else if (s == "Svensson")
219 else
220 QL_FAIL("Yield curve interpolation method " << s << " not recognized");
221};
222
224 if (s == "Zero")
226 else if (s == "Discount")
228 else if (s == "Forward")
230 else
231 QL_FAIL("Yield curve interpolation variable " << s << " not recognized");
232};
233
234std::ostream& operator<<(std::ostream& out, const YieldCurve::InterpolationMethod m) {
236 return out << "Linear";
238 return out << "LogLinear";
240 return out << "NaturalCubic";
242 return out << "FinancialCubic";
244 return out << "ConvexMonotone";
246 return out << "ExponentialSplines";
248 return out << "Quadratic";
250 return out << "LogQuadratic";
252 return out << "LogNaturalCubic";
254 return out << "LogFinancialCubic";
256 return out << "LogCubicSpline";
258 return out << "Hermite";
260 return out << "CubicSpline";
262 return out << "DefaultLogMixedLinearCubic";
264 return out << "MonotonicLogMixedLinearCubic";
266 return out << "KrugerLogMixedLinearCubic";
268 return out << "LogMixedLinearCubicNaturalSpline";
270 return out << "NelsonSiegel";
272 return out << "Svensson";
273 else
274 QL_FAIL("Yield curve interpolation method " << static_cast<int>(m) << " not recognized");
275}
276
278 const Loader& loader, const map<string, QuantLib::ext::shared_ptr<YieldCurve>>& requiredYieldCurves,
279 const map<string, QuantLib::ext::shared_ptr<DefaultCurve>>& requiredDefaultCurves,
280 const FXTriangulation& fxTriangulation,
281 const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceData,
282 const IborFallbackConfig& iborFallbackConfig, const bool preserveQuoteLinkage,
283 const bool buildCalibrationInfo, const Market* market)
284 : asofDate_(asof), curveSpec_(curveSpec), loader_(loader), requiredYieldCurves_(requiredYieldCurves),
285 requiredDefaultCurves_(requiredDefaultCurves), fxTriangulation_(fxTriangulation), referenceData_(referenceData),
286 iborFallbackConfig_(iborFallbackConfig), preserveQuoteLinkage_(preserveQuoteLinkage),
287 buildCalibrationInfo_(buildCalibrationInfo), market_(market) {
288
289 try {
290
292 QL_REQUIRE(curveConfig_, "No yield curve configuration found "
293 "for config ID "
295 currency_ = parseCurrency(curveConfig_->currency());
296
297 /* If discount curve is not the curve being built, look for it in the map that is passed in. */
298 string discountCurveID = curveConfig_->discountCurveID();
299 if (discountCurveID != curveConfig_->curveID() && !discountCurveID.empty()) {
300 discountCurveID = yieldCurveKey(currency_, discountCurveID, asofDate_);
301 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
302 it = requiredYieldCurves_.find(discountCurveID);
303 if (it != requiredYieldCurves_.end()) {
304 discountCurve_ = it->second;
305 } else {
306 QL_FAIL("The discount curve, " << discountCurveID
307 << ", required in the building "
308 "of the curve, "
309 << curveSpec_.name() << ", was not found.");
310 }
311 }
312
313 curveSegments_ = curveConfig_->curveSegments();
316 zeroDayCounter_ = parseDayCounter(curveConfig_->zeroDayCounter());
317 extrapolation_ = curveConfig_->extrapolation();
318
320 DLOG("Building DiscountCurve " << curveSpec_);
322 } else if (curveSegments_[0]->type() == YieldCurveSegment::Type::Zero) {
323 DLOG("Building ZeroCurve " << curveSpec_);
325 } else if (curveSegments_[0]->type() == YieldCurveSegment::Type::ZeroSpread) {
326 DLOG("Building ZeroSpreadedCurve " << curveSpec_);
329 DLOG("Building discount ratio yield curve " << curveSpec_);
331 } else if (curveSegments_[0]->type() == YieldCurveSegment::Type::FittedBond) {
332 DLOG("Building FittedBondCurve " << curveSpec_);
335 DLOG("Building WeightedAverageCurve " << curveSpec_);
338 DLOG("Building YieldPlusDefaultCurve " << curveSpec_);
340 } else if (curveSegments_[0]->type() == YieldCurveSegment::Type::IborFallback) {
341 DLOG("Building IborFallbackCurve " << curveSpec_);
344 DLOG("Building BondYieldShiftedCurve " << curveSpec_);
346 } else {
347 DLOG("Bootstrapping YieldCurve " << curveSpec_);
349 }
350
351 h_.linkTo(p_);
352 if (extrapolation_) {
353 h_->enableExtrapolation();
354 }
355
356 // populate shared calibration info
357
359 if (calibrationInfo_ == nullptr)
360 calibrationInfo_ = QuantLib::ext::make_shared<YieldCurveCalibrationInfo>();
361 calibrationInfo_->dayCounter = zeroDayCounter_.name();
362 calibrationInfo_->currency = currency_.code();
363 if (calibrationInfo_->pillarDates.empty()) {
365 calibrationInfo_->pillarDates.push_back(asofDate_ + p);
366 }
367 for (auto const& d : calibrationInfo_->pillarDates) {
368 calibrationInfo_->zeroRates.push_back(p_->zeroRate(d, zeroDayCounter_, Continuous));
369 calibrationInfo_->discountFactors.push_back(p_->discount(d));
370 calibrationInfo_->times.push_back(p_->timeFromReference(d));
371 }
372 }
373
374 } catch (QuantLib::Error& e) {
375 QL_FAIL("yield curve building failed for curve " << curveSpec_.curveConfigID() << " on date "
376 << io::iso_date(asof) << ": " << e.what());
377 } catch (std::exception& e) {
378 QL_FAIL(e.what());
379 } catch (...) {
380 QL_FAIL("unknown error");
381 }
382
383 // force bootstrap so that errors are thrown during the build, not later
384 h_->discount(QL_EPSILON);
385
386 DLOG("Yield curve " << curveSpec_.name() << " built");
387}
388
389QuantLib::ext::shared_ptr<YieldTermStructure>
390YieldCurve::piecewisecurve(vector<QuantLib::ext::shared_ptr<RateHelper>> instruments) {
391
392 // Ensure that the instruments are sorted. This is done in IterativeBootstrap, but we need
393 // a sorted instruments vector in the code here as well.
394 std::sort(instruments.begin(), instruments.end(), QuantLib::detail::BootstrapHelperSorter());
395
396 // Get configuration values for bootstrap
397 Real accuracy = curveConfig_->bootstrapConfig().accuracy();
398 Real globalAccuracy = curveConfig_->bootstrapConfig().globalAccuracy();
399 bool dontThrow = curveConfig_->bootstrapConfig().dontThrow();
400 Size maxAttempts = curveConfig_->bootstrapConfig().maxAttempts();
401 Real maxFactor = curveConfig_->bootstrapConfig().maxFactor();
402 Real minFactor = curveConfig_->bootstrapConfig().minFactor();
403 Size dontThrowSteps = curveConfig_->bootstrapConfig().dontThrowSteps();
404
405 QuantLib::ext::shared_ptr<YieldTermStructure> yieldts;
406 switch (interpolationVariable_) {
408 switch (interpolationMethod_) {
410 typedef PiecewiseYieldCurve<ZeroYield, Linear, QuantExt::IterativeBootstrap> my_curve;
411 yieldts = QuantLib::ext::make_shared<my_curve>(
412 asofDate_, instruments, zeroDayCounter_, Linear(),
413 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
414 minFactor, dontThrowSteps));
415 } break;
417 typedef PiecewiseYieldCurve<ZeroYield, LogLinear, QuantExt::IterativeBootstrap> my_curve;
418 yieldts = QuantLib::ext::make_shared<my_curve>(
419 asofDate_, instruments, zeroDayCounter_, LogLinear(),
420 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
421 minFactor, dontThrowSteps));
422 } break;
424 typedef PiecewiseYieldCurve<ZeroYield, Cubic, QuantExt::IterativeBootstrap> my_curve;
425 yieldts = QuantLib::ext::make_shared<my_curve>(
426 asofDate_, instruments, zeroDayCounter_, Cubic(CubicInterpolation::Kruger, true),
427 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
428 minFactor, dontThrowSteps));
429 } break;
431 typedef PiecewiseYieldCurve<ZeroYield, Cubic, QuantExt::IterativeBootstrap> my_curve;
432 yieldts = QuantLib::ext::make_shared<my_curve>(
433 asofDate_, instruments, zeroDayCounter_,
434 Cubic(CubicInterpolation::Kruger, true, CubicInterpolation::SecondDerivative, 0.0,
435 CubicInterpolation::FirstDerivative),
436 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
437 minFactor, dontThrowSteps));
438 } break;
440 typedef PiecewiseYieldCurve<ZeroYield, ConvexMonotone, QuantExt::IterativeBootstrap> my_curve;
441 yieldts = QuantLib::ext::make_shared<my_curve>(
443 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
444 minFactor, dontThrowSteps));
445 } break;
447 typedef PiecewiseYieldCurve<ZeroYield, Cubic, QuantExt::IterativeBootstrap> my_curve;
448 yieldts = QuantLib::ext::make_shared<my_curve>(
449 asofDate_, instruments, zeroDayCounter_, Cubic(CubicInterpolation::Parabolic),
450 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
451 minFactor, dontThrowSteps));
452 } break;
454 typedef PiecewiseYieldCurve<ZeroYield, Cubic, QuantExt::IterativeBootstrap> my_curve;
455 yieldts = QuantLib::ext::make_shared<my_curve>(
456 asofDate_, instruments, zeroDayCounter_,
457 Cubic(CubicInterpolation::Spline, false, CubicInterpolation::SecondDerivative, 0.0,
458 CubicInterpolation::SecondDerivative, 0.0),
459 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor,
460 dontThrowSteps));
461 } break;
463 typedef PiecewiseYieldCurve<ZeroYield, QuantExt::Quadratic, QuantExt::IterativeBootstrap> my_curve;
464 yieldts =
465 QuantLib::ext::make_shared<my_curve>(
466 asofDate_, instruments, zeroDayCounter_, QuantExt::Quadratic(1, 0, 1, 0, 1),
467 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
468 minFactor, dontThrowSteps));
469 } break;
471 typedef PiecewiseYieldCurve<ZeroYield, QuantExt::LogQuadratic, QuantExt::IterativeBootstrap> my_curve;
472 yieldts = QuantLib::ext::make_shared<my_curve>(
473 asofDate_, instruments, zeroDayCounter_, QuantExt::LogQuadratic(1, 0, -1, 0, 1),
474 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
475 minFactor, dontThrowSteps));
476 } break;
478 typedef PiecewiseYieldCurve<ZeroYield, LogCubic, QuantExt::IterativeBootstrap> my_curve;
479 yieldts = QuantLib::ext::make_shared<my_curve>(
480 asofDate_, instruments, zeroDayCounter_, LogCubic(CubicInterpolation::Kruger, true),
481 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
482 minFactor, dontThrowSteps));
483 } break;
485 typedef PiecewiseYieldCurve<ZeroYield, LogCubic, QuantExt::IterativeBootstrap> my_curve;
486 yieldts = QuantLib::ext::make_shared<my_curve>(
487 asofDate_, instruments, zeroDayCounter_,
488 LogCubic(CubicInterpolation::Kruger, true, CubicInterpolation::SecondDerivative, 0.0,
489 CubicInterpolation::FirstDerivative),
490 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor,
491 dontThrowSteps));
492 } break;
494 typedef PiecewiseYieldCurve<ZeroYield, LogCubic, QuantExt::IterativeBootstrap> my_curve;
495 yieldts = QuantLib::ext::make_shared<my_curve>(
496 asofDate_, instruments, zeroDayCounter_,
497 LogCubic(CubicInterpolation::Spline, false, CubicInterpolation::SecondDerivative, 0.0,
498 CubicInterpolation::SecondDerivative, 0.0),
499 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor,
500 dontThrowSteps));
501 } break;
503 typedef PiecewiseYieldCurve<ZeroYield, DefaultLogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
504 yieldts = QuantLib::ext::make_shared<my_curve>(
506 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
507 minFactor, dontThrowSteps));
508 } break;
510 typedef PiecewiseYieldCurve<ZeroYield, MonotonicLogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
511 yieldts = QuantLib::ext::make_shared<my_curve>(
513 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
514 minFactor, dontThrowSteps));
515 } break;
517 typedef PiecewiseYieldCurve<ZeroYield, KrugerLogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
518 yieldts = QuantLib::ext::make_shared<my_curve>(
520 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
521 minFactor, dontThrowSteps));
522 } break;
524 typedef PiecewiseYieldCurve<ZeroYield, LogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
525 yieldts = QuantLib::ext::make_shared<my_curve>(
526 asofDate_, instruments, zeroDayCounter_,
527 LogMixedLinearCubic(mixedInterpolationSize_, MixedInterpolation::ShareRanges,
528 CubicInterpolation::Spline, false, CubicInterpolation::SecondDerivative, 0.0,
529 CubicInterpolation::SecondDerivative, 0.0),
530 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
531 minFactor, dontThrowSteps));
532 } break;
533 default:
534 QL_FAIL("Interpolation method '" << interpolationMethod_ << "' not recognised.");
535 }
536 break;
538 switch (interpolationMethod_) {
540 typedef PiecewiseYieldCurve<Discount, Linear, QuantExt::IterativeBootstrap> my_curve;
541 yieldts = QuantLib::ext::make_shared<my_curve>(
542 asofDate_, instruments, zeroDayCounter_, Linear(),
543 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
544 minFactor, dontThrowSteps));
545 } break;
547 typedef PiecewiseYieldCurve<Discount, LogLinear, QuantExt::IterativeBootstrap> my_curve;
548 yieldts = QuantLib::ext::make_shared<my_curve>(
549 asofDate_, instruments, zeroDayCounter_, LogLinear(),
550 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
551 minFactor, dontThrowSteps));
552 } break;
554 typedef PiecewiseYieldCurve<Discount, Cubic, QuantExt::IterativeBootstrap> my_curve;
555 yieldts = QuantLib::ext::make_shared<my_curve>(
556 asofDate_, instruments, zeroDayCounter_, Cubic(CubicInterpolation::Kruger, true),
557 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
558 minFactor, dontThrowSteps));
559 } break;
561 typedef PiecewiseYieldCurve<Discount, Cubic, QuantExt::IterativeBootstrap> my_curve;
562 yieldts = QuantLib::ext::make_shared<my_curve>(
563 asofDate_, instruments, zeroDayCounter_,
564 Cubic(CubicInterpolation::Kruger, true, CubicInterpolation::SecondDerivative, 0.0,
565 CubicInterpolation::FirstDerivative),
566 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
567 minFactor, dontThrowSteps));
568 } break;
570 typedef PiecewiseYieldCurve<Discount, ConvexMonotone, QuantExt::IterativeBootstrap> my_curve;
571 yieldts = QuantLib::ext::make_shared<my_curve>(
573 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
574 minFactor, dontThrowSteps));
575 } break;
577 typedef PiecewiseYieldCurve<Discount, Cubic, QuantExt::IterativeBootstrap> my_curve;
578 yieldts = QuantLib::ext::make_shared<my_curve>(
579 asofDate_, instruments, zeroDayCounter_, Cubic(CubicInterpolation::Parabolic),
580 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
581 minFactor, dontThrowSteps));
582 } break;
584 typedef PiecewiseYieldCurve<Discount, Cubic, QuantExt::IterativeBootstrap> my_curve;
585 yieldts = QuantLib::ext::make_shared<my_curve>(
586 asofDate_, instruments, zeroDayCounter_,
587 Cubic(CubicInterpolation::Spline, false, CubicInterpolation::SecondDerivative, 0.0,
588 CubicInterpolation::SecondDerivative, 0.0),
589 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
590 minFactor, dontThrowSteps));
591 } break;
593 typedef PiecewiseYieldCurve<Discount, QuantExt::Quadratic, QuantExt::IterativeBootstrap> my_curve;
594 yieldts = QuantLib::ext::make_shared<my_curve>(
595 asofDate_, instruments, zeroDayCounter_, QuantExt::Quadratic(1, 0, 1, 0, 1),
596 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
597 minFactor, dontThrowSteps));
598 } break;
600 typedef PiecewiseYieldCurve<Discount, QuantExt::LogQuadratic, QuantExt::IterativeBootstrap> my_curve;
601 yieldts = QuantLib::ext::make_shared<my_curve>(
602 asofDate_, instruments, zeroDayCounter_, QuantExt::LogQuadratic(1, 0, -1, 0, 1),
603 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
604 minFactor, dontThrowSteps));
605 } break;
607 typedef PiecewiseYieldCurve<Discount, LogCubic, QuantExt::IterativeBootstrap> my_curve;
608 yieldts = QuantLib::ext::make_shared<my_curve>(
609 asofDate_, instruments, zeroDayCounter_, LogCubic(CubicInterpolation::Kruger, true),
610 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
611 minFactor, dontThrowSteps));
612 } break;
614 typedef PiecewiseYieldCurve<Discount, LogCubic, QuantExt::IterativeBootstrap> my_curve;
615 yieldts = QuantLib::ext::make_shared<my_curve>(
616 asofDate_, instruments, zeroDayCounter_,
617 QuantLib::LogCubic(CubicInterpolation::Kruger, true, CubicInterpolation::SecondDerivative, 0.0,
618 CubicInterpolation::FirstDerivative),
619 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor,
620 dontThrowSteps));
621 } break;
623 typedef PiecewiseYieldCurve<Discount,LogCubic, QuantExt::IterativeBootstrap> my_curve;
624 yieldts = QuantLib::ext::make_shared<my_curve>(
625 asofDate_, instruments, zeroDayCounter_,
626 LogCubic(CubicInterpolation::Spline, false, CubicInterpolation::SecondDerivative, 0.0,
627 CubicInterpolation::SecondDerivative, 0.0),
628 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor,
629 dontThrowSteps));
630 } break;
632 typedef PiecewiseYieldCurve<Discount, DefaultLogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
633 yieldts = QuantLib::ext::make_shared<my_curve>(
635 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
636 minFactor, dontThrowSteps));
637 } break;
639 typedef PiecewiseYieldCurve<Discount, MonotonicLogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
640 yieldts = QuantLib::ext::make_shared<my_curve>(
642 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
643 minFactor, dontThrowSteps));
644 } break;
646 typedef PiecewiseYieldCurve<Discount, KrugerLogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
647 yieldts = QuantLib::ext::make_shared<my_curve>(
649 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
650 minFactor, dontThrowSteps));
651 } break;
653 typedef PiecewiseYieldCurve<Discount, LogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
654 yieldts = QuantLib::ext::make_shared<my_curve>(
655 asofDate_, instruments, zeroDayCounter_,
656 LogMixedLinearCubic(mixedInterpolationSize_, MixedInterpolation::ShareRanges,
657 CubicInterpolation::Spline, false, CubicInterpolation::SecondDerivative, 0.0,
658 CubicInterpolation::SecondDerivative, 0.0),
659 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
660 minFactor, dontThrowSteps));
661 } break;
662 default:
663 QL_FAIL("Interpolation method '" << interpolationMethod_ << "' not recognised.");
664 }
665 break;
667 switch (interpolationMethod_) {
669 typedef PiecewiseYieldCurve<ForwardRate, Linear, QuantExt::IterativeBootstrap> my_curve;
670 yieldts = QuantLib::ext::make_shared<my_curve>(
671 asofDate_, instruments, zeroDayCounter_, Linear(),
672 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
673 minFactor, dontThrowSteps));
674 } break;
676 typedef PiecewiseYieldCurve<ForwardRate, LogLinear, QuantExt::IterativeBootstrap> my_curve;
677 yieldts = QuantLib::ext::make_shared<my_curve>(
678 asofDate_, instruments, zeroDayCounter_, LogLinear(),
679 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
680 minFactor, dontThrowSteps));
681 } break;
683 typedef PiecewiseYieldCurve<ForwardRate, Cubic, QuantExt::IterativeBootstrap> my_curve;
684 yieldts = QuantLib::ext::make_shared<my_curve>(
685 asofDate_, instruments, zeroDayCounter_, Cubic(CubicInterpolation::Kruger, true),
686 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
687 minFactor, dontThrowSteps));
688 } break;
690 typedef PiecewiseYieldCurve<ForwardRate, Cubic, QuantExt::IterativeBootstrap> my_curve;
691 yieldts = QuantLib::ext::make_shared<my_curve>(
692 asofDate_, instruments, zeroDayCounter_,
693 Cubic(CubicInterpolation::Kruger, true, CubicInterpolation::SecondDerivative, 0.0,
694 CubicInterpolation::FirstDerivative),
695 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
696 minFactor, dontThrowSteps));
697 } break;
699 typedef PiecewiseYieldCurve<ForwardRate, ConvexMonotone, QuantExt::IterativeBootstrap> my_curve;
700 yieldts = QuantLib::ext::make_shared<my_curve>(
702 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
703 minFactor, dontThrowSteps));
704 } break;
706 typedef PiecewiseYieldCurve<ForwardRate, Cubic, QuantExt::IterativeBootstrap> my_curve;
707 yieldts = QuantLib::ext::make_shared<my_curve>(
708 asofDate_, instruments, zeroDayCounter_, Cubic(CubicInterpolation::Parabolic),
709 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
710 minFactor, dontThrowSteps));
711 } break;
713 typedef PiecewiseYieldCurve<ForwardRate, Cubic, QuantExt::IterativeBootstrap> my_curve;
714 yieldts = QuantLib::ext::make_shared<my_curve>(
715 asofDate_, instruments, zeroDayCounter_,
716 Cubic(CubicInterpolation::Spline, false, CubicInterpolation::SecondDerivative, 0.0,
717 CubicInterpolation::SecondDerivative, 0.0),
718 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
719 minFactor, dontThrowSteps));
720 } break;
722 typedef PiecewiseYieldCurve<ForwardRate, QuantExt::Quadratic, QuantExt::IterativeBootstrap> my_curve;
723 yieldts = QuantLib::ext::make_shared<my_curve>(
724 asofDate_, instruments, zeroDayCounter_, QuantExt::Quadratic(1, 0, 1, 0, 1),
725 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
726 minFactor, dontThrowSteps));
727 } break;
729 typedef PiecewiseYieldCurve<ForwardRate, QuantExt::LogQuadratic, QuantExt::IterativeBootstrap> my_curve;
730 yieldts = QuantLib::ext::make_shared<my_curve>(
731 asofDate_, instruments, zeroDayCounter_, QuantExt::LogQuadratic(1, 0, -1, 0, 1),
732 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
733 minFactor, dontThrowSteps));
734 } break;
736 typedef PiecewiseYieldCurve<ForwardRate, LogCubic, QuantExt::IterativeBootstrap> my_curve;
737 yieldts = QuantLib::ext::make_shared<my_curve>(
738 asofDate_, instruments, zeroDayCounter_, LogCubic(CubicInterpolation::Kruger, true),
739 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor,
740 dontThrowSteps));
741 } break;
743 typedef PiecewiseYieldCurve<ForwardRate, LogCubic, QuantExt::IterativeBootstrap> my_curve;
744 yieldts = QuantLib::ext::make_shared<my_curve>(
745 asofDate_, instruments, zeroDayCounter_,
746 LogCubic(CubicInterpolation::Kruger, true, CubicInterpolation::SecondDerivative, 0.0,
747 CubicInterpolation::FirstDerivative),
748 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor,
749 dontThrowSteps));
750 } break;
752 typedef PiecewiseYieldCurve<ForwardRate, LogCubic, QuantExt::IterativeBootstrap> my_curve;
753 yieldts = QuantLib::ext::make_shared<my_curve>(
754 asofDate_, instruments, zeroDayCounter_,
755 LogCubic(CubicInterpolation::Spline, false, CubicInterpolation::SecondDerivative, 0.0,
756 CubicInterpolation::SecondDerivative, 0.0),
757 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor,
758 dontThrowSteps));
759 } break;
761 typedef PiecewiseYieldCurve<ForwardRate, DefaultLogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
762 yieldts = QuantLib::ext::make_shared<my_curve>(
764 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
765 minFactor, dontThrowSteps));
766 } break;
768 typedef PiecewiseYieldCurve<ForwardRate, MonotonicLogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
769 yieldts = QuantLib::ext::make_shared<my_curve>(
771 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
772 minFactor, dontThrowSteps));
773 } break;
775 typedef PiecewiseYieldCurve<ForwardRate, KrugerLogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
776 yieldts = QuantLib::ext::make_shared<my_curve>(
778 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
779 minFactor, dontThrowSteps));
780 } break;
782 typedef PiecewiseYieldCurve<ForwardRate, LogMixedLinearCubic, QuantExt::IterativeBootstrap> my_curve;
783 yieldts = QuantLib::ext::make_shared<my_curve>(
784 asofDate_, instruments, zeroDayCounter_,
785 LogMixedLinearCubic(mixedInterpolationSize_, MixedInterpolation::ShareRanges,
786 CubicInterpolation::Spline, false, CubicInterpolation::SecondDerivative, 0.0,
787 CubicInterpolation::SecondDerivative, 0.0),
788 my_curve::bootstrap_type(accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor,
789 minFactor, dontThrowSteps));
790 } break;
791 default:
792 QL_FAIL("Interpolation method '" << interpolationMethod_ << "' not recognised.");
793 }
794 break;
795 default:
796 QL_FAIL("Interpolation variable not recognised.");
797 }
798
800 p_ = yieldts;
801 else {
802 // Build fixed zero/discount curve that matches the bootstrapped curve
803 // initially, but does NOT react to quote changes: This is a workaround
804 // for a QuantLib problem, where a fixed reference date piecewise
805 // yield curve reacts to evaluation date changes because the bootstrap
806 // helper recompute their start date (because they are relative date
807 // helper for deposits, fras, swaps, etc.).
808 vector<Date> dates(instruments.size() + 1, asofDate_);
809 vector<Real> zeros(instruments.size() + 1, 0.0);
810 vector<Real> discounts(instruments.size() + 1, 1.0);
811 vector<Real> forwards(instruments.size() + 1, 0.0);
812
813 if (extrapolation_) {
814 yieldts->enableExtrapolation();
815 }
816 for (Size i = 0; i < instruments.size(); i++) {
817 dates[i + 1] = instruments[i]->pillarDate();
818 zeros[i + 1] = yieldts->zeroRate(dates[i + 1], zeroDayCounter_, Continuous);
819 discounts[i + 1] = yieldts->discount(dates[i + 1]);
820 forwards[i + 1] = yieldts->forwardRate(dates[i + 1], dates[i + 1], zeroDayCounter_, Continuous);
821 }
822 zeros[0] = zeros[1];
823 forwards[0] = forwards[1];
830 else
831 QL_FAIL("Interpolation variable not recognised.");
832 }
833
834 // set calibration info
836 calibrationInfo_ = QuantLib::ext::make_shared<PiecewiseYieldCurveCalibrationInfo>();
837 for (Size i = 0; i < instruments.size(); ++i) {
838 calibrationInfo_->pillarDates.push_back(instruments[i]->pillarDate());
839 }
840 }
841
842 return p_;
843}
844
846
847 QL_REQUIRE(curveSegments_.size() <= 1, "More than one zero curve "
848 "segment not supported yet.");
849 QL_REQUIRE(curveSegments_[0]->type() == YieldCurveSegment::Type::Zero, "The curve segment is not of type Zero.");
850
851 const QuantLib::ext::shared_ptr<Conventions>& conventions = InstrumentConventions::instance().conventions();
852
853 // Fill a vector of zero quotes.
854 vector<QuantLib::ext::shared_ptr<ZeroQuote>> zeroQuotes;
855 QuantLib::ext::shared_ptr<DirectYieldCurveSegment> zeroCurveSegment =
856 QuantLib::ext::dynamic_pointer_cast<DirectYieldCurveSegment>(curveSegments_[0]);
857 auto zeroQuoteIDs = zeroCurveSegment->quotes();
858
859 for (Size i = 0; i < zeroQuoteIDs.size(); ++i) {
860 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(zeroQuoteIDs[i], asofDate_);
861 if (marketQuote) {
862 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::ZERO,
863 "Market quote not of type zero.");
864 QuantLib::ext::shared_ptr<ZeroQuote> zeroQuote = QuantLib::ext::dynamic_pointer_cast<ZeroQuote>(marketQuote);
865 zeroQuotes.push_back(zeroQuote);
866 }
867 }
868
869 // Create the (date, zero) pairs.
870 map<Date, Rate> data;
871 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(curveSegments_[0]->conventionsID());
872 QL_REQUIRE(convention, "No conventions found with ID: " << curveSegments_[0]->conventionsID());
873 QL_REQUIRE(convention->type() == Convention::Type::Zero, "Conventions ID does not give zero rate conventions.");
874 QuantLib::ext::shared_ptr<ZeroRateConvention> zeroConvention = QuantLib::ext::dynamic_pointer_cast<ZeroRateConvention>(convention);
875 DayCounter quoteDayCounter = zeroConvention->dayCounter();
876 for (Size i = 0; i < zeroQuotes.size(); ++i) {
877 QL_REQUIRE(quoteDayCounter == zeroQuotes[i]->dayCounter(),
878 "The day counter should be the same between the conventions"
879 "and the quote.");
880 if (!zeroQuotes[i]->tenorBased()) {
881 data[zeroQuotes[i]->date()] = zeroQuotes[i]->quote()->value();
882 } else {
883 QL_REQUIRE(zeroConvention->tenorBased(), "Using tenor based "
884 "zero rates without tenor based zero rate conventions.");
885 Date zeroDate = asofDate_;
886 if (zeroConvention->spotLag() > 0) {
887 zeroDate = zeroConvention->spotCalendar().advance(zeroDate, zeroConvention->spotLag() * Days);
888 }
889 zeroDate = zeroConvention->tenorCalendar().advance(zeroDate, zeroQuotes[i]->tenor(),
890 zeroConvention->rollConvention(), zeroConvention->eom());
891 data[zeroDate] = zeroQuotes[i]->quote()->value();
892 }
893 }
894
895 QL_REQUIRE(data.size() > 0, "No market data found for curve spec " << curveSpec_.name() << " with as of date "
896 << io::iso_date(asofDate_));
897
898 // \todo review - more flexible (flat vs. linear extrap)?
899 if (data.begin()->first > asofDate_) {
900 Rate rate = data.begin()->second;
901 data[asofDate_] = rate;
902 DLOG("Insert zero curve point at time zero for " << curveSpec_.name() << ": "
903 << "date " << QuantLib::io::iso_date(asofDate_) << ", "
904 << "zero " << fixed << setprecision(4) << data[asofDate_]);
905 }
906
907 QL_REQUIRE(data.size() > 1, "The single zero rate quote provided "
908 "should be associated with a date greater than as of date.");
909
910 // First build temporary curves
911 vector<Date> dates;
912 vector<Rate> zeroes, discounts;
913 dates.push_back(data.begin()->first);
914 zeroes.push_back(data.begin()->second);
915 discounts.push_back(1.0);
916
917 Compounding zeroCompounding = zeroConvention->compounding();
918 Frequency zeroCompoundingFreq = zeroConvention->compoundingFrequency();
919 map<Date, Rate>::iterator it;
920 for (it = ++data.begin(); it != data.end(); ++it) {
921 dates.push_back(it->first);
922 InterestRate tempRate(it->second, quoteDayCounter, zeroCompounding, zeroCompoundingFreq);
923 Time t = quoteDayCounter.yearFraction(asofDate_, it->first);
924 /* Convert zero rate to continuously compounded if necessary */
925 if (zeroCompounding == Continuous) {
926 zeroes.push_back(it->second);
927 } else {
928 zeroes.push_back(tempRate.equivalentRate(Continuous, NoFrequency, t));
929 }
930 discounts.push_back(tempRate.discountFactor(t));
931 DLOG("Add zero curve point for " << curveSpec_.name() << ": " << io::iso_date(dates.back()) << " " << fixed
932 << setprecision(4) << zeroes.back() << " / " << discounts.back());
933 }
934
935 QL_REQUIRE(dates.size() == zeroes.size(), "Date and zero vectors differ in size.");
936 QL_REQUIRE(dates.size() == discounts.size(), "Date and discount vectors differ in size.");
937
938 // Now build curve with requested conventions
940 QuantLib::ext::shared_ptr<YieldTermStructure> tempCurve =
941 zerocurve(dates, zeroes, quoteDayCounter, interpolationMethod_);
942 zeroes.clear();
943 for (Size i = 0; i < dates.size(); ++i) {
944 Rate zero = tempCurve->zeroRate(dates[i], zeroDayCounter_, Continuous);
945 zeroes.push_back(zero);
946 }
949 QuantLib::ext::shared_ptr<YieldTermStructure> tempCurve =
950 discountcurve(dates, discounts, quoteDayCounter, interpolationMethod_);
951 discounts.clear();
952 for (Size i = 0; i < dates.size(); ++i) {
953 DiscountFactor discount = tempCurve->discount(dates[i]);
954 discounts.push_back(discount);
955 }
957 } else {
958 QL_FAIL("Unknown yield curve interpolation variable.");
959 }
960}
961
963 QL_REQUIRE(curveSegments_.size() <= 1, "More than one zero spreaded curve "
964 "segment not supported yet.");
966 "The curve segment is not of type Zero Spread.");
967
968 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
969
970 // Fill a vector of zero spread quotes.
971 vector<QuantLib::ext::shared_ptr<ZeroQuote>> quotes;
972 QuantLib::ext::shared_ptr<ZeroSpreadedYieldCurveSegment> segment =
973 QuantLib::ext::dynamic_pointer_cast<ZeroSpreadedYieldCurveSegment>(curveSegments_[0]);
974 auto quoteIDs = segment->quotes();
975
976 Date today = Settings::instance().evaluationDate();
977 vector<Date> dates;
978 vector<Handle<Quote>> quoteHandles;
979 for (Size i = 0; i < quoteIDs.size(); ++i) {
980 if (QuantLib::ext::shared_ptr<MarketDatum> md = loader_.get(quoteIDs[i], asofDate_)) {
981 QL_REQUIRE(md->instrumentType() == MarketDatum::InstrumentType::ZERO, "Market quote not of type zero.");
982 QL_REQUIRE(md->quoteType() == MarketDatum::QuoteType::YIELD_SPREAD,
983 "Market quote not of type yield spread.");
984 QuantLib::ext::shared_ptr<ZeroQuote> zeroQuote = QuantLib::ext::dynamic_pointer_cast<ZeroQuote>(md);
985 quotes.push_back(zeroQuote);
986 dates.push_back(zeroQuote->tenorBased() ? today + zeroQuote->tenor() : zeroQuote->date());
987 quoteHandles.push_back(zeroQuote->quote());
988 }
989 }
990
991 QL_REQUIRE(!quotes.empty(),
992 "Cannot build curve with spec " << curveSpec_.name() << " because there are no spread quotes");
993
994 string referenceCurveID = segment->referenceCurveID();
995 QuantLib::ext::shared_ptr<YieldCurve> referenceCurve;
996 if (referenceCurveID != curveConfig_->curveID() && !referenceCurveID.empty()) {
997 referenceCurveID = yieldCurveKey(currency_, referenceCurveID, asofDate_);
998 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
999 it = requiredYieldCurves_.find(referenceCurveID);
1000 if (it != requiredYieldCurves_.end()) {
1001 referenceCurve = it->second;
1002 } else {
1003 QL_FAIL("The reference curve, " << referenceCurveID
1004 << ", required in the building "
1005 "of the curve, "
1006 << curveSpec_.name() << ", was not found.");
1007 }
1008 }
1009
1010 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
1011 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
1012 QL_REQUIRE(convention->type() == Convention::Type::Zero, "Conventions ID does not give zero rate conventions.");
1013 QuantLib::ext::shared_ptr<ZeroRateConvention> zeroConvention = QuantLib::ext::dynamic_pointer_cast<ZeroRateConvention>(convention);
1014 DayCounter quoteDayCounter = zeroConvention->dayCounter();
1015 Compounding comp = zeroConvention->compounding();
1016 Frequency freq = zeroConvention->compoundingFrequency();
1017
1018 p_ = QuantLib::ext::shared_ptr<YieldTermStructure>(new PiecewiseZeroSpreadedTermStructure(
1019 referenceCurve->handle(), quoteHandles, dates, comp, freq, quoteDayCounter));
1020}
1021
1023 QL_REQUIRE(curveSegments_.size() == 1,
1024 "One segment required for weighted average curve, got " << curveSegments_.size());
1026 "The curve segment is not of type Weighted Average.");
1027 auto segment = QuantLib::ext::dynamic_pointer_cast<WeightedAverageYieldCurveSegment>(curveSegments_[0]);
1028 QL_REQUIRE(segment != nullptr, "expected WeightedAverageYieldCurveSegment, this is unexpected");
1029 auto it1 = requiredYieldCurves_.find(yieldCurveKey(currency_, segment->referenceCurveID1(), asofDate_));
1030 auto it2 = requiredYieldCurves_.find(yieldCurveKey(currency_, segment->referenceCurveID2(), asofDate_));
1031 QL_REQUIRE(it1 != requiredYieldCurves_.end(), "Could not find reference curve1: " << segment->referenceCurveID1());
1032 QL_REQUIRE(it2 != requiredYieldCurves_.end(), "Could not find reference curve2: " << segment->referenceCurveID2());
1033 p_ = QuantLib::ext::make_shared<WeightedYieldTermStructure>(it1->second->handle(), it2->second->handle(),
1034 segment->weight1(), segment->weight2());
1035}
1036
1038 QL_REQUIRE(curveSegments_.size() == 1,
1039 "One segment required for yield plus default curve, got " << curveSegments_.size());
1041 "The curve segment is not of type Yield Plus Default.");
1042 auto segment = QuantLib::ext::dynamic_pointer_cast<YieldPlusDefaultYieldCurveSegment>(curveSegments_[0]);
1043 QL_REQUIRE(segment != nullptr, "expected YieldPlusDefaultCurveSegment, this is unexpected");
1044 auto it = requiredYieldCurves_.find(yieldCurveKey(currency_, segment->referenceCurveID(), asofDate_));
1045 QL_REQUIRE(it != requiredYieldCurves_.end(), "Could not find reference curve: " << segment->referenceCurveID());
1046 std::vector<Handle<DefaultProbabilityTermStructure>> defaultCurves;
1047 std::vector<Handle<Quote>> recRates;
1048 for (Size i = 0; i < segment->defaultCurveIDs().size(); ++i) {
1049 auto it = requiredDefaultCurves_.find(segment->defaultCurveIDs()[i]);
1050 QL_REQUIRE(it != requiredDefaultCurves_.end(),
1051 "Could not find default curve: " << segment->defaultCurveIDs()[i]);
1052 defaultCurves.push_back(Handle<DefaultProbabilityTermStructure>(it->second->creditCurve()->curve()));
1053 recRates.push_back(Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(it->second->recoveryRate())));
1054 }
1055 p_ = QuantLib::ext::make_shared<YieldPlusDefaultYieldTermStructure>(it->second->handle(), defaultCurves, recRates,
1056 segment->weights());
1057}
1058
1060 QL_REQUIRE(curveSegments_.size() == 1,
1061 "One segment required for ibor fallback curve, got " << curveSegments_.size());
1063 "The curve segment is not of type Ibor Fallback");
1064 auto segment = QuantLib::ext::dynamic_pointer_cast<IborFallbackCurveSegment>(curveSegments_[0]);
1065 QL_REQUIRE(segment != nullptr, "expected IborFallbackCurve, internal error");
1066 auto it = requiredYieldCurves_.find(segment->rfrCurve());
1067 QL_REQUIRE(it != requiredYieldCurves_.end(), "Could not find rfr curve: '" << segment->rfrCurve() << "')");
1068 QL_REQUIRE(
1069 (segment->rfrIndex() && segment->spread()) || iborFallbackConfig_.isIndexReplaced(segment->iborIndex()),
1070 "buildIborFallbackCurve(): ibor index '"
1071 << segment->iborIndex()
1072 << "' must be specified in ibor fallback config, if RfrIndex or Spread is not specified in curve config");
1073 std::string rfrIndexName = segment->rfrIndex() ? *segment->rfrIndex() : iborFallbackConfig_.fallbackData(segment->iborIndex()).rfrIndex;
1074 Real spread = segment->spread() ? *segment->spread() : iborFallbackConfig_.fallbackData(segment->iborIndex()).spread;
1075 // we don't support convention based indices here, this might change with ore ticket 1758
1076 Handle<YieldTermStructure> dummyCurve(QuantLib::ext::make_shared<FlatForward>(asofDate_, 0.0, zeroDayCounter_));
1077 auto originalIndex = parseIborIndex(segment->iborIndex(), dummyCurve);
1078 auto rfrIndex = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(parseIborIndex(rfrIndexName, it->second->handle()));
1079 QL_REQUIRE(rfrIndex, "buidlIborFallbackCurve(): rfr index '"
1080 << rfrIndexName << "' could not be cast to OvernightIndex, is this index name correct?");
1081 DLOG("building ibor fallback curve for '" << segment->iborIndex() << "' with rfrIndex='" << rfrIndexName
1082 << "' and spread=" << spread);
1083 if (auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(originalIndex)) {
1084 p_ = QuantLib::ext::make_shared<OvernightFallbackCurve>(on, rfrIndex, spread, Date::minDate());
1085 } else {
1086 p_ = QuantLib::ext::make_shared<IborFallbackCurve>(originalIndex, rfrIndex, spread, Date::minDate());
1087 }
1088}
1089
1091
1092 QL_REQUIRE(curveSegments_.size() <= 1, "More than one discount curve "
1093 "segment not supported yet.");
1094 QL_REQUIRE(curveSegments_[0]->type() == YieldCurveSegment::Type::Discount,
1095 "The curve segment is not of type Discount.");
1096
1097 // Create the (date, discount) pairs.
1098 map<Date, DiscountFactor> data;
1099 QuantLib::ext::shared_ptr<DirectYieldCurveSegment> discountCurveSegment =
1100 QuantLib::ext::dynamic_pointer_cast<DirectYieldCurveSegment>(curveSegments_[0]);
1101 auto discountQuoteIDs = discountCurveSegment->quotes();
1102
1103 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
1104 QuantLib::ext::shared_ptr<Convention> convention;
1105
1106 vector<string> quotes;
1107 quotes.reserve(discountQuoteIDs.size()); // Reserve space for efficiency
1108
1109 std::transform(discountQuoteIDs.begin(), discountQuoteIDs.end(), std::back_inserter(quotes),
1110 [](const std::pair<string, bool>& pair) {
1111 return pair.first; // Extract only the quote part
1112 });
1113
1114 auto wildcard = getUniqueWildcard(quotes);
1115
1116 std::set<QuantLib::ext::shared_ptr<MarketDatum>> marketData;
1117 if (wildcard) {
1118 marketData = loader_.get(*wildcard, asofDate_);
1119 } else {
1120 std::ostringstream ss;
1122 Wildcard w(ss.str());
1123 marketData = loader_.get(w, asofDate_);
1124 }
1125
1126 for (const auto& marketQuote : marketData) {
1127 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::DISCOUNT,
1128 "Market quote not of type Discount.");
1129 QuantLib::ext::shared_ptr<DiscountQuote> discountQuote = QuantLib::ext::dynamic_pointer_cast<DiscountQuote>(marketQuote);
1130
1131 // filtering
1132 if (!wildcard) {
1133 vector<string>::const_iterator it = find(quotes.begin(), quotes.end(), discountQuote->name());
1134 if (it == quotes.end())
1135 continue;
1136 }
1137
1138 if (discountQuote->date() != Date()) {
1139
1140 data[discountQuote->date()] = discountQuote->quote()->value();
1141
1142 } else if (discountQuote->tenor() != Period()) {
1143
1144 if (!convention)
1145 convention = conventions->get(discountCurveSegment->conventionsID());
1146 QuantLib::ext::shared_ptr<ZeroRateConvention> zeroConvention =
1147 QuantLib::ext::dynamic_pointer_cast<ZeroRateConvention>(convention);
1148 QL_REQUIRE(zeroConvention, "could not cast to ZeroRateConvention");
1149
1150 Calendar cal = zeroConvention->tenorCalendar();
1151 BusinessDayConvention rollConvention = zeroConvention->rollConvention();
1152 Date date = cal.adjust(cal.adjust(asofDate_, rollConvention) + discountQuote->tenor(), rollConvention);
1153 DLOG("YieldCurve::buildDiscountCurve - tenor " << discountQuote->tenor() << " to date "
1154 << io::iso_date(date));
1155 data[date] = discountQuote->quote()->value();
1156
1157 } else {
1158 QL_FAIL("YieldCurve::buildDiscountCurve - neither date nor tenor recognised");
1159 }
1160 }
1161
1162 // Some logging and checks
1163 QL_REQUIRE(data.size() > 0, "No market data found for curve spec " << curveSpec_.name() << " with as of date "
1164 << io::iso_date(asofDate_));
1165 if (!wildcard) {
1166 QL_REQUIRE(data.size() == quotes.size(), "Found " << data.size() << " quotes, but "
1167 << quotes.size()
1168 << " quotes given in config " << curveConfig_->curveID());
1169 }
1170
1171 if (data.begin()->first > asofDate_) {
1172 DLOG("Insert discount curve point at time zero for " << curveSpec_.name());
1173 data[asofDate_] = 1.0;
1174 }
1175
1176 QL_REQUIRE(data.size() > 1, "The single discount quote provided "
1177 "should be associated with a date greater than as of date.");
1178
1179 // First build a temporary discount curve
1180 vector<Date> dates;
1181 vector<DiscountFactor> discounts;
1182 map<Date, DiscountFactor>::iterator it;
1183 for (it = data.begin(); it != data.end(); ++it) {
1184 dates.push_back(it->first);
1185 discounts.push_back(it->second);
1186 DLOG("Add discount curve point for " << curveSpec_.name() << ": " << io::iso_date(dates.back()) << " "
1187 << discounts.back());
1188 }
1189
1190 QL_REQUIRE(dates.size() == discounts.size(), "Date and discount vectors differ in size.");
1191
1192 QuantLib::ext::shared_ptr<YieldTermStructure> tempDiscCurve =
1194
1195 // Now build curve with requested conventions
1197 p_ = tempDiscCurve;
1199 vector<Rate> zeroes;
1200 for (Size i = 0; i < dates.size(); ++i) {
1201 Rate zero = tempDiscCurve->zeroRate(dates[i], zeroDayCounter_, Continuous);
1202 zeroes.push_back(zero);
1203 }
1205 } else {
1206 QL_FAIL("Unknown yield curve interpolation variable.");
1207 }
1208}
1209
1211
1212 QL_REQUIRE(!curveSegments_.empty(), "no curve segments defined.");
1213
1214 /* Loop over segments and add helpers for each segment. */
1215
1216 DLOG("Building instrument sets for yield curve segments 0..." << curveSegments_.size() - 1);
1217
1218 std::vector<vector<QuantLib::ext::shared_ptr<RateHelper>>> instrumentsPerSegment(curveSegments_.size());
1219
1220 for (Size i = 0; i < curveSegments_.size(); ++i) {
1221 switch (curveSegments_[i]->type()) {
1223 addDeposits(curveSegments_[i], instrumentsPerSegment[i]);
1224 break;
1226 addFras(curveSegments_[i], instrumentsPerSegment[i]);
1227 break;
1229 addFutures(curveSegments_[i], instrumentsPerSegment[i]);
1230 break;
1232 addOISs(curveSegments_[i], instrumentsPerSegment[i]);
1233 break;
1235 addSwaps(curveSegments_[i], instrumentsPerSegment[i]);
1236 break;
1238 addAverageOISs(curveSegments_[i], instrumentsPerSegment[i]);
1239 break;
1241 addTenorBasisSwaps(curveSegments_[i], instrumentsPerSegment[i]);
1242 break;
1244 addTenorBasisTwoSwaps(curveSegments_[i], instrumentsPerSegment[i]);
1245 break;
1247 addBMABasisSwaps(curveSegments_[i], instrumentsPerSegment[i]);
1248 break;
1250 addFXForwards(curveSegments_[i], instrumentsPerSegment[i]);
1251 break;
1253 addCrossCcyBasisSwaps(curveSegments_[i], instrumentsPerSegment[i]);
1254 break;
1256 addCrossCcyFixFloatSwaps(curveSegments_[i], instrumentsPerSegment[i]);
1257 break;
1258 default:
1259 QL_FAIL("Yield curve segment type not recognized.");
1260 break;
1261 }
1262 }
1263
1264 /* If we have two instruments with identical pillar dates wthin a segment, remove the earlier one */
1265
1266 for (Size i = 0; i < curveSegments_.size(); ++i) {
1267 for (auto it = instrumentsPerSegment[i].begin(); it != instrumentsPerSegment[i].end();) {
1268 if (std::next(it, 1) != instrumentsPerSegment[i].end() &&
1269 (*it)->pillarDate() == (*std::next(it, 1))->pillarDate()) {
1270 DLOG("Removing instrument with pillar date "
1271 << (*it)->pillarDate() << " in segment #" << i
1272 << " because the next instrument in the same segment has the same pillar date");
1273 it = instrumentsPerSegment[i].erase(it);
1274 } else
1275 ++it;
1276 }
1277 }
1278
1279 /* Determine min and max pillar date in each segment */
1280
1281 std::vector<std::pair<Date, Date>> minMaxDatePerSegment(curveSegments_.size(),
1282 std::make_pair(Date::maxDate(), Date::minDate()));
1283 for (Size i = 0; i < curveSegments_.size(); ++i) {
1284 if (!instrumentsPerSegment[i].empty()) {
1285 auto [minIt, maxIt] =
1286 std::minmax_element(instrumentsPerSegment[i].begin(), instrumentsPerSegment[i].end(),
1287 [](const QuantLib::ext::shared_ptr<RateHelper>& h, const QuantLib::ext::shared_ptr<RateHelper>& j) {
1288 return h->pillarDate() < h->pillarDate();
1289 });
1290 minMaxDatePerSegment[i] = std::make_pair((*minIt)->pillarDate(), (*maxIt)->pillarDate());
1291 }
1292 }
1293
1294 /* If there are two segments with different priorities and overlapping instruments, remove instruments as
1295 * appropriate */
1296
1297 for (Size i = 0; i < curveSegments_.size(); ++i) {
1298 if (i < curveSegments_.size() - 1 && curveSegments_[i]->priority() > curveSegments_[i + 1]->priority()) {
1299 for (auto it = instrumentsPerSegment[i].begin(); it != instrumentsPerSegment[i].end();) {
1300 if ((*it)->pillarDate() > minMaxDatePerSegment[i + 1].first - curveSegments_[i]->minDistance()) {
1301 DLOG("Removing instrument in segment #"
1302 << i << " (priority " << curveSegments_[i]->priority() << ") because its pillar date "
1303 << (*it)->pillarDate() << " > " << minMaxDatePerSegment[i + 1].first
1304 << " (min pillar date in segment #" << (i + 1) << ", priority "
1305 << curveSegments_[i + 1]->priority() << ") minus " << curveSegments_[i]->minDistance()
1306 << " (min distance in segment #" << i << ")");
1307 it = instrumentsPerSegment[i].erase(it);
1308 } else
1309 ++it;
1310 }
1311 }
1312 if (i > 0 && curveSegments_[i - 1]->priority() < curveSegments_[i]->priority()) {
1313 for (auto it = instrumentsPerSegment[i].begin(); it != instrumentsPerSegment[i].end();) {
1314 if ((*it)->pillarDate() < minMaxDatePerSegment[i - 1].second + curveSegments_[i - 1]->minDistance()) {
1315 DLOG("Removing instrument in segment #"
1316 << i << " (priority " << curveSegments_[i]->priority() << ") because its pillar date "
1317 << (*it)->pillarDate() << " < " << minMaxDatePerSegment[i - 1].second
1318 << " (max pillar date in segment #" << (i - 1) << ", priority "
1319 << curveSegments_[i - 1]->priority() << ") plus " << curveSegments_[i - 1]->minDistance()
1320 << " (min distance in segment #" << (i - 1) << ")");
1321 it = instrumentsPerSegment[i].erase(it);
1322 } else
1323 ++it;
1324 }
1325 }
1326 }
1327
1328 /* Set mixed interpolation size using the specified cutoff for the number of segments */
1329
1331 for (Size i = 0; i < curveConfig_->mixedInterpolationCutoff(); ++i)
1332 mixedInterpolationSize_ += instrumentsPerSegment[i].size();
1333
1334 /* Now put all remaining instruments into a single vector. */
1335
1336 vector<QuantLib::ext::shared_ptr<RateHelper>> instruments;
1337 for (Size i = 0; i < curveSegments_.size(); ++i) {
1338 instruments.insert(instruments.end(), instrumentsPerSegment[i].begin(), instrumentsPerSegment[i].end());
1339 DLOG("Adding " << instrumentsPerSegment[i].size() << " instruments for segment #" << i);
1340 }
1341
1342 /* Build the bootstrapped curve from the instruments. */
1343
1344 DLOG("Bootstrapping with " << instruments.size() << " instruments");
1345 QL_REQUIRE(instruments.size() > 0,
1346 "Empty instrument list for date = " << io::iso_date(asofDate_) << " and curve = " << curveSpec_.name());
1347 p_ = piecewisecurve(instruments);
1348}
1349
1351 QL_REQUIRE(curveSegments_.size() == 1, "A discount ratio curve must contain exactly one segment");
1353 "The curve segment is not of type 'DiscountRatio'.");
1354
1355 QuantLib::ext::shared_ptr<DiscountRatioYieldCurveSegment> segment =
1356 QuantLib::ext::dynamic_pointer_cast<DiscountRatioYieldCurveSegment>(curveSegments_[0]);
1357
1358 // Find the underlying curves in the reference curves
1359 QuantLib::ext::shared_ptr<YieldCurve> baseCurve = getYieldCurve(segment->baseCurveCurrency(), segment->baseCurveId());
1360 QL_REQUIRE(baseCurve, "The base curve '" << segment->baseCurveId() << "' cannot be empty");
1361
1362 QuantLib::ext::shared_ptr<YieldCurve> numCurve =
1363 getYieldCurve(segment->numeratorCurveCurrency(), segment->numeratorCurveId());
1364 QL_REQUIRE(numCurve, "The numerator curve '" << segment->numeratorCurveId() << "' cannot be empty");
1365
1366 QuantLib::ext::shared_ptr<YieldCurve> denCurve =
1367 getYieldCurve(segment->denominatorCurveCurrency(), segment->denominatorCurveId());
1368 QL_REQUIRE(denCurve, "The denominator curve '" << segment->denominatorCurveId() << "' cannot be empty");
1369
1370 p_ = QuantLib::ext::make_shared<DiscountRatioModifiedCurve>(baseCurve->handle(), numCurve->handle(), denCurve->handle());
1371}
1372
1373QuantLib::ext::shared_ptr<YieldCurve> YieldCurve::getYieldCurve(const string& ccy, const string& id) const {
1374 if (id != curveConfig_->curveID() && !id.empty()) {
1375 string idLookup = yieldCurveKey(parseCurrency(ccy), id, asofDate_);
1376 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::const_iterator it = requiredYieldCurves_.find(idLookup);
1377 QL_REQUIRE(it != requiredYieldCurves_.end(), "The curve '" << idLookup
1378 << "' required in the building of the curve '"
1379 << curveSpec_.name() << "' was not found.");
1380 return it->second;
1381 } else {
1382 return nullptr;
1383 }
1384}
1385
1387 QL_REQUIRE(curveSegments_.size() == 1, "FittedBond curve must contain exactly one segment.");
1388 QL_REQUIRE(curveSegments_[0]->type() == YieldCurveSegment::Type::FittedBond,
1389 "The curve segment is not of type 'FittedBond'.");
1390
1391 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
1392
1393 QuantLib::ext::shared_ptr<FittedBondYieldCurveSegment> curveSegment =
1394 QuantLib::ext::dynamic_pointer_cast<FittedBondYieldCurveSegment>(curveSegments_[0]);
1395 QL_REQUIRE(curveSegment != nullptr, "could not cast to FittedBondYieldCurveSegment, this is unexpected");
1396
1397 // init calibration info for this curve
1398
1399 auto calInfo = QuantLib::ext::make_shared<FittedBondCurveCalibrationInfo>();
1401 calInfo->dayCounter = zeroDayCounter_.name();
1402 calInfo->currency = currency_.code();
1403 }
1404
1405 // build vector of bond helpers
1406
1407 auto quoteIDs = curveSegment->quotes();
1408 std::vector<QuantLib::ext::shared_ptr<QuantLib::Bond>> bonds;
1409 std::vector<QuantLib::ext::shared_ptr<BondHelper>> helpers;
1410 std::vector<Real> marketPrices, marketYields;
1411 std::vector<std::string> securityIDs;
1412 std::vector<Date> securityMaturityDates;
1413 Date lastMaturity = Date::minDate(), firstMaturity = Date::maxDate();
1414
1415 // not really relevant, we just need a working engine configuration so that the bond can be built
1416 // the pricing engine here is _not_ used during the curve fitting, for this a local engine is
1417 // set up within FittedBondDiscountCurve
1418 auto engineData = QuantLib::ext::make_shared<EngineData>();
1419 engineData->model("Bond") = "DiscountedCashflows";
1420 engineData->engine("Bond") = "DiscountingRiskyBondEngine";
1421 engineData->engineParameters("Bond") = {{"TimestepPeriod", "6M"}};
1422
1423 std::map<std::string, Handle<YieldTermStructure>> iborCurveMapping;
1424 for (auto const& c : curveSegment->iborIndexCurves()) {
1425 auto index = parseIborIndex(c.first);
1426 auto key = yieldCurveKey(index->currency(), c.second, asofDate_);
1427 auto y = requiredYieldCurves_.find(key);
1428 QL_REQUIRE(y != requiredYieldCurves_.end(), "required yield curve '" << key << "' for iborIndex '" << c.first
1429 << "' not provided for fitted bond curve");
1430 iborCurveMapping[c.first] = y->second->handle();
1431 }
1432
1433 auto engineFactory =
1434 QuantLib::ext::make_shared<EngineFactory>(engineData, QuantLib::ext::make_shared<FittedBondCurveHelperMarket>(iborCurveMapping),
1435 std::map<MarketContext, string>(), referenceData_, iborFallbackConfig_);
1436
1437 for (Size i = 0; i < quoteIDs.size(); ++i) {
1438 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(quoteIDs[i], asofDate_);
1439 if (marketQuote) {
1440 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::BOND &&
1441 marketQuote->quoteType() == MarketDatum::QuoteType::PRICE,
1442 "Market quote not of type Bond / Price.");
1443 QuantLib::ext::shared_ptr<BondPriceQuote> bondQuote = QuantLib::ext::dynamic_pointer_cast<BondPriceQuote>(marketQuote);
1444 QL_REQUIRE(bondQuote, "market quote has type bond quote, but can not be casted, this is unexpected.");
1445 auto m = [](Real x) { return 100.0 * x; };
1446 Handle<Quote> rescaledBondQuote(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(bondQuote->quote(), m));
1447 string securityID = bondQuote->securityID();
1448
1449 QL_REQUIRE(referenceData_ != nullptr, "reference data required to build fitted bond curve");
1450 auto res = BondFactory::instance().build(engineFactory, referenceData_, securityID);
1451 auto qlInstr = res.bond;
1452 // skip bonds with settlement date <= curve reference date or which are otherwise non-tradeable
1453 if (qlInstr->settlementDate() > asofDate_ && QuantLib::BondFunctions::isTradable(*qlInstr)) {
1454 bonds.push_back(qlInstr);
1455 helpers.push_back(QuantLib::ext::make_shared<BondHelper>(rescaledBondQuote, qlInstr));
1456 Date thisMaturity = qlInstr->maturityDate();
1457 lastMaturity = std::max(lastMaturity, thisMaturity);
1458 firstMaturity = std::min(firstMaturity, thisMaturity);
1459 Real inflationFactor = res.inflationFactor();
1460 Real marketYield = qlInstr->yield(rescaledBondQuote->value() * inflationFactor,
1461 ActualActual(ActualActual::ISDA),
1462 Continuous, NoFrequency);
1463 DLOG("added bond " << securityID << ", maturity = " << QuantLib::io::iso_date(thisMaturity)
1464 << ", clean price = " << rescaledBondQuote->value() * inflationFactor
1465 << ", yield (cont,act/act) = " << marketYield);
1466 marketPrices.push_back(bondQuote->quote()->value() * inflationFactor);
1467 securityIDs.push_back(securityID);
1468 marketYields.push_back(marketYield);
1469 securityMaturityDates.push_back(thisMaturity);
1470 } else {
1471 DLOG("skipped bond " << securityID << " with settlement date "
1472 << QuantLib::io::iso_date(qlInstr->settlementDate()) << ", isTradable = "
1473 << std::boolalpha << QuantLib::BondFunctions::isTradable(*qlInstr));
1474 }
1475 }
1476 }
1477
1478 calInfo->securities = securityIDs;
1479 calInfo->securityMaturityDates = securityMaturityDates;
1480 calInfo->marketPrices = marketPrices;
1481 calInfo->marketYields = marketYields;
1482
1483 // fit bond curve to helpers
1484
1485 QL_REQUIRE(helpers.size() >= 1, "no bonds for fitting bond curve");
1486 DLOG("Fitting bond curve with " << helpers.size() << " bonds.");
1487
1488 Real minCutoffTime = 0.0, maxCutoffTime = QL_MAX_REAL;
1489 if (curveSegment->extrapolateFlat()) {
1490 minCutoffTime = zeroDayCounter_.yearFraction(asofDate_, firstMaturity);
1491 maxCutoffTime = zeroDayCounter_.yearFraction(asofDate_, lastMaturity);
1492 DLOG("extrapolate flat outside " << minCutoffTime << "," << maxCutoffTime);
1493 }
1494
1495 QuantLib::ext::shared_ptr<FittedBondDiscountCurve::FittingMethod> method;
1496 switch (interpolationMethod_) {
1498 method = QuantLib::ext::make_shared<ExponentialSplinesFitting>(true, Array(), ext::shared_ptr<OptimizationMethod>(),
1499 Array(), minCutoffTime, maxCutoffTime);
1500 calInfo->fittingMethod = "ExponentialSplines";
1501 break;
1503 method = QuantLib::ext::make_shared<NelsonSiegelFitting>(Array(), ext::shared_ptr<OptimizationMethod>(), Array(),
1504 minCutoffTime, maxCutoffTime);
1505 calInfo->fittingMethod = "NelsonSiegel";
1506 break;
1508 method = QuantLib::ext::make_shared<SvenssonFitting>(Array(), ext::shared_ptr<OptimizationMethod>(), Array(),
1509 minCutoffTime, maxCutoffTime);
1510 calInfo->fittingMethod = "Svensson";
1511 break;
1512 default:
1513 QL_FAIL("unknown fitting method");
1514 }
1515
1516 QuantLib::ext::shared_ptr<FittedBondDiscountCurve> tmp, current;
1517 Real minError = QL_MAX_REAL;
1518 HaltonRsg halton(method->size(), 42);
1519 // TODO randomised optimisation seeds are only implemented for NelsonSiegel so far
1520 Size trials = 1;
1522 trials = curveConfig_->bootstrapConfig().maxAttempts();
1523 } else {
1524 if (curveConfig_->bootstrapConfig().maxAttempts() > 1) {
1525 WLOG("randomised optimisation seeds not implemented for given interpolation method");
1526 }
1527 }
1528 for (Size i = 0; i < trials; ++i) {
1529 Array guess;
1530 // first guess is the default guess (empty array, will be set to a zero vector in
1531 // FittedBondDiscountCurve::calculate())
1532 if (i == 0) {
1534 // first guess will be based on the last bond yield and first bond yield
1535 guess = Array(4);
1536 Integer maxMaturity = static_cast<Integer>(
1537 std::distance(securityMaturityDates.begin(),
1538 std::max_element(securityMaturityDates.begin(), securityMaturityDates.end())));
1539 Integer minMaturity = static_cast<Integer>(
1540 std::distance(securityMaturityDates.begin(),
1541 std::min_element(securityMaturityDates.begin(), securityMaturityDates.end())));
1542 guess[0] = marketYields[maxMaturity]; // long term yield
1543 guess[1] = marketYields[minMaturity] - guess[0]; // short term component
1544 guess[2] = 0.0;
1545 guess[3] = 5.0;
1546 DLOG("using smart NelsonSiegel guess for trial #" << (i + 1) << ": " << guess);
1547 }
1548 } else {
1549 auto seq = halton.nextSequence();
1550 guess = Array(seq.value.begin(), seq.value.end());
1552 guess[0] = guess[0] * 0.10 - 0.05; // long term yield
1553 guess[1] = guess[1] * 0.10 - 0.05; // short term component
1554 guess[2] = guess[2] * 0.10 - 0.05; // medium term component
1555 guess[3] = guess[3] * 5.0; // decay factor
1556 DLOG("using random NelsonSiegel guess for trial #" << (i + 1) << ": " << guess);
1557 } else {
1558 QL_FAIL("randomised optimisation seed not implemented");
1559 }
1560 }
1561 current = QuantLib::ext::make_shared<FittedBondDiscountCurve>(asofDate_, helpers, zeroDayCounter_, *method, 1.0e-10,
1562 10000, guess);
1563 Real cost = std::sqrt(current->fitResults().minimumCostValue());
1564 if (cost < minError) {
1565 minError = cost;
1566 tmp = current;
1567 }
1568 DLOG("calibration trial #" << (i + 1) << " out of " << trials << ": cost = " << cost
1569 << ", best so far = " << minError);
1570 if (cost < curveConfig_->bootstrapConfig().accuracy()) {
1571 DLOG("reached desired accuracy (" << curveConfig_->bootstrapConfig().accuracy()
1572 << ") - do not attempt more calibrations");
1573 break;
1574 }
1575 }
1576 QL_REQUIRE(tmp, "no best solution found for fitted bond curve - this is unexpected.");
1577
1578 if (Norm2(tmp->fitResults().solution()) < 1.0e-4) {
1579 WLOG("Fit solution is close to 0. The curve fitting should be reviewed.");
1580 }
1581
1582 DLOG("Fitted Bond Curve Summary:");
1583 DLOG(" solution: " << tmp->fitResults().solution());
1584 DLOG(" iterations: " << tmp->fitResults().numberOfIterations());
1585 DLOG(" cost value: " << minError);
1586
1587 std::vector<Real> modelPrices, modelYields;
1588 auto engine = QuantLib::ext::make_shared<DiscountingBondEngine>(Handle<YieldTermStructure>(tmp));
1589 for (Size i = 0; i < bonds.size(); ++i) {
1590 bonds[i]->setPricingEngine(engine);
1591 modelPrices.push_back(bonds[i]->cleanPrice() / 100.0);
1592 modelYields.push_back(bonds[i]->yield(bonds[i]->cleanPrice(), ActualActual(ActualActual::ISDA), Continuous, NoFrequency));
1593 DLOG("bond " << securityIDs[i] << ", model clean price = " << modelPrices.back()
1594 << ", yield (cont,actact) = " << modelYields.back() << ", NPV = " << bonds[i]->NPV());
1595 }
1596
1597 Real tolerance = curveConfig_->bootstrapConfig().globalAccuracy() == Null<Real>()
1598 ? curveConfig_->bootstrapConfig().accuracy()
1599 : curveConfig_->bootstrapConfig().globalAccuracy();
1600 QL_REQUIRE(curveConfig_->bootstrapConfig().dontThrow() || minError < tolerance,
1601 "Fitted Bond Curve cost value (" << minError << ") exceeds tolerance (" << tolerance << ")");
1602
1603 if (extrapolation_)
1604 tmp->enableExtrapolation();
1605
1606 p_ = tmp;
1607
1608 Array solution = tmp->fitResults().solution();
1609
1611 calInfo->modelPrices = modelPrices;
1612 calInfo->modelYields = modelYields;
1613 calInfo->tolerance = tolerance;
1614 calInfo->costValue = minError;
1615 calInfo->solution = std::vector<double>(solution.begin(), solution.end());
1616 calInfo->iterations = static_cast<int>(tmp->fitResults().numberOfIterations());
1617 calibrationInfo_ = calInfo;
1618 }
1619}
1620
1622 QL_REQUIRE(curveSegments_.size() == 1,
1623 "One segment required for bond yield shifted curve, got " << curveSegments_.size());
1625 "The curve segment is not of type Bond Yield Shifted.");
1626 auto segment = QuantLib::ext::dynamic_pointer_cast<BondYieldShiftedYieldCurveSegment>(curveSegments_[0]);
1627 QL_REQUIRE(segment != nullptr, "expected BondYieldShiftedYieldCurveSegment, this is unexpected");
1628 auto it = requiredYieldCurves_.find(yieldCurveKey(currency_, segment->referenceCurveID(), asofDate_));
1629 QL_REQUIRE(it != requiredYieldCurves_.end(), "Could not find reference curve: " << segment->referenceCurveID());
1630
1631 //prepare parameters
1632 auto quoteIDs = segment->quotes();
1633 QuantLib::ext::shared_ptr<QuantLib::Bond> bond;
1634 string securityID;
1635 Date securityMaturityDate;
1636 Rate bondYield = Null<Real>();
1637 Real thisDuration = Null<Real>();
1638
1639 auto engineData = QuantLib::ext::make_shared<EngineData>();
1640 engineData->model("Bond") = "DiscountedCashflows";
1641 engineData->engine("Bond") = "DiscountingRiskyBondEngine";
1642 engineData->engineParameters("Bond") = {{"TimestepPeriod", "3M"}};
1643
1644 // needed to link the ibors in case bond is a floater
1645 std::map<std::string, Handle<YieldTermStructure>> iborCurveMapping;
1646 for (auto const& c : segment->iborIndexCurves()) {
1647 auto index = parseIborIndex(c.first);
1648 auto key = yieldCurveKey(index->currency(), c.second, asofDate_);
1649 auto y = requiredYieldCurves_.find(key);
1650 QL_REQUIRE(y != requiredYieldCurves_.end(), "required ibor curve '" << key << "' for iborIndex '" << c.first
1651 << "' not provided");
1652 iborCurveMapping[c.first] = y->second->handle();
1653 }
1654
1655 auto engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData,
1656 QuantLib::ext::make_shared<FittedBondCurveHelperMarket>(iborCurveMapping),
1657 std::map<MarketContext, string>(),
1660
1661 QL_REQUIRE(quoteIDs.size() > 0, "at least one bond for shifting of the reference curve required.");
1662
1663 std::vector<Real> bondYields, bondDurations;
1664
1665 for (Size b = 0; b < quoteIDs.size(); ++b) {
1666
1667 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(quoteIDs[b], asofDate_);
1668
1669 QL_REQUIRE(marketQuote != nullptr,"no quotes for the bond " << quoteIDs[b].first << " found. this is unexpected");
1670
1671 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::BOND && marketQuote->quoteType() == MarketDatum::QuoteType::PRICE,
1672 "Market quote not of type Bond / Price.");
1673 QuantLib::ext::shared_ptr<BondPriceQuote> bondQuote = QuantLib::ext::dynamic_pointer_cast<BondPriceQuote>(marketQuote);
1674 QL_REQUIRE(bondQuote, "market quote has type bond quote, but can not be casted, this is unexpected.");
1675 auto m = [bondQuote](Real x) { return x * 100.0; };
1676 Handle<Quote> rescaledBondQuote(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(bondQuote->quote(), m));
1677
1678 securityID = bondQuote->securityID();
1679 QL_REQUIRE(referenceData_ != nullptr, "reference data required to build bond yield shifted curve");
1680
1681 auto res = BondFactory::instance().build(engineFactory, referenceData_, securityID);
1682 auto qlInstr = res.bond;
1683 auto inflationQuoteFactor = res.inflationFactor();
1684
1685 // skip bonds with settlement date <= curve reference date or which are otherwise non-tradeable
1686 if (qlInstr->settlementDate() > asofDate_ && QuantLib::BondFunctions::isTradable(*qlInstr)) {
1687
1688 Date thisMaturity = qlInstr->maturityDate();
1689 bondYield = qlInstr->yield(rescaledBondQuote->value() * inflationQuoteFactor,
1690 ActualActual(ActualActual::ISDA), Continuous, NoFrequency);
1691
1692 thisDuration = QuantLib::BondFunctions::duration(*qlInstr,InterestRate(bondYield,ActualActual(ActualActual::ISDA),Continuous,NoFrequency)
1693 ,Duration::Modified,asofDate_);
1694
1695 DLOG("found bond " << securityID << ", maturity = " << QuantLib::io::iso_date(thisMaturity)
1696 << ", clean price = " << rescaledBondQuote->value() * inflationQuoteFactor
1697 << ", yield (cont,act/act) = " << bondYield
1698 << ", duration = " << thisDuration);
1699
1700 bondYields.push_back(bondYield);
1701 bondDurations.push_back(thisDuration);
1702
1703 DLOG("calculated duration of the bond " << securityID << " is equal to " << thisDuration)
1704
1705 } else {
1706
1707 DLOG("Skipping bond " << securityID
1708 << ", with settlement date " << QuantLib::io::iso_date(qlInstr->settlementDate())
1709 << ", isTradable = " << std::boolalpha << QuantLib::BondFunctions::isTradable(*qlInstr));
1710 }
1711
1712 }
1713
1714 p_ = QuantLib::ext::make_shared<BondYieldShiftedCurveTermStructure>(it->second->handle(), bondYields, bondDurations);
1715
1716}
1717
1718void YieldCurve::addDeposits(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
1719 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
1720
1721 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
1722
1723 // Get the conventions associated with the segment.
1724 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
1725 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
1726 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
1727 QL_REQUIRE(convention->type() == Convention::Type::Deposit,
1728 "Conventions ID does not give deposit rate conventions.");
1729 QuantLib::ext::shared_ptr<DepositConvention> depositConvention = QuantLib::ext::dynamic_pointer_cast<DepositConvention>(convention);
1730
1731 QuantLib::ext::shared_ptr<SimpleYieldCurveSegment> depositSegment =
1732 QuantLib::ext::dynamic_pointer_cast<SimpleYieldCurveSegment>(segment);
1733 auto depositQuoteIDs = depositSegment->quotes();
1734
1735 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
1736 "Deposit segment does not support pillar choice " << segment->pillarChoice());
1737 for (Size i = 0; i < depositQuoteIDs.size(); i++) {
1738 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(depositQuoteIDs[i], asofDate_);
1739
1740 // Check that we have a valid deposit quote
1741 if (marketQuote) {
1742 QuantLib::ext::shared_ptr<MoneyMarketQuote> depositQuote;
1743 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::MM,
1744 "Market quote not of type Deposit.");
1745 depositQuote = QuantLib::ext::dynamic_pointer_cast<MoneyMarketQuote>(marketQuote);
1746
1747 // Create a deposit helper if we do.
1748 QuantLib::ext::shared_ptr<RateHelper> depositHelper;
1749 Period depositTerm = depositQuote->term();
1750 Period fwdStart = depositQuote->fwdStart();
1751 Natural fwdStartDays = static_cast<Natural>(fwdStart.length());
1752 Handle<Quote> hQuote(depositQuote->quote());
1753
1754 QL_REQUIRE(fwdStart.units() == Days, "The forward start time unit for deposits "
1755 "must be expressed in days.");
1756
1757 if (depositConvention->indexBased()) {
1758 // indexName will have the form ccy-name so examples would be:
1759 // EUR-EONIA, USD-FedFunds, EUR-EURIBOR, USD-LIBOR, etc.
1760 string indexName = depositConvention->index();
1761 QuantLib::ext::shared_ptr<IborIndex> index;
1762 if (isOvernightIndex(indexName)) {
1763 // No need for the term here
1764 index = parseIborIndex(indexName);
1765 } else {
1766 // Note that a depositTerm with units Days here could end up as a string with another unit
1767 // For example:
1768 // 7 * Days will go to ccy-tenor-1W - CNY IR index is a case
1769 // 28 * Days will go to ccy-tenor-4W - MXN TIIE is a case
1770 // parseIborIndex should be able to handle this for appropriate depositTerm values
1771 stringstream ss;
1772 ss << indexName << "-" << io::short_period(depositTerm);
1773 indexName = ss.str();
1774 index = parseIborIndex(indexName);
1775 }
1776 depositHelper = QuantLib::ext::make_shared<DepositRateHelper>(
1777 hQuote, depositTerm, fwdStartDays, index->fixingCalendar(), index->businessDayConvention(),
1778 index->endOfMonth(), index->dayCounter());
1779 } else {
1780 depositHelper = QuantLib::ext::make_shared<DepositRateHelper>(
1781 hQuote, depositTerm, fwdStartDays, depositConvention->calendar(), depositConvention->convention(),
1782 depositConvention->eom(), depositConvention->dayCounter());
1783 }
1784 instruments.push_back(depositHelper);
1785 }
1786 }
1787}
1788
1789void YieldCurve::addFutures(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
1790 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
1791
1792 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
1793
1794 // Get the conventions associated with the segment.
1795 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
1796 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
1797 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
1798 QL_REQUIRE(convention->type() == Convention::Type::Future,
1799 "Conventions ID does not give deposit rate conventions.");
1800 QuantLib::ext::shared_ptr<FutureConvention> futureConvention = QuantLib::ext::dynamic_pointer_cast<FutureConvention>(convention);
1801
1802 QuantLib::ext::shared_ptr<SimpleYieldCurveSegment> futureSegment =
1803 QuantLib::ext::dynamic_pointer_cast<SimpleYieldCurveSegment>(segment);
1804 auto futureQuoteIDs = futureSegment->quotes();
1805
1806 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
1807 "Future segment does not support pillar choice " << segment->pillarChoice());
1808 for (Size i = 0; i < futureQuoteIDs.size(); i++) {
1809 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(futureQuoteIDs[i], asofDate_);
1810
1811 // Check that we have a valid future quote
1812 if (marketQuote) {
1813
1814 if (auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(futureConvention->index())) {
1815 // Overnight Index Future
1816 QuantLib::ext::shared_ptr<OIFutureQuote> futureQuote;
1817 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::OI_FUTURE,
1818 "Market quote not of type Overnight Index Future.");
1819 futureQuote = QuantLib::ext::dynamic_pointer_cast<OIFutureQuote>(marketQuote);
1820
1821 // check that the tenor of the quote is expressed in months or years, otherwise the date calculations
1822 // below do not make sense
1823 QL_REQUIRE(futureQuote->tenor().units() == Months || futureQuote->tenor().units() == Years,
1824 "Tenor of future quote (" << futureQuote->name()
1825 << ") must be expressed in months or years");
1826
1827 // Create a Overnight index future helper
1828 Date startDate, endDate;
1829 if (futureConvention->dateGenerationRule() == FutureConvention::DateGenerationRule::IMM) {
1830 Date refEnd = Date(1, futureQuote->expiryMonth(), futureQuote->expiryYear());
1831 Date refStart = refEnd - futureQuote->tenor();
1832 startDate = IMM::nextDate(refStart, false);
1833 endDate = IMM::nextDate(refEnd, false);
1834 } else if (futureConvention->dateGenerationRule() ==
1836 endDate = Date(1, futureQuote->expiryMonth(), futureQuote->expiryYear()) + 1 * Months;
1837 startDate = endDate - futureQuote->tenor();
1838 }
1839
1840 if (endDate <= asofDate_) {
1841 DLOG("Skipping the " << io::ordinal(i + 1) << " overnight index future instrument because its "
1842 << "end date, " << io::iso_date(endDate)
1843 << ", is on or before the valuation date, " << io::iso_date(asofDate_) << ".");
1844 continue;
1845 }
1846
1847 QuantLib::ext::shared_ptr<RateHelper> futureHelper = QuantLib::ext::make_shared<OvernightIndexFutureRateHelper>(
1848 futureQuote->quote(), startDate, endDate, on, Handle<Quote>(),
1849 futureConvention->overnightIndexFutureNettingType());
1850 instruments.push_back(futureHelper);
1851
1852 TLOG("adding OI future helper: price=" << futureQuote->quote()->value() << " start=" << startDate
1853 << " end=" << endDate << " nettingType="
1854 << futureConvention->overnightIndexFutureNettingType());
1855
1856 } else {
1857 // MM Future
1858 QuantLib::ext::shared_ptr<MMFutureQuote> futureQuote;
1859 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::MM_FUTURE,
1860 "Market quote not of type Money Market Future.");
1861 futureQuote = QuantLib::ext::dynamic_pointer_cast<MMFutureQuote>(marketQuote);
1862
1863 // Create a MM future helper
1864 QL_REQUIRE(
1865 futureConvention->dateGenerationRule() == FutureConvention::DateGenerationRule::IMM,
1866 "For MM Futures only 'IMM' is allowed as the date generation rule, check the future convention '"
1867 << segment->conventionsID() << "'");
1868 Date refDate(1, futureQuote->expiryMonth(), futureQuote->expiryYear());
1869 Date immDate = IMM::nextDate(refDate, false);
1870
1871 if (immDate < asofDate_) {
1872 DLOG("Skipping the " << io::ordinal(i + 1) << " money market future instrument because its "
1873 << "start date, " << io::iso_date(immDate)
1874 << ", is before the valuation date, " << io::iso_date(asofDate_) << ".");
1875 continue;
1876 }
1877
1878 QuantLib::ext::shared_ptr<RateHelper> futureHelper =
1879 QuantLib::ext::make_shared<FuturesRateHelper>(futureQuote->quote(), immDate, futureConvention->index());
1880
1881 instruments.push_back(futureHelper);
1882 }
1883 }
1884 }
1885}
1886
1887void YieldCurve::addFras(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
1888 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
1889
1890 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
1891
1892 // Get the conventions associated with the segment.
1893 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
1894 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
1895 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
1896 QL_REQUIRE(convention->type() == Convention::Type::FRA, "Conventions ID does not give FRA conventions.");
1897 QuantLib::ext::shared_ptr<FraConvention> fraConvention = QuantLib::ext::dynamic_pointer_cast<FraConvention>(convention);
1898
1899 QuantLib::ext::shared_ptr<SimpleYieldCurveSegment> fraSegment =
1900 QuantLib::ext::dynamic_pointer_cast<SimpleYieldCurveSegment>(segment);
1901 auto fraQuoteIDs = fraSegment->quotes();
1902
1903 for (Size i = 0; i < fraQuoteIDs.size(); i++) {
1904 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(fraQuoteIDs[i], asofDate_);
1905
1906 // Check that we have a valid FRA quote
1907 if (marketQuote) {
1908 QL_REQUIRE((marketQuote->instrumentType() == MarketDatum::InstrumentType::FRA) ||
1909 (marketQuote->instrumentType() == MarketDatum::InstrumentType::IMM_FRA),
1910 "Market quote not of type FRA.");
1911
1912 // Create a FRA helper if we do.
1913
1914 QuantLib::ext::shared_ptr<RateHelper> fraHelper;
1915
1916 if (marketQuote->instrumentType() == MarketDatum::InstrumentType::IMM_FRA) {
1917 QuantLib::ext::shared_ptr<ImmFraQuote> immFraQuote;
1918 immFraQuote = QuantLib::ext::dynamic_pointer_cast<ImmFraQuote>(marketQuote);
1919 Size imm1 = immFraQuote->imm1();
1920 Size imm2 = immFraQuote->imm2();
1921 fraHelper = QuantLib::ext::make_shared<ImmFraRateHelper>(immFraQuote->quote(), imm1, imm2,
1922 fraConvention->index(), fraSegment->pillarChoice());
1923 } else if (marketQuote->instrumentType() == MarketDatum::InstrumentType::FRA) {
1924 QuantLib::ext::shared_ptr<FRAQuote> fraQuote;
1925 fraQuote = QuantLib::ext::dynamic_pointer_cast<FRAQuote>(marketQuote);
1926 Period periodToStart = fraQuote->fwdStart();
1927 fraHelper = QuantLib::ext::make_shared<FraRateHelper>(fraQuote->quote(), periodToStart, fraConvention->index(),
1928 fraSegment->pillarChoice());
1929 } else {
1930 QL_FAIL("Market quote not of type FRA.");
1931 }
1932
1933 instruments.push_back(fraHelper);
1934 }
1935 }
1936}
1937
1938void YieldCurve::addOISs(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
1939 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
1940
1941 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
1942
1943 // Get the conventions associated with the segment.
1944 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
1945 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
1946 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
1947 QL_REQUIRE(convention->type() == Convention::Type::OIS, "Conventions ID does not give OIS conventions.");
1948 QuantLib::ext::shared_ptr<OisConvention> oisConvention = QuantLib::ext::dynamic_pointer_cast<OisConvention>(convention);
1949
1950 QuantLib::ext::shared_ptr<SimpleYieldCurveSegment> oisSegment =
1951 QuantLib::ext::dynamic_pointer_cast<SimpleYieldCurveSegment>(segment);
1952
1953 // If projection curve ID is not the this curve.
1954 string projectionCurveID = oisSegment->projectionCurveID();
1955 QuantLib::ext::shared_ptr<OvernightIndex> onIndex = oisConvention->index();
1956 if (projectionCurveID != curveConfig_->curveID() && !projectionCurveID.empty()) {
1957 projectionCurveID = yieldCurveKey(currency_, projectionCurveID, asofDate_);
1958 QuantLib::ext::shared_ptr<YieldCurve> projectionCurve;
1959 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
1960 it = requiredYieldCurves_.find(projectionCurveID);
1961 if (it != requiredYieldCurves_.end()) {
1962 projectionCurve = it->second;
1963 } else {
1964 QL_FAIL("The projection curve, " << projectionCurveID
1965 << ", required in the building "
1966 "of the curve, "
1967 << curveSpec_.name() << ", was not found.");
1968 }
1969 onIndex = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(onIndex->clone(projectionCurve->handle()));
1970 }
1971
1972 // BRL CDI overnight needs a specialised rate helper so we use this below to switch
1973 QuantLib::ext::shared_ptr<BRLCdi> brlCdiIndex = QuantLib::ext::dynamic_pointer_cast<BRLCdi>(onIndex);
1974
1975 auto oisQuoteIDs = oisSegment->quotes();
1976 for (Size i = 0; i < oisQuoteIDs.size(); i++) {
1977 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(oisQuoteIDs[i], asofDate_);
1978
1979 // Check that we have a valid OIS quote
1980 if (marketQuote) {
1981 QuantLib::ext::shared_ptr<SwapQuote> oisQuote;
1982 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::IR_SWAP,
1983 "Market quote (" << marketQuote->name() << ") not of type swap.");
1984 oisQuote = QuantLib::ext::dynamic_pointer_cast<SwapQuote>(marketQuote);
1985
1986 // Create a swap helper if we do.
1987 Period oisTenor = oisQuote->term();
1988 QuantLib::ext::shared_ptr<RateHelper> oisHelper;
1989 if (brlCdiIndex) {
1990 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
1991 "OIS segment for BRL-CDI does not support pillar choice " << segment->pillarChoice());
1992 oisHelper = QuantLib::ext::make_shared<BRLCdiRateHelper>(
1993 oisTenor, oisQuote->quote(), brlCdiIndex,
1994 discountCurve_ ? discountCurve_->handle() : Handle<YieldTermStructure>(), true);
1995 } else {
1996
1997 if (oisQuote->startDate() == Null<Date>() || oisQuote->maturityDate() == Null<Date>())
1998 oisHelper = QuantLib::ext::make_shared<QuantExt::OISRateHelper>(
1999 oisConvention->spotLag(), oisTenor, oisQuote->quote(), onIndex, oisConvention->fixedDayCounter(),
2000 oisConvention->fixedCalendar(), oisConvention->paymentLag(), oisConvention->eom(),
2001 oisConvention->fixedFrequency(), oisConvention->fixedConvention(),
2002 oisConvention->fixedPaymentConvention(), oisConvention->rule(),
2003 discountCurve_ ? discountCurve_->handle() : Handle<YieldTermStructure>(), true,
2004 oisSegment->pillarChoice());
2005 else {
2006 oisHelper = QuantLib::ext::make_shared<QuantExt::DatedOISRateHelper>(oisQuote->startDate(),
2007 oisQuote->maturityDate(), oisQuote->quote(), onIndex, oisConvention->fixedDayCounter(),
2008 oisConvention->fixedCalendar(), oisConvention->paymentLag(),
2009 oisConvention->fixedFrequency(), oisConvention->fixedConvention(),
2010 oisConvention->fixedPaymentConvention(), oisConvention->rule(),
2011 discountCurve_ ? discountCurve_->handle() : Handle<YieldTermStructure>(), true,
2012 oisSegment->pillarChoice());
2013 }
2014 }
2015 instruments.push_back(oisHelper);
2016 }
2017 }
2018}
2019
2020void YieldCurve::addSwaps(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
2021 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
2022
2023 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
2024
2025 // Get the conventions associated with the segment.
2026 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
2027 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
2028 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
2029 QL_REQUIRE(convention->type() == Convention::Type::Swap, "Conventions ID does not give swap conventions.");
2030 QuantLib::ext::shared_ptr<IRSwapConvention> swapConvention = QuantLib::ext::dynamic_pointer_cast<IRSwapConvention>(convention);
2031
2032 QuantLib::ext::shared_ptr<SimpleYieldCurveSegment> swapSegment =
2033 QuantLib::ext::dynamic_pointer_cast<SimpleYieldCurveSegment>(segment);
2034 if (swapSegment->projectionCurveID() != curveConfig_->curveID() && !swapSegment->projectionCurveID().empty()) {
2035 QL_FAIL("Solving for discount curve given the projection"
2036 " curve is not implemented yet");
2037 }
2038 auto swapQuoteIDs = swapSegment->quotes();
2039
2040 for (Size i = 0; i < swapQuoteIDs.size(); i++) {
2041 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(swapQuoteIDs[i], asofDate_);
2042
2043 // Check that we have a valid swap quote
2044 if (marketQuote) {
2045 QuantLib::ext::shared_ptr<SwapQuote> swapQuote;
2046 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::IR_SWAP,
2047 "Market quote not of type swap.");
2048 swapQuote = QuantLib::ext::dynamic_pointer_cast<SwapQuote>(marketQuote);
2049 QL_REQUIRE(swapQuote->startDate() == Null<Date>(),
2050 "swap quote with fixed start date is not supported for ibor / subperiods swap instruments");
2051 // Create a swap helper if we do.
2052 Period swapTenor = swapQuote->term();
2053 QuantLib::ext::shared_ptr<RateHelper> swapHelper;
2054 if (swapConvention->hasSubPeriod()) {
2055 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
2056 "Subperiod Swap segment does not support pillar choice " << segment->pillarChoice());
2057 swapHelper = QuantLib::ext::make_shared<SubPeriodsSwapHelper>(
2058 swapQuote->quote(), swapTenor, Period(swapConvention->fixedFrequency()),
2059 swapConvention->fixedCalendar(), swapConvention->fixedDayCounter(),
2060 swapConvention->fixedConvention(), Period(swapConvention->floatFrequency()),
2061 swapConvention->index(), swapConvention->index()->dayCounter(),
2062 discountCurve_ ? discountCurve_->handle() : Handle<YieldTermStructure>(),
2063 swapConvention->subPeriodsCouponType());
2064 } else {
2065 swapHelper = QuantLib::ext::make_shared<SwapRateHelper>(
2066 swapQuote->quote(), swapTenor, swapConvention->fixedCalendar(), swapConvention->fixedFrequency(),
2067 swapConvention->fixedConvention(), swapConvention->fixedDayCounter(), swapConvention->index(),
2068 Handle<Quote>(), 0 * Days, discountCurve_ ? discountCurve_->handle() : Handle<YieldTermStructure>(),
2069 Null<Natural>(), swapSegment->pillarChoice());
2070 }
2071
2072 instruments.push_back(swapHelper);
2073 }
2074 }
2075}
2076
2077void YieldCurve::addAverageOISs(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
2078 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
2079
2080 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
2081
2082 // Get the conventions associated with the segment.
2083 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
2084 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
2085 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
2086 QL_REQUIRE(convention->type() == Convention::Type::AverageOIS,
2087 "Conventions ID does not give average OIS conventions.");
2088 QuantLib::ext::shared_ptr<AverageOisConvention> averageOisConvention =
2089 QuantLib::ext::dynamic_pointer_cast<AverageOisConvention>(convention);
2090
2091 QuantLib::ext::shared_ptr<AverageOISYieldCurveSegment> averageOisSegment =
2092 QuantLib::ext::dynamic_pointer_cast<AverageOISYieldCurveSegment>(segment);
2093
2094 // If projection curve ID is not the this curve.
2095 string projectionCurveID = averageOisSegment->projectionCurveID();
2096 QuantLib::ext::shared_ptr<OvernightIndex> onIndex = averageOisConvention->index();
2097 if (projectionCurveID != curveConfig_->curveID() && !projectionCurveID.empty()) {
2098 projectionCurveID = yieldCurveKey(currency_, projectionCurveID, asofDate_);
2099 QuantLib::ext::shared_ptr<YieldCurve> projectionCurve;
2100 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
2101 it = requiredYieldCurves_.find(projectionCurveID);
2102 if (it != requiredYieldCurves_.end()) {
2103 projectionCurve = it->second;
2104 } else {
2105 QL_FAIL("The projection curve, " << projectionCurveID
2106 << ", required in the building "
2107 "of the curve, "
2108 << curveSpec_.name() << ", was not found.");
2109 }
2110 onIndex = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(onIndex->clone(projectionCurve->handle()));
2111 }
2112
2113 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
2114 "Average OIS segment does not support pillar choice " << segment->pillarChoice());
2115 auto averageOisQuoteIDs = averageOisSegment->quotes();
2116 for (Size i = 0; i < averageOisQuoteIDs.size(); i += 2) {
2117 // we are assuming i = spread, i+1 = rate
2118 QL_REQUIRE(i % 2 == 0, "i is not even");
2119 /* An average OIS quote is a composite of a swap quote and a basis
2120 spread quote. Check first that we have these. */
2121 // Firstly, the rate quote.
2122 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(averageOisQuoteIDs[i], asofDate_);
2123 QuantLib::ext::shared_ptr<SwapQuote> swapQuote;
2124 if (marketQuote) {
2125 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::IR_SWAP,
2126 "Market quote not of type swap.");
2127 swapQuote = QuantLib::ext::dynamic_pointer_cast<SwapQuote>(marketQuote);
2128 QL_REQUIRE(swapQuote->startDate() == Null<Date>(),
2129 "swap quote with fixed start date is not supported for average ois instrument");
2130
2131 // Secondly, the basis spread quote.
2132 marketQuote = loader_.get(averageOisQuoteIDs[i + 1], asofDate_);
2133 QuantLib::ext::shared_ptr<BasisSwapQuote> basisQuote;
2134 if (marketQuote) {
2135 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::BASIS_SWAP,
2136 "Market quote not of type basis swap.");
2137 basisQuote = QuantLib::ext::dynamic_pointer_cast<BasisSwapQuote>(marketQuote);
2138
2139 // Create an average OIS helper if we do.
2140 Period AverageOisTenor = swapQuote->term();
2141 QL_REQUIRE(AverageOisTenor == basisQuote->maturity(),
2142 "The swap "
2143 "and basis swap components of the Average OIS must "
2144 "have the same maturity.");
2145 QuantLib::ext::shared_ptr<RateHelper> averageOisHelper(new QuantExt::AverageOISRateHelper(
2146 swapQuote->quote(), averageOisConvention->spotLag() * Days, AverageOisTenor,
2147 averageOisConvention->fixedTenor(), averageOisConvention->fixedDayCounter(),
2148 averageOisConvention->fixedCalendar(), averageOisConvention->fixedConvention(),
2149 averageOisConvention->fixedPaymentConvention(), onIndex, averageOisConvention->onTenor(),
2150 basisQuote->quote(), averageOisConvention->rateCutoff(),
2151 discountCurve_ ? discountCurve_->handle() : Handle<YieldTermStructure>(), true));
2152
2153 instruments.push_back(averageOisHelper);
2154 }
2155 }
2156 }
2157}
2158
2159void YieldCurve::addTenorBasisSwaps(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
2160 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
2161
2162 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
2163
2164 // Get the conventions associated with the segment.
2165 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
2166 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
2167 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
2168 QL_REQUIRE(convention->type() == Convention::Type::TenorBasisSwap,
2169 "Conventions ID does not give tenor basis swap conventions.");
2170 QuantLib::ext::shared_ptr<TenorBasisSwapConvention> basisSwapConvention =
2171 QuantLib::ext::dynamic_pointer_cast<TenorBasisSwapConvention>(convention);
2172
2173 QuantLib::ext::shared_ptr<TenorBasisYieldCurveSegment> basisSwapSegment =
2174 QuantLib::ext::dynamic_pointer_cast<TenorBasisYieldCurveSegment>(segment);
2175
2176 // If short index projection curve ID is not this curve.
2177 string receiveCurveID = basisSwapSegment->receiveProjectionCurveID();
2178 QuantLib::ext::shared_ptr<IborIndex> receiveIndex = basisSwapConvention->receiveIndex();
2179 if (receiveCurveID != curveConfig_->curveID() && !receiveCurveID.empty()) {
2180 receiveCurveID = yieldCurveKey(currency_, receiveCurveID, asofDate_);
2181 QuantLib::ext::shared_ptr<YieldCurve> shortCurve;
2182 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
2183 it = requiredYieldCurves_.find(receiveCurveID);
2184 if (it != requiredYieldCurves_.end()) {
2185 shortCurve = it->second;
2186 } else {
2187 QL_FAIL("The short side projection curve, " << receiveCurveID
2188 << ", required in the building "
2189 "of the curve, "
2190 << curveSpec_.name() << ", was not found.");
2191 }
2192 receiveIndex = receiveIndex->clone(shortCurve->handle());
2193 }
2194
2195 // If long index projection curve ID is not this curve.
2196 string payCurveID = basisSwapSegment->payProjectionCurveID();
2197 QuantLib::ext::shared_ptr<IborIndex> payIndex = basisSwapConvention->payIndex();
2198 if (payCurveID != curveConfig_->curveID() && !payCurveID.empty()) {
2199 payCurveID = yieldCurveKey(currency_, payCurveID, asofDate_);
2200 QuantLib::ext::shared_ptr<YieldCurve> longCurve;
2201 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
2202 it = requiredYieldCurves_.find(payCurveID);
2203 if (it != requiredYieldCurves_.end()) {
2204 longCurve = it->second;
2205 } else {
2206 QL_FAIL("The long side projection curve, " << payCurveID
2207 << ", required in the building "
2208 "of the curve, "
2209 << curveSpec_.name() << ", was not found.");
2210 }
2211 payIndex = payIndex->clone(longCurve->handle());
2212 }
2213
2214 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
2215 "Tenor basis swap segment does not support pillar choice " << segment->pillarChoice());
2216 auto basisSwapQuoteIDs = basisSwapSegment->quotes();
2217 for (Size i = 0; i < basisSwapQuoteIDs.size(); i++) {
2218 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(basisSwapQuoteIDs[i], asofDate_);
2219
2220 // Check that we have a valid basis swap quote
2221 if (marketQuote) {
2222 QuantLib::ext::shared_ptr<BasisSwapQuote> basisSwapQuote;
2223 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::BASIS_SWAP,
2224 "Market quote not of type basis swap.");
2225 basisSwapQuote = QuantLib::ext::dynamic_pointer_cast<BasisSwapQuote>(marketQuote);
2226
2227 // Create a tenor basis swap helper if we do.
2228 Period basisSwapTenor = basisSwapQuote->maturity();
2229 QuantLib::ext::shared_ptr<RateHelper> basisSwapHelper;
2230 bool telescopicValueDates = true;
2231 basisSwapHelper.reset(
2232 new TenorBasisSwapHelper(basisSwapQuote->quote(), basisSwapTenor, payIndex, receiveIndex,
2233 discountCurve_ ? discountCurve_->handle() : Handle<YieldTermStructure>(),
2234 basisSwapConvention->spreadOnRec(), basisSwapConvention->includeSpread(),
2235 basisSwapConvention->payFrequency(), basisSwapConvention->receiveFrequency(),
2236 telescopicValueDates, basisSwapConvention->subPeriodsCouponType()));
2237
2238 instruments.push_back(basisSwapHelper);
2239 }
2240 }
2241}
2242
2243void YieldCurve::addTenorBasisTwoSwaps(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
2244 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
2245
2246 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
2247
2248 // Get the conventions associated with the segment.
2249 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
2250 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
2251 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
2252 QL_REQUIRE(convention->type() == Convention::Type::TenorBasisTwoSwap,
2253 "Conventions ID does not give tenor basis two swap conventions.");
2254 QuantLib::ext::shared_ptr<TenorBasisTwoSwapConvention> basisSwapConvention =
2255 QuantLib::ext::dynamic_pointer_cast<TenorBasisTwoSwapConvention>(convention);
2256
2257 QuantLib::ext::shared_ptr<TenorBasisYieldCurveSegment> basisSwapSegment =
2258 QuantLib::ext::dynamic_pointer_cast<TenorBasisYieldCurveSegment>(segment);
2259
2260 // If short index projection curve ID is not this curve.
2261 string shortCurveID = basisSwapSegment->receiveProjectionCurveID();
2262 QuantLib::ext::shared_ptr<IborIndex> shortIndex = basisSwapConvention->shortIndex();
2263 if (shortCurveID != curveConfig_->curveID() && !shortCurveID.empty()) {
2264 shortCurveID = yieldCurveKey(currency_, shortCurveID, asofDate_);
2265 QuantLib::ext::shared_ptr<YieldCurve> shortCurve;
2266 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
2267 it = requiredYieldCurves_.find(shortCurveID);
2268 if (it != requiredYieldCurves_.end()) {
2269 shortCurve = it->second;
2270 } else {
2271 QL_FAIL("The short side projection curve, " << shortCurveID
2272 << ", required in the building "
2273 "of the curve, "
2274 << curveSpec_.name() << ", was not found.");
2275 }
2276 shortIndex = shortIndex->clone(shortCurve->handle());
2277 }
2278
2279 // If long index projection curve ID is not this curve.
2280 string longCurveID = basisSwapSegment->payProjectionCurveID();
2281 QuantLib::ext::shared_ptr<IborIndex> longIndex = basisSwapConvention->longIndex();
2282 if (longCurveID != curveConfig_->curveID() && !longCurveID.empty()) {
2283 longCurveID = yieldCurveKey(currency_, longCurveID, asofDate_);
2284 QuantLib::ext::shared_ptr<YieldCurve> longCurve;
2285 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
2286 it = requiredYieldCurves_.find(longCurveID);
2287 if (it != requiredYieldCurves_.end()) {
2288 longCurve = it->second;
2289 } else {
2290 QL_FAIL("The projection curve, " << longCurveID
2291 << ", required in the building "
2292 "of the curve, "
2293 << curveSpec_.name() << ", was not found.");
2294 }
2295 longIndex = longIndex->clone(longCurve->handle());
2296 }
2297
2298 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
2299 "Tenor basis two swap segment does not support pillar choice " << segment->pillarChoice());
2300 auto basisSwapQuoteIDs = basisSwapSegment->quotes();
2301 for (Size i = 0; i < basisSwapQuoteIDs.size(); i++) {
2302 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(basisSwapQuoteIDs[i], asofDate_);
2303
2304 // Check that we have a valid basis swap quote
2305 QuantLib::ext::shared_ptr<BasisSwapQuote> basisSwapQuote;
2306 if (marketQuote) {
2307 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::BASIS_SWAP,
2308 "Market quote not of type basis swap.");
2309 basisSwapQuote = QuantLib::ext::dynamic_pointer_cast<BasisSwapQuote>(marketQuote);
2310
2311 // Create a tenor basis swap helper if we do.
2312 Period basisSwapTenor = basisSwapQuote->maturity();
2313 QuantLib::ext::shared_ptr<RateHelper> basisSwapHelper(new BasisTwoSwapHelper(
2314 basisSwapQuote->quote(), basisSwapTenor, basisSwapConvention->calendar(),
2315 basisSwapConvention->longFixedFrequency(), basisSwapConvention->longFixedConvention(),
2316 basisSwapConvention->longFixedDayCounter(), longIndex, basisSwapConvention->shortFixedFrequency(),
2317 basisSwapConvention->shortFixedConvention(), basisSwapConvention->shortFixedDayCounter(), shortIndex,
2318 basisSwapConvention->longMinusShort(),
2319 discountCurve_ ? discountCurve_->handle() : Handle<YieldTermStructure>()));
2320
2321 instruments.push_back(basisSwapHelper);
2322 }
2323 }
2324}
2325
2326void YieldCurve::addBMABasisSwaps(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
2327 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
2328
2329 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
2330
2331 // Get the conventions associated with the segment.
2332 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
2333 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
2334 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
2335 QL_REQUIRE(convention->type() == Convention::Type::BMABasisSwap,
2336 "Conventions ID does not give bma basis swap conventions.");
2337 QuantLib::ext::shared_ptr<BMABasisSwapConvention> bmaBasisSwapConvention =
2338 QuantLib::ext::dynamic_pointer_cast<BMABasisSwapConvention>(convention);
2339
2340 QuantLib::ext::shared_ptr<SimpleYieldCurveSegment> bmaBasisSwapSegment =
2341 QuantLib::ext::dynamic_pointer_cast<SimpleYieldCurveSegment>(segment);
2342 QL_REQUIRE(bmaBasisSwapSegment, "BMA basis swap segment of " + curveSpec_.ccy() + "/" + curveSpec_.curveConfigID() +
2343 " did not successfully cast to a BMA basis swap yield curve segment!");
2344
2345 // TODO: should be checking here whether or not the bma index is forwarding on this curve. either way, we make sure!
2346 QuantLib::ext::shared_ptr<BMAIndexWrapper> bmaIndex = bmaBasisSwapConvention->bmaIndex();
2347 bmaIndex = dynamic_pointer_cast<BMAIndexWrapper>(bmaIndex->clone(handle()));
2348
2349 // If libor index projection curve ID is not this curve.
2350 string liborCurveID = bmaBasisSwapSegment->projectionCurveID();
2351 QuantLib::ext::shared_ptr<IborIndex> liborIndex = bmaBasisSwapConvention->liborIndex();
2352 liborCurveID = yieldCurveKey(currency_, liborCurveID, asofDate_);
2353 QuantLib::ext::shared_ptr<YieldCurve> liborCurve;
2354 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
2355 it = requiredYieldCurves_.find(liborCurveID);
2356 if (it != requiredYieldCurves_.end()) {
2357 liborCurve = it->second;
2358 } else {
2359 QL_FAIL("The libor side projection curve, " << liborCurveID
2360 << ", required in the building "
2361 "of the curve, "
2362 << curveSpec_.name() << ", was not found.");
2363 }
2364 liborIndex = liborIndex->clone(liborCurve->handle());
2365
2366 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
2367 "BMA swap segment does not support pillar choice " << segment->pillarChoice());
2368 auto bmaBasisSwapQuoteIDs = bmaBasisSwapSegment->quotes();
2369 for (Size i = 0; i < bmaBasisSwapQuoteIDs.size(); i++) {
2370 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(bmaBasisSwapQuoteIDs[i], asofDate_);
2371
2372 // Check that we have a valid bma basis swap quote
2373 if (marketQuote) {
2374 QuantLib::ext::shared_ptr<BMASwapQuote> bmaBasisSwapQuote;
2375 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::BMA_SWAP,
2376 "Market quote not of type bma swap.");
2377 QL_REQUIRE(marketQuote->quoteType() == MarketDatum::QuoteType::RATIO, "Market quote not of type ratio.");
2378 bmaBasisSwapQuote = QuantLib::ext::dynamic_pointer_cast<BMASwapQuote>(marketQuote);
2379
2380 // Create bma basis swap helper if we do.
2381 QuantLib::ext::shared_ptr<RateHelper> bmaSwapHelper;
2382 bmaSwapHelper.reset(new BMASwapRateHelper(bmaBasisSwapQuote->quote(), bmaBasisSwapQuote->maturity(),
2383 bmaIndex->fixingDays(), bmaIndex->fixingCalendar(),
2384 bmaBasisSwapQuote->term(), bmaIndex->businessDayConvention(),
2385 bmaIndex->dayCounter(), bmaIndex->bma(), liborIndex));
2386 instruments.push_back(bmaSwapHelper);
2387 }
2388 }
2389}
2390
2391void YieldCurve::addFXForwards(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
2392 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
2393
2394 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
2395
2396 // Get the conventions associated with the segment.
2397 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
2398 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
2399 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
2400 QL_REQUIRE(convention->type() == Convention::Type::FX, "Conventions ID does not give FX forward conventions.");
2401
2402 QuantLib::ext::shared_ptr<FXConvention> fxConvention = QuantLib::ext::dynamic_pointer_cast<FXConvention>(convention);
2403
2404 QuantLib::ext::shared_ptr<CrossCcyYieldCurveSegment> fxForwardSegment =
2405 QuantLib::ext::dynamic_pointer_cast<CrossCcyYieldCurveSegment>(segment);
2406
2407 /* Need to retrieve the discount curve in the other currency. These are called the known discount
2408 curve and known discount currency respectively. */
2409 Currency knownCurrency;
2410 if (currency_ == fxConvention->sourceCurrency()) {
2411 knownCurrency = fxConvention->targetCurrency();
2412 } else if (currency_ == fxConvention->targetCurrency()) {
2413 knownCurrency = fxConvention->sourceCurrency();
2414 } else {
2415 QL_FAIL("One of the currencies in the FX forward bootstrap "
2416 "instruments needs to match the yield curve currency.");
2417 }
2418
2419 string knownDiscountID = fxForwardSegment->foreignDiscountCurveID();
2420 Handle<YieldTermStructure> knownDiscountCurve;
2421
2422 if (!knownDiscountID.empty()) {
2423 knownDiscountID = yieldCurveKey(knownCurrency, knownDiscountID, asofDate_);
2424 auto it = requiredYieldCurves_.find(knownDiscountID);
2425 if (it != requiredYieldCurves_.end()) {
2426 knownDiscountCurve = it->second->handle();
2427 } else {
2428 QL_FAIL("The foreign discount curve, " << knownDiscountID
2429 << ", required in the building "
2430 "of the curve, "
2431 << curveSpec_.name() << ", was not found.");
2432 }
2433 } else {
2434 // fall back on the foreign discount curve if no index given
2435 // look up the inccy discount curve - falls back to defualt if no inccy
2436 DLOG("YieldCurve::addFXForwards No discount curve provided for building curve " <<
2437 curveSpec_.name() << ", looking up the inccy curve in the market.")
2438 knownDiscountCurve = market_->discountCurve(knownCurrency.code(), Market::inCcyConfiguration);
2439 }
2440
2441
2442 /* Need to retrieve the market FX spot rate */
2443 string spotRateID = fxForwardSegment->spotRateID();
2444 QuantLib::ext::shared_ptr<FXSpotQuote> fxSpotQuote = getFxSpotQuote(spotRateID);
2445
2446 /* Create an FX spot quote from the retrieved FX spot rate */
2447 Currency fxSpotSourceCcy = parseCurrency(fxSpotQuote->unitCcy());
2448 Currency fxSpotTargetCcy = parseCurrency(fxSpotQuote->ccy());
2449
2450 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
2451 "FX Forward segment does not support pillar choice " << segment->pillarChoice());
2452 DLOG("YieldCurve::addFXForwards(), create FX forward quotes and helpers");
2453 auto fxForwardQuoteIDs = fxForwardSegment->quotes();
2454 for (Size i = 0; i < fxForwardQuoteIDs.size(); i++) {
2455 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(fxForwardQuoteIDs[i], asofDate_);
2456
2457 // Check that we have a valid FX forward quote
2458 if (marketQuote) {
2459 QuantLib::ext::shared_ptr<FXForwardQuote> fxForwardQuote;
2460 Handle<Quote> spotFx;
2461
2462 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::FX_FWD,
2463 "Market quote not of type FX forward.");
2464 fxForwardQuote = QuantLib::ext::dynamic_pointer_cast<FXForwardQuote>(marketQuote);
2465
2466 QL_REQUIRE(fxSpotQuote->unitCcy() == fxForwardQuote->unitCcy() &&
2467 fxSpotQuote->ccy() == fxForwardQuote->ccy(),
2468 "Currency mismatch between spot \"" << spotRateID << "\" and fwd \""
2469 << fxForwardQuoteIDs[i].first << "\"");
2470
2471 // QL expects the FX Fwd quote to be per spot, not points. If the quote is an outright, handle conversion to points convention here.
2472
2473 Handle<Quote> qlFXForwardQuote;
2474 if (fxForwardQuote->quoteType() == MarketDatum::QuoteType::PRICE) {
2475 auto m = [f = fxSpotQuote->quote()->value()](Real x) { return x - f; };
2476 qlFXForwardQuote =
2477 Handle<Quote>(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(fxForwardQuote->quote(), m));
2478 } else {
2479 auto m = [p = fxConvention->pointsFactor()](Real x) { return x / p; };
2480 qlFXForwardQuote =
2481 Handle<Quote>(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(fxForwardQuote->quote(), m));
2482 }
2483
2484 Natural spotDays = fxConvention->spotDays();
2485 if (matchFxFwdStringTerm(fxForwardQuote->term(), FXForwardQuote::FxFwdString::ON)) {
2486 // Overnight rate is the spread over todays fx, for settlement on t+1. We need 'todays' rate in order
2487 // to use this to determine yield curve value at t+1.
2488 // If spotDays is 0 it is spread over Spot.
2489 // If spotDays is 1 we can subtract the ON spread from spot to get todays fx.
2490 // If spotDays is 2 we also need Tomorrow next rate to get todays fx.
2491 // If spotDays is greater than 2 we can't use this.
2492 Real tnSpread = Null<Real>();
2493 Real totalSpread = 0.0;
2494 switch (spotDays) {
2495 case 0:
2496 spotFx = fxSpotQuote->quote();
2497 break;
2498 case 1: {
2499 // TODO: this isn't registeredWith the ON basis quote
2500 auto m = [f = qlFXForwardQuote->value()](Real x) { return x - f; };
2501 spotFx = Handle<Quote>(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(fxSpotQuote->quote(), m));
2502 break;
2503 }
2504 case 2: {
2505 // find the TN quote
2506 for (auto q : loader_.loadQuotes(asofDate_)) {
2507 if (q->instrumentType() == MarketDatum::InstrumentType::FX_FWD) {
2508 auto fxq = QuantLib::ext::dynamic_pointer_cast<FXForwardQuote>(q);
2509 if (fxq && fxSpotQuote->unitCcy() == fxq->unitCcy() && fxSpotQuote->ccy() == fxq->ccy() &&
2511 tnSpread = fxq->quote()->value() / fxConvention->pointsFactor();
2512 break;
2513 }
2514 }
2515 }
2516 if (tnSpread == Null<Real>()) {
2517 WLOG("YieldCurve::AddFxForwards cannot use ON rate, when SpotDays are 2 we also require the TN "
2518 "rate");
2519 continue;
2520 }
2521 totalSpread = tnSpread + qlFXForwardQuote->value();
2522 // TODO: this isn't registeredWith the ON or TN basis quote
2523 auto m2 = [totalSpread](Real x) { return x - totalSpread; };
2524 spotFx = Handle<Quote>(QuantLib::ext::make_shared<DerivedQuote<decltype(m2)>>(fxSpotQuote->quote(), m2));
2525 break;
2526 }
2527 default:
2528 WLOG("YieldCurve::AddFxForwards cannot use ON rate, when SpotDays are " << spotDays <<
2529 ", only valid for SpotDays of 0, 1 or 2.");
2530 continue;
2531 }
2532 } else if (matchFxFwdStringTerm(fxForwardQuote->term(), FXForwardQuote::FxFwdString::TN)) {
2533 // TODO: this isn't registeredWith the TN basis quote
2534 auto m = [f = qlFXForwardQuote->value()](Real x) { return x - f; };
2535 spotFx = Handle<Quote>(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(fxSpotQuote->quote(), m));
2536 } else {
2537 spotFx = fxSpotQuote->quote();
2538 }
2539
2540 Period fxForwardTenor = fxFwdQuoteTenor(fxForwardQuote->term());
2541 Period fxStartTenor = fxFwdQuoteStartTenor(fxForwardQuote->term(), fxConvention);
2542 bool isFxBaseCurrencyCollateralCurrency = knownCurrency == fxSpotSourceCcy;
2543
2544 QuantLib::ext::shared_ptr<RateHelper> fxForwardHelper(new FxSwapRateHelper(
2545 qlFXForwardQuote, spotFx, fxForwardTenor, fxStartTenor.length(), fxConvention->advanceCalendar(),
2546 fxConvention->convention(), fxConvention->endOfMonth(), isFxBaseCurrencyCollateralCurrency,
2547 knownDiscountCurve));
2548
2549 instruments.push_back(fxForwardHelper);
2550 }
2551 }
2552
2553 DLOG("YieldCurve::addFXForwards() done");
2554}
2555
2556void YieldCurve::addCrossCcyBasisSwaps(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
2557 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
2558
2559 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
2560
2561 // Get the conventions associated with the segment.
2562 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
2563 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
2564 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
2565 QL_REQUIRE(convention->type() == Convention::Type::CrossCcyBasis, "Conventions ID does not give cross currency "
2566 "basis swap conventions.");
2567 QuantLib::ext::shared_ptr<CrossCcyBasisSwapConvention> basisSwapConvention =
2568 QuantLib::ext::dynamic_pointer_cast<CrossCcyBasisSwapConvention>(convention);
2569
2570 /* Is this yield curve on the flat side or spread side */
2571 bool onFlatSide = (currency_ == basisSwapConvention->flatIndex()->currency());
2572
2573 QuantLib::ext::shared_ptr<CrossCcyYieldCurveSegment> basisSwapSegment =
2574 QuantLib::ext::dynamic_pointer_cast<CrossCcyYieldCurveSegment>(segment);
2575
2576 /* Need to retrieve the market FX spot rate */
2577 string spotRateID = basisSwapSegment->spotRateID();
2578 QuantLib::ext::shared_ptr<FXSpotQuote> fxSpotQuote = getFxSpotQuote(spotRateID);
2579
2580 /* Create an FX spot quote from the retrieved FX spot rate */
2581 Currency fxSpotSourceCcy = parseCurrency(fxSpotQuote->unitCcy());
2582 Currency fxSpotTargetCcy = parseCurrency(fxSpotQuote->ccy());
2583
2584 /* Need to retrieve the discount curve in the other (foreign) currency. */
2585 string foreignDiscountID = basisSwapSegment->foreignDiscountCurveID();
2586 Currency foreignCcy = fxSpotSourceCcy == currency_ ? fxSpotTargetCcy : fxSpotSourceCcy;
2587 Handle<YieldTermStructure> foreignDiscountCurve;
2588 if (!foreignDiscountID.empty()) {
2589 foreignDiscountID = yieldCurveKey(foreignCcy, foreignDiscountID, asofDate_);
2590 auto it = requiredYieldCurves_.find(foreignDiscountID);
2591 if (it != requiredYieldCurves_.end()) {
2592 foreignDiscountCurve = it->second->handle();
2593 } else {
2594 QL_FAIL("The foreign discount curve, " << foreignDiscountID
2595 << ", required in the building "
2596 "of the curve, "
2597 << curveSpec_.name() << ", was not found.");
2598 }
2599 } else {
2600 // fall back on the foreign discount curve if no index given
2601 // look up the inccy discount curve - falls back to defualt if no inccy
2602 DLOG("YieldCurve::addCrossCcyBasisSwaps No discount curve provided for building curve "
2603 << curveSpec_.name() << ", looking up the inccy curve in the market.")
2604 foreignDiscountCurve = market_->discountCurve(foreignCcy.code(), Market::inCcyConfiguration);
2605 }
2606
2607 /* Need to retrieve the foreign projection curve in the other currency. If its ID is empty,
2608 set it equal to the foreign discount curve. */
2609 string foreignProjectionCurveID = basisSwapSegment->foreignProjectionCurveID();
2610 QuantLib::ext::shared_ptr<IborIndex> foreignIndex =
2611 onFlatSide ? basisSwapConvention->spreadIndex() : basisSwapConvention->flatIndex();
2612 if (foreignProjectionCurveID.empty()) {
2613 foreignIndex = foreignIndex->clone(foreignDiscountCurve);
2614 } else {
2615 foreignProjectionCurveID = yieldCurveKey(foreignCcy, foreignProjectionCurveID, asofDate_);
2616 QuantLib::ext::shared_ptr<YieldCurve> foreignProjectionCurve;
2617 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
2618 it = requiredYieldCurves_.find(foreignProjectionCurveID);
2619 if (it != requiredYieldCurves_.end()) {
2620 foreignProjectionCurve = it->second;
2621 } else {
2622 QL_FAIL("The foreign projection curve, " << foreignProjectionCurveID
2623 << ", required in the building "
2624 "of the curve, "
2625 << curveSpec_.name() << ", was not found.");
2626 }
2627 foreignIndex = foreignIndex->clone(foreignProjectionCurve->handle());
2628 }
2629
2630 // If domestic index projection curve ID is not this curve.
2631 string domesticProjectionCurveID = basisSwapSegment->domesticProjectionCurveID();
2632 QuantLib::ext::shared_ptr<IborIndex> domesticIndex =
2633 onFlatSide ? basisSwapConvention->flatIndex() : basisSwapConvention->spreadIndex();
2634 if (domesticProjectionCurveID != curveConfig_->curveID() && !domesticProjectionCurveID.empty()) {
2635 domesticProjectionCurveID = yieldCurveKey(currency_, domesticProjectionCurveID, asofDate_);
2636 QuantLib::ext::shared_ptr<YieldCurve> domesticProjectionCurve;
2637 map<string, QuantLib::ext::shared_ptr<YieldCurve>>::iterator it;
2638 it = requiredYieldCurves_.find(domesticProjectionCurveID);
2639 if (it != requiredYieldCurves_.end()) {
2640 domesticProjectionCurve = it->second;
2641 } else {
2642 QL_FAIL("The domestic projection curve, " << domesticProjectionCurveID
2643 << ", required in the"
2644 " building of the curve, "
2645 << curveSpec_.name() << ", was not found.");
2646 }
2647 domesticIndex = domesticIndex->clone(domesticProjectionCurve->handle());
2648 }
2649
2650 /* Arrange the discount curves and indices for use in the helper */
2651 RelinkableHandle<YieldTermStructure> flatDiscountCurve;
2652 RelinkableHandle<YieldTermStructure> spreadDiscountCurve;
2653 QuantLib::ext::shared_ptr<IborIndex> flatIndex;
2654 QuantLib::ext::shared_ptr<IborIndex> spreadIndex;
2655 if (onFlatSide) {
2656 if (discountCurve_) {
2657 flatDiscountCurve.linkTo(*discountCurve_->handle());
2658 }
2659 spreadDiscountCurve.linkTo(*foreignDiscountCurve);
2660 flatIndex = domesticIndex;
2661 spreadIndex = foreignIndex;
2662 } else {
2663 flatDiscountCurve.linkTo(*foreignDiscountCurve);
2664 if (discountCurve_) {
2665 spreadDiscountCurve.linkTo(*discountCurve_->handle());
2666 }
2667 flatIndex = foreignIndex;
2668 spreadIndex = domesticIndex;
2669 }
2670
2671 Period flatTenor = basisSwapConvention->flatTenor();
2672 Period spreadTenor = basisSwapConvention->spreadTenor();
2673
2674 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
2675 "XCcy basis segment does not support pillar choice " << segment->pillarChoice());
2676 auto basisSwapQuoteIDs = basisSwapSegment->quotes();
2677 for (Size i = 0; i < basisSwapQuoteIDs.size(); i++) {
2678 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(basisSwapQuoteIDs[i], asofDate_);
2679
2680 // Check that we have a valid basis swap quote
2681 if (marketQuote) {
2682 QuantLib::ext::shared_ptr<CrossCcyBasisSwapQuote> basisSwapQuote;
2683 QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::CC_BASIS_SWAP,
2684 "Market quote not of type cross "
2685 "currency basis swap.");
2686 basisSwapQuote = QuantLib::ext::dynamic_pointer_cast<CrossCcyBasisSwapQuote>(marketQuote);
2687
2688 // Create a cross currency basis swap helper if we do.
2689 Period basisSwapTenor = basisSwapQuote->maturity();
2690 bool isResettableSwap = basisSwapConvention->isResettable();
2691 if (!isResettableSwap) {
2692 instruments.push_back(QuantLib::ext::make_shared<CrossCcyBasisSwapHelper>(
2693 basisSwapQuote->quote(), fxSpotQuote->quote(), basisSwapConvention->settlementDays(),
2694 basisSwapConvention->settlementCalendar(), basisSwapTenor, basisSwapConvention->rollConvention(),
2695 flatIndex, spreadIndex, flatDiscountCurve, spreadDiscountCurve, basisSwapConvention->eom(),
2696 flatIndex->currency().code() != fxSpotQuote->unitCcy(), flatTenor, spreadTenor, 0.0, 1.0, 1.0,
2697 Calendar(), Calendar(), std::vector<Natural>(), std::vector<Calendar>(),
2698 basisSwapConvention->paymentLag(), basisSwapConvention->flatPaymentLag(),
2699 basisSwapConvention->includeSpread(), basisSwapConvention->lookback(),
2700 basisSwapConvention->fixingDays(), basisSwapConvention->rateCutoff(),
2701 basisSwapConvention->isAveraged(), basisSwapConvention->flatIncludeSpread(),
2702 basisSwapConvention->flatLookback(), basisSwapConvention->flatFixingDays(),
2703 basisSwapConvention->flatRateCutoff(), basisSwapConvention->flatIsAveraged(), true));
2704 } else { // the quote is for a cross currency basis swap with a resetting notional
2705 bool resetsOnFlatLeg = basisSwapConvention->flatIndexIsResettable();
2706 // the convention here is to call the resetting leg the "domestic leg",
2707 // and the constant notional leg the "foreign leg"
2708 bool spreadOnForeignCcy = resetsOnFlatLeg ? true : false;
2709 QuantLib::ext::shared_ptr<IborIndex> foreignIndex = resetsOnFlatLeg ? spreadIndex : flatIndex;
2710 Handle<YieldTermStructure> foreignDiscount = resetsOnFlatLeg ? spreadDiscountCurve : flatDiscountCurve;
2711 QuantLib::ext::shared_ptr<IborIndex> domesticIndex = resetsOnFlatLeg ? flatIndex : spreadIndex;
2712 Handle<YieldTermStructure> domesticDiscount = resetsOnFlatLeg ? flatDiscountCurve : spreadDiscountCurve;
2713 Handle<Quote> finalFxSpotQuote = fxSpotQuote->quote();
2714 // we might have to flip the given fx spot quote
2715 if (foreignIndex->currency().code() != fxSpotQuote->unitCcy()) {
2716 auto m = [](Real x) { return 1.0 / x; };
2717 finalFxSpotQuote =
2718 Handle<Quote>(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(fxSpotQuote->quote(), m));
2719 }
2720 Period foreignTenor = resetsOnFlatLeg ? spreadTenor : flatTenor;
2721 Period domesticTenor = resetsOnFlatLeg ? flatTenor : spreadTenor;
2722
2723 // Use foreign and dom discount curves for projecting FX forward rates (for e.g. resetting cashflows)
2724 instruments.push_back(QuantLib::ext::make_shared<CrossCcyBasisMtMResetSwapHelper>(
2725 basisSwapQuote->quote(), finalFxSpotQuote, basisSwapConvention->settlementDays(),
2726 basisSwapConvention->settlementCalendar(), basisSwapTenor, basisSwapConvention->rollConvention(),
2727 foreignIndex, domesticIndex, foreignDiscount, domesticDiscount, Handle<YieldTermStructure>(),
2728 Handle<YieldTermStructure>(), basisSwapConvention->eom(), spreadOnForeignCcy, foreignTenor,
2729 domesticTenor, basisSwapConvention->paymentLag(), basisSwapConvention->flatPaymentLag(),
2730 basisSwapConvention->includeSpread(), basisSwapConvention->lookback(),
2731 basisSwapConvention->fixingDays(), basisSwapConvention->rateCutoff(),
2732 basisSwapConvention->isAveraged(), basisSwapConvention->flatIncludeSpread(),
2733 basisSwapConvention->flatLookback(), basisSwapConvention->flatFixingDays(),
2734 basisSwapConvention->flatRateCutoff(), basisSwapConvention->flatIsAveraged(), true));
2735 }
2736 }
2737 }
2738}
2739void YieldCurve::addCrossCcyFixFloatSwaps(const QuantLib::ext::shared_ptr<YieldCurveSegment>& segment,
2740 vector<QuantLib::ext::shared_ptr<RateHelper>>& instruments) {
2741
2742 DLOG("Adding Segment " << segment->typeID() << " with conventions \"" << segment->conventionsID() << "\"");
2743
2744 // Get the conventions associated with the segment
2745 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
2746 QuantLib::ext::shared_ptr<Convention> convention = conventions->get(segment->conventionsID());
2747 QL_REQUIRE(convention, "No conventions found with ID: " << segment->conventionsID());
2748 QL_REQUIRE(convention->type() == Convention::Type::CrossCcyFixFloat,
2749 "Conventions ID does not give cross currency fix float swap conventions.");
2750 QuantLib::ext::shared_ptr<CrossCcyFixFloatSwapConvention> swapConvention =
2751 QuantLib::ext::dynamic_pointer_cast<CrossCcyFixFloatSwapConvention>(convention);
2752
2753 QL_REQUIRE(swapConvention->fixedCurrency() == currency_,
2754 "The yield curve currency must "
2755 << "equal the cross currency fix float swap's fixed leg currency");
2756
2757 // Cast the segment
2758 QuantLib::ext::shared_ptr<CrossCcyYieldCurveSegment> swapSegment =
2759 QuantLib::ext::dynamic_pointer_cast<CrossCcyYieldCurveSegment>(segment);
2760
2761 // Retrieve the discount curve on the float leg
2762 QuantLib::ext::shared_ptr<IborIndex> floatIndex = swapConvention->index();
2763 Currency floatLegCcy = floatIndex->currency();
2764 string foreignDiscountID = swapSegment->foreignDiscountCurveID();
2765 Handle<YieldTermStructure> floatLegDisc;
2766
2767 QuantLib::ext::shared_ptr<YieldCurve> foreignDiscountCurve;
2768 if (!foreignDiscountID.empty()) {
2769 string floatLegDiscId = yieldCurveKey(floatLegCcy, foreignDiscountID, asofDate_);
2770 auto it = requiredYieldCurves_.find(floatLegDiscId);
2771 if (it != requiredYieldCurves_.end()) {
2772 floatLegDisc = it->second->handle();
2773 } else {
2774 QL_FAIL("The foreign discount curve, " << floatLegDiscId
2775 << ", required in the building "
2776 "of the curve, "
2777 << curveSpec_.name() << ", was not found.");
2778 }
2779 } else {
2780 // fall back on the foreign discount curve if no index given
2781 // look up the inccy discount curve - falls back to defualt if no inccy
2782 DLOG("YieldCurve::addCrossCcyFixFloatSwaps No discount curve provided for building curve "
2783 << curveSpec_.name() << ", looking up the inccy curve in the market.")
2784 floatLegDisc = market_->discountCurve(floatLegCcy.code(), Market::inCcyConfiguration);
2785 }
2786
2787 // Retrieve the projection curve on the float leg. If empty, use discount curve.
2788 string floatLegProjId = swapSegment->foreignProjectionCurveID();
2789 if (floatLegProjId.empty()) {
2790 floatIndex = floatIndex->clone(floatLegDisc);
2791 } else {
2792 floatLegProjId = yieldCurveKey(floatLegCcy, floatLegProjId, asofDate_);
2793 auto it = requiredYieldCurves_.find(floatLegProjId);
2794 QL_REQUIRE(it != requiredYieldCurves_.end(), "The projection curve " << floatLegProjId
2795 << " required in the building of curve "
2796 << curveSpec_.name() << " was not found.");
2797 floatIndex = floatIndex->clone(it->second->handle());
2798 }
2799
2800 // Create the FX spot quote for the helper. The quote needs to be number of units of fixed leg
2801 // currency for 1 unit of float leg currency. We convert the market quote here if needed.
2802 string fxSpotId = swapSegment->spotRateID();
2803 QuantLib::ext::shared_ptr<FXSpotQuote> fxSpotMd = getFxSpotQuote(fxSpotId);
2804 Currency mdUnitCcy = parseCurrency(fxSpotMd->unitCcy());
2805 Currency mdCcy = parseCurrency(fxSpotMd->ccy());
2806 Handle<Quote> fxSpotQuote;
2807 if (mdUnitCcy == floatLegCcy && mdCcy == currency_) {
2808 fxSpotQuote = fxSpotMd->quote();
2809 } else if (mdUnitCcy == currency_ && mdCcy == floatLegCcy) {
2810 auto m=[](Real x) { return 1.0/x;};
2811 fxSpotQuote = Handle<Quote>(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(fxSpotMd->quote(), m));
2812 } else {
2813 QL_FAIL("The FX spot market quote " << mdUnitCcy << "/" << mdCcy << " cannot be used "
2814 << "in the building of the curve " << curveSpec_.name() << ".");
2815 }
2816
2817 // Create the helpers
2818 QL_REQUIRE(segment->pillarChoice() == QuantLib::Pillar::LastRelevantDate,
2819 "XCcy fix-float basis segment does not support pillar choice " << segment->pillarChoice());
2820 auto quoteIds = swapSegment->quotes();
2821 for (Size i = 0; i < quoteIds.size(); i++) {
2822
2823 // Throws if quote not found
2824 QuantLib::ext::shared_ptr<MarketDatum> marketQuote = loader_.get(quoteIds[i], asofDate_);
2825
2826 // Check that we have a valid basis swap quote
2827 if (marketQuote) {
2828 QuantLib::ext::shared_ptr<CrossCcyFixFloatSwapQuote> swapQuote =
2829 QuantLib::ext::dynamic_pointer_cast<CrossCcyFixFloatSwapQuote>(marketQuote);
2830 QL_REQUIRE(swapQuote, "Market quote should be of type 'CrossCcyFixFloatSwapQuote'");
2831 bool isResettableSwap = swapConvention->isResettable();
2832 QuantLib::ext::shared_ptr<RateHelper> helper;
2833 if (!isResettableSwap) {
2834 // Create the helper
2835 helper = QuantLib::ext::make_shared<CrossCcyFixFloatSwapHelper>(
2836 swapQuote->quote(), fxSpotQuote, swapConvention->settlementDays(), swapConvention->settlementCalendar(),
2837 swapConvention->settlementConvention(), swapQuote->maturity(), currency_,
2838 swapConvention->fixedFrequency(), swapConvention->fixedConvention(), swapConvention->fixedDayCounter(),
2839 floatIndex, floatLegDisc, Handle<Quote>(), swapConvention->eom());
2840 } else {
2841 bool resetsOnFloatLeg = swapConvention->floatIndexIsResettable();
2842 helper = QuantLib::ext::make_shared<CrossCcyFixFloatMtMResetSwapHelper>(
2843 swapQuote->quote(), fxSpotQuote, swapConvention->settlementDays(), swapConvention->settlementCalendar(),
2844 swapConvention->settlementConvention(), swapQuote->maturity(), currency_, swapConvention->fixedFrequency(),
2845 swapConvention->fixedConvention(), swapConvention->fixedDayCounter(), floatIndex,
2846 floatLegDisc, Handle<Quote>(), swapConvention->eom(), resetsOnFloatLeg);
2847 }
2848 instruments.push_back(helper);
2849 }
2850 }
2851}
2852
2853QuantLib::ext::shared_ptr<FXSpotQuote> YieldCurve::getFxSpotQuote(string spotId) {
2854 // check the spot id, if like FX/RATE/CCY/CCY we go straight to the loader first
2855 std::vector<string> tokens;
2856 split(tokens, spotId, boost::is_any_of("/"));
2857
2858 QuantLib::ext::shared_ptr<FXSpotQuote> fxSpotQuote;
2859 if (tokens.size() == 4 && tokens[0] == "FX" && tokens[1] == "RATE") {
2860 if (loader_.has(spotId, asofDate_)) {
2861 QuantLib::ext::shared_ptr<MarketDatum> fxSpotMarketQuote = loader_.get(spotId, asofDate_);
2862
2863 if (fxSpotMarketQuote) {
2864 QL_REQUIRE(fxSpotMarketQuote->instrumentType() == MarketDatum::InstrumentType::FX_SPOT,
2865 "Market quote not of type FX spot.");
2866 fxSpotQuote = QuantLib::ext::dynamic_pointer_cast<FXSpotQuote>(fxSpotMarketQuote);
2867 return fxSpotQuote;
2868 }
2869 }
2870 }
2871
2872 // Try to use triangulation otherwise
2873 string unitCcy;
2874 string ccy;
2875 Handle<Quote> spot;
2876 if (tokens.size() > 1 && tokens[0] == "FX") {
2877 if (tokens.size() == 3) {
2878 unitCcy = tokens[1];
2879 ccy = tokens[2];
2880 } else if (tokens.size() == 4 && tokens[1] == "RATE") {
2881 unitCcy = tokens[2];
2882 ccy = tokens[3];
2883 } else {
2884 QL_FAIL("Invalid FX spot ID " << spotId);
2885 }
2886 } else if (tokens.size() == 1 && spotId.size() == 6) {
2887 unitCcy = spotId.substr(0, 3);
2888 ccy = spotId.substr(3);
2889 } else {
2890 QL_FAIL("Could not find quote for ID " << spotId << " with as of date " << io::iso_date(asofDate_) << ".");
2891 }
2892 spot = fxTriangulation_.getQuote(unitCcy + ccy);
2893 fxSpotQuote =
2894 QuantLib::ext::make_shared<FXSpotQuote>(spot->value(), asofDate_, spotId, MarketDatum::QuoteType::RATE, unitCcy, ccy);
2895 return fxSpotQuote;
2896}
2897
2898} // namespace data
2899} // namespace ore
Bond trade data model and serialization.
Container class for all Curve Configurations.
const std::string & curveConfigID() const
Definition: curvespec.hpp:83
string name() const
returns the unique curve name
Definition: curvespec.hpp:78
QuantLib::Handle< QuantLib::Quote > getQuote(const std::string &pair) const
const FallbackData & fallbackData(const string &iborIndex) const
bool isIndexReplaced(const string &iborIndex, const QuantLib::Date &asof=QuantLib::Date::maxDate()) const
Market data loader base class.
Definition: loader.hpp:47
virtual std::vector< QuantLib::ext::shared_ptr< MarketDatum > > loadQuotes(const QuantLib::Date &) const =0
get all quotes, TODO change the return value to std::set
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
Definition: loader.cpp:24
virtual bool has(const std::string &name, const QuantLib::Date &d) const
Default implementation, returns false if get throws or returns a null pointer.
Definition: loader.cpp:53
static const string inCcyConfiguration
InCcy configuration label.
Definition: market.hpp:299
Handle< YieldTermStructure > discountCurve(const string &ccy, const string &configuration=Market::defaultConfiguration) const
Definition: market.cpp:351
QuantLib::ext::shared_ptr< YieldTermStructure > p_
Definition: yieldcurve.hpp:139
void addDeposits(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
void buildBondYieldShiftedCurve()
Build a yield curve that uses QuantExt::bondYieldShiftedCurve.
const bool preserveQuoteLinkage_
Definition: yieldcurve.hpp:172
void addCrossCcyBasisSwaps(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
void buildDiscountRatioCurve()
Build a yield curve that uses QuantExt::DiscountRatioModifiedCurve.
void addTenorBasisSwaps(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
DayCounter zeroDayCounter_
Definition: yieldcurve.hpp:132
void addOISs(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
void addFutures(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
map< string, QuantLib::ext::shared_ptr< DefaultCurve > > requiredDefaultCurves_
Definition: yieldcurve.hpp:168
InterpolationVariable
Supported interpolation variables.
Definition: yieldcurve.hpp:64
void addBMABasisSwaps(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
void addSwaps(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
void buildYieldPlusDefaultCurve()
Build a yield curve that uses QuantExt::YieldPlusDefaultYieldTermStructure.
void buildFittedBondCurve()
Build a yield curve that uses QuantLib::FittedBondCurve.
YieldCurve(Date asof, YieldCurveSpec curveSpec, const CurveConfigurations &curveConfigs, const Loader &loader, const map< string, QuantLib::ext::shared_ptr< YieldCurve > > &requiredYieldCurves=map< string, QuantLib::ext::shared_ptr< YieldCurve > >(), const map< string, QuantLib::ext::shared_ptr< DefaultCurve > > &requiredDefaultCurves=map< string, QuantLib::ext::shared_ptr< DefaultCurve > >(), const FXTriangulation &fxTriangulation=FXTriangulation(), const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceData=nullptr, const IborFallbackConfig &iborfallbackConfig=IborFallbackConfig::defaultConfig(), const bool preserveQuoteLinkage=false, const bool buildCalibrationInfo=true, const Market *market=nullptr)
Constructor.
Definition: yieldcurve.cpp:277
RelinkableHandle< YieldTermStructure > h_
Definition: yieldcurve.hpp:138
const QuantLib::ext::shared_ptr< ReferenceDataManager > referenceData_
Definition: yieldcurve.hpp:170
const Market * market_
Definition: yieldcurve.hpp:174
void addFras(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
YieldCurveSpec curveSpec_
Definition: yieldcurve.hpp:131
QuantLib::ext::shared_ptr< YieldCurveConfig > curveConfig_
Definition: yieldcurve.hpp:162
const FXTriangulation & fxTriangulation_
Definition: yieldcurve.hpp:169
void buildIborFallbackCurve()
Build a yield curve that uses QuantExt::IborFallbackCurve.
const Handle< YieldTermStructure > & handle() const
Definition: yieldcurve.hpp:120
InterpolationMethod
Supported interpolation methods.
Definition: yieldcurve.hpp:67
void addTenorBasisTwoSwaps(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
void addFXForwards(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
const Loader & loader_
Definition: yieldcurve.hpp:137
InterpolationVariable interpolationVariable_
Definition: yieldcurve.hpp:164
map< string, QuantLib::ext::shared_ptr< YieldCurve > > requiredYieldCurves_
Definition: yieldcurve.hpp:167
InterpolationMethod interpolationMethod_
Definition: yieldcurve.hpp:165
IborFallbackConfig iborFallbackConfig_
Definition: yieldcurve.hpp:171
QuantLib::ext::shared_ptr< YieldCurveCalibrationInfo > calibrationInfo_
Definition: yieldcurve.hpp:140
vector< QuantLib::ext::shared_ptr< YieldCurveSegment > > curveSegments_
Definition: yieldcurve.hpp:163
void addCrossCcyFixFloatSwaps(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
void buildWeightedAverageCurve()
Build a yield curve that uses QuantExt::WeightedYieldTermStructure.
QuantLib::ext::shared_ptr< FXSpotQuote > getFxSpotQuote(string spotId)
QuantLib::ext::shared_ptr< YieldTermStructure > piecewisecurve(vector< QuantLib::ext::shared_ptr< RateHelper > > instruments)
Definition: yieldcurve.cpp:390
QuantLib::ext::shared_ptr< YieldCurve > discountCurve_
Definition: yieldcurve.hpp:134
QuantLib::ext::shared_ptr< YieldCurve > getYieldCurve(const std::string &ccy, const std::string &id) const
Return the yield curve with the given id from the requiredYieldCurves_ map.
void addAverageOISs(const QuantLib::ext::shared_ptr< YieldCurveSegment > &segment, vector< QuantLib::ext::shared_ptr< RateHelper > > &instruments)
Yield curve description.
Definition: curvespec.hpp:108
const string & ccy() const
Definition: curvespec.hpp:121
Wrapper class for building Default curves.
Pricing Engine Factory.
trade envelope data model and serialization
A market implementation providing curves for setting up bond rate helpers.
QuantLib::ext::shared_ptr< IborIndex > parseIborIndex(const string &s, const Handle< YieldTermStructure > &h)
Convert std::string to QuantLib::IborIndex.
bool isOvernightIndex(const string &indexName)
Return true if the indexName is that of an overnight index, otherwise false.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
Map text representations to QuantLib/QuantExt types.
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
QuantLib::ext::shared_ptr< FXConvention > fxConvention
Market Datum parser.
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
QuantLib::ext::shared_ptr< YieldTermStructure > buildYieldCurve(const vector< Date > &dates, const vector< QuantLib::Real > &rates, const DayCounter &dayCounter, YieldCurve::InterpolationMethod interpolationMethod, Size n)
Templated function to build a YieldTermStructure and apply interpolation methods to it.
Definition: yieldcurve.cpp:92
YieldCurve::InterpolationVariable parseYieldCurveInterpolationVariable(const string &s)
Helper function for parsing interpolation variable.
Definition: yieldcurve.cpp:223
Size size(const ValueType &v)
Definition: value.cpp:145
bool matchFxFwdStringTerm(const boost::variant< QuantLib::Period, FXForwardQuote::FxFwdString > &term, const FXForwardQuote::FxFwdString &fxfwdString)
QuantLib::Period fxFwdQuoteStartTenor(const boost::variant< QuantLib::Period, FXForwardQuote::FxFwdString > &term, const QuantLib::ext::shared_ptr< FXConvention > &fxConvention)
QuantLib::ext::shared_ptr< YieldTermStructure > discountcurve(const vector< Date > &dates, const vector< DiscountFactor > &dfs, const DayCounter &dayCounter, YieldCurve::InterpolationMethod interpolationMethod, Size n)
Create a Interpolated Discount Curve and apply interpolators.
Definition: yieldcurve.cpp:166
YieldCurve::InterpolationMethod parseYieldCurveInterpolationMethod(const string &s)
Helper function for parsing interpolation method.
Definition: yieldcurve.cpp:180
QuantLib::ext::shared_ptr< YieldTermStructure > forwardcurve(const vector< Date > &dates, const vector< Rate > &forwards, const DayCounter &dayCounter, YieldCurve::InterpolationMethod interpolationMethod, Size n)
Create a Interpolated Forward Curve and apply interpolators.
Definition: yieldcurve.cpp:172
QuantLib::ext::shared_ptr< YieldTermStructure > zerocurve(const vector< Date > &dates, const vector< Rate > &yields, const DayCounter &dayCounter, YieldCurve::InterpolationMethod interpolationMethod, Size n)
Create a Interpolated Zero Curve and apply interpolators.
Definition: yieldcurve.cpp:160
boost::optional< Wildcard > getUniqueWildcard(const C &c)
checks if at most one element in C has a wild card and returns it in this case
Definition: wildcard.hpp:65
QuantLib::Period fxFwdQuoteTenor(const boost::variant< QuantLib::Period, FXForwardQuote::FxFwdString > &term)
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
QuantLib::BootstrapHelper< QuantLib::OptionletVolatilityStructure > helper
Reference data model and serialization.
static const std::vector< QuantLib::Period > defaultPeriods
vector< string > curveConfigs
string conversion utilities
Wrapper class for QuantLib term structures.