Пример #1
0
        private void initialize()
        {
            // ensure helpers are sorted
            ts_.instruments_.Sort((x, y) => x.latestDate().CompareTo(y.latestDate()));

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

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

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

            errors_ = new List <BootstrapError <T, U> >(alive_ + 1);
            dates.Add(firstDate);
            times.Add(ts_.timeFromReference(dates[0]));
            for (int i = 1, j = firstAliveHelper_; j < n_; ++i, ++j)
            {
                BootstrapHelper <U> helper = ts_.instruments_[j];
                dates.Add(helper.latestDate());
                times.Add(ts_.timeFromReference(dates[i]));
                // check for duplicated maturity
                Utils.QL_REQUIRE(dates[i - 1] != dates[i], () => "more than one instrument with maturity " + dates[i]);
                errors_.Add(new BootstrapError <T, U>(ts_, helper, i));
            }

            // 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;
        }
Пример #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.latestDate() + ") 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;

            for (int iteration = 0; ; ++iteration)
            {
                previousData_ = ts_.data_;

                for (int i = 1; i <= alive_; ++i)
                {
                    // pillar loop

                    bool validData = validCurve_ || iteration > 0;

                    // 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)
                    {
                        validCurve_ = false;
                        Utils.QL_FAIL((iteration + 1) + " iteration: failed " +
                                      "at " + (i) + " alive instrument, " +
                                      "maturity " + ts_.instruments_[i - 1].latestDate() +
                                      ", reference date " + ts_.dates_[0] +
                                      ": " + e.Message);
                    }
                }

                if (!ts_.interpolator_.global)
                {
                    break;                         // no need for convergence loop
                }
                else if (iteration == 0)
                {
                    continue;                     // at least one more iteration to convergence check
                }
                // 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);
            }
            validCurve_ = true;
        }