Exemplo n.º 1
0
        public double dontThrowFallback(BootstrapError <T, U> error,
                                        double xMin, double xMax,
                                        int steps)
        {
            Utils.QL_REQUIRE(xMin < xMax, () => "Expected xMin to be less than xMax");

            // Set the initial value of the result to xMin and store the absolute bootstrap error at xMin
            double result   = xMin;
            double absError = Math.Abs(error.value(xMin));
            double minError = absError;

            // Step out to xMax
            double stepSize = (xMax - xMin) / steps;

            for (int i = 0; i < steps; i++)
            {
                // Get absolute bootstrap error at updated x value
                xMin    += stepSize;
                absError = Math.Abs(error.value(xMin));

                // If this absolute bootstrap error is less than the minimum, update result and minError
                if (absError < minError)
                {
                    result   = xMin;
                    minError = absError;
                }
            }

            return(result);
        }
Exemplo n.º 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      = accuracy_ != null ? accuracy_.Value : 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_);

                List <double?> minValues = new InitializedList <double?>(alive_ + 1, null);
                List <double?> maxValues = new InitializedList <double?>(alive_ + 1, null);
                List <int>     attempts  = new InitializedList <int>(alive_ + 1, 1);

                for (int i = 1; i <= alive_; ++i)
                {
                    // shorter aliases for readability and to avoid duplication
                    double?min = minValues[i];
                    double?max = maxValues[i];

                    // bracket root and calculate guess
                    if (min == null)
                    {
                        min = (minValue_ != null ? minValue_ :
                               ts_.minValueAfter(i, ts_, validData, firstAliveHelper_));
                        max = (maxValue_ != null ? maxValue_ :
                               ts_.maxValueAfter(i, ts_, validData, firstAliveHelper_));
                    }
                    else
                    {
                        min = (min.Value < 0.0 ? min.Value * minFactor_ : min.Value / minFactor_);
                        max = (max.Value > 0.0 ? max.Value * maxFactor_ : max.Value / maxFactor_);
                    }
                    double guess = ts_.guess(i, ts_, validData, firstAliveHelper_);

                    // adjust guess if needed
                    if (guess >= max.Value)
                    {
                        guess = max.Value - (max.Value - min.Value) / 5.0;
                    }
                    else if (guess <= min.Value)
                    {
                        guess = min.Value + (max.Value - min.Value) / 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_);
                        }
                        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_.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.Value, max.Value);
                        }
                        else
                        {
                            ts_.data_[i] = firstSolver_.solve(error, accuracy, guess, min.Value, max.Value);
                        }
                    }
                    catch (Exception e)
                    {
                        if (validCurve_)
                        {
                            // the previous curve state might have been a
                            // bad guess, so we retry without using it.
                            // This would be tricky to do here (we're
                            // inside multiple nested for loops, we need
                            // to re-initialize...), so we invalidate the
                            // curve, make a recursive call and then exit.
                            validCurve_ = initialized_ = false;
                            calculate();
                            return;
                        }

                        // If we have more attempts left on this iteration, try again. Note that the max and min
                        // bounds will be widened on the retry.
                        if (attempts[i] < maxAttempts_)
                        {
                            attempts[i]++;
                            i--;
                            continue;
                        }

                        if (dontThrow_)
                        {
                            // Use the fallback value
                            var error = new BootstrapError <T, U>(ts_, ts_.instruments_[i - 1], i);
                            ts_.data_[i] = dontThrowFallback(error, min.Value, max.Value, dontThrowSteps_);

                            // Remember to update the interpolation. If we don't and we are on the last "i", we will still
                            // have the last attempted value in the solver being used in ts_->interpolation_.
                            ts_.interpolation_.update();
                        }
                        else
                        {
                            Utils.QL_FAIL((iteration + 1) + " iteration: failed " +
                                          "at " + (i) + " alive instrument, " +
                                          "pillar " + ts_.instruments_[i - 1].pillarDate() + ", " +
                                          "maturity " + ts_.instruments_[i - 1].maturityDate() +
                                          ", 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;
        }