Exemple #1
0
        private void initialize()
        {
            // ensure helpers are sorted
            ts_.instruments_.Sort((x, y) => x.pillarDate().CompareTo(y.pillarDate()));

            // skip expired helpers
            Date firstDate = ts_.initialDate();

            Utils.QL_REQUIRE(ts_.instruments_[n_ - 1].pillarDate() > firstDate, () => "all instruments expired");
            firstAliveHelper_ = 0;
            while (ts_.instruments_[firstAliveHelper_].pillarDate() <= firstDate)
            {
                ++firstAliveHelper_;
            }
            alive_ = n_ - firstAliveHelper_;
            Utils.QL_REQUIRE(alive_ >= ts_.interpolator_.requiredPoints - 1, () =>
                             "not enough alive instruments: " + alive_ +
                             " provided, " + (ts_.interpolator_.requiredPoints - 1) +
                             " required");


            if (ts_.dates_ == null)
            {
                ts_.dates_ = new InitializedList <Date>(alive_ + 1);
                ts_.times_ = new InitializedList <double>(alive_ + 1);
            }
            else if (ts_.dates_.Count != alive_ + 1)
            {
                ts_.dates_.Resize(alive_ + 1);
                ts_.times_.Resize(alive_ + 1);
            }

            List <Date>   dates = ts_.dates_;
            List <double> times = ts_.times_;


            errors_  = new List <BootstrapError <T, U> >(alive_ + 1);
            dates[0] = firstDate;
            times[0] = (ts_.timeFromReference(dates[0]));
            Date latestRelevantDate, maxDate = firstDate;

            for (int i = 1, j = firstAliveHelper_; j < n_; ++i, ++j)
            {
                BootstrapHelper <U> helper = ts_.instruments_[j];
                dates[i] = (helper.pillarDate());
                times[i] = (ts_.timeFromReference(dates[i]));
                // check for duplicated maturity
                Utils.QL_REQUIRE(dates[i - 1] != dates[i], () => "more than one instrument with maturity " + dates[i]);
                latestRelevantDate = helper.latestRelevantDate();
                // check that the helper is really extending the curve, i.e. that
                // pillar-sorted helpers are also sorted by latestRelevantDate
                Utils.QL_REQUIRE(latestRelevantDate > maxDate, () =>
                                 (j + 1) + " instrument (pillar: " +
                                 dates[i] + ") has latestRelevantDate (" +
                                 latestRelevantDate + ") before or equal to " +
                                 "previous instrument's latestRelevantDate (" +
                                 maxDate + ")");
                maxDate = latestRelevantDate;

                // when a pillar date is different from the last relevant date the
                // convergence loop is required even if the Interpolator is local
                if (dates[i] != latestRelevantDate)
                {
                    loopRequired_ = true;
                }

                errors_.Add(new BootstrapError <T, U>(ts_, helper, i));
            }
            ts_.maxDate_ = maxDate;

            // set initial guess only if the current curve cannot be used as guess
            if (!validCurve_ || ts_.data_.Count != alive_ + 1)
            {
                // ts_->data_[0] is the only relevant item,
                // but reasonable numbers might be needed for the whole data vector
                // because, e.g., of interpolation's early checks
                ts_.data_     = new InitializedList <double>(alive_ + 1, ts_.initialValue());
                previousData_ = new List <double>(alive_ + 1);
            }
            initialized_ = true;
        }
Exemple #2
0
        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_ = 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;
        }