hasquant-0.8.0.0: cbits/qlTermStructureAux.cpp
#include <ql/shared_ptr.hpp>
using QuantLib::ext::shared_ptr;
#include "qlTermStructureAux.h"
namespace hasquant {
#include "qlEnumObjects.h"
}
using namespace QuantLib;
#if QL_HEX_VERSION >= 0x01440000
static std::function<Array(const std::vector<Time>&, const std::vector<Real>&)>
bootstrapInitialGuess(const QlCallback& owner) {
if (!owner) return nullptr;
return [owner](const std::vector<Time>& times, const std::vector<Real>& data) {
QL_REQUIRE(!times.empty(), "initial guess needs curve times");
Array out(times.size() - 1);
QlCallbackArgs args{0.0, 0.0, 0.0, times.data(), out.begin(), (unsigned)times.size(), 0};
args.input2 = data.data();
args.size2 = (unsigned)data.size();
args.outputSize = (unsigned)out.size();
owner->invoke(args);
return out;
};
}
#endif
// Shared piecewise-curve dispatcher; the entry points differ only in leading arguments.
// Use the curve's bootstrap type to avoid invalid template instantiation.
template <class Curve>
typename Curve::bootstrap_type makeIterativeBootstrap(const QlIterativeBootstrapOpts& b) {
return typename Curve::bootstrap_type(b.accuracy, b.minValue, b.maxValue, b.maxAttempts,
b.maxFactor, b.minFactor, b.dontThrow != 0, b.dontThrowSteps, b.maxEvaluations);
}
// Cubic and LogCubic take the same constructor arguments, hence one helper over both.
template <class C>
C makeCubic(int approximator, int approximatorArg) {
switch (approximator) {
case hasquant::NaturalSpline:
return C(CubicInterpolation::Spline, approximatorArg, CubicInterpolation::SecondDerivative, 0.0, CubicInterpolation::SecondDerivative, 0.0);
case hasquant::Kruger: return C(CubicInterpolation::Kruger);
case hasquant::FritschButland: return C(CubicInterpolation::FritschButland);
case hasquant::Parabolic: return C(CubicInterpolation::Parabolic, approximatorArg);
default: QL_FAIL("Unsupported approximation " << approximator);
}
}
// Type-only tag, for the dispatchers whose selected type is a trait (no runtime state to carry,
// unlike an interpolator instance). `make(Tag<QuantLib::Discount>())` reaches the type inside the
// callable as `typename decltype(t)::type`.
template <class T> struct Tag { using type = T; };
// Use an explicit result type: a decltype probe can prevent clang from emitting the selected
// curve's vtable. The same rule applies to the dispatchers in qlPricingEngineAux.cpp.
template <class Ret, class F>
Ret dispatchInterpolation(int interpolator, int approximator, int approximatorArg, F&& make) {
switch (interpolator) {
case hasquant::BackwardFlat: return make(BackwardFlat());
case hasquant::ForwardFlat: return make(ForwardFlat());
case hasquant::Linear: return make(Linear());
case hasquant::LogLinear: return make(LogLinear());
case hasquant::Cubic: return make(makeCubic<Cubic>(approximator, approximatorArg));
case hasquant::LogCubic: return make(makeCubic<LogCubic>(approximator, approximatorArg));
// hasquant::Abcd (InterpolationType's 7th case) has no arm: QuantLib's Abcd interpolation
// isn't usable as a PiecewiseYieldCurve interpolator.
default:
QL_FAIL("Unsupported interpolation " << interpolator);
}
}
// Args is the entry-point-specific prefix (a Date, or settlementDays + Calendar) followed by
// instruments/dayCounter/jumps/jumpDates; every PiecewiseYieldCurve constructor ends with the
// interpolator and the bootstrapper, so those two go last in the lambda below.
template <class... Args>
YieldTermStructure *dispatchTrait(int trait, int interpolator, int approximator, int approximatorArg,
const QlIterativeBootstrapOpts& b, Args&&... args) {
auto makeForTrait = [&](auto t) -> YieldTermStructure* {
using Trait = typename decltype(t)::type;
return dispatchInterpolation<YieldTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
using CurveType = PiecewiseYieldCurve<Trait, decltype(i)>;
return new CurveType(std::forward<Args>(args)..., i, makeIterativeBootstrap<CurveType>(b));
});
};
switch (trait) {
case hasquant::Discount: return makeForTrait(Tag<QuantLib::Discount>());
case hasquant::ForwardRate: return makeForTrait(Tag<QuantLib::ForwardRate>());
case hasquant::ZeroYield: return makeForTrait(Tag<QuantLib::ZeroYield>());
case hasquant::SimpleZeroYield: return makeForTrait(Tag<QuantLib::SimpleZeroYield>());
default:
QL_FAIL("Unsupported trait" << trait);
}
}
// QuantLib-SWIG's canned GlobalBootstrap functors (SWIG/piecewiseyieldcurve.i:186-282).
// AdditionalErrors is a fixed interpolation formula, not a user callback, and both functors are
// trait-independent, so they need neither Haskell marshalling nor per-trait definitions.
class AdditionalErrors {
std::vector<shared_ptr<RateHelper> > additionalHelpers_;
public:
AdditionalErrors(const std::vector<shared_ptr<RateHelper> >& additionalHelpers)
: additionalHelpers_(additionalHelpers) {}
Array operator()() const {
Array errors(additionalHelpers_.size() - 2);
Real a = additionalHelpers_.front()->impliedQuote();
Real b = additionalHelpers_.back()->impliedQuote();
for (Size k = 0; k < errors.size(); ++k) {
errors[k] = (static_cast<Real>(errors.size()-k) * a + static_cast<Real>(1+k) * b) / static_cast<Real>(errors.size()+1)
- additionalHelpers_.at(1+k)->impliedQuote();
}
return errors;
}
};
class AdditionalDates {
std::vector<Date> additionalDates_;
public:
AdditionalDates(const std::vector<Date>& additionalDates) : additionalDates_(additionalDates) {}
std::vector<Date> operator()() const { return additionalDates_; }
};
// Spelled CurveType::bootstrap_type(accuracy), not GlobalBootstrap<CurveType>(accuracy):
// GlobalBootstrap overrides pure-virtual methods from MultiCurveBootstrapContributor, and
// [temp.inst] instantiates a class's virtual member function bodies together with the class
// itself. Naming GlobalBootstrap<CurveType> directly starts instantiating *that* specialization
// first, which then needs CurveType complete -- but CurveType is simultaneously mid-instantiation
// because of this very argument (its bootstrap_ field has type GlobalBootstrap<CurveType>),
// producing a hard "incomplete type" error (reproduced independently with clang and g++-16).
// Naming CurveType (via ::bootstrap_type) first instead makes CurveType the outer instantiation,
// so GlobalBootstrap<CurveType>'s virtual bodies are only instantiated once CurveType is
// complete. Don't "simplify" this back to the direct spelling -- it silently reintroduces the
// compile failure. Templated over Trait/Interp (same shape makeCurveLocalBootstrap already uses
// for LocalBootstrap below) so each trait/interpolator combination is one dispatch arm instead of
// a hand-duplicated block.
template <class Trait, class Interp, class... Args>
YieldTermStructure *makeGlobalBootstrapCurve(const Interp& interp, double accuracy,
const std::vector<double>& instrumentWeights, [[maybe_unused]] const QlCallback& initialGuess, Args&&... args) {
using CurveType = PiecewiseYieldCurve<Trait, Interp, QuantLib::GlobalBootstrap>;
return new CurveType(std::forward<Args>(args)..., interp,
typename CurveType::bootstrap_type(accuracy, nullptr, nullptr, instrumentWeights QL144_ARGS(bootstrapInitialGuess(initialGuess))));
}
// GlobalBootstrap is wired up only for the combinations concrete use cases have asked for --
// Discount/LogLinear (the multi-curve relinkable-handle test), SimpleZeroYield/Linear (upstream
// QuantLib-SWIG's GlobalLinearSimpleZeroCurve), and ForwardRate/Linear and ZeroYield/Linear (the
// other two IterativeBootstrap traits paired with the cheapest interpolator, see issue #15) --
// not the full trait x interpolator matrix; CLAUDE.md is explicit about not building dispatch for
// hypothetical future combinations.
template <class... Args>
YieldTermStructure *dispatchTraitGlobalBootstrap(int trait, int interpolator, double accuracy,
const std::vector<double>& instrumentWeights, [[maybe_unused]] const QlCallback& initialGuess, Args&&... args) {
switch (trait) {
case hasquant::Discount:
QL_REQUIRE(interpolator == hasquant::LogLinear,
"GlobalBootstrap-based PiecewiseYieldCurve construction with trait=Discount only "
"supports interpolator=LogLinear (got interpolator " << interpolator << ")");
return makeGlobalBootstrapCurve<QuantLib::Discount>(QuantLib::LogLinear(), accuracy,
instrumentWeights, initialGuess, std::forward<Args>(args)...);
case hasquant::SimpleZeroYield:
QL_REQUIRE(interpolator == hasquant::Linear,
"GlobalBootstrap-based PiecewiseYieldCurve construction with trait=SimpleZeroYield "
"only supports interpolator=Linear (got interpolator " << interpolator << ")");
return makeGlobalBootstrapCurve<QuantLib::SimpleZeroYield>(QuantLib::Linear(), accuracy,
instrumentWeights, initialGuess, std::forward<Args>(args)...);
case hasquant::ForwardRate:
QL_REQUIRE(interpolator == hasquant::Linear,
"GlobalBootstrap-based PiecewiseYieldCurve construction with trait=ForwardRate only "
"supports interpolator=Linear (got interpolator " << interpolator << ")");
return makeGlobalBootstrapCurve<QuantLib::ForwardRate>(QuantLib::Linear(), accuracy,
instrumentWeights, initialGuess, std::forward<Args>(args)...);
case hasquant::ZeroYield:
QL_REQUIRE(interpolator == hasquant::Linear,
"GlobalBootstrap-based PiecewiseYieldCurve construction with trait=ZeroYield only "
"supports interpolator=Linear (got interpolator " << interpolator << ")");
return makeGlobalBootstrapCurve<QuantLib::ZeroYield>(QuantLib::Linear(), accuracy,
instrumentWeights, initialGuess, std::forward<Args>(args)...);
default:
QL_FAIL("GlobalBootstrap-based PiecewiseYieldCurve construction is only supported for "
"trait=Discount/interpolator=LogLinear, trait=ForwardRate/interpolator=Linear, "
"trait=ZeroYield/interpolator=Linear, or trait=SimpleZeroYield/interpolator=Linear "
"(got trait " << trait << ", interpolator " << interpolator << ")");
}
}
// PiecewiseYieldCurve<SimpleZeroYield, Linear, GlobalBootstrap> built via GlobalBootstrap's
// functor-callback constructor, taking additionalHelpers/additionalDates and constructing
// AdditionalErrors/AdditionalDates internally -- the same GlobalLinearSimpleZeroCurve combination
// QuantLib-SWIG demonstrates. A separate entry point from qlPiecewiseYieldCurveAux1's
// bootstrap==1 branch (not a widened version of it): that branch's plain accuracy/
// instrumentWeights constructor and this functor constructor are different GlobalBootstrap
// overloads entirely, not more parameters on the same one.
template <class... Args>
YieldTermStructure *piecewiseYieldCurveGlobalBootstrapFull(
const std::vector<shared_ptr<RateHelper> >& instr,
const DayCounter& dayCount,
const std::vector<Handle<Quote> >& jumps, const std::vector<Date>& jumpDates,
const std::vector<shared_ptr<RateHelper> >& additionalHelpers,
const std::vector<Date>& additionalDates,
double accuracy, const std::vector<double>& instrumentWeights, [[maybe_unused]] const QlCallback& initialGuess, Args&&... reference) {
// AdditionalErrors supplies two fewer equations than helpers; additionalDates must provide
// the remaining unknowns for GlobalBootstrap's square system.
QL_REQUIRE(additionalHelpers.size() >= 2,
"GlobalBootstrap's canned AdditionalErrors formula needs at least 2 additionalHelpers "
"(got " << additionalHelpers.size() << ")");
QL_REQUIRE(additionalDates.size() == additionalHelpers.size() - 2,
"additionalDates must have exactly additionalHelpers.size() - 2 entries for "
"GlobalBootstrap's canned AdditionalErrors formula (got " << additionalHelpers.size() <<
" additionalHelpers and " << additionalDates.size() << " additionalDates, expected " <<
(additionalHelpers.size() - 2) << " additionalDates)");
using CurveType = PiecewiseYieldCurve<QuantLib::SimpleZeroYield, QuantLib::Linear, QuantLib::GlobalBootstrap>;
// CurveType::bootstrap_type(...) naming order -- see the comment on the plain-constructor
// GlobalBootstrap branch in qlPiecewiseYieldCurveAux1, same [temp.inst] reason.
return new CurveType(std::forward<Args>(reference)..., instr, dayCount, jumps, jumpDates,
QuantLib::Linear(),
CurveType::bootstrap_type(additionalHelpers, AdditionalDates(additionalDates),
AdditionalErrors(additionalHelpers), accuracy, nullptr, nullptr, nullptr, instrumentWeights QL144_ARGS(bootstrapInitialGuess(initialGuess))));
}
YieldTermStructure *qlPiecewiseYieldCurveGlobalBootstrapFullAux(const Date& date,
const std::vector<shared_ptr<RateHelper> >& instr, const DayCounter& dayCount,
const std::vector<Handle<Quote> >& jumps, const std::vector<Date>& jumpDates,
const std::vector<shared_ptr<RateHelper> >& additionalHelpers,
const std::vector<Date>& additionalDates, double accuracy, const std::vector<double>& instrumentWeights, [[maybe_unused]] const QlCallback& initialGuess) {
return piecewiseYieldCurveGlobalBootstrapFull(instr, dayCount, jumps, jumpDates,
additionalHelpers, additionalDates, accuracy, instrumentWeights, initialGuess, date);
}
YieldTermStructure *qlPiecewiseYieldCurveGlobalBootstrapFullAux(unsigned settl,
const Calendar &cal, const std::vector<shared_ptr<RateHelper> >& instr,
const DayCounter& dayCount, const std::vector<Handle<Quote> >& jumps,
const std::vector<Date>& jumpDates,
const std::vector<shared_ptr<RateHelper> >& additionalHelpers,
const std::vector<Date>& additionalDates, double accuracy, const std::vector<double>& instrumentWeights, [[maybe_unused]] const QlCallback& initialGuess) {
return piecewiseYieldCurveGlobalBootstrapFull(instr, dayCount, jumps, jumpDates,
additionalHelpers, additionalDates, accuracy, instrumentWeights, initialGuess, settl, cal);
}
// LocalBootstrap requires its Interpolator to provide localInterpolate(), which upstream only
// ConvexMonotoneInterpolation (ql/math/interpolations/convexmonotoneinterpolation.hpp) supplies
// -- Linear/LogLinear/Cubic/LogCubic/BackwardFlat/ForwardFlat do not. So unlike dispatchTrait
// (interpolator-generic), this dispatches trait only, with the interpolator fixed to
// ConvexMonotone.
//
// Discount is excluded: its guesses produce invalid discount factors with
// ConvexMonotone/LocalBootstrap. The supported traits bootstrap ordinary deposit rates.
template <class Trait, class... Args>
YieldTermStructure *makeCurveLocalBootstrap(const ConvexMonotone& interp, Size localisation,
bool forcePositive, double accuracy, Args&&... args) {
using CurveType = PiecewiseYieldCurve<Trait, QuantLib::ConvexMonotone, QuantLib::LocalBootstrap>;
// CurveType::bootstrap_type(...) naming order -- see the comment on the GlobalBootstrap branch
// in qlPiecewiseYieldCurveAux1, same [temp.inst] reasoning applied defensively here too.
return new CurveType(std::forward<Args>(args)..., interp,
typename CurveType::bootstrap_type(localisation, forcePositive, accuracy));
}
template <class... Args>
YieldTermStructure *dispatchTraitLocalBootstrap(int trait, const ConvexMonotone& interp,
Size localisation, bool forcePositive, double accuracy, Args&&... args) {
switch (trait) {
case hasquant::ForwardRate:
return makeCurveLocalBootstrap<QuantLib::ForwardRate>(interp, localisation, forcePositive, accuracy, std::forward<Args>(args)...);
case hasquant::ZeroYield:
return makeCurveLocalBootstrap<QuantLib::ZeroYield>(interp, localisation, forcePositive, accuracy, std::forward<Args>(args)...);
case hasquant::SimpleZeroYield:
return makeCurveLocalBootstrap<QuantLib::SimpleZeroYield>(interp, localisation, forcePositive, accuracy, std::forward<Args>(args)...);
case hasquant::Discount:
QL_FAIL("LocalBootstrap-based PiecewiseYieldCurve construction with trait=Discount produces "
"numerically incorrect results with ConvexMonotone (verified independently against "
"upstream QuantLib) -- use ForwardRate, ZeroYield or SimpleZeroYield instead");
default:
QL_FAIL("Unsupported trait" << trait);
}
}
YieldTermStructure *qlPiecewiseYieldCurveLocalBootstrapAux1(unsigned settl, const Calendar &cal,
const std::vector<shared_ptr<RateHelper> >& instr,
const DayCounter& dayCount,
const std::vector<Handle<Quote> >& jumps, const std::vector<Date>& jumpDates,
int trait, Size localisation, bool forcePositive, double accuracy,
double quadraticity, double monotonicity, bool convexForcePositive) {
return dispatchTraitLocalBootstrap(trait, ConvexMonotone(quadraticity, monotonicity, convexForcePositive),
localisation, forcePositive, accuracy, settl, cal, instr, dayCount, jumps, jumpDates);
}
YieldTermStructure *qlPiecewiseYieldCurveLocalBootstrapAux(const Date& date,
const std::vector<shared_ptr<RateHelper> >& instr,
const DayCounter& dayCount,
const std::vector<Handle<Quote> >& jumps, const std::vector<Date>& jumpDates,
int trait, Size localisation, bool forcePositive, double accuracy,
double quadraticity, double monotonicity, bool convexForcePositive) {
return dispatchTraitLocalBootstrap(trait, ConvexMonotone(quadraticity, monotonicity, convexForcePositive),
localisation, forcePositive, accuracy, date, instr, dayCount, jumps, jumpDates);
}
// extracted some template-heavy stuff into a separate file to speed up the compilation
YieldTermStructure *qlPiecewiseYieldCurveAux(const Date &date,
const std::vector<shared_ptr<RateHelper> >& instr,
const DayCounter& dayCount,
const std::vector<Handle<Quote> >& jumps, const std::vector<Date>& jumpDates,
int trait, int interpolator, int approximator, int approximatorArg,
const QlIterativeBootstrapOpts& bootstrapOpts) {
return dispatchTrait(trait, interpolator, approximator, approximatorArg, bootstrapOpts,
date, instr, dayCount, jumps, jumpDates);
}
YieldTermStructure *qlPiecewiseYieldCurveAux1(unsigned settl, const Calendar &cal,
const std::vector<shared_ptr<RateHelper> >& instr,
const DayCounter& dayCount,
const std::vector<Handle<Quote> >& jumps, const std::vector<Date>& jumpDates,
int trait, int interpolator, int approximator, int approximatorArg,
int bootstrap, double accuracy, const std::vector<double>& instrumentWeights,
const QlIterativeBootstrapOpts& bootstrapOpts, [[maybe_unused]] const QlCallback& initialGuess) {
if (bootstrap == 1) {
return dispatchTraitGlobalBootstrap(trait, interpolator, accuracy, instrumentWeights, initialGuess,
settl, cal, instr, dayCount, jumps, jumpDates);
}
return dispatchTrait(trait, interpolator, approximator, approximatorArg, bootstrapOpts,
settl, cal, instr, dayCount, jumps, jumpDates);
}
YieldTermStructure *qlPiecewiseYieldCurveGlobalBootstrapAux(const Date& date,
const std::vector<shared_ptr<RateHelper> >& instr,
const DayCounter& dayCount,
const std::vector<Handle<Quote> >& jumps, const std::vector<Date>& jumpDates,
int trait, int interpolator, double accuracy,
const std::vector<double>& instrumentWeights, [[maybe_unused]] const QlCallback& initialGuess) {
return dispatchTraitGlobalBootstrap(trait, interpolator, accuracy, instrumentWeights, initialGuess,
date, instr, dayCount, jumps, jumpDates);
}
// Each function below spells its curve constructor once inside dispatchInterpolation. Interpolator
// instances and any intervening constructor defaults are explicit so every dispatch arm has the
// same shape.
YieldTermStructure *qlInterpolatedDiscountCurveAux(
const std::vector<Date> &dfDates,
const std::vector<double>& dfs,
const DayCounter& dayCount,
const Calendar& cal,
const std::vector<Handle<Quote> >& jumps,
const std::vector<Date>& jumpDates,
int interpolator, int approximator, int approximatorArg) {
// makeCubic carries NaturalSpline's monotonic flag through the LogCubic arm.
return dispatchInterpolation<YieldTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedDiscountCurve<decltype(i)>(dfDates, dfs, dayCount, cal, jumps, jumpDates, i);
});
}
YieldTermStructure *qlInterpolatedForwardCurveAux(
const std::vector<Date> &fwdDates,
const std::vector<double>& fwds,
const DayCounter& dayCount,
const Calendar& cal,
const std::vector<Handle<Quote> >& jumps,
const std::vector<Date>& jumpDates,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<YieldTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedForwardCurve<decltype(i)>(fwdDates, fwds, dayCount, cal, jumps, jumpDates, i);
});
}
YieldTermStructure *qlInterpolatedZeroCurveAux(
const std::vector<Date> &yDates,
const std::vector<double>& yields,
const DayCounter& dayCount,
const Calendar& cal,
const std::vector<Handle<Quote> >& jumps,
const std::vector<Date>& jumpDates,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<YieldTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedZeroCurve<decltype(i)>(yDates, yields, dayCount, cal, jumps, jumpDates, i);
});
}
YieldTermStructure *qlInterpolatedSimpleZeroCurveAux(
const std::vector<Date> &yDates,
const std::vector<double>& yields,
const DayCounter& dayCount,
const Calendar& cal,
const std::vector<Handle<Quote> >& jumps,
const std::vector<Date>& jumpDates,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<YieldTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedSimpleZeroCurve<decltype(i)>(yDates, yields, dayCount, cal, jumps, jumpDates, i);
});
}
YieldTermStructure *qlInterpolatedSpreadDiscountCurveAux(
const Handle<YieldTermStructure>& baseCurve,
const std::vector<Date>& dates,
const std::vector<double>& dfs,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<YieldTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedSpreadDiscountCurve<decltype(i)>(baseCurve, dates, dfs, i);
});
}
YieldTermStructure *qlPiecewiseZeroSpreadedTermStructureAux(
const Handle<YieldTermStructure>& baseCurve,
const std::vector<Handle<Quote> >& spreads,
const std::vector<Date>& dates,
Compounding comp, Frequency freq,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<YieldTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedPiecewiseZeroSpreadedTermStructure<decltype(i)>(baseCurve, spreads, dates, comp, freq, i);
});
}
YieldTermStructure *qlPiecewiseForwardSpreadedTermStructureAux(
const Handle<YieldTermStructure>& baseCurve,
const std::vector<Handle<Quote> >& spreads,
const std::vector<Date>& dates,
int interpolator, int approximator, int approximatorArg) {
QL_REQUIRE(interpolator != hasquant::LogLinear && interpolator != hasquant::LogCubic,
"piecewise forward-spreaded term structure needs an interpolation with a primitive; "
"LogLinear and LogCubic are not supported");
return dispatchInterpolation<YieldTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedPiecewiseForwardSpreadedTermStructure<decltype(i)>(baseCurve, spreads, dates, i);
});
}
YieldTermStructure *qlPiecewiseSpreadYieldCurveAux(
const Handle<YieldTermStructure>& baseCurve,
const std::vector<shared_ptr<RateHelper> >& instr,
int interpolator, int approximator, int approximatorArg,
const QlIterativeBootstrapOpts& b) {
return dispatchInterpolation<YieldTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
using CurveType = PiecewiseSpreadYieldCurve<QuantLib::Discount, decltype(i)>;
return new CurveType(baseCurve, instr, i, makeIterativeBootstrap<CurveType>(b));
});
}
// Bootstrap spelled through CurveType::bootstrap_type, as in makeGlobalBootstrapCurve.
YieldTermStructure *qlPiecewiseSpreadYieldCurveGlobalBootstrapAux(
const Handle<YieldTermStructure>& baseCurve,
const std::vector<shared_ptr<RateHelper> >& instr,
double accuracy, const std::vector<double>& instrumentWeights, [[maybe_unused]] const QlCallback& initialGuess) {
using CurveType = PiecewiseSpreadYieldCurve<QuantLib::Discount, QuantLib::LogLinear, QuantLib::GlobalBootstrap>;
return new CurveType(baseCurve, instr, QuantLib::LogLinear(),
typename CurveType::bootstrap_type(accuracy, nullptr, nullptr, instrumentWeights QL144_ARGS(bootstrapInitialGuess(initialGuess))));
}
// some credit stuff
DefaultProbabilityTermStructure *qlInterpolatedDefaultDensityCurveAux(
const std::vector<Date>& dates,
const std::vector<double>& densities,
const DayCounter& dayCounter,
const Calendar& calendar,
const std::vector<Handle<Quote> >& jumps,
const std::vector<Date>& jumpDates,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<DefaultProbabilityTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedDefaultDensityCurve<decltype(i)>(dates, densities, dayCounter, calendar, jumps, jumpDates, i);
});
}
DefaultProbabilityTermStructure *qlInterpolatedHazardRateCurveAux(
const std::vector<Date>& dates,
const std::vector<double>& hazardRates,
const DayCounter& dayCounter,
const Calendar& cal,
const std::vector<Handle<Quote> >& jumps,
const std::vector<Date>& jumpDates,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<DefaultProbabilityTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedHazardRateCurve<decltype(i)>(dates, hazardRates, dayCounter, cal, jumps, jumpDates, i);
});
}
OneFactorAffineSurvivalStructure *qlInterpolatedAffineHazardRateCurveAux(
const std::vector<Date>& dates,
const std::vector<double>& hazardRates,
const DayCounter& dayCounter,
const ext::shared_ptr<OneFactorAffineModel>& model,
const Calendar& cal,
const std::vector<Handle<Quote> >& jumps,
const std::vector<Date>& jumpDates,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<OneFactorAffineSurvivalStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedAffineHazardRateCurve<decltype(i)>(dates, hazardRates, dayCounter, model, cal, jumps, jumpDates, i);
});
}
DefaultProbabilityTermStructure *qlInterpolatedSurvivalProbabilityCurveAux(
const std::vector<Date>& dates,
const std::vector<double>& probabilities,
const DayCounter& dayCounter,
const Calendar& calendar,
const std::vector<Handle<Quote> >& jumps,
const std::vector<Date>& jumpDates,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<DefaultProbabilityTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedSurvivalProbabilityCurve<decltype(i)>(dates, probabilities, dayCounter, calendar, jumps, jumpDates, i);
});
}
// PiecewiseDefaultCurve's own trait axis (HazardRate/DefaultDensity/SurvivalProbability) --
// entirely separate from the yield curve's Discount/ForwardRate/ZeroYield/SimpleZeroYield above,
// hence its own dispatcher. Both entry points below differ only in their leading constructor
// arguments (a Date, or settlementDays + Calendar), so the nested trait x interpolator dispatch
// is written once and the difference lives in the lambda each passes.
template <class Ret, class F>
Ret dispatchDefaultTrait(int trait, F&& make) {
switch (trait) {
case hasquant::HazardRate: return make(Tag<QuantLib::HazardRate>());
case hasquant::DefaultDensity: return make(Tag<QuantLib::DefaultDensity>());
case hasquant::SurvivalProbability: return make(Tag<QuantLib::SurvivalProbability>());
default: QL_FAIL("Unsupported trait" << trait);
}
}
// The trait x interpolator product, with the entry-point-specific constructor prefix forwarded
// as a variadic pack -- same shape as dispatchTrait's for PiecewiseYieldCurve above.
template <class... Args>
DefaultProbabilityTermStructure *makePiecewiseDefaultCurve(int trait, int interpolator,
int approximator, int approximatorArg, const QlIterativeBootstrapOpts& b, Args&&... args) {
return dispatchDefaultTrait<DefaultProbabilityTermStructure*>(trait, [&](auto t) {
using Trait = typename decltype(t)::type;
return dispatchInterpolation<DefaultProbabilityTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
using CurveType = PiecewiseDefaultCurve<Trait, decltype(i)>;
return new CurveType(std::forward<Args>(args)..., i, makeIterativeBootstrap<CurveType>(b));
});
});
}
DefaultProbabilityTermStructure* qlPiecewiseDefaultCurveAux(const Date &referenceDate,
const std::vector<shared_ptr<DefaultProbabilityHelper> >& instruments,
DayCounter& dayCounter,
const std::vector<Handle<Quote> >& jumps, const std::vector<Date>& jumpDates,
int trait, int interpolator, int approximator, int approximatorArg,
const QlIterativeBootstrapOpts& bootstrapOpts) {
return makePiecewiseDefaultCurve(trait, interpolator, approximator, approximatorArg, bootstrapOpts,
referenceDate, instruments, dayCounter, jumps, jumpDates);
}
DefaultProbabilityTermStructure* qlPiecewiseDefaultCurveAux1(unsigned settlementDays,
const Calendar& calendar,
const std::vector<shared_ptr<DefaultProbabilityHelper> >& instruments,
DayCounter& dayCounter,
const std::vector<Handle<Quote> >& jumps, const std::vector<Date>& jumpDates,
int trait, int interpolator, int approximator, int approximatorArg,
const QlIterativeBootstrapOpts& bootstrapOpts) {
return makePiecewiseDefaultCurve(trait, interpolator, approximator, approximatorArg, bootstrapOpts,
settlementDays, calendar, instruments, dayCounter, jumps, jumpDates);
}
// Selects ConstantLossModel's Gaussian or Student-T copula policy.
template <class Ret, class F>
Ret dispatchCopulaPolicy(bool useStudent, F&& make) {
if (useStudent) return make(Tag<TCopulaPolicy>());
return make(Tag<GaussianCopulaPolicy>());
}
// The policies use different init-trait types; initialize Student-T orders only on its branch.
DefaultLossModel* qlConstantLossModelAux(const Handle<Quote>& mktCorrel,
const std::vector<Real>& recoveries,
LatentModelIntegrationType::LatentModelIntegrationType integralType,
Size nVariables, const std::vector<Integer>& tOrders) {
return dispatchCopulaPolicy<DefaultLossModel*>(!tOrders.empty(), [&](auto tag) {
using Policy = typename decltype(tag)::type;
typename Policy::initTraits ini{};
if constexpr (std::is_same_v<Policy, TCopulaPolicy>) ini.tOrders = tOrders;
return new ConstantLossModel<Policy>(mktCorrel, recoveries, integralType, nVariables, ini);
});
}
// The 1.0e-14 / 1.0e-12 accuracies below are upstream's own defaults, spelled explicitly because
// the interpolator follows them (piecewisezeroinflationcurve.hpp / piecewiseyoyinflationcurve.hpp).
ZeroInflationTermStructure *qlPiecewiseZeroInflationCurveAux(
const Date &referenceDate,
const Date &baseDate,
Frequency frequency,
const DayCounter& dayCounter,
const std::vector<shared_ptr<BootstrapHelper<ZeroInflationTermStructure> > >& instruments,
const shared_ptr<Seasonality>& seasonality,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<ZeroInflationTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new PiecewiseZeroInflationCurve<decltype(i)>(referenceDate, baseDate, frequency,
dayCounter, instruments, seasonality, 1.0e-14, i);
});
}
YoYInflationTermStructure *qlPiecewiseYoYInflationCurveAux(
const Date &referenceDate,
const Date &baseDate,
Rate baseYoYRate,
Frequency frequency,
const DayCounter& dayCounter,
const std::vector<shared_ptr<BootstrapHelper<YoYInflationTermStructure> > >& instruments,
const shared_ptr<Seasonality>& seasonality,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<YoYInflationTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new PiecewiseYoYInflationCurve<decltype(i)>(referenceDate, baseDate, baseYoYRate,
frequency, dayCounter, instruments, seasonality, 1.0e-12, i);
});
}
YoYInflationTermStructure *qlInterpolatedYoYInflationCurveAux(
const Date &referenceDate,
const std::vector<Date> &dates,
const std::vector<Rate> &rates,
Frequency frequency,
const DayCounter& dayCounter,
const shared_ptr<Seasonality>& seasonality,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<YoYInflationTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedYoYInflationCurve<decltype(i)>(referenceDate, dates, rates,
frequency, dayCounter, seasonality, i);
});
}
ZeroInflationTermStructure *qlInterpolatedZeroInflationCurveAux(
const Date &referenceDate,
const std::vector<Date> &dates,
const std::vector<Rate> &rates,
Frequency frequency,
const DayCounter& dayCounter,
const shared_ptr<Seasonality>& seasonality,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<ZeroInflationTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedZeroInflationCurve<decltype(i)>(referenceDate, dates, rates,
frequency, dayCounter, seasonality, i);
});
}
// ---------------------------------------------------------------------------------------------
// These entry points keep per-interpolation template instantiation in the auxiliary translation
// unit. Callers pass raw enum values and never name an interpolator type.
// ---------------------------------------------------------------------------------------------
// Interpolation set after construction rather than through a template parameter -- these two
// classes take a member template instead. Same one-body-per-Interpolation cost, so the same
// dispatchInterpolation shape applies, with Ret = void.
void qlSetBlackVarianceCurveInterpolationAux(BlackVarianceCurve *o,
int interpolator, int approximator, int approximatorArg) {
dispatchInterpolation<void>(interpolator, approximator, approximatorArg,
[&](auto i) { o->setInterpolation<decltype(i)>(i); });
}
// 2-D counterpart, for BlackVarianceSurface. setInterpolation is a member *template* taking a
// default-constructed Interpolator, so there is no approximator or approximatorArg to thread and
// no Interpolation2D object to marshal -- just a two-case switch. Same set QuantLib-SWIG exposes
// (SWIG/volatilities.i).
void qlSetBlackVarianceSurfaceInterpolationAux(BlackVarianceSurface *o, int interpolator) {
switch (interpolator) {
case hasquant::Bilinear: o->setInterpolation<QuantLib::Bilinear>(); break;
case hasquant::Bicubic: o->setInterpolation<QuantLib::Bicubic>(); break;
default: QL_FAIL("Unsupported 2-D interpolation " << interpolator);
}
}
// The 2-D (cap/floor price grid) interpolator axis, shared by both price surfaces below.
// Bilinear/Bicubic are the only two QuantLib offers here, and neither carries constructor
// arguments, so this is a plain Tag dispatch with no approximator to thread.
template <class Ret, class F>
Ret dispatchInterpolation2D(int interpolator2D, F&& make) {
switch (interpolator2D) {
case hasquant::Bilinear: return make(Tag<QuantLib::Bilinear>());
case hasquant::Bicubic: return make(Tag<QuantLib::Bicubic>());
default: QL_FAIL("Unsupported 2-D interpolation " << interpolator2D);
}
}
CPICapFloorTermPriceSurface *qlCPICapFloorTermPriceSurfaceAux(
double nominal, double baseRate, const Period &observationLag, const Calendar &cal,
BusinessDayConvention bdc, const DayCounter &dc, const shared_ptr<ZeroInflationIndex> &zii,
CPI::InterpolationType interpolationType, const Handle<YieldTermStructure> &yts,
const std::vector<Rate> &cStrikes, const std::vector<Rate> &fStrikes,
const std::vector<Period> &cfMaturities, const Matrix &cPrice, const Matrix &fPrice,
int interpolator2D) {
return dispatchInterpolation2D<CPICapFloorTermPriceSurface*>(interpolator2D, [&](auto i2d) {
return new InterpolatedCPICapFloorTermPriceSurface<typename decltype(i2d)::type>(nominal, baseRate,
observationLag, cal, bdc, dc, zii, interpolationType, yts, cStrikes, fStrikes, cfMaturities,
cPrice, fPrice);
});
}
// Dispatch both interpolator axes: the 2-D price grid and the inner 1-D per-maturity curve.
YoYCapFloorTermPriceSurface *qlYoYCapFloorTermPriceSurfaceAux(
Natural fixingDays, const Period &yyLag, const shared_ptr<YoYInflationIndex> &yii,
CPI::InterpolationType interpolation, const Handle<YieldTermStructure> &nominal,
const DayCounter &dc, const Calendar &cal, BusinessDayConvention bdc,
const std::vector<Rate> &cStrikes, const std::vector<Rate> &fStrikes,
const std::vector<Period> &cfMaturities, const Matrix &cPrice, const Matrix &fPrice,
int interpolator2D, int interpolator1D, int approximator, int approximatorArg) {
return dispatchInterpolation2D<YoYCapFloorTermPriceSurface*>(interpolator2D, [&](auto i2d) {
using I2D = typename decltype(i2d)::type;
return dispatchInterpolation<YoYCapFloorTermPriceSurface*>(interpolator1D, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedYoYCapFloorTermPriceSurface<I2D, decltype(i)>(fixingDays, yyLag, yii,
interpolation, nominal, dc, cal, bdc, cStrikes, fStrikes, cfMaturities, cPrice, fPrice,
I2D(), i);
});
});
}
// The stripper's own internal PiecewiseYoYOptionletVolatilityCurve<Interpolator1D> always
// default-constructs its interpolator (interpolatedyoyoptionletstripper.hpp's initialize()
// never passes one) -- only the surface's own K-direction interpolator (factory1D_) takes an
// explicit instance, so only that one needs the Cubic/LogCubic approximator-specific
// construction.
template <class Interpolator1D>
YoYOptionletVolatilitySurface *makeKInterpolatedYoYOptionletVolatilitySurface(
unsigned settlementDays, const Calendar &cal, BusinessDayConvention bdc, const DayCounter &dc,
const shared_ptr<YoYCapFloorTermPriceSurface> &capFloorPrices,
const shared_ptr<YoYInflationCapFloorEngine> &engine, double slope,
const Interpolator1D &interpolator) {
return new KInterpolatedYoYOptionletVolatilitySurface<Interpolator1D>(
settlementDays, cal, bdc, dc, capFloorPrices->observationLag(), capFloorPrices, engine,
shared_ptr<YoYOptionletStripper>(new InterpolatedYoYOptionletStripper<Interpolator1D>()),
slope, interpolator);
}
// Not written in terms of dispatchInterpolation, unlike everything else in this file: that
// dispatcher has a LogCubic arm and this one must not.
YoYOptionletVolatilitySurface *qlKInterpolatedYoYOptionletVolatilitySurfaceAux(
unsigned settlementDays, const Calendar &cal, BusinessDayConvention bdc, const DayCounter &dc,
const shared_ptr<YoYCapFloorTermPriceSurface> &capFloorPrices,
const shared_ptr<YoYInflationCapFloorEngine> &engine, double slope,
int interpolator1D, int approximator, int approximatorArg) {
auto make = [&](auto i) -> YoYOptionletVolatilitySurface* {
return makeKInterpolatedYoYOptionletVolatilitySurface(settlementDays, cal, bdc, dc,
capFloorPrices, engine, slope, i);
};
switch (interpolator1D) {
case hasquant::BackwardFlat: return make(BackwardFlat());
case hasquant::ForwardFlat: return make(ForwardFlat());
case hasquant::Linear: return make(Linear());
case hasquant::LogLinear: return make(LogLinear());
case hasquant::Cubic: return make(makeCubic<Cubic>(approximator, approximatorArg));
// LogCubic cannot instantiate InterpolatedYoYOptionletStripper: its virtual initialize()
// default-constructs the interpolator, but QuantLib's LogCubic requires DerivativeApprox.
case hasquant::LogCubic:
QL_FAIL("LogCubic cannot back InterpolatedYoYOptionletStripper/KInterpolatedYoYOptionletVolatilitySurface -- "
"see the comment above this case");
default:
QL_FAIL("Unsupported interpolation " << interpolator1D);
}
}
// ZabrSmileSection<Evaluation> is templated over the evaluation-tag axis: four type-only C++ tags
// (no runtime state to carry), same Tag<T> idiom as dispatchTrait above. The explicit leading Ret
// template argument is load-bearing here too -- see the comment above dispatchInterpolation.
template <class Ret, class F>
Ret dispatchZabrEvaluation(int evaluation, F&& make) {
switch (evaluation) {
case hasquant::ZabrShortMaturityLognormal: return make(Tag<ZabrShortMaturityLognormal>());
case hasquant::ZabrShortMaturityNormal: return make(Tag<ZabrShortMaturityNormal>());
case hasquant::ZabrLocalVolatility: return make(Tag<ZabrLocalVolatility>());
case hasquant::ZabrFullFd: return make(Tag<ZabrFullFd>());
default:
QL_FAIL("Unsupported ZABR evaluation " << evaluation);
}
}
SmileSection *qlZabrSmileSectionAux(int evaluation, double timeToExpiry, double forward,
const std::vector<Real> ¶ms, const std::vector<Real> &moneyness, Size fdRefinement) {
return dispatchZabrEvaluation<SmileSection*>(evaluation, [&](auto tag) {
using Evaluation = typename decltype(tag)::type;
return new ZabrSmileSection<Evaluation>(timeToExpiry, forward, params, moneyness, fdRefinement);
});
}
SmileSection *qlZabrSmileSectionAux1(int evaluation, const Date &d, double forward,
const std::vector<Real> ¶ms, const DayCounter &dc, const std::vector<Real> &moneyness,
Size fdRefinement) {
return dispatchZabrEvaluation<SmileSection*>(evaluation, [&](auto tag) {
using Evaluation = typename decltype(tag)::type;
return new ZabrSmileSection<Evaluation>(d, forward, params, dc, moneyness, fdRefinement);
});
}
ZabrInterpolatedSmileSectionHandle *qlZabrInterpolatedSmileSectionAux(
int evaluation, const Date &optionDate, const Handle<Quote> &forward,
const std::vector<Rate> &strikes, bool hasFloatingStrikes, const Handle<Quote> &atmVolatility,
const std::vector<Handle<Quote>> &volHandles, Real alpha, Real beta, Real nu, Real rho, Real gamma,
bool isAlphaFixed, bool isBetaFixed, bool isNuFixed, bool isRhoFixed, bool isGammaFixed,
bool vegaWeighted, const shared_ptr<EndCriteria> &endCriteria,
const shared_ptr<OptimizationMethod> &method, const DayCounter &dc) {
return dispatchZabrEvaluation<ZabrInterpolatedSmileSectionHandle*>(evaluation, [&](auto tag) {
using Evaluation = typename decltype(tag)::type;
auto section = ext::make_shared<ZabrInterpolatedSmileSection<Evaluation>>(
optionDate, forward, strikes, hasFloatingStrikes, atmVolatility, volHandles,
alpha, beta, nu, rho, gamma, isAlphaFixed, isBetaFixed, isNuFixed, isRhoFixed, isGammaFixed,
vegaWeighted, endCriteria, method, dc);
section->atmLevel(); // force calibration now, surfacing failures at construction
return new ZabrInterpolatedSmileSectionHandle{
section,
[section]{ return section->alpha(); }, [section]{ return section->beta(); },
[section]{ return section->nu(); }, [section]{ return section->rho(); },
[section]{ return section->gamma(); }, [section]{ return section->rmsError(); },
[section]{ return section->maxError(); }, [section]{ return section->endCriteria(); }};
});
}
/* vim: set ft=cpp ff=unix ts=8 sts=2 sw=2 et: */