Skip to content

Commit

Permalink
Allow constraints in FittedBondDiscountCurve::FittingMethod (#1954) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
lballabio authored Aug 20, 2024
2 parents 302973d + ce726f4 commit fc4210f
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 111 deletions.
12 changes: 8 additions & 4 deletions ql/termstructures/yield/fittedbonddiscountcurve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,15 @@ namespace QuantLib {
ext::shared_ptr<OptimizationMethod> optimizationMethod,
Array l2,
const Real minCutoffTime,
const Real maxCutoffTime)
const Real maxCutoffTime,
Constraint constraint)
: constrainAtZero_(constrainAtZero), weights_(weights), l2_(std::move(l2)),
calculateWeights_(weights.empty()), optimizationMethod_(std::move(optimizationMethod)),
minCutoffTime_(minCutoffTime), maxCutoffTime_(maxCutoffTime) {}
constraint_(std::move(constraint)),
minCutoffTime_(minCutoffTime), maxCutoffTime_(maxCutoffTime) {
if (constraint_.empty())
constraint_ = NoConstraint();
}

void FittedBondDiscountCurve::FittingMethod::init() {
// yield conventions
Expand Down Expand Up @@ -188,7 +193,6 @@ namespace QuantLib {
void FittedBondDiscountCurve::FittingMethod::calculate() {

FittingCost& costFunction = *costFunction_;
Constraint constraint = NoConstraint();

// start with the guess solution, if it exists
Array x(size(), 0.0);
Expand Down Expand Up @@ -221,7 +225,7 @@ namespace QuantLib {
if(!optimization){
optimization = ext::make_shared<Simplex>(curve_->simplexLambda_);
}
Problem problem(costFunction, constraint, x);
Problem problem(costFunction, constraint_, x);

Real rootEpsilon = curve_->accuracy_;
Real functionEpsilon = curve_->accuracy_;
Expand Down
12 changes: 11 additions & 1 deletion ql/termstructures/yield/fittedbonddiscountcurve.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <ql/patterns/lazyobject.hpp>
#include <ql/math/array.hpp>
#include <ql/utilities/clone.hpp>
#include <ql/math/optimization/constraint.hpp>

namespace QuantLib {

Expand Down Expand Up @@ -209,6 +210,8 @@ namespace QuantLib {
Array l2() const;
//! return optimization method being used
ext::shared_ptr<OptimizationMethod> optimizationMethod() const;
//! return optimization contraint
const Constraint& constraint() const;
//! open discountFunction to public
DiscountFactor discount(const Array& x, Time t) const;
protected:
Expand All @@ -219,7 +222,8 @@ namespace QuantLib {
ext::shared_ptr<OptimizationMethod>(),
Array l2 = Array(),
Real minCutoffTime = 0.0,
Real maxCutoffTime = QL_MAX_REAL);
Real maxCutoffTime = QL_MAX_REAL,
Constraint constraint = NoConstraint());
//! rerun every time instruments/referenceDate changes
virtual void init();
//! discount function called by FittedBondDiscountCurve
Expand Down Expand Up @@ -257,6 +261,8 @@ namespace QuantLib {
EndCriteria::Type errorCode_ = EndCriteria::None;
// optimization method to be used, if none provided use Simplex
ext::shared_ptr<OptimizationMethod> optimizationMethod_;
// optimization constraint, if none provided use NoConstraint
Constraint constraint_;
// flat extrapolation of instantaneous forward before / after cutoff
Real minCutoffTime_, maxCutoffTime_;
};
Expand Down Expand Up @@ -329,6 +335,10 @@ namespace QuantLib {
return optimizationMethod_;
}

inline const Constraint& FittedBondDiscountCurve::FittingMethod::constraint() const {
return constraint_;
}

inline DiscountFactor FittedBondDiscountCurve::FittingMethod::discount(const Array& x, Time t) const {
if (t < minCutoffTime_) {
// flat fwd extrapolation before min cutoff time
Expand Down
159 changes: 74 additions & 85 deletions ql/termstructures/yield/nonlinearfittingmethods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,39 +33,37 @@ namespace QuantLib {
const Real minCutoffTime,
const Real maxCutoffTime,
const Size numCoeffs,
const Real fixedKappa)
: FittedBondDiscountCurve::FittingMethod(
constrainAtZero, weights, optimizationMethod, l2, minCutoffTime, maxCutoffTime),
numCoeffs_(numCoeffs), fixedKappa_(fixedKappa)
{
const Real fixedKappa,
Constraint constraint)
: FittedBondDiscountCurve::FittingMethod(constrainAtZero, weights, optimizationMethod, l2,
minCutoffTime, maxCutoffTime, std::move(constraint)),
numCoeffs_(numCoeffs), fixedKappa_(fixedKappa) {
QL_REQUIRE(ExponentialSplinesFitting::size() > 0, "At least 1 unconstrained coefficient required");
}

ExponentialSplinesFitting::ExponentialSplinesFitting(bool constrainAtZero,
ExponentialSplinesFitting::ExponentialSplinesFitting(
bool constrainAtZero,
const Array& weights,
const Array& l2, const Real minCutoffTime, const Real maxCutoffTime,
const Size numCoeffs, const Real fixedKappa)
: FittedBondDiscountCurve::FittingMethod(constrainAtZero, weights, ext::shared_ptr<OptimizationMethod>(), l2,
minCutoffTime, maxCutoffTime),
numCoeffs_(numCoeffs),fixedKappa_(fixedKappa)
{
QL_REQUIRE(ExponentialSplinesFitting::size() > 0, "At least 1 unconstrained coefficient required");
}
const Size numCoeffs, const Real fixedKappa,
Constraint constraint)
: ExponentialSplinesFitting(constrainAtZero, weights, {}, l2,
minCutoffTime, maxCutoffTime,
numCoeffs, fixedKappa, std::move(constraint)) {}

ExponentialSplinesFitting::ExponentialSplinesFitting(bool constrainAtZero,
const Size numCoeffs,
const Real fixedKappa,
const Array& weights )
: FittedBondDiscountCurve::FittingMethod(constrainAtZero, weights, ext::shared_ptr<OptimizationMethod>(), Array(),0.0,QL_MAX_REAL),
numCoeffs_(numCoeffs), fixedKappa_(fixedKappa)
{
QL_REQUIRE(ExponentialSplinesFitting::size() > 0, "At least 1 unconstrained coefficient required");
}
ExponentialSplinesFitting::ExponentialSplinesFitting(
bool constrainAtZero,
const Size numCoeffs,
const Real fixedKappa,
const Array& weights,
Constraint constraint)
: ExponentialSplinesFitting(constrainAtZero, weights, {}, Array(),
0.0, QL_MAX_REAL,
numCoeffs, fixedKappa, std::move(constraint)) {}

std::unique_ptr<FittedBondDiscountCurve::FittingMethod>
ExponentialSplinesFitting::clone() const {
return std::unique_ptr<FittedBondDiscountCurve::FittingMethod>(
new ExponentialSplinesFitting(*this));
return std::make_unique<ExponentialSplinesFitting>(*this);
}

Size ExponentialSplinesFitting::size() const {
Expand Down Expand Up @@ -107,20 +105,23 @@ namespace QuantLib {
const ext::shared_ptr<OptimizationMethod>& optimizationMethod,
const Array& l2,
const Real minCutoffTime,
const Real maxCutoffTime)
: FittedBondDiscountCurve::FittingMethod(
true, weights, optimizationMethod, l2, minCutoffTime, maxCutoffTime) {}
const Real maxCutoffTime,
Constraint constraint)
: FittedBondDiscountCurve::FittingMethod(true, weights, optimizationMethod, l2,
minCutoffTime, maxCutoffTime, std::move(constraint)) {}

NelsonSiegelFitting::NelsonSiegelFitting(const Array& weights,
const Array& l2,
const Real minCutoffTime, const Real maxCutoffTime)
: FittedBondDiscountCurve::FittingMethod(true, weights, ext::shared_ptr<OptimizationMethod>(), l2,
minCutoffTime, maxCutoffTime) {}
NelsonSiegelFitting::NelsonSiegelFitting(
const Array& weights,
const Array& l2,
const Real minCutoffTime,
const Real maxCutoffTime,
Constraint constraint)
: NelsonSiegelFitting(weights, {}, l2,
minCutoffTime, maxCutoffTime, std::move(constraint)) {}

std::unique_ptr<FittedBondDiscountCurve::FittingMethod>
NelsonSiegelFitting::clone() const {
return std::unique_ptr<FittedBondDiscountCurve::FittingMethod>(
new NelsonSiegelFitting(*this));
return std::make_unique<NelsonSiegelFitting>(*this);
}

Size NelsonSiegelFitting::size() const {
Expand All @@ -143,19 +144,22 @@ namespace QuantLib {
const ext::shared_ptr<OptimizationMethod>& optimizationMethod,
const Array& l2,
const Real minCutoffTime,
const Real maxCutoffTime)
: FittedBondDiscountCurve::FittingMethod(
true, weights, optimizationMethod, l2, minCutoffTime, maxCutoffTime) {}
const Real maxCutoffTime,
Constraint constraint)
: FittedBondDiscountCurve::FittingMethod(true, weights, optimizationMethod, l2,
minCutoffTime, maxCutoffTime, std::move(constraint)) {}

SvenssonFitting::SvenssonFitting(const Array& weights,
const Array& l2, const Real minCutoffTime, const Real maxCutoffTime)
: FittedBondDiscountCurve::FittingMethod(true, weights, ext::shared_ptr<OptimizationMethod>(), l2,
minCutoffTime, maxCutoffTime) {}
const Array& l2,
const Real minCutoffTime,
const Real maxCutoffTime,
Constraint constraint)
: SvenssonFitting(weights, {}, l2,
minCutoffTime, maxCutoffTime, std::move(constraint)) {}

std::unique_ptr<FittedBondDiscountCurve::FittingMethod>
SvenssonFitting::clone() const {
return std::unique_ptr<FittedBondDiscountCurve::FittingMethod>(
new SvenssonFitting(*this));
return std::make_unique<SvenssonFitting>(*this);
}

Size SvenssonFitting::size() const {
Expand Down Expand Up @@ -184,9 +188,10 @@ namespace QuantLib {
const ext::shared_ptr<OptimizationMethod>& optimizationMethod,
const Array& l2,
const Real minCutoffTime,
const Real maxCutoffTime)
: FittedBondDiscountCurve::FittingMethod(
constrainAtZero, weights, optimizationMethod, l2, minCutoffTime, maxCutoffTime),
const Real maxCutoffTime,
Constraint constraint)
: FittedBondDiscountCurve::FittingMethod(constrainAtZero, weights, optimizationMethod, l2,
minCutoffTime, maxCutoffTime, std::move(constraint)),
splines_(3, knots.size() - 5, knots) {

QL_REQUIRE(knots.size() >= 8,
Expand All @@ -208,43 +213,24 @@ namespace QuantLib {
}
}

CubicBSplinesFitting::CubicBSplinesFitting(const std::vector<Time>& knots,
CubicBSplinesFitting::CubicBSplinesFitting(
const std::vector<Time>& knots,
bool constrainAtZero,
const Array& weights,
const Array& l2,
const Real minCutoffTime, const Real maxCutoffTime)
: FittedBondDiscountCurve::FittingMethod(constrainAtZero, weights, ext::shared_ptr<OptimizationMethod>(), l2,
minCutoffTime, maxCutoffTime),
splines_(3, knots.size() - 5, knots) {

QL_REQUIRE(knots.size() >= 8,
"At least 8 knots are required");
Size basisFunctions = knots.size() - 4;

if (constrainAtZero) {
size_ = basisFunctions - 1;

// Note: A small but nonzero N_th basis function at t=0 may
// lead to an ill conditioned problem
N_ = 1;

QL_REQUIRE(std::abs(splines_(N_, 0.0)) > QL_EPSILON,
"N_th cubic B-spline must be nonzero at t=0");
}
else {
size_ = basisFunctions;
N_ = 0;
}
}
const Real minCutoffTime,
const Real maxCutoffTime,
Constraint constraint)
: CubicBSplinesFitting(knots, constrainAtZero, weights, {}, l2,
minCutoffTime, maxCutoffTime, std::move(constraint)) {}

Real CubicBSplinesFitting::basisFunction(Integer i, Time t) const {
return splines_(i,t);
}

std::unique_ptr<FittedBondDiscountCurve::FittingMethod>
CubicBSplinesFitting::clone() const {
return std::unique_ptr<FittedBondDiscountCurve::FittingMethod>(
new CubicBSplinesFitting(*this));
return std::make_unique<CubicBSplinesFitting>(*this);
}

Size CubicBSplinesFitting::size() const {
Expand Down Expand Up @@ -287,22 +273,26 @@ namespace QuantLib {
const ext::shared_ptr<OptimizationMethod>& optimizationMethod,
const Array& l2,
const Real minCutoffTime,
const Real maxCutoffTime)
: FittedBondDiscountCurve::FittingMethod(
constrainAtZero, weights, optimizationMethod, l2, minCutoffTime, maxCutoffTime),
const Real maxCutoffTime,
Constraint constraint)
: FittedBondDiscountCurve::FittingMethod(constrainAtZero, weights, optimizationMethod, l2,
minCutoffTime, maxCutoffTime, std::move(constraint)),
size_(constrainAtZero ? degree : degree + 1) {}

SimplePolynomialFitting::SimplePolynomialFitting(Natural degree, bool constrainAtZero,
const Array& weights, const Array& l2,
const Real minCutoffTime, const Real maxCutoffTime)
: FittedBondDiscountCurve::FittingMethod(constrainAtZero, weights,
ext::shared_ptr<OptimizationMethod>(), l2, minCutoffTime, maxCutoffTime),
size_(constrainAtZero ? degree : degree + 1) {}
SimplePolynomialFitting::SimplePolynomialFitting(
Natural degree,
bool constrainAtZero,
const Array& weights,
const Array& l2,
const Real minCutoffTime,
const Real maxCutoffTime,
Constraint constraint)
: SimplePolynomialFitting(degree, constrainAtZero, weights, {}, l2,
minCutoffTime, maxCutoffTime, std::move(constraint)) {}

std::unique_ptr<FittedBondDiscountCurve::FittingMethod>
SimplePolynomialFitting::clone() const {
return std::unique_ptr<FittedBondDiscountCurve::FittingMethod>(
new SimplePolynomialFitting(*this));
return std::make_unique<SimplePolynomialFitting>(*this);
}

Size SimplePolynomialFitting::size() const {
Expand Down Expand Up @@ -342,8 +332,7 @@ namespace QuantLib {

std::unique_ptr<FittedBondDiscountCurve::FittingMethod>
SpreadFittingMethod::clone() const {
return std::unique_ptr<FittedBondDiscountCurve::FittingMethod>(
new SpreadFittingMethod(*this));
return std::make_unique<SpreadFittingMethod>(*this);
}

Size SpreadFittingMethod::size() const {
Expand Down
Loading

0 comments on commit fc4210f

Please sign in to comment.