23#ifndef quantext_iterative_bootstrap_hpp
24#define quantext_iterative_bootstrap_hpp
26#include <ql/math/interpolations/linearinterpolation.hpp>
27#include <ql/math/solvers1d/brent.hpp>
28#include <ql/math/solvers1d/finitedifferencenewtonsafe.hpp>
29#include <ql/termstructures/bootstraperror.hpp>
30#include <ql/termstructures/bootstraphelper.hpp>
31#include <ql/utilities/dataformatters.hpp>
42QuantLib::Real
dontThrowFallback(
const QuantLib::BootstrapError<Curve>& error, QuantLib::Real xMin, QuantLib::Real xMax,
43 QuantLib::Size
steps) {
45 QL_REQUIRE(xMin < xMax,
"Expected xMin to be less than xMax");
47 QuantLib::Real result = xMin;
48 QuantLib::Real minError = QL_MAX_REAL;
49 QuantLib::Real stepSize = (xMax - xMin) /
steps;
51 for (QuantLib::Size i = 0; i <=
steps; ++i) {
52 QuantLib::Real x = xMin + stepSize *
static_cast<double>(i);
53 QuantLib::Real absError = QL_MAX_REAL;
55 absError = std::abs(error(x));
59 if (absError < minError) {
78 typedef typename Curve::traits_type
Traits;
96 QuantLib::Real globalAccuracy = QuantLib::Null<QuantLib::Real>(),
bool dontThrow =
false,
97 QuantLib::Size maxAttempts = 1, QuantLib::Real maxFactor = 2.0, QuantLib::Real minFactor = 2.0,
98 QuantLib::Size dontThrowSteps = 10);
112 mutable std::vector<QuantLib::ext::shared_ptr<QuantLib::BootstrapError<Curve> > >
errors_;
122template <
class Curve>
124 QuantLib::Size maxAttempts, QuantLib::Real maxFactor,
125 QuantLib::Real minFactor, QuantLib::Size dontThrowSteps)
126 : ts_(0), n_(0), initialized_(false), validCurve_(false), loopRequired_(
Interpolator::global),
127 firstAliveHelper_(0), alive_(0), accuracy_(accuracy), globalAccuracy_(globalAccuracy), dontThrow_(dontThrow),
128 maxAttempts_(maxAttempts), maxFactor_(maxFactor), minFactor_(minFactor), dontThrowSteps_(dontThrowSteps) {}
132 n_ = ts_->instruments_.size();
133 QL_REQUIRE(n_ > 0,
"no bootstrap helpers given");
134 for (QuantLib::Size j = 0; j < n_; ++j)
135 ts_->registerWith(ts_->instruments_[j]);
141 std::sort(ts_->instruments_.begin(), ts_->instruments_.end(), QuantLib::detail::BootstrapHelperSorter());
144 QuantLib::Date firstDate = Traits::initialDate(ts_);
145 QL_REQUIRE(ts_->instruments_[n_ - 1]->pillarDate() > firstDate,
"all instruments expired");
146 firstAliveHelper_ = 0;
147 while (ts_->instruments_[firstAliveHelper_]->pillarDate() <= firstDate)
149 alive_ = n_ - firstAliveHelper_;
150 QL_REQUIRE(alive_ >= Interpolator::requiredPoints - 1,
151 "not enough alive instruments: " << alive_ <<
" provided, " << Interpolator::requiredPoints - 1
155 std::vector<QuantLib::Date>& dates = ts_->dates_;
156 std::vector<QuantLib::Time>& times = ts_->times_;
157 dates.resize(alive_ + 1);
158 times.resize(alive_ + 1);
159 errors_.resize(alive_ + 1);
160 dates[0] = firstDate;
161 times[0] = ts_->timeFromReference(dates[0]);
163 QuantLib::Date latestRelevantDate, maxDate = firstDate;
167 for (QuantLib::Size i = 1, j = firstAliveHelper_; j < n_; ++i, ++j) {
169 const QuantLib::ext::shared_ptr<typename Traits::helper>&
helper = ts_->instruments_[j];
170 dates[i] =
helper->pillarDate();
171 times[i] = ts_->timeFromReference(dates[i]);
174 QL_REQUIRE(dates[i - 1] != dates[i],
"more than one instrument with pillar " << dates[i]);
176 latestRelevantDate =
helper->latestRelevantDate();
179 QL_REQUIRE(latestRelevantDate > maxDate, QuantLib::io::ordinal(j + 1)
180 <<
" instrument (pillar: " << dates[i]
181 <<
") has latestRelevantDate (" << latestRelevantDate
182 <<
") before or equal to "
183 "previous instrument's latestRelevantDate ("
185 maxDate = latestRelevantDate;
189 if (dates[i] != latestRelevantDate)
190 loopRequired_ =
true;
192 errors_[i] = QuantLib::ext::make_shared<QuantLib::BootstrapError<Curve> >(ts_,
helper, i);
194 ts_->maxDate_ = maxDate;
197 if (!validCurve_ || ts_->data_.size() != alive_ + 1) {
201 ts_->data_ = std::vector<QuantLib::Real>(alive_ + 1, Traits::initialValue(ts_));
202 previousData_.resize(alive_ + 1);
214 if (!initialized_ || ts_->moving_)
218 for (QuantLib::Size j = firstAliveHelper_; j < n_; ++j) {
219 const QuantLib::ext::shared_ptr<typename Traits::helper>&
helper = ts_->instruments_[j];
222 QL_REQUIRE(
helper->quote()->isValid(), QuantLib::io::ordinal(j + 1)
223 <<
" instrument (maturity: " <<
helper->maturityDate()
224 <<
", pillar: " <<
helper->pillarDate() <<
") has an invalid quote");
229 helper->setTermStructure(
const_cast<Curve*
>(ts_));
232 const std::vector<QuantLib::Time>& times = ts_->times_;
233 const std::vector<QuantLib::Real>& data = ts_->data_;
234 QuantLib::Real accuracy = accuracy_ != QuantLib::Null<QuantLib::Real>() ? accuracy_ : ts_->accuracy_;
235 QuantLib::Real globalAccuracy = globalAccuracy_ == QuantLib::Null<QuantLib::Real>() ? accuracy : globalAccuracy_;
237 QuantLib::Size maxIterations = Traits::maxIterations() - 1;
240 bool validData = validCurve_;
242 for (QuantLib::Size iteration = 0;; ++iteration) {
243 previousData_ = ts_->data_;
245 std::vector<QuantLib::Real> minValues(alive_, QuantLib::Null<QuantLib::Real>());
246 std::vector<QuantLib::Real> maxValues(alive_, QuantLib::Null<QuantLib::Real>());
247 std::vector<QuantLib::Size> attempts(alive_, 1);
249 for (QuantLib::Size i = 1; i <= alive_; ++i) {
252 if (minValues[i - 1] == QuantLib::Null<QuantLib::Real>()) {
253 minValues[i - 1] = Traits::minValueAfter(i, ts_, validData, firstAliveHelper_);
256 minValues[i - 1] < 0.0 ? minFactor_ * minValues[i - 1] : minValues[i - 1] / minFactor_;
258 if (maxValues[i - 1] == QuantLib::Null<QuantLib::Real>()) {
259 maxValues[i - 1] = Traits::maxValueAfter(i, ts_, validData, firstAliveHelper_);
262 maxValues[i - 1] > 0.0 ? maxFactor_ * maxValues[i - 1] : maxValues[i - 1] / maxFactor_;
264 QuantLib::Real guess = Traits::guess(i, ts_, validData, firstAliveHelper_);
267 if (guess >= maxValues[i - 1])
268 guess = maxValues[i - 1] - (maxValues[i - 1] - minValues[i - 1]) / 5.0;
269 else if (guess <= minValues[i - 1])
270 guess = minValues[i - 1] + (maxValues[i - 1] - minValues[i - 1]) / 5.0;
276 ts_->interpolation_ =
277 ts_->interpolator_.interpolate(times.begin(), times.begin() + i + 1, data.begin());
279 if (!Interpolator::global)
284 ts_->interpolation_ =
285 QuantLib::Linear().interpolate(times.begin(), times.begin() + i + 1, data.begin());
287 ts_->interpolation_.update();
292 solver_.solve(*errors_[i], accuracy, guess, minValues[i - 1], maxValues[i - 1]);
294 firstSolver_.solve(*errors_[i], accuracy, guess, minValues[i - 1], maxValues[i - 1]);
295 }
catch (std::exception& e) {
303 validCurve_ = initialized_ =
false;
310 if (attempts[i - 1] < maxAttempts_) {
323 ts_->interpolation_.update();
325 QL_FAIL(QuantLib::io::ordinal(iteration + 1)
326 <<
" iteration: failed "
328 << QuantLib::io::ordinal(i)
329 <<
" alive instrument, "
331 << errors_[i]->
helper()->pillarDate() <<
", maturity "
332 << errors_[i]->
helper()->maturityDate() <<
", reference date " << ts_->dates_[0] <<
": "
342 QuantLib::Real change = std::fabs(data[1] - previousData_[1]);
343 for (QuantLib::Size i = 2; i <= alive_; ++i)
344 change = std::max(change, std::fabs(data[i] - previousData_[i]));
346 if (change <= globalAccuracy || change <= accuracy)
350 if (iteration == maxIterations) {
354 QL_FAIL(
"convergence not reached after " << iteration <<
" iterations; last improvement " << change
355 <<
", required accuracy "
356 << std::max(globalAccuracy, accuracy));
QuantLib::Brent firstSolver_
QuantLib::Real globalAccuracy_
QuantLib::Size maxAttempts_
IterativeBootstrap(QuantLib::Real accuracy=QuantLib::Null< QuantLib::Real >(), QuantLib::Real globalAccuracy=QuantLib::Null< QuantLib::Real >(), bool dontThrow=false, QuantLib::Size maxAttempts=1, QuantLib::Real maxFactor=2.0, QuantLib::Real minFactor=2.0, QuantLib::Size dontThrowSteps=10)
QuantLib::FiniteDifferenceNewtonSafe solver_
Curve::interpolator_type Interpolator
std::vector< QuantLib::Real > previousData_
QuantLib::Real maxFactor_
Curve::traits_type Traits
std::vector< QuantLib::ext::shared_ptr< QuantLib::BootstrapError< Curve > > > errors_
QuantLib::Size dontThrowSteps_
QuantLib::Real minFactor_
QuantLib::Size firstAliveHelper_
QuantLib::Real dontThrowFallback(const QuantLib::BootstrapError< Curve > &error, QuantLib::Real xMin, QuantLib::Real xMax, QuantLib::Size steps)
QuantLib::BootstrapHelper< QuantLib::OptionletVolatilityStructure > helper
std::vector< Size > steps