Skip to content

Commit

Permalink
Fix lower/upper bounds for global bootstrap (#2120)
Browse files Browse the repository at this point in the history
  • Loading branch information
lballabio authored Nov 28, 2024
2 parents ce96c8e + c987e2d commit e44806f
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 9 deletions.
15 changes: 8 additions & 7 deletions ql/termstructures/globalbootstrap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ template <class Curve> class GlobalBootstrap {
The additional helpers are treated like the usual rate helpers, but no standard pillar dates are added for them.
WARNING: This class is known to work with Traits Discount, ZeroYield, Forward (i.e. the usual traits for IR curves
in QL), it might fail for other traits - check the usage of Traits::updateGuess(), Traits::guess(),
Traits::minValueAfter(), Traits::maxValueAfter() in this class against them.
WARNING: This class is known to work with Traits Discount, ZeroYield, Forward, i.e. the usual traits for IR curves
in QL. It requires Traits::minValueGlobal() and Traits::maxValueGlobal() to be implemented. Also, check the usage
of Traits::updateGuess(), Traits::guess() in this class.
*/
GlobalBootstrap(std::vector<ext::shared_ptr<typename Traits::helper> > additionalHelpers,
std::function<std::vector<Date>()> additionalDates,
Expand Down Expand Up @@ -246,9 +246,8 @@ template <class Curve> void GlobalBootstrap<Curve>::calculate() const {
const Size numberBounds = ts_->times_.size() - 1;
std::vector<Real> lowerBounds(numberBounds), upperBounds(numberBounds);
for (Size i = 0; i < numberBounds; ++i) {
// just pass zero as the first alive helper, it's not used in the standard QL traits anyway
lowerBounds[i] = Traits::minValueAfter(i + 1, ts_, validCurve_, 0);
upperBounds[i] = Traits::maxValueAfter(i + 1, ts_, validCurve_, 0);
lowerBounds[i] = Traits::minValueGlobal(i + 1, ts_, validCurve_);
upperBounds[i] = Traits::maxValueGlobal(i + 1, ts_, validCurve_);
}

// setup cost function
Expand Down Expand Up @@ -284,7 +283,9 @@ template <class Curve> void GlobalBootstrap<Curve>::calculate() const {
Array guess(numberBounds);
for (Size i = 0; i < numberBounds; ++i) {
// just pass zero as the first alive helper, it's not used in the standard QL traits anyway
guess[i] = transformInverse(Traits::guess(i + 1, ts_, validCurve_, 0), i);
// 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] = transformInverse(ts_->data_[i + 1], i);
}

// setup problem
Expand Down
52 changes: 50 additions & 2 deletions ql/termstructures/yield/bootstraptraits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace QuantLib {
namespace detail {
const Real avgRate = 0.05;
const Real maxRate = 1.0;
const Real maxDF = 10.0;
}

//! Discount-curve traits
Expand Down Expand Up @@ -101,6 +102,18 @@ namespace QuantLib {
return c->data()[i-1] * std::exp(detail::maxRate * dt);
}

// possible constraints for global optimization
template <class C>
static Real minValueGlobal(Size i, const C* c, bool validData)
{
return 0;
}
template <class C>
static Real maxValueGlobal(Size i, const C* c, bool validData)
{
return detail::maxDF;
}

// root-finding update
static void updateGuess(std::vector<Real>& data,
Real discount,
Expand Down Expand Up @@ -180,6 +193,18 @@ namespace QuantLib {
return detail::maxRate;
}

// possible constraints for global optimization
template <class C>
static Real minValueGlobal(Size i, const C* c, bool validData)
{
return -detail::maxRate;
}
template <class C>
static Real maxValueGlobal(Size i, const C* c, bool validData)
{
return detail::maxRate;
}

// root-finding update
static void updateGuess(std::vector<Real>& data,
Real rate,
Expand Down Expand Up @@ -261,6 +286,18 @@ namespace QuantLib {
return detail::maxRate;
}

// possible constraints for global optimization
template <class C>
static Real minValueGlobal(Size i, const C* c, bool validData)
{
return -detail::maxRate;
}
template <class C>
static Real maxValueGlobal(Size i, const C* c, bool validData)
{
return detail::maxRate;
}

// root-finding update
static void updateGuess(std::vector<Real>& data,
Real forward,
Expand Down Expand Up @@ -327,8 +364,7 @@ namespace QuantLib {
// We choose as min a value very unlikely to be exceeded.
result = -detail::maxRate;
}
Real t = c->timeFromReference(c->dates()[i]);
return std::max(result, -1.0 / t + 1E-8);
return std::max(result, -1.0 / c->times()[i] + 1E-8);
}
template <class C>
static Real maxValueAfter(Size,
Expand All @@ -345,6 +381,18 @@ namespace QuantLib {
return detail::maxRate;
}

// possible constraints for global optimization
template <class C>
static Real minValueGlobal(Size i, const C* c, bool validData)
{
return std::max(-detail::maxRate, -1.0 / c->times()[i] + 1E-8);
}
template <class C>
static Real maxValueGlobal(Size i, const C* c, bool validData)
{
return detail::maxRate;
}

// root-finding update
static void updateGuess(std::vector<Real>& data,
Real rate,
Expand Down

0 comments on commit e44806f

Please sign in to comment.