Exemplo n.º 1
0
        void addEffectiveInterestRateAmortizing()
        {
            // Amortizing Schedule
            Schedule schedule = new Schedule(_tradeDate, _maturityDate, new Period(_payFrequency),
                                             _calendar, BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted,
                                             DateGeneration.Rule.Backward, false);
            double currentNominal = _marketValue;
            Date   prevDate       = _tradeDate;
            Date   actualDate     = _tradeDate;

            for (int i = 1; i < schedule.Count; ++i)
            {
                actualDate = schedule[i];
                InterestRate    rate = new InterestRate(_yield, _dCounter, Compounding.Simple, Frequency.Annual);
                InterestRate    rate2 = new InterestRate(_couponRate, _dCounter, Compounding.Simple, Frequency.Annual);
                FixedRateCoupon r, r2;
                if (i > 1)
                {
                    r  = new FixedRateCoupon(actualDate, currentNominal, rate, prevDate, actualDate, prevDate, actualDate);
                    r2 = new FixedRateCoupon(actualDate, currentNominal, rate2, prevDate, actualDate, prevDate, actualDate, null, _originalPayment);
                }

                else
                {
                    Calendar nullCalendar = new NullCalendar();
                    Period   p1           = new Period(_payFrequency);
                    Date     testDate     = nullCalendar.advance(actualDate, -1 * p1);

                    r  = new FixedRateCoupon(actualDate, currentNominal, rate, testDate, actualDate, prevDate, actualDate);
                    r2 = new FixedRateCoupon(actualDate, currentNominal, rate2, testDate, actualDate, prevDate, actualDate, null, _originalPayment);
                }

                double amort = Math.Round(Math.Abs(_originalPayment - r.amount()), 2);

                AmortizingPayment p = new AmortizingPayment(amort, actualDate);
                if (_isPremium)
                {
                    currentNominal -= Math.Abs(amort);
                }
                else
                {
                    currentNominal += Math.Abs(amort);
                }


                cashflows_.Add(r2);
                cashflows_.Add(p);
                prevDate = actualDate;
            }

            // Add single redemption for yield calculation
            setSingleRedemption(_faceValue, 100, _maturityDate);
        }
Exemplo n.º 2
0
        public Schedule value()
        {
            // check for mandatory arguments
            Utils.QL_REQUIRE(effectiveDate_ != null, () => "effective date not provided");
            Utils.QL_REQUIRE(terminationDate_ != null, () => "termination date not provided");
            Utils.QL_REQUIRE((object)tenor_ != null, () => "tenor/frequency not provided");

            // if no calendar was set...
            if (calendar_ == null)
            {
                // ...we use a null one.
                calendar_ = new NullCalendar();
            }

            // set dynamic defaults:
            BusinessDayConvention convention;

            // if a convention was set, we use it.
            if (convention_ != null)
            {
                convention = convention_.Value;
            }
            else
            {
                if (!calendar_.empty())
                {
                    // ...if we set a calendar, we probably want it to be used;
                    convention = BusinessDayConvention.Following;
                }
                else
                {
                    // if not, we don't care.
                    convention = BusinessDayConvention.Unadjusted;
                }
            }

            BusinessDayConvention terminationDateConvention;

            // if set explicitly, we use it;
            if (terminationDateConvention_ != null)
            {
                terminationDateConvention = terminationDateConvention_.Value;
            }
            else
            {
                // Unadjusted as per ISDA specification
                terminationDateConvention = convention;
            }

            return(new Schedule(effectiveDate_, terminationDate_, tenor_, calendar_,
                                convention, terminationDateConvention,
                                rule_, endOfMonth_, firstDate_, nextToLastDate_));
        }
Exemplo n.º 3
0
        /// <summary>
        /// rule based constructor
        /// </summary>
        /// <param name="effectiveDate"></param>
        /// <param name="terminationDate"></param>
        /// <param name="tenor"></param>
        /// <param name="calendar"></param>
        /// <param name="convention"></param>
        /// <param name="terminationDateConvention"></param>
        /// <param name="rule"></param>
        /// <param name="endOfMonth"></param>
        /// <param name="firstDate"></param>
        /// <param name="nextToLastDate"></param>
        public Schedule(Date effectiveDate,
                        Date terminationDate,
                        Period tenor,
                        Calendar calendar,
                        BusinessDayConvention convention,
                        BusinessDayConvention terminationDateConvention,
                        DateGeneration.Rule rule,
                        bool endOfMonth,
                        Date firstDate      = null,
                        Date nextToLastDate = null)
        {
            calendar_       = calendar ?? new NullCalendar();
            firstDate_      = firstDate == effectiveDate ? null : firstDate;
            nextToLastDate_ = nextToLastDate == terminationDate ? null : nextToLastDate;

            tenor_      = tenor;
            convention_ = convention;
            terminationDateConvention_ = terminationDateConvention;
            rule_       = rule;
            endOfMonth_ = allowsEndOfMonth(tenor) && endOfMonth;

            // sanity checks
            Utils.QL_REQUIRE(terminationDate != null, () => "null termination date");

            // in many cases (e.g. non-expired bonds) the effective date is not
            // really necessary. In these cases a decent placeholder is enough
            if (effectiveDate == null && firstDate == null && rule == DateGeneration.Rule.Backward)
            {
                Date evalDate = Settings.Instance.evaluationDate();
                Utils.QL_REQUIRE(evalDate < terminationDate, () => "null effective date", QLNetExceptionEnum.NullEffectiveDate);
                int y;
                if (nextToLastDate != null)
                {
                    y             = (nextToLastDate - evalDate) / 366 + 1;
                    effectiveDate = nextToLastDate - new Period(y, TimeUnit.Years);
                }
                else
                {
                    y             = (terminationDate - evalDate) / 366 + 1;
                    effectiveDate = terminationDate - new Period(y, TimeUnit.Years);
                }
                // More accurate , is the previous coupon date
                if (effectiveDate > evalDate)
                {
                    effectiveDate = effectiveDate - new Period(tenor_.length(), TimeUnit.Months);
                }
                else if (effectiveDate + new Period(tenor_.length(), TimeUnit.Months) < evalDate)
                {
                    effectiveDate = effectiveDate + new Period(tenor_.length(), TimeUnit.Months);
                }
            }
            else
            {
                Utils.QL_REQUIRE(effectiveDate != null, () => "null effective date", QLNetExceptionEnum.NullEffectiveDate);
            }

            Utils.QL_REQUIRE(effectiveDate < terminationDate, () =>
                             "effective date (" + effectiveDate +
                             ") later than or equal to termination date (" +
                             terminationDate + ")"
                             );

            if (tenor_.length() == 0)
            {
                rule_ = DateGeneration.Rule.Zero;
            }
            else
            {
                Utils.QL_REQUIRE(tenor.length() > 0, () => "non positive tenor (" + tenor + ") not allowed");
            }

            if (firstDate_ != null)
            {
                switch (rule_.Value)
                {
                case DateGeneration.Rule.Backward:
                case DateGeneration.Rule.Forward:
                    Utils.QL_REQUIRE(firstDate_ > effectiveDate &&
                                     firstDate_ < terminationDate, () =>
                                     "first date (" + firstDate_ + ") out of effective-termination date range [" +
                                     effectiveDate + ", " + terminationDate + ")");
                    // we should ensure that the above condition is still verified after adjustment
                    break;

                case DateGeneration.Rule.ThirdWednesday:
                    Utils.QL_REQUIRE(IMM.isIMMdate(firstDate_, false), () => "first date (" + firstDate_ + ") is not an IMM date");
                    break;

                case DateGeneration.Rule.Zero:
                case DateGeneration.Rule.Twentieth:
                case DateGeneration.Rule.TwentiethIMM:
                case DateGeneration.Rule.OldCDS:
                case DateGeneration.Rule.CDS:
                case DateGeneration.Rule.CDS2015:
                    Utils.QL_FAIL("first date incompatible with " + rule_.Value + " date generation rule");
                    break;

                default:
                    Utils.QL_FAIL("unknown rule (" + rule_.Value + ")");
                    break;
                }
            }

            if (nextToLastDate_ != null)
            {
                switch (rule_.Value)
                {
                case DateGeneration.Rule.Backward:
                case DateGeneration.Rule.Forward:
                    Utils.QL_REQUIRE(nextToLastDate_ > effectiveDate && nextToLastDate_ < terminationDate, () =>
                                     "next to last date (" + nextToLastDate_ + ") out of effective-termination date range (" +
                                     effectiveDate + ", " + terminationDate + "]");
                    // we should ensure that the above condition is still verified after adjustment
                    break;

                case DateGeneration.Rule.ThirdWednesday:
                    Utils.QL_REQUIRE(IMM.isIMMdate(nextToLastDate_, false), () => "next-to-last date (" + nextToLastDate_ +
                                     ") is not an IMM date");
                    break;

                case DateGeneration.Rule.Zero:
                case DateGeneration.Rule.Twentieth:
                case DateGeneration.Rule.TwentiethIMM:
                case DateGeneration.Rule.OldCDS:
                case DateGeneration.Rule.CDS:
                case DateGeneration.Rule.CDS2015:
                    Utils.QL_FAIL("next to last date incompatible with " + rule_.Value + " date generation rule");
                    break;

                default:
                    Utils.QL_FAIL("unknown rule (" + rule_.Value + ")");
                    break;
                }
            }

            // calendar needed for endOfMonth adjustment
            Calendar nullCalendar = new NullCalendar();
            int      periods = 1;
            Date     seed = new Date(), exitDate = new Date();

            switch (rule_.Value)
            {
            case DateGeneration.Rule.Zero:
                tenor_ = new Period(0, TimeUnit.Years);
                dates_.Add(effectiveDate);
                dates_.Add(terminationDate);
                isRegular_.Add(true);
                break;

            case DateGeneration.Rule.Backward:
                dates_.Add(terminationDate);

                seed = terminationDate;
                if (nextToLastDate_ != null)
                {
                    dates_.Insert(0, nextToLastDate_);
                    Date temp = nullCalendar.advance(seed, -periods * tenor_, convention_, endOfMonth_.Value);
                    if (temp != nextToLastDate_)
                    {
                        isRegular_.Insert(0, false);
                    }
                    else
                    {
                        isRegular_.Insert(0, true);
                    }
                    seed = nextToLastDate_;
                }
                exitDate = effectiveDate;
                if (firstDate_ != null)
                {
                    exitDate = firstDate_;
                }

                while (true)
                {
                    Date temp = nullCalendar.advance(seed, -periods * tenor_, convention_, endOfMonth_.Value);
                    if (temp < exitDate)
                    {
                        if (firstDate_ != null && (calendar_.adjust(dates_.First(), convention_) !=
                                                   calendar_.adjust(firstDate_, convention_)))
                        {
                            dates_.Insert(0, firstDate_);
                            isRegular_.Insert(0, false);
                        }
                        break;
                    }
                    else
                    {
                        // skip dates that would result in duplicates
                        // after adjustment
                        if (calendar_.adjust(dates_.First(), convention_) != calendar_.adjust(temp, convention_))
                        {
                            dates_.Insert(0, temp);
                            isRegular_.Insert(0, true);
                        }
                        ++periods;
                    }
                }

                if (calendar_.adjust(dates_.First(), convention) != calendar_.adjust(effectiveDate, convention))
                {
                    dates_.Insert(0, effectiveDate);
                    isRegular_.Insert(0, false);
                }

                break;

            case DateGeneration.Rule.Twentieth:
            case DateGeneration.Rule.TwentiethIMM:
            case DateGeneration.Rule.ThirdWednesday:
            case DateGeneration.Rule.OldCDS:
            case DateGeneration.Rule.CDS:
            case DateGeneration.Rule.CDS2015:
                Utils.QL_REQUIRE(!endOfMonth, () => "endOfMonth convention incompatible with " + rule_.Value + " date generation rule");
                goto case DateGeneration.Rule.Forward;   // fall through

            case DateGeneration.Rule.Forward:
                if (rule_.Value == DateGeneration.Rule.CDS ||
                    rule_.Value == DateGeneration.Rule.CDS2015)
                {
                    dates_.Add(previousTwentieth(effectiveDate, rule_.Value));
                }
                else
                {
                    dates_.Add(effectiveDate);
                }

                seed = dates_.Last();
                if (firstDate_ != null)
                {
                    dates_.Add(firstDate_);
                    Date temp = nullCalendar.advance(seed, periods * tenor_, convention_, endOfMonth_.Value);
                    if (temp != firstDate_)
                    {
                        isRegular_.Add(false);
                    }
                    else
                    {
                        isRegular_.Add(true);
                    }
                    seed = firstDate_;
                }
                else if (rule_.Value == DateGeneration.Rule.Twentieth ||
                         rule_.Value == DateGeneration.Rule.TwentiethIMM ||
                         rule_.Value == DateGeneration.Rule.OldCDS ||
                         rule_.Value == DateGeneration.Rule.CDS ||
                         rule_.Value == DateGeneration.Rule.CDS2015)
                {
                    Date next20th = nextTwentieth(effectiveDate, rule_.Value);
                    if (rule_ == DateGeneration.Rule.OldCDS)
                    {
                        // distance rule inforced in natural days
                        long stubDays = 30;
                        if (next20th - effectiveDate < stubDays)
                        {
                            // +1 will skip this one and get the next
                            next20th = nextTwentieth(next20th + 1, rule_.Value);
                        }
                    }
                    if (next20th != effectiveDate)
                    {
                        dates_.Add(next20th);
                        isRegular_.Add(false);
                        seed = next20th;
                    }
                }

                exitDate = terminationDate;
                if (nextToLastDate_ != null)
                {
                    exitDate = nextToLastDate_;
                }
                if (rule_ == DateGeneration.Rule.CDS2015 &&
                    nextTwentieth(terminationDate, rule_.Value) == terminationDate &&
                    terminationDate.month() % 2 == 1)
                {
                    exitDate = nextTwentieth(terminationDate + 1, rule_.Value);
                }
                while (true)
                {
                    Date temp = nullCalendar.advance(seed, periods * tenor_, convention_, endOfMonth_.Value);
                    if (temp > exitDate)
                    {
                        if (nextToLastDate_ != null &&
                            (calendar_.adjust(dates_.Last(), convention_) != calendar_.adjust(nextToLastDate_, convention_)))
                        {
                            dates_.Add(nextToLastDate_);
                            isRegular_.Add(false);
                        }
                        break;
                    }
                    else
                    {
                        // skip dates that would result in duplicates
                        // after adjustment
                        if (calendar_.adjust(dates_.Last(), convention_) != calendar_.adjust(temp, convention_))
                        {
                            dates_.Add(temp);
                            isRegular_.Add(true);
                        }
                        ++periods;
                    }
                }

                if (calendar_.adjust(dates_.Last(), terminationDateConvention_.Value) !=
                    calendar_.adjust(terminationDate, terminationDateConvention_.Value))
                {
                    if (rule_.Value == DateGeneration.Rule.Twentieth ||
                        rule_.Value == DateGeneration.Rule.TwentiethIMM ||
                        rule_.Value == DateGeneration.Rule.OldCDS ||
                        rule_.Value == DateGeneration.Rule.CDS)
                    {
                        dates_.Add(nextTwentieth(terminationDate, rule_.Value));
                        isRegular_.Add(true);
                    }
                    else if (rule_ == DateGeneration.Rule.CDS2015)
                    {
                        Date tentativeTerminationDate = nextTwentieth(terminationDate, rule_.Value);
                        if (tentativeTerminationDate.month() % 2 == 0)
                        {
                            dates_.Add(tentativeTerminationDate);
                            isRegular_.Add(true);
                        }
                    }
                    else
                    {
                        dates_.Add(terminationDate);
                        isRegular_.Add(false);
                    }
                }
                break;

            default:
                Utils.QL_FAIL("unknown rule (" + rule_.Value + ")");
                break;
            }

            // adjustments
            if (rule_ == DateGeneration.Rule.ThirdWednesday)
            {
                for (int i = 1; i < dates_.Count - 1; ++i)
                {
                    dates_[i] = Date.nthWeekday(3, DayOfWeek.Wednesday, dates_[i].Month, dates_[i].Year);
                }
            }

            if (endOfMonth && calendar_.isEndOfMonth(seed))
            {
                // adjust to end of month
                if (convention_ == BusinessDayConvention.Unadjusted)
                {
                    for (int i = 1; i < dates_.Count - 1; ++i)
                    {
                        dates_[i] = Date.endOfMonth(dates_[i]);
                    }
                }
                else
                {
                    for (int i = 1; i < dates_.Count - 1; ++i)
                    {
                        dates_[i] = calendar_.endOfMonth(dates_[i]);
                    }
                }
                if (terminationDateConvention_ != BusinessDayConvention.Unadjusted)
                {
                    dates_[0] = calendar_.endOfMonth(dates_.First());
                    dates_[dates_.Count - 1] = calendar_.endOfMonth(dates_.Last());
                }
                else
                {
                    // the termination date is the first if going backwards,
                    // the last otherwise.
                    if (rule_ == DateGeneration.Rule.Backward)
                    {
                        dates_[dates_.Count - 1] = Date.endOfMonth(dates_.Last());
                    }
                    else
                    {
                        dates_[0] = Date.endOfMonth(dates_.First());
                    }
                }
            }
            else
            {
                // first date not adjusted for CDS schedules
                if (rule_ != DateGeneration.Rule.OldCDS)
                {
                    dates_[0] = calendar_.adjust(dates_[0], convention_);
                }
                for (int i = 1; i < dates_.Count - 1; ++i)
                {
                    dates_[i] = calendar_.adjust(dates_[i], convention_);
                }

                // termination date is NOT adjusted as per ISDA specifications, unless otherwise specified in the
                // confirmation of the deal or unless we're creating a CDS schedule
                if (terminationDateConvention_.Value != BusinessDayConvention.Unadjusted &&
                    rule_.Value != DateGeneration.Rule.CDS &&
                    rule_.Value != DateGeneration.Rule.CDS2015)
                {
                    dates_[dates_.Count - 1] = calendar_.adjust(dates_.Last(), terminationDateConvention_.Value);
                }
            }

            // Final safety checks to remove extra next-to-last date, if
            // necessary.  It can happen to be equal or later than the end
            // date due to EOM adjustments (see the Schedule test suite
            // for an example).
            if (dates_.Count >= 2 && dates_[dates_.Count - 2] >= dates_.Last())
            {
                isRegular_[isRegular_.Count - 2] = (dates_[dates_.Count - 2] == dates_.Last());
                dates_[dates_.Count - 2]         = dates_.Last();

                dates_.RemoveAt(dates_.Count - 1);
                isRegular_.RemoveAt(isRegular_.Count - 1);
            }

            if (dates_.Count >= 2 && dates_[1] <= dates_.First())
            {
                isRegular_[1] = (dates_[1] == dates_.First());
                dates_[1]     = dates_.First();
                dates_.RemoveAt(0);
                isRegular_.RemoveAt(0);
            }

            Utils.QL_REQUIRE(dates_.Count > 1,
                             () => "degenerate single date (" + dates_[0] + ") schedule" +
                             "\n seed date: " + seed +
                             "\n exit date: " + exitDate +
                             "\n effective date: " + effectiveDate +
                             "\n first date: " + firstDate +
                             "\n next to last date: " + nextToLastDate +
                             "\n termination date: " + terminationDate +
                             "\n generation rule: " + rule_.Value +
                             "\n end of month: " + endOfMonth_.Value);
        }
Exemplo n.º 4
0
        // Index interface
        // The forecastTodaysFixing parameter (required by the Index interface) is currently ignored.
        public override double fixing(Date fixingDate, bool forecastTodaysFixing = false)
        {
            Date today                     = Settings.Instance.evaluationDate();
            Date todayMinusLag             = today - availabilityLag_;
            KeyValuePair <Date, Date> limm = Utils.inflationPeriod(todayMinusLag, frequency_);
            Date lastFix                   = limm.Key - 1;

            Date flatMustForecastOn   = lastFix + 1;
            Date interpMustForecastOn = lastFix + 1 - new Period(frequency_);


            if (interpolated() && fixingDate >= interpMustForecastOn)
            {
                return(forecastFixing(fixingDate));
            }

            if (!interpolated() && fixingDate >= flatMustForecastOn)
            {
                return(forecastFixing(fixingDate));
            }

            // four cases with ratio() and interpolated()
            if (ratio())
            {
                if (interpolated())
                {
                    // IS ratio, IS interpolated
                    KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_);
                    Date fixMinus1Y = new NullCalendar().advance(fixingDate, new Period(-1, TimeUnit.Years), BusinessDayConvention.ModifiedFollowing);
                    KeyValuePair <Date, Date> limBef = Utils.inflationPeriod(fixMinus1Y, frequency_);
                    double dp    = lim.Value + 1 - lim.Key;
                    double dpBef = limBef.Value + 1 - limBef.Key;
                    double dl    = fixingDate - lim.Key;
                    // potentially does not work on 29th Feb
                    double dlBef = fixMinus1Y - limBef.Key;
                    // get the four relevant fixings
                    // recall that they are stored flat for every day
                    double?limFirstFix =
                        IndexManager.Instance.getHistory(name())[lim.Key];
                    Utils.QL_REQUIRE(limFirstFix != null, () => "Missing " + name() + " fixing for " + lim.Key);
                    double?limSecondFix =
                        IndexManager.Instance.getHistory(name())[lim.Value + 1];
                    Utils.QL_REQUIRE(limSecondFix != null, () => "Missing " + name() + " fixing for " + lim.Value + 1);
                    double?limBefFirstFix =
                        IndexManager.Instance.getHistory(name())[limBef.Key];
                    Utils.QL_REQUIRE(limBefFirstFix != null, () => "Missing " + name() + " fixing for " + limBef.Key);
                    double?limBefSecondFix =
                        IndexManager.Instance.getHistory(name())[limBef.Value + 1];
                    Utils.QL_REQUIRE(limBefSecondFix != null, () => "Missing " + name() + " fixing for " + limBef.Value + 1);

                    double linearNow = limFirstFix.Value + (limSecondFix.Value - limFirstFix.Value) * dl / dp;
                    double linearBef = limBefFirstFix.Value + (limBefSecondFix.Value - limBefFirstFix.Value) * dlBef / dpBef;
                    double wasYES    = linearNow / linearBef - 1.0;

                    return(wasYES);
                }
                else
                {
                    // IS ratio, NOT interpolated
                    double?pastFixing = IndexManager.Instance.getHistory(name())[fixingDate];
                    Utils.QL_REQUIRE(pastFixing != null, () => "Missing " + name() + " fixing for " + fixingDate);
                    Date   previousDate   = fixingDate - new Period(1, TimeUnit.Years);
                    double?previousFixing = IndexManager.Instance.getHistory(name())[previousDate];
                    Utils.QL_REQUIRE(previousFixing != null, () => "Missing " + name() + " fixing for " + previousDate);
                    return(pastFixing.Value / previousFixing.Value - 1.0);
                }
            }
            else
            {
                // NOT ratio
                if (interpolated())
                {
                    // NOT ratio, IS interpolated
                    KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_);
                    double dp          = lim.Value + 1 - lim.Key;
                    double dl          = fixingDate - lim.Key;
                    double?limFirstFix = IndexManager.Instance.getHistory(name())[lim.Key];
                    Utils.QL_REQUIRE(limFirstFix != null, () => "Missing " + name() + " fixing for " + lim.Key);
                    double?limSecondFix = IndexManager.Instance.getHistory(name())[lim.Value + 1];
                    Utils.QL_REQUIRE(limSecondFix != null, () => "Missing " + name() + " fixing for " + lim.Value + 1);
                    double linearNow = limFirstFix.Value + (limSecondFix.Value - limFirstFix.Value) * dl / dp;
                    return(linearNow);
                }
                else
                {
                    // NOT ratio, NOT interpolated
                    // so just flat
                    double?pastFixing = IndexManager.Instance.getHistory(name())[fixingDate];
                    Utils.QL_REQUIRE(pastFixing != null, () => "Missing " + name() + " fixing for " + fixingDate);
                    return(pastFixing.Value);
                }
            }
        }