Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support error functions with arguments #2131

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
45 changes: 33 additions & 12 deletions ql/termstructures/globalbootstrap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ namespace QuantLib {
template <class Curve> class GlobalBootstrap {
typedef typename Curve::traits_type Traits; // ZeroYield, Discount, ForwardRate
typedef typename Curve::interpolator_type Interpolator; // Linear, LogLinear, ...
typedef std::function<Array(const std::vector<Time>&, const std::vector<Real>&)>
AdditionalErrors;

public:
GlobalBootstrap(Real accuracy = Null<Real>(),
Expand All @@ -60,6 +62,12 @@ template <class Curve> class GlobalBootstrap {
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,
AdditionalErrors additionalErrors,
Real accuracy = Null<Real>(),
ext::shared_ptr<OptimizationMethod> optimizer = nullptr,
ext::shared_ptr<EndCriteria> endCriteria = nullptr);
GlobalBootstrap(std::vector<ext::shared_ptr<typename Traits::helper> > additionalHelpers,
std::function<std::vector<Date>()> additionalDates,
std::function<Array()> additionalErrors,
Expand All @@ -77,7 +85,7 @@ template <class Curve> class GlobalBootstrap {
ext::shared_ptr<EndCriteria> endCriteria_;
mutable std::vector<ext::shared_ptr<typename Traits::helper> > additionalHelpers_;
std::function<std::vector<Date>()> additionalDates_;
std::function<Array()> additionalErrors_;
AdditionalErrors additionalErrors_;
mutable bool initialized_ = false, validCurve_ = false;
mutable Size firstHelper_, numberHelpers_;
mutable Size firstAdditionalHelper_, numberAdditionalHelpers_;
Expand All @@ -97,14 +105,30 @@ template <class Curve>
GlobalBootstrap<Curve>::GlobalBootstrap(
std::vector<ext::shared_ptr<typename Traits::helper> > additionalHelpers,
std::function<std::vector<Date>()> additionalDates,
std::function<Array()> additionalErrors,
AdditionalErrors additionalErrors,
Real accuracy,
ext::shared_ptr<OptimizationMethod> optimizer,
ext::shared_ptr<EndCriteria> endCriteria)
: ts_(nullptr), accuracy_(accuracy), optimizer_(std::move(optimizer)),
endCriteria_(std::move(endCriteria)), additionalHelpers_(std::move(additionalHelpers)),
additionalDates_(std::move(additionalDates)), additionalErrors_(std::move(additionalErrors)) {}

template <class Curve>
GlobalBootstrap<Curve>::GlobalBootstrap(
std::vector<ext::shared_ptr<typename Traits::helper> > additionalHelpers,
std::function<std::vector<Date>()> additionalDates,
std::function<Array()> additionalErrors,
Real accuracy,
ext::shared_ptr<OptimizationMethod> optimizer,
ext::shared_ptr<EndCriteria> endCriteria)
: GlobalBootstrap(std::move(additionalHelpers), std::move(additionalDates),
additionalErrors
? [f=std::move(additionalErrors)](const std::vector<Time>&, const std::vector<Real>&) {
return f();
}
: AdditionalErrors(),
accuracy, std::move(optimizer), std::move(endCriteria)) {}

template <class Curve> void GlobalBootstrap<Curve>::setup(Curve *ts) {
ts_ = ts;
for (Size j = 0; j < ts_->instruments_.size(); ++j)
Expand Down Expand Up @@ -264,19 +288,16 @@ template <class Curve> void GlobalBootstrap<Curve>::calculate() const {
Traits::updateGuess(ts_->data_, transformDirect(x[i], i), i + 1);
}
ts_->interpolation_.update();
std::vector<Real> result(numberHelpers_);
for (Size i = 0; i < numberHelpers_; ++i) {
result[i] = ts_->instruments_[firstHelper_ + i]->quote()->value() -
ts_->instruments_[firstHelper_ + i]->impliedQuote();
}
Array result(numberHelpers_);
std::transform(ts_->instruments_.begin() + firstHelper_, ts_->instruments_.end(),
result.begin(),
[](const auto& helper) { return helper->quoteError(); });
if (additionalErrors_) {
Array tmp = additionalErrors_();
Array tmp = additionalErrors_(ts_->times_, ts_->data_);
result.resize(numberHelpers_ + tmp.size());
for (Size i = 0; i < tmp.size(); ++i) {
result[numberHelpers_ + i] = tmp[i];
}
std::copy(tmp.begin(), tmp.end(), result.begin() + numberHelpers_);
}
return Array(result.begin(), result.end());
return result;
});

// setup guess
Expand Down
98 changes: 98 additions & 0 deletions test-suite/piecewiseyieldcurve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,104 @@ BOOST_AUTO_TEST_CASE(testGlobalBootstrap, *precondition(usingAtParCoupons())) {
}
}

BOOST_AUTO_TEST_CASE(testGlobalBootstrapPenalty, *precondition(usingAtParCoupons())) {

Settings::instance().evaluationDate() = Date(26, Sep, 2019);

// market rates
Real refMktRate[] = {-0.373, -0.388, -0.402, -0.418, -0.431, -0.441, -0.45,
-0.457, -0.463, -0.469, -0.461, -0.463, -0.479, -0.4511,
-0.45418, -0.439, -0.4124, -0.37703, -0.3335, -0.28168, -0.22725,
-0.1745, -0.12425, -0.07746, 0.0385, 0.1435, 0.17525, 0.17275,
0.1515, 0.1225, 0.095, 0.0644};

// expected outputs
Date refDate[] = {
Date(31, Mar, 2020), Date(30, Apr, 2020), Date(29, May, 2020), Date(30, Jun, 2020),
Date(31, Jul, 2020), Date(31, Aug, 2020), Date(30, Sep, 2020), Date(30, Oct, 2020),
Date(30, Nov, 2020), Date(31, Dec, 2020), Date(29, Jan, 2021), Date(26, Feb, 2021),
Date(31, Mar, 2021), Date(30, Sep, 2021), Date(30, Sep, 2022), Date(29, Sep, 2023),
Date(30, Sep, 2024), Date(30, Sep, 2025), Date(30, Sep, 2026), Date(30, Sep, 2027),
Date(29, Sep, 2028), Date(28, Sep, 2029), Date(30, Sep, 2030), Date(30, Sep, 2031),
Date(29, Sep, 2034), Date(30, Sep, 2039), Date(30, Sep, 2044), Date(30, Sep, 2049),
Date(30, Sep, 2054), Date(30, Sep, 2059), Date(30, Sep, 2064), Date(30, Sep, 2069)};

Real refZeroRateNP[] = {
-0.00373354, -0.00386194, -0.00395205, -0.00403303, -0.00408033, -0.00410875, -0.00411935,
-0.00419161, -0.00424817, -0.00429923, -0.00428029, -0.00429178, -0.00434401, -0.00445243,
-0.00448506, -0.0043369, -0.00407401, -0.00372752, -0.0033005, -0.00279139, -0.00225477,
-0.00173422, -0.00123688, -0.00077236, 0.00038550, 0.00144208, 0.00175947, 0.00172834,
0.00150757, 0.00121131, 0.00093384, 0.00062891};

Real refZeroRateGP[] = {
-0.00377892, -0.00386127, -0.00394737, -0.00402914, -0.00409541, -0.00413252, -0.00415463,
-0.00419484, -0.00424238, -0.00427875, -0.00429712, -0.00431898, -0.00436027, -0.00445297,
-0.00448502, -0.00433694, -0.00407406, -0.00372755, -0.00330018, -0.00279133, -0.00225491,
-0.00173429, -0.00123643, -0.00077298, 0.00038547, 0.00144206, 0.00175948, 0.00172834,
0.00150756, 0.00121135, 0.00093379, 0.00062895};

// build ql helpers
std::vector<ext::shared_ptr<RateHelper>> helpers;
ext::shared_ptr<IborIndex> index = ext::make_shared<Euribor>(6 * Months);

helpers.push_back(ext::make_shared<DepositRateHelper>(
refMktRate[0] / 100.0, 6 * Months, 2, TARGET(), ModifiedFollowing, true, Actual360()));

for (Size i = 0; i < 12; ++i) {
helpers.push_back(
ext::make_shared<FraRateHelper>(refMktRate[1 + i] / 100.0, (i + 1) * Months, index));
}

Size swapTenors[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 20, 25, 30, 35, 40, 45, 50};
for (Size i = 0; i < std::size(swapTenors); ++i) {
helpers.push_back(ext::make_shared<SwapRateHelper>(
refMktRate[13 + i] / 100.0, swapTenors[i] * Years, TARGET(), Annual, ModifiedFollowing,
Thirty360(Thirty360::BondBasis), index));
}

// build the curve without penalties first
typedef PiecewiseYieldCurve<ForwardRate, BackwardFlat, GlobalBootstrap> Curve;
auto curve = ext::make_shared<Curve>(
2, TARGET(), helpers, Actual365Fixed(), std::vector<Handle<Quote>>(), std::vector<Date>(),
BackwardFlat(),
Curve::bootstrap_type({}, nullptr, std::function<Array()>(), 1.0e-12));

// check expected pillar dates
for (Size i = 0; i < std::size(refDate); ++i) {
BOOST_CHECK_EQUAL(refDate[i], helpers[i]->pillarDate());
}

// check expected zero rates
for (Size i = 0; i < std::size(refZeroRateNP); ++i) {
// 0.01 basis points tolerance
QL_CHECK_SMALL(
refZeroRateNP[i] - curve->zeroRate(refDate[i], Actual360(), Continuous).rate(),
1E-6);
}

// build the curve with gradient penalties
auto gradientPenalty = [](const std::vector<Time>& times, const std::vector<Real>& data) {
Array errors(times.size() - 1);
for (Size i = 0; i < times.size() - 1; ++i) {
errors[i] = 0.01 * (data[i+1] - data[i]) / (times[i+1] - times[i]);
}
return errors;
};

curve = ext::make_shared<Curve>(
2, TARGET(), helpers, Actual365Fixed(), std::vector<Handle<Quote>>(), std::vector<Date>(),
BackwardFlat(),
Curve::bootstrap_type({}, nullptr, gradientPenalty, 1.0e-12));

// check expected zero rates
for (Size i = 0; i < std::size(refZeroRateGP); ++i) {
// 0.01 basis points tolerance
QL_CHECK_SMALL(
refZeroRateGP[i] - curve->zeroRate(refDate[i], Actual360(), Continuous).rate(),
1E-6);
}
}

/* This test attempts to build an ARS collateralised in USD curve as of 25 Sep 2019. Using the default
IterativeBootstrap with no retries, the yield curve building fails. Allowing retries, it expands the min and max
bounds and passes.
Expand Down
Loading