hasquant-0.7.0.0: cbits/qlTermStructureAux.cpp
#include <ql/shared_ptr.hpp>
#include <type_traits>
using QuantLib::ext::shared_ptr;
#include "qlTermStructureAux.h"
namespace hasquant {
#include "qlEnumObjects.h"
}
using namespace QuantLib;
// 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; };
// The shared interpolator x approximation dispatch. `make` receives a constructed interpolator
// instance, so each caller spells its curve constructor once.
//
// The result type is an explicit leading template argument
// (`dispatchInterpolation<YieldTermStructure*>(...)`), not a trailing
// `-> decltype(make(Linear()))`. That is load-bearing: a trailing return type is an *unevaluated*
// operand, so the probe call it names becomes the first instantiation of the caller's lambda for
// that interpolator -- and clang then emits the curve's constructor but never marks its vtable
// used, leaving the destructor and its thunks undefined. It surfaces as a link/dlopen failure for
// one arm only (whichever type the probe named), which reads like a QuantLib packaging problem
// rather than a bug here. Keep the explicit `Ret`; the same rule applies to every dispatcher in
// cbits/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. Pre-existing gap, preserved.
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, 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));
}
// 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, 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, 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, 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, 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, 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, Args&&... reference) {
// AdditionalErrors returns additionalHelpers.size()-2 equations; GlobalBootstrap requires
// #equations == #unknowns, so additionalDates must supply exactly that many extra unknowns
// (confirmed empirically by an earlier standalone spike, which crashed at runtime with
// QuantLib's own "less functions than available variables" until the two were matched).
// Surfaced here with a message in terms of the Haskell-visible arguments, not left to that
// internal QuantLib error.
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));
}
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) {
return piecewiseYieldCurveGlobalBootstrapFull(instr, dayCount, jumps, jumpDates,
additionalHelpers, additionalDates, accuracy, 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) {
return piecewiseYieldCurveGlobalBootstrapFull(instr, dayCount, jumps, jumpDates,
additionalHelpers, additionalDates, accuracy, 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 deliberately excluded, unlike dispatchTrait's IterativeBootstrap dispatch (which
// includes it): a standalone raw-C++ reproduction against this same installed libQuantLib (no
// hasquant involved) showed PiecewiseYieldCurve<Discount, ConvexMonotone, LocalBootstrap>
// returning wildly wrong discount factors (>1, growing with maturity) for ordinary deposit-rate
// inputs, reproducibly across both a flat 3% quote and varied per-tenor quotes, and independent
// of LocalBootstrap's accuracy parameter -- not a hasquant marshalling bug, a genuine numerical
// incompatibility between Discount's discount-factor-space guess/updateGuess and ConvexMonotone's
// localInterpolate. ForwardRate, ZeroYield and SimpleZeroYield all reproduce the expected
// 1/(1+rate*tau) values correctly under the same fixture. This matches upstream's own
// test-suite/piecewiseyieldcurve.cpp, whose only LocalBootstrap+ConvexMonotone coverage
// (testLocalBootstrapConsistency) uses ForwardRate, never Discount.
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) {
if (bootstrap == 1) {
return dispatchTraitGlobalBootstrap(trait, interpolator, accuracy, instrumentWeights,
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) {
return dispatchTraitGlobalBootstrap(trait, interpolator, accuracy, instrumentWeights,
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 *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);
});
}
// 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);
});
}
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 `{}` seasonality and the 1.0e-14 / 1.0e-12 accuracy below are upstream's own default
// arguments, spelled explicitly only because the interpolator sits after 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,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<ZeroInflationTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new PiecewiseZeroInflationCurve<decltype(i)>(referenceDate, baseDate, frequency,
dayCounter, instruments, {}, 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,
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, {}, 1.0e-12, i);
});
}
YoYInflationTermStructure *qlInterpolatedYoYInflationCurveAux(
const Date &referenceDate,
const std::vector<Date> &dates,
const std::vector<Rate> &rates,
Frequency frequency,
const DayCounter& dayCounter,
int interpolator, int approximator, int approximatorArg) {
return dispatchInterpolation<YoYInflationTermStructure*>(interpolator, approximator, approximatorArg,
[&](auto i) {
return new InterpolatedYoYInflationCurve<decltype(i)>(referenceDate, dates, rates,
frequency, dayCounter, {}, 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);
});
}
/* vim: set ft=cpp ff=unix ts=8 sts=2 sw=2 et: */