Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 46 additions & 10 deletions ql/termstructures/globalbootstrap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <ql/utilities/dataformatters.hpp>
#include <algorithm>
#include <functional>
#include <type_traits>
#include <utility>

namespace QuantLib {
Expand Down Expand Up @@ -98,9 +99,8 @@ class AdditionalBootstrapVariables {
for a concrete implementation of this interface.

WARNING: This class is known to work with Traits Discount, ZeroYield, Forward,
i.e. the usual IR curves traits in QL. It requires Traits::transformDirect()
and Traits::transformInverse() to be implemented. Also, check the usage of
Traits::updateGuess(), Traits::guess() in this class.
i.e. the usual IR curves traits in QL. For new Traits you may want to implement
Traits::transformDirect()/transformInverse()/globalGuess().
*/
template <class Curve> class GlobalBootstrap final : public MultiCurveBootstrapContributor {
typedef typename Curve::traits_type Traits; // ZeroYield, Discount, ForwardRate
Expand Down Expand Up @@ -133,6 +133,12 @@ template <class Curve> class GlobalBootstrap final : public MultiCurveBootstrapC
void calculate() const;

private:
template <class T, class = void>
static constexpr bool hasTransform = false;

template <class T, class = void>
static constexpr bool hasGlobalGuess = false;

void initialize() const;
void
setParentBootstrapper(const ext::shared_ptr<MultiCurveBootstrap>& b) const override;
Expand All @@ -158,6 +164,18 @@ template <class Curve> class GlobalBootstrap final : public MultiCurveBootstrapC

// template definitions

template <class Curve>
template <class T>
constexpr bool GlobalBootstrap<Curve>::hasTransform<
T,
std::void_t<decltype(T::transformDirect(Real(), Size(), std::declval<const Curve*>()))>> = true;

template <class Curve>
template <class T>
constexpr bool GlobalBootstrap<Curve>::hasGlobalGuess<
T,
std::void_t<decltype(T::globalGuess(Size(), std::declval<const Curve*>(), true))>> = true;

template <class Curve>
GlobalBootstrap<Curve>::GlobalBootstrap(Real accuracy,
ext::shared_ptr<OptimizationMethod> optimizer,
Expand Down Expand Up @@ -353,8 +371,13 @@ template <class Curve> Array GlobalBootstrap<Curve>::setupCostFunction() const {

// setup interpolation
if (!validCurve_) {
ts_->interpolation_ = ts_->interpolator_.interpolate(ts_->times_.begin(), ts_->times_.end(),
ts_->data_.begin());
ts_->interpolation_ = detail::interpolateWithoutUpdate(
ts_->interpolator_, ts_->times_.begin(), ts_->times_.end(), ts_->data_.begin());
if constexpr (!hasGlobalGuess<Traits>) {
// Update interpolation because Traits::guess() might rely on it. Implementing
// Traits::globalGuess() is a more efficient option.
ts_->interpolation_.update();
}
}

// Initial guess. We have guesses for the curve values first (numberPillars),
Expand All @@ -365,10 +388,19 @@ template <class Curve> Array GlobalBootstrap<Curve>::setupCostFunction() const {
}
Array guess(ts_->times_.size() - 1 + additionalGuesses.size());
for (Size i = 0; i < ts_->times_.size() - 1; ++i) {
// just pass zero as the first alive helper, it's not used in the standard QL traits anyway
// update ts_->data_ since Traits::guess() usually depends on previous values
Traits::updateGuess(ts_->data_, Traits::guess(i + 1, ts_, validCurve_, 0), i + 1);
guess[i] = Traits::transformInverse(ts_->data_[i + 1], i + 1, ts_);
Real value;
if constexpr (hasGlobalGuess<Traits>) {
value = Traits::globalGuess(i + 1, ts_, validCurve_);
} else {
// Just pass zero as the first alive helper, it's not used in the standard QL traits
// anyway. Update ts_->data_ since Traits::guess() usually depends on previous values.
Traits::updateGuess(ts_->data_, Traits::guess(i + 1, ts_, validCurve_, 0), i + 1);
value = ts_->data_[i + 1];
}
if constexpr (hasTransform<Traits>) {
value = Traits::transformInverse(value, i + 1, ts_);
}
guess[i] = value;
}
std::copy(additionalGuesses.begin(), additionalGuesses.end(),
guess.begin() + ts_->times_.size() - 1);
Expand All @@ -380,7 +412,11 @@ void GlobalBootstrap<Curve>::setCostFunctionArgument(const Array& x) const {
// x has the same layout as guess above: the first numberPillars values go into
// the curve, while the rest are new values for the additional variables.
for (Size i = 0; i < ts_->times_.size() - 1; ++i) {
Traits::updateGuess(ts_->data_, Traits::transformDirect(x[i], i + 1, ts_), i + 1);
Real value = x[i];
if constexpr (hasTransform<Traits>) {
value = Traits::transformDirect(value, i + 1, ts_);
}
Traits::updateGuess(ts_->data_, value, i + 1);
}
ts_->interpolation_.update();
if (additionalVariables_) {
Expand Down
32 changes: 14 additions & 18 deletions ql/termstructures/inflation/inflationtraits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ namespace QuantLib {

return detail::avgInflation;
}
template <class C>
static Real globalGuess(Size i,
const C* c,
bool validData)
{
return guess(i, c, validData, 0);
}

// constraints
template <class C>
Expand Down Expand Up @@ -101,15 +108,6 @@ namespace QuantLib {
if (i==1)
data[0] = level; // the first point is updated as well
}
// transformation to add constraints to an unconstrained optimization
template <class C>
static Real transformDirect(Real x, Size, const C*) {
return x;
}
template <class C>
static Real transformInverse(Real x, Size, const C*) {
return x;
}
// upper bound for convergence loop
static Size maxIterations() { return 40; }
};
Expand Down Expand Up @@ -142,6 +140,13 @@ namespace QuantLib {

return detail::avgInflation;
}
template <class C>
static Real globalGuess(Size i,
const C* c,
bool validData)
{
return guess(i, c, validData, 0);
}

// constraints
template <class C>
Expand Down Expand Up @@ -177,15 +182,6 @@ namespace QuantLib {
Size i) {
data[i] = level;
}
// transformation to add constraints to an unconstrained optimization
template <class C>
static Real transformDirect(Real x, Size, const C*) {
return x;
}
template <class C>
static Real transformInverse(Real x, Size, const C*) {
return x;
}
// upper bound for convergence loop
static Size maxIterations() { return 40; }
};
Expand Down
60 changes: 36 additions & 24 deletions ql/termstructures/yield/bootstraptraits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ namespace QuantLib {
Real r = -std::log(c->data()[i-1])/c->times()[i-1];
return std::exp(-r * c->times()[i]);
}
template <class C>
static Real globalGuess(Size i,
const C* c,
bool validData)
{
if (validData) // previous iteration value
return c->data()[i];
return std::exp(-detail::avgRate * c->times()[i]);
}

// possible constraints based on previous values
template <class C>
Expand Down Expand Up @@ -161,6 +170,15 @@ namespace QuantLib {
return c->zeroRate(d, c->dayCounter(),
Continuous, Annual, true);
}
template <class C>
static Real globalGuess(Size i,
const C* c,
bool validData)
{
if (validData) // previous iteration value
return c->data()[i];
return detail::avgRate;
}

// possible constraints based on previous values
template <class C>
Expand Down Expand Up @@ -192,18 +210,6 @@ namespace QuantLib {
return detail::maxRate;
}

// transformation to add constraints to an unconstrained optimization
template <class C>
static Real transformDirect(Real x, Size i, const C* c)
{
return x;
}
template <class C>
static Real transformInverse(Real x, Size i, const C* c)
{
return x;
}

// root-finding update
static void updateGuess(std::vector<Real>& data,
Real rate,
Expand Down Expand Up @@ -254,6 +260,15 @@ namespace QuantLib {
return c->forwardRate(d, d, c->dayCounter(),
Continuous, Annual, true);
}
template <class C>
static Real globalGuess(Size i,
const C* c,
bool validData)
{
if (validData) // previous iteration value
return c->data()[i];
return detail::avgRate;
}

// possible constraints based on previous values
template <class C>
Expand Down Expand Up @@ -285,18 +300,6 @@ namespace QuantLib {
return detail::maxRate;
}

// transformation to add constraints to an unconstrained optimization
template <class C>
static Real transformDirect(Real x, Size i, const C* c)
{
return x;
}
template <class C>
static Real transformInverse(Real x, Size i, const C* c)
{
return x;
}

// root-finding update
static void updateGuess(std::vector<Real>& data,
Real forward,
Expand Down Expand Up @@ -346,6 +349,15 @@ namespace QuantLib {
return c->zeroRate(d, c->dayCounter(),
Simple, Annual, true);
}
template <class C>
static Real globalGuess(Size i,
const C* c,
bool validData)
{
if (validData) // previous iteration value
return c->data()[i];
return detail::avgRate;
}

// possible constraints based on previous values
template <class C>
Expand Down
Loading