public void calculate() { //prepare instruments int n = ts_.instruments_.Count, i; // ensure rate helpers are sorted ts_.instruments_.Sort((x, y) => x.latestDate().CompareTo(y.latestDate())); // check that there is no instruments with the same maturity for (i = 1; i < n; ++i) { Date m1 = ts_.instruments_[i - 1].latestDate(), m2 = ts_.instruments_[i].latestDate(); if (m1 == m2) throw new ArgumentException("two instruments have the same maturity (" + m1 + ")"); } // check that there is no instruments with invalid quote if ((i = ts_.instruments_.FindIndex(x => !x.quoteIsValid())) != -1) throw new ArgumentException("instrument " + i + " (maturity: " + ts_.instruments_[i].latestDate() + ") has an invalid quote"); // setup instruments and register with them ts_.instruments_.ForEach(x => x.setTermStructure(ts_)); // calculate dates and times ts_.dates_ = new InitializedList<Date>(n + 1); ts_.times_ = new InitializedList<double>(n + 1); ts_.dates_[0] = ts_.initialDate(ts_); ts_.times_[0] = ts_.timeFromReference(ts_.dates_[0]); for (i = 0; i < n; ++i) { ts_.dates_[i + 1] = ts_.instruments_[i].latestDate(); ts_.times_[i + 1] = ts_.timeFromReference(ts_.dates_[i + 1]); } // set initial guess only if the current curve cannot be used as guess if (validCurve_) { if (ts_.data_.Count != n + 1) throw new ArgumentException("dimension mismatch: expected " + n + 1 + ", actual " + ts_.data_.Count); } else { ts_.data_ = new InitializedList<double>(n + 1); ts_.data_[0] = ts_.initialValue(ts_); for (i=0; i<n; ++i) ts_.data_[i+1] = ts_.initialGuess(); } Brent solver = new Brent(); int maxIterations = ts_.maxIterations(); for (int iteration=0; ; ++iteration) { List<double> previousData = ts_.data(); // restart from the previous interpolation if (validCurve_) { ts_.interpolation_ = ts_.interpolator_.interpolate(ts_.times_, ts_.times_.Count, ts_.data_); } for (i=1; i<n+1; ++i) { // calculate guess before extending interpolation to ensure that any extrapolation is performed // using the curve bootstrapped so far and no more RateHelper instrument = ts_.instruments_[i-1]; double guess = 0; if (validCurve_ || iteration>0) { guess = ts_.data_[i]; } else if (i==1) { guess = ts_.initialGuess(); } else { // most traits extrapolate guess = ts_.guess(ts_, ts_.dates_[i]); } // bracket double min = ts_.minValueAfter(i, ts_.data_); double max = ts_.maxValueAfter(i, ts_.data_); if (guess <= min || guess >= max) guess = (min + max) / 2.0; if (!validCurve_ && iteration == 0) { // extend interpolation a point at a time try { ts_.interpolation_ = ts_.interpolator_.interpolate(ts_.times_, i + 1, ts_.data_); } catch { if (!ts_.interpolator_.global) throw; // no chance to fix it in a later iteration // otherwise, if the target interpolation is not usable yet ts_.interpolation_ = new Linear().interpolate(ts_.times_, i + 1, ts_.data_); } } // required because we just changed the data // is it really required? ts_.interpolation_.update(); try { var error = new BootstrapError(ts_, instrument, i); double r = solver.solve(error, ts_.accuracy_, guess, min, max); // redundant assignment (as it has been already performed by BootstrapError in solve procedure), but safe ts_.data_[i] = r; } catch (Exception e) { validCurve_ = false; throw new ArgumentException(" iteration: " + iteration + 1 + "could not bootstrap the " + i + " instrument, maturity " + ts_.dates_[i] + ": " + e.Message); } } if (!ts_.interpolator_.global) break; // no need for convergence loop else if (!validCurve_ && iteration == 0) { // ensure the target interpolation is used ts_.interpolation_ = ts_.interpolator_.interpolate(ts_.times_, ts_.times_.Count, ts_.data_); // at least one more iteration is needed to check convergence continue; } // exit conditions double improvement = 0.0; for (i=1; i<n+1; ++i) improvement = Math.Max(improvement, Math.Abs(ts_.data_[i]-previousData[i])); if (improvement<=ts_.accuracy_) // convergence reached break; if (!(iteration+1 < maxIterations)) throw new ArgumentException("convergence not reached after " + iteration+1 + " iterations; last improvement " + improvement + ", required accuracy " + ts_.accuracy_); } validCurve_ = true; }
public void calculate() { // we might have to call initialize even if the curve is initialized // and not moving, just because helpers might be date relative and change // with evaluation date change. // anyway it makes little sense to use date relative helpers with a // non-moving curve if the evaluation date changes if (!initialized_ || ts_.moving_) { initialize(); } // setup helpers for (int j = firstAliveHelper_; j < n_; ++j) { BootstrapHelper <U> helper = ts_.instruments_[j]; // check for valid quote Utils.QL_REQUIRE(helper.quote().link.isValid(), () => (j + 1) + " instrument (maturity: " + helper.pillarDate() + ") has an invalid quote"); // don't try this at home! // This call creates helpers, and removes "const". // There is a significant interaction with observability. ts_.setTermStructure(ts_.instruments_[j]); } List <double> times = ts_.times_; List <double> data = ts_.data_; double accuracy = ts_.accuracy_; int maxIterations = ts_.maxIterations() - 1; // there might be a valid curve state to use as guess bool validData = validCurve_; for (int iteration = 0; ; ++iteration) { previousData_ = new List <double>(ts_.data_); for (int i = 1; i <= alive_; ++i) { // pillar loop // bracket root and calculate guess double min = ts_.minValueAfter(i, ts_, validData, firstAliveHelper_); double max = ts_.maxValueAfter(i, ts_, validData, firstAliveHelper_); double guess = ts_.guess(i, ts_, validData, firstAliveHelper_); // adjust guess if needed if (guess >= max) { guess = max - (max - min) / 5.0; } else if (guess <= min) { guess = min + (max - min) / 5.0; } // extend interpolation if needed if (!validData) { try { // extend interpolation a point at a time // including the pillar to be boostrapped ts_.interpolation_ = ts_.interpolator_.interpolate(ts_.times_, i + 1, ts_.data_); //ts_.interpolation_ = ts_.interpolator_.interpolate(times, times.Count, data); } catch (Exception) { if (!ts_.interpolator_.global) { throw; // no chance to fix it in a later iteration } // otherwise use Linear while the target // interpolation is not usable yet ts_.interpolation_ = new Linear().interpolate(ts_.times_, i + 1, ts_.data_); //ts_.interpolation_ = new Linear().interpolate(times, times.Count, data); } ts_.interpolation_.update(); } try { var error = new BootstrapError <T, U>(ts_, ts_.instruments_[i - 1], i); if (validData) { ts_.data_[i] = solver_.solve(error, accuracy, guess, min, max); } else { ts_.data_[i] = firstSolver_.solve(error, accuracy, guess, min, max); } } catch (Exception e) { // the previous curve state could have been a bad guess // let's restart without using it if (validCurve_) { validCurve_ = validData = false; continue; } Utils.QL_FAIL((iteration + 1) + " iteration: failed " + "at " + (i) + " alive instrument, " + "maturity " + ts_.instruments_[i - 1].pillarDate() + ", reference date " + ts_.dates_[0] + ": " + e.Message); } } if (!loopRequired_) { break; // no need for convergence loop } // exit condition double change = Math.Abs(data[1] - previousData_[1]); for (int i = 2; i <= alive_; ++i) { change = Math.Max(change, Math.Abs(data[i] - previousData_[i])); } if (change <= accuracy) // convergence reached { break; } Utils.QL_REQUIRE(iteration < maxIterations, () => "convergence not reached after " + iteration + " iterations; last improvement " + change + ", required accuracy " + accuracy); validData = true; } validCurve_ = true; }
public void calculate() { //prepare instruments int n = ts_.instruments_.Count, i; // ensure rate helpers are sorted ts_.instruments_.Sort((x, y) => x.latestDate().CompareTo(y.latestDate())); // check that there is no instruments with the same maturity for (i = 1; i < n; ++i) { Date m1 = ts_.instruments_[i - 1].latestDate(), m2 = ts_.instruments_[i].latestDate(); if (m1 == m2) { throw new ArgumentException("two instruments have the same maturity (" + m1 + ")"); } } // check that there is no instruments with invalid quote if ((i = ts_.instruments_.FindIndex(x => !x.quoteIsValid())) != -1) { throw new ArgumentException("instrument " + i + " (maturity: " + ts_.instruments_[i].latestDate() + ") has an invalid quote"); } // setup instruments and register with them ts_.instruments_.ForEach(x => x.setTermStructure(ts_)); // calculate dates and times ts_.dates_ = new InitializedList <Date>(n + 1); ts_.times_ = new InitializedList <double>(n + 1); ts_.dates_[0] = ts_.initialDate(ts_); ts_.times_[0] = ts_.timeFromReference(ts_.dates_[0]); for (i = 0; i < n; ++i) { ts_.dates_[i + 1] = ts_.instruments_[i].latestDate(); ts_.times_[i + 1] = ts_.timeFromReference(ts_.dates_[i + 1]); } // set initial guess only if the current curve cannot be used as guess if (validCurve_) { if (ts_.data_.Count != n + 1) { throw new ArgumentException("dimension mismatch: expected " + n + 1 + ", actual " + ts_.data_.Count); } } else { ts_.data_ = new InitializedList <double>(n + 1); ts_.data_[0] = ts_.initialValue(ts_); for (i = 0; i < n; ++i) { ts_.data_[i + 1] = ts_.initialGuess(); } } Brent solver = new Brent(); int maxIterations = ts_.maxIterations(); for (int iteration = 0; ; ++iteration) { List <double> previousData = ts_.data(); // restart from the previous interpolation if (validCurve_) { ts_.interpolation_ = ts_.interpolator_.interpolate(ts_.times_, ts_.times_.Count, ts_.data_); } for (i = 1; i < n + 1; ++i) { // calculate guess before extending interpolation to ensure that any extrapolation is performed // using the curve bootstrapped so far and no more RateHelper instrument = ts_.instruments_[i - 1]; double guess = 0; if (validCurve_ || iteration > 0) { guess = ts_.data_[i]; } else if (i == 1) { guess = ts_.initialGuess(); } else { // most traits extrapolate guess = ts_.guess(ts_, ts_.dates_[i]); } // bracket double min = ts_.minValueAfter(i, ts_.data_); double max = ts_.maxValueAfter(i, ts_.data_); if (guess <= min || guess >= max) { guess = (min + max) / 2.0; } if (!validCurve_ && iteration == 0) { // extend interpolation a point at a time try { ts_.interpolation_ = ts_.interpolator_.interpolate(ts_.times_, i + 1, ts_.data_); } catch { if (!ts_.interpolator_.global) { throw; // no chance to fix it in a later iteration } // otherwise, if the target interpolation is not usable yet ts_.interpolation_ = new Linear().interpolate(ts_.times_, i + 1, ts_.data_); } } // required because we just changed the data // is it really required? ts_.interpolation_.update(); try { var error = new BootstrapError(ts_, instrument, i); double r = solver.solve(error, ts_.accuracy_, guess, min, max); // redundant assignment (as it has been already performed by BootstrapError in solve procedure), but safe ts_.data_[i] = r; } catch (Exception e) { validCurve_ = false; throw new ArgumentException(" iteration: " + iteration + 1 + "could not bootstrap the " + i + " instrument, maturity " + ts_.dates_[i] + ": " + e.Message); } } if (!ts_.interpolator_.global) { break; // no need for convergence loop } else if (!validCurve_ && iteration == 0) { // ensure the target interpolation is used ts_.interpolation_ = ts_.interpolator_.interpolate(ts_.times_, ts_.times_.Count, ts_.data_); // at least one more iteration is needed to check convergence continue; } // exit conditions double improvement = 0.0; for (i = 1; i < n + 1; ++i) { improvement = Math.Max(improvement, Math.Abs(ts_.data_[i] - previousData[i])); } if (improvement <= ts_.accuracy_) // convergence reached { break; } if (!(iteration + 1 < maxIterations)) { throw new ArgumentException("convergence not reached after " + iteration + 1 + " iterations; last improvement " + improvement + ", required accuracy " + ts_.accuracy_); } } validCurve_ = true; }