Beispiel #1
0
        public void calculate()
        {
            validCurve_ = false;
            int nInsts = 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 < nInsts; ++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(j => j.setTermStructure(ts_));

            // set initial guess only if the current curve cannot be used as guess
            if (validCurve_)
            {
                if (ts_.data_.Count != nInsts + 1)
                {
                    throw new ArgumentException("dimension mismatch: expected " + nInsts + 1 + ", actual " + ts_.data_.Count);
                }
            }
            else
            {
                ts_.data_    = new InitializedList <double>(nInsts + 1);
                ts_.data_[0] = ts_.initialValue(ts_);
            }

            // calculate dates and times
            ts_.dates_    = new InitializedList <Date>(nInsts + 1);
            ts_.times_    = new InitializedList <double>(nInsts + 1);
            ts_.dates_[0] = ts_.initialDate(ts_);
            ts_.times_[0] = ts_.timeFromReference(ts_.dates_[0]);
            for (i = 0; i < nInsts; ++i)
            {
                ts_.dates_[i + 1] = ts_.instruments_[i].latestDate();
                ts_.times_[i + 1] = ts_.timeFromReference(ts_.dates_[i + 1]);
                if (!validCurve_)
                {
                    ts_.data_[i + 1] = ts_.data_[i];
                }
            }

            LevenbergMarquardt solver           = new LevenbergMarquardt(ts_.accuracy_, ts_.accuracy_, ts_.accuracy_);
            EndCriteria        endCriteria      = new EndCriteria(100, 10, 0.00, ts_.accuracy_, 0.00);
            PositiveConstraint posConstraint    = new PositiveConstraint();
            NoConstraint       noConstraint     = new NoConstraint();
            Constraint         solverConstraint = forcePositive_ ? (Constraint)posConstraint : (Constraint)noConstraint;

            // now start the bootstrapping.
            int iInst = localisation_ - 1;

            int dataAdjust = (ts_.interpolator_ as ConvexMonotone).dataSizeAdjustment;

            do
            {
                int    initialDataPt = iInst + 1 - localisation_ + dataAdjust;
                Vector startArray    = new Vector(localisation_ + 1 - dataAdjust);
                for (int j = 0; j < startArray.size() - 1; ++j)
                {
                    startArray[j] = ts_.data_[initialDataPt + j];
                }

                // here we are extending the interpolation a point at a
                // time... but the local interpolator can make an
                // approximation for the final localisation period.
                // e.g. if the localisation is 2, then the first section
                // of the curve will be solved using the first 2
                // instruments... with the local interpolator making
                // suitable boundary conditions.
                ts_.interpolation_ = (ts_.interpolator_ as ConvexMonotone).localInterpolate(ts_.times_, iInst + 2, ts_.data_,
                                                                                            localisation_, ts_.interpolation_ as ConvexMonotoneInterpolation, nInsts + 1);

                if (iInst >= localisation_)
                {
                    startArray[localisation_ - dataAdjust] = ts_.guess(ts_, ts_.dates_[iInst]);
                }
                else
                {
                    startArray[localisation_ - dataAdjust] = ts_.data_[0];
                }

                var currentCost = new PenaltyFunction <PiecewiseYieldCurve>(ts_, initialDataPt, ts_.instruments_,
                                                                            iInst - localisation_ + 1, iInst + 1);
                Problem          toSolve = new Problem(currentCost, solverConstraint, startArray);
                EndCriteria.Type endType = solver.minimize(toSolve, endCriteria);

                // check the end criteria
                if (!(endType == EndCriteria.Type.StationaryFunctionAccuracy ||
                      endType == EndCriteria.Type.StationaryFunctionValue))
                {
                    throw new ApplicationException("Unable to strip yieldcurve to required accuracy ");
                }
                ++iInst;
            } while (iInst < nInsts);

            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;
        }