public static double macaulayDuration(Leg leg, InterestRate y, bool includeSettlementDateFlows, Date settlementDate, Date npvDate) { Utils.QL_REQUIRE(y.compounding() == Compounding.Compounded, () => "compounded rate required"); return((1.0 + y.rate() / (int)y.frequency()) * modifiedDuration(leg, y, includeSettlementDateFlows, settlementDate, npvDate)); }
//! Basis-point sensitivity of the cash flows. // The result is the change in NPV due to a uniform // 1-basis-point change in the rate paid by the cash // flows. The change for each coupon is discounted according // to the given constant interest rate. The result is // affected by the choice of the interest-rate compounding // and the relative frequency and day counter. public static double bps(Leg leg, InterestRate yield, bool includeSettlementDateFlows, Date settlementDate = null, Date npvDate = null) { if (leg.empty()) { return(0.0); } if (settlementDate == null) { settlementDate = Settings.Instance.evaluationDate(); } if (npvDate == null) { npvDate = settlementDate; } FlatForward flatRate = new FlatForward(settlementDate, yield.rate(), yield.dayCounter(), yield.compounding(), yield.frequency()); return(bps(leg, flatRate, includeSettlementDateFlows, settlementDate, npvDate)); }
// creator public override List <CashFlow> value() { if (couponRates_.Count == 0) { throw new ArgumentException("no coupon rates given"); } if (notionals_.Count == 0) { throw new ArgumentException("no nominals given"); } List <CashFlow> leg = new List <CashFlow>(); Calendar schCalendar = schedule_.calendar(); // first period might be short or long Date start = schedule_[0], end = schedule_[1]; Date paymentDate = calendar_.adjust(end, paymentAdjustment_); Date exCouponDate = null; InterestRate rate = couponRates_[0]; double nominal = notionals_[0]; if (exCouponPeriod_ != null) { exCouponDate = exCouponCalendar_.advance(paymentDate, -exCouponPeriod_, exCouponAdjustment_, exCouponEndOfMonth_); } if (schedule_.isRegular(1)) { if (!(firstPeriodDC_ == null || firstPeriodDC_ == rate.dayCounter())) { throw new ArgumentException("regular first coupon does not allow a first-period day count"); } leg.Add(new FixedRateCoupon(paymentDate, nominal, rate, start, end, start, end, exCouponDate)); } else { Date refer = end - schedule_.tenor(); refer = schCalendar.adjust(refer, schedule_.businessDayConvention()); InterestRate r = new InterestRate(rate.rate(), (firstPeriodDC_ == null || firstPeriodDC_.empty()) ? rate.dayCounter() : firstPeriodDC_, rate.compounding(), rate.frequency()); leg.Add(new FixedRateCoupon(paymentDate, nominal, r, start, end, refer, end, exCouponDate)); } // regular periods for (int i = 2; i < schedule_.Count - 1; ++i) { start = end; end = schedule_[i]; paymentDate = calendar_.adjust(end, paymentAdjustment_); if (exCouponPeriod_ != null) { exCouponDate = exCouponCalendar_.advance(paymentDate, -exCouponPeriod_, exCouponAdjustment_, exCouponEndOfMonth_); } if ((i - 1) < couponRates_.Count) { rate = couponRates_[i - 1]; } else { rate = couponRates_.Last(); } if ((i - 1) < notionals_.Count) { nominal = notionals_[i - 1]; } else { nominal = notionals_.Last(); } leg.Add(new FixedRateCoupon(paymentDate, nominal, rate, start, end, start, end, exCouponDate)); } if (schedule_.Count > 2) { // last period might be short or long int N = schedule_.Count; start = end; end = schedule_[N - 1]; paymentDate = calendar_.adjust(end, paymentAdjustment_); if (exCouponPeriod_ != null) { exCouponDate = exCouponCalendar_.advance(paymentDate, -exCouponPeriod_, exCouponAdjustment_, exCouponEndOfMonth_); } if ((N - 2) < couponRates_.Count) { rate = couponRates_[N - 2]; } else { rate = couponRates_.Last(); } if ((N - 2) < notionals_.Count) { nominal = notionals_[N - 2]; } else { nominal = notionals_.Last(); } InterestRate r = new InterestRate(rate.rate(), lastPeriodDC_ == null ? rate.dayCounter() : lastPeriodDC_, rate.compounding(), rate.frequency()); if (schedule_.isRegular(N - 1)) { leg.Add(new FixedRateCoupon(paymentDate, nominal, r, start, end, start, end, exCouponDate)); } else { Date refer = start + schedule_.tenor(); refer = schCalendar.adjust(refer, schedule_.businessDayConvention()); leg.Add(new FixedRateCoupon(paymentDate, nominal, r, start, end, start, refer, exCouponDate)); } } return(leg); }
public static double modifiedDuration(Leg leg, InterestRate y, bool includeSettlementDateFlows, Date settlementDate, Date npvDate) { if (leg.empty()) { return(0.0); } if (settlementDate == null) { settlementDate = Settings.Instance.evaluationDate(); } if (npvDate == null) { npvDate = settlementDate; } double P = 0.0; double t = 0.0; double dPdy = 0.0; double r = y.rate(); int N = (int)y.frequency(); Date lastDate = npvDate; DayCounter dc = y.dayCounter(); for (int i = 0; i < leg.Count; ++i) { if (leg[i].hasOccurred(settlementDate, includeSettlementDateFlows)) { continue; } double c = leg[i].amount(); if (leg[i].tradingExCoupon(settlementDate)) { c = 0.0; } t += getStepwiseDiscountTime(leg[i], dc, npvDate, lastDate); double B = y.discountFactor(t); P += c * B; switch (y.compounding()) { case Compounding.Simple: dPdy -= c * B * B * t; break; case Compounding.Compounded: dPdy -= c * t * B / (1 + r / N); break; case Compounding.Continuous: dPdy -= c * B * t; break; case Compounding.SimpleThenCompounded: if (t <= 1.0 / N) { dPdy -= c * B * B * t; } else { dPdy -= c * t * B / (1 + r / N); } break; default: Utils.QL_FAIL("unknown compounding convention (" + y.compounding() + ")"); break; } lastDate = leg[i].date(); } if (P.IsEqual(0.0)) // no cashflows { return(0.0); } return(-dPdy / P); // reverse derivative sign }
//! Cash-flow convexity public static double convexity(Leg leg, InterestRate yield, bool includeSettlementDateFlows, Date settlementDate = null, Date npvDate = null) { if (leg.empty()) { return(0.0); } if (settlementDate == null) { settlementDate = Settings.Instance.evaluationDate(); } if (npvDate == null) { npvDate = settlementDate; } DayCounter dc = yield.dayCounter(); double P = 0.0; double t = 0.0; double d2Pdy2 = 0.0; double r = yield.rate(); int N = (int)yield.frequency(); Date lastDate = npvDate; for (int i = 0; i < leg.Count; ++i) { if (leg[i].hasOccurred(settlementDate, includeSettlementDateFlows)) { continue; } double c = leg[i].amount(); if (leg[i].tradingExCoupon(settlementDate)) { c = 0.0; } t += getStepwiseDiscountTime(leg[i], dc, npvDate, lastDate); double B = yield.discountFactor(t); P += c * B; switch (yield.compounding()) { case Compounding.Simple: d2Pdy2 += c * 2.0 * B * B * B * t * t; break; case Compounding.Compounded: d2Pdy2 += c * B * t * (N * t + 1) / (N * (1 + r / N) * (1 + r / N)); break; case Compounding.Continuous: d2Pdy2 += c * B * t * t; break; case Compounding.SimpleThenCompounded: if (t <= 1.0 / N) { d2Pdy2 += c * 2.0 * B * B * B * t * t; } else { d2Pdy2 += c * B * t * (N * t + 1) / (N * (1 + r / N) * (1 + r / N)); } break; default: Utils.QL_FAIL("unknown compounding convention (" + yield.compounding() + ")"); break; } lastDate = leg[i].date(); } if (P.IsEqual(0.0)) { // no cashflows return(0.0); } return(d2Pdy2 / P); }