protected override double zeroYieldImpl(double t) { // to be fixed: user-defined daycounter should be used InterestRate zeroRate = originalCurve_.link.zeroRate(t, comp_, freq_, true); InterestRate spreadedRate = new InterestRate(zeroRate.value() + spread_.value(), zeroRate.dayCounter(), zeroRate.compounding(), zeroRate.frequency()); return spreadedRate.equivalentRate(t, Compounding.Continuous, Frequency.NoFrequency).value(); }
// constructors public FixedRateCoupon(double nominal, Date paymentDate, double rate, DayCounter dayCounter, Date accrualStartDate, Date accrualEndDate, Date refPeriodStart = null, Date refPeriodEnd = null,double? amount = null) : base(nominal, paymentDate, accrualStartDate, accrualEndDate, refPeriodStart, refPeriodEnd, amount) { rate_ = new InterestRate(rate, dayCounter, Compounding.Simple,Frequency.Annual); }
public FixedRateCoupon(double nominal, Date paymentDate, InterestRate interestRate, Date accrualStartDate, Date accrualEndDate, Date refPeriodStart = null, Date refPeriodEnd = null,double? amount = null) : base(nominal, paymentDate, accrualStartDate, accrualEndDate, refPeriodStart, refPeriodEnd, amount) { rate_ = interestRate; }
public static double modifiedDuration(List<CashFlow> cashflows, InterestRate y, Date settlementDate) { if (cashflows.Count == 0) return 0.0; double P = 0.0; double dPdy = 0.0; double r = y.rate(); int N = (int)y.frequency(); DayCounter dc = y.dayCounter(); foreach (CashFlow cf in cashflows.Where(cf => !cf.hasOccurred(settlementDate))) { double t = dc.yearFraction(settlementDate, cf.date()); double c = cf.amount(); 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: throw new ArgumentException("unknown compounding convention (" + y.compounding() + ")"); } } if (P == 0.0) // no cashflows return 0.0; return -dPdy / P; // reverse derivative sign }
private void CHECK_NPV(bool includeRef, double expected, InterestRate no_discount, List<CashFlow> leg,Date today) { do { double NPV = CashFlows.npv(leg, no_discount, includeRef, today); if (Math.Abs(NPV - expected) > 1e-6) { Assert.Fail("NPV mismatch:\n" + " calculated: " + NPV + "\n" + " expected: " + expected); } } while (false); }
public static double simpleDuration(List<CashFlow> cashflows, InterestRate y, Date settlementDate) { if (cashflows.Count == 0) return 0.0; double P = 0, dPdy = 0; DayCounter dc = y.dayCounter(); foreach (CashFlow cf in cashflows.Where(cf => !cf.hasOccurred(settlementDate))) { double t = dc.yearFraction(settlementDate, cf.date()); double c = cf.amount(); double B = y.discountFactor(t); P += c * B; dPdy += t * c * B; } // no cashflows if (P == 0.0) return 0.0; return dPdy / P; }
public void testBondFromScheduleWithDateVector() { // Testing South African R2048 bond price using Schedule constructor with Date vector SavedSettings backup = new SavedSettings(); //When pricing bond from Yield To Maturity, use NullCalendar() Calendar calendar = new NullCalendar(); int settlementDays = 3; Date issueDate = new Date(29, Month.June, 2012); Date today = new Date(7, Month.September, 2015); Date evaluationDate = calendar.adjust(today); Date settlementDate = calendar.advance(evaluationDate, new Period(settlementDays, TimeUnit.Days)); Settings.setEvaluationDate(evaluationDate); // For the schedule to generate correctly for Feb-28's, make maturity date on Feb 29 Date maturityDate = new Date(29, Month.February, 2048); double coupon = 0.0875; Compounding comp = Compounding.Compounded; Frequency freq = Frequency.Semiannual; DayCounter dc = new ActualActual(ActualActual.Convention.Bond); // Yield as quoted in market InterestRate yield = new InterestRate(0.09185, dc, comp, freq); Period tenor = new Period(6, TimeUnit.Months); Period exCouponPeriod = new Period(10, TimeUnit.Days); // Generate coupon dates for 31 Aug and end of Feb each year // For leap years, this will generate 29 Feb, but the bond // actually pays coupons on 28 Feb, regardsless of whether // it is a leap year or not. Schedule schedule = new Schedule(issueDate, maturityDate, tenor, new NullCalendar(), BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, true); // Adjust the 29 Feb's to 28 Feb List<Date> dates = new List<Date>(); for (int i = 0; i < schedule.Count; ++i) { Date d = schedule.date(i); if (d.Month == 2 && d.Day == 29) dates.Add(new Date(28, Month.February, d.Year)); else dates.Add(d); } schedule = new Schedule(dates, schedule.calendar(), schedule.businessDayConvention(), schedule.terminationDateBusinessDayConvention(), schedule.tenor(), schedule.rule(), schedule.endOfMonth(), schedule.isRegular()); FixedRateBond bond = new FixedRateBond( 0, 100.0, schedule, new List<double>() { coupon }, dc, BusinessDayConvention.Following, 100.0, issueDate, calendar, exCouponPeriod, calendar, BusinessDayConvention.Unadjusted, false); double calculatedPrice = BondFunctions.dirtyPrice(bond, yield, settlementDate); double expectedPrice = 95.75706; double tolerance = 1e-5; if (Math.Abs(calculatedPrice - expectedPrice) > tolerance) { Assert.Fail(string.Format("failed to reproduce R2048 dirty price\nexpected: {0}\ncalculated: {1}", expectedPrice, calculatedPrice)); } }
//! 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.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); }
//! Cash-flow convexity /*! The convexity of a string of cash flows is defined as * \f[ * C = \frac{1}{P} \frac{\partial^2 P}{\partial y^2} * \f] * where \f$ P \f$ is the present value of the cash flows according to the given IRR \f$ y \f$. */ public static double convexity(List <CashFlow> cashflows, InterestRate rate, Date settlementDate = null) { if (cashflows.Count == 0) { return(0.0); } if (settlementDate == null) { settlementDate = Settings.evaluationDate(); } DayCounter dayCounter = rate.dayCounter(); double P = 0; double d2Pdy2 = 0; double y = rate.rate(); int N = (int)rate.frequency(); foreach (CashFlow cashflow in cashflows.Where(cashflow => !cashflow.hasOccurred(settlementDate))) { double t = dayCounter.yearFraction(settlementDate, cashflow.Date); double c = cashflow.amount(); double B = rate.discountFactor(t); P += c * B; switch (rate.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 + y / N) * (1 + y / 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 + y / N) * (1 + y / N)); } break; default: throw new ArgumentException("unknown compounding convention (" + rate.compounding() + ")"); } } // no cashflows if (P == 0) { return(0); } return(d2Pdy2 / P); }
public override double value(double y) { InterestRate yield = new InterestRate(y, dayCounter_, compounding_, frequency_); double NPV = CashFlows.npv(leg_, yield, includeSettlementDateFlows_, settlementDate_, npvDate_); return npv_ - NPV; }
public static double simpleDuration(Leg leg,InterestRate y, bool includeSettlementDateFlows, Date settlementDate,Date npvDate) { if (leg.empty()) return 0.0; if (settlementDate == null) settlementDate = Settings.evaluationDate(); if (npvDate == null) npvDate = settlementDate; double P = 0.0; double dPdy = 0.0; double t = 0.0; Date lastDate = npvDate; Date refStartDate, refEndDate; 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; } Date couponDate = leg[i].date(); Coupon coupon = leg[i] as Coupon; if (coupon != null) { refStartDate = coupon.refPeriodStart; refEndDate = coupon.refPeriodEnd; } else { if (lastDate == npvDate) { // we don't have a previous coupon date, // so we fake it refStartDate = couponDate - new Period(1,TimeUnit.Years); } else { refStartDate = lastDate; } refEndDate = couponDate; } t += dc.yearFraction(lastDate, couponDate, refStartDate, refEndDate); double B = y.discountFactor(t); P += c * B; dPdy += t * c * B; lastDate = couponDate; } if (P == 0.0) // no cashflows return 0.0; return dPdy/P; }
public override double derivative(double y) { InterestRate yield = new InterestRate(y, dayCounter_, compounding_, frequency_); return(modifiedDuration(leg_, yield, includeSettlementDateFlows_, settlementDate_, npvDate_)); }
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.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; Date refStartDate, refEndDate; 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; } Date couponDate = leg[i].date(); Coupon coupon = leg[i] as Coupon; if (coupon != null) { refStartDate = coupon.refPeriodStart; refEndDate = coupon.refPeriodEnd; } else { if (lastDate == npvDate) { // we don't have a previous coupon date, // so we fake it refStartDate = couponDate - new Period(1,TimeUnit.Years); } else { refStartDate = lastDate; } refEndDate = couponDate; } t += dc.yearFraction(lastDate, couponDate, refStartDate, refEndDate); 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 = couponDate; } if (P == 0.0) // no cashflows return 0.0; return -dPdy/P; // reverse derivative sign }
public FixedRateLeg withCouponRates(InterestRate couponRate) { couponRates_.Clear(); couponRates_.Add(couponRate); return this; }
protected override void performCalculations() { rate_ = new InterestRate(forward_.value(), dayCounter(), compounding_, frequency_); }
public override double derivative(double x) { InterestRate y = new InterestRate(x, dayCounter_, compounding_, frequency_); return modifiedDuration(cashflows_, y, settlementDate_); }
// 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(List<CashFlow> cashflows, InterestRate irr, Date settlementDate = null) { if (settlementDate == null) settlementDate = Settings.evaluationDate(); var flatRate = new FlatForward(settlementDate, irr.rate(), irr.dayCounter(), irr.compounding(), irr.frequency()); return bps(cashflows, flatRate, settlementDate, settlementDate); }
//! 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.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); }
public static double cleanPrice(Bond bond, InterestRate yield, Date settlementDate = null) { return(dirtyPrice(bond, yield, settlementDate) - bond.accruedAmount(settlementDate)); }
public static double dirtyPriceFromYield(double faceAmount, List <CashFlow> cashflows, double yield, DayCounter dayCounter, Compounding compounding, Frequency frequency, Date settlement) { if (frequency == Frequency.NoFrequency || frequency == Frequency.Once) { frequency = Frequency.Annual; } InterestRate y = new InterestRate(yield, dayCounter, compounding, frequency); double price = 0.0; double discount = 1.0; Date lastDate = null; for (int i = 0; i < cashflows.Count - 1; ++i) { if (cashflows[i].hasOccurred(settlement)) { continue; } Date couponDate = cashflows[i].Date; double amount = cashflows[i].amount(); if (lastDate == null) { // first not-expired coupon if (i > 0) { lastDate = cashflows[i - 1].Date; } else { if (cashflows[i].GetType().IsSubclassOf(typeof(Coupon))) { lastDate = ((Coupon)cashflows[i]).accrualStartDate(); } else { lastDate = couponDate - new Period(1, TimeUnit.Years); } } discount *= y.discountFactor(settlement, couponDate, lastDate, couponDate); } else { discount *= y.discountFactor(lastDate, couponDate); } lastDate = couponDate; price += amount * discount; } CashFlow redemption = cashflows.Last(); if (!redemption.hasOccurred(settlement)) { Date redemptionDate = redemption.Date; double amount = redemption.amount(); if (lastDate == null) { // no coupons lastDate = redemptionDate - new Period(1, TimeUnit.Years); discount *= y.discountFactor(settlement, redemptionDate, lastDate, redemptionDate); } else { discount *= y.discountFactor(lastDate, redemptionDate); } price += amount * discount; } return(price / faceAmount * 100.0); }
// 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_); InterestRate rate = couponRates_[0]; double nominal = notionals_[0]; 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(nominal, paymentDate, rate, start, end, start, end)); } 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(nominal, paymentDate, r, start, end, refer, end)); } // regular periods for (int i = 2; i < schedule_.Count - 1; ++i) { start = end; end = schedule_[i]; paymentDate = calendar_.adjust(end, paymentAdjustment_); 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(nominal, paymentDate, rate, start, end, start, end)); } 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 ((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(); } if (schedule_.isRegular(N - 1)) { leg.Add(new FixedRateCoupon(nominal, paymentDate, rate, start, end, start, end)); } else { Date refer = start + schedule_.tenor(); refer = schCalendar.adjust(refer, schedule_.businessDayConvention()); leg.Add(new FixedRateCoupon(nominal, paymentDate, rate, start, end, start, refer)); } } return(leg); }
public FixedRateLeg withCouponRates(InterestRate couponRate) { couponRates_.Clear(); couponRates_.Add(couponRate); return(this); }
// 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_); InterestRate rate = couponRates_[0]; double nominal = notionals_[0]; 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(nominal, paymentDate, rate, start, end, start, end)); } 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(nominal, paymentDate, r, start, end, refer, end)); } // regular periods for (int i=2; i<schedule_.Count-1; ++i) { start = end; end = schedule_[i]; paymentDate = calendar_.adjust(end, paymentAdjustment_); 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(nominal, paymentDate, rate, start, end, start, end)); } 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 ((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(); if (schedule_.isRegular(N-1)) leg.Add(new FixedRateCoupon(nominal, paymentDate, rate, start, end, start, end)); else { Date refer = start + schedule_.tenor(); refer = schCalendar.adjust(refer, schedule_.businessDayConvention()); leg.Add(new FixedRateCoupon(nominal, paymentDate, rate, start, end, start, refer)); } } return leg; }
public void testSettings() { // Testing cash-flow settings... SavedSettings backup = new SavedSettings(); Date today = Date.Today; Settings.setEvaluationDate(today); // cash flows at T+0, T+1, T+2 List<CashFlow> leg = new List<CashFlow>(); for (int i=0; i<3; ++i) leg.Add(new SimpleCashFlow(1.0, today+i)); // case 1: don't include reference-date payments, no override at // today's date Settings.includeReferenceDateEvents = false; Settings.includeTodaysCashFlows = null; CHECK_INCLUSION(0, 0, false,leg,today); CHECK_INCLUSION(0, 1, false,leg,today); CHECK_INCLUSION(1, 0, true,leg,today); CHECK_INCLUSION(1, 1, false,leg,today); CHECK_INCLUSION(1, 2, false,leg,today); CHECK_INCLUSION(2, 1, true,leg,today); CHECK_INCLUSION(2, 2, false,leg,today); CHECK_INCLUSION(2, 3, false,leg,today); // case 2: same, but with explicit setting at today's date Settings.includeReferenceDateEvents = false; Settings.includeTodaysCashFlows = false; CHECK_INCLUSION(0, 0, false,leg,today); CHECK_INCLUSION(0, 1, false,leg,today); CHECK_INCLUSION(1, 0, true,leg,today); CHECK_INCLUSION(1, 1, false,leg,today); CHECK_INCLUSION(1, 2, false,leg,today); CHECK_INCLUSION(2, 1, true,leg,today); CHECK_INCLUSION(2, 2, false,leg,today); CHECK_INCLUSION(2, 3, false,leg,today); // case 3: do include reference-date payments, no override at // today's date Settings.includeReferenceDateEvents = true; Settings.includeTodaysCashFlows = null; CHECK_INCLUSION(0, 0, true,leg,today); CHECK_INCLUSION(0, 1, false,leg,today); CHECK_INCLUSION(1, 0, true,leg,today); CHECK_INCLUSION(1, 1, true,leg,today); CHECK_INCLUSION(1, 2, false,leg,today); CHECK_INCLUSION(2, 1, true,leg,today); CHECK_INCLUSION(2, 2, true,leg,today); CHECK_INCLUSION(2, 3, false,leg,today); // case 4: do include reference-date payments, explicit (and same) // setting at today's date Settings.includeReferenceDateEvents = true; Settings.includeTodaysCashFlows = true; CHECK_INCLUSION(0, 0, true,leg,today); CHECK_INCLUSION(0, 1, false,leg,today); CHECK_INCLUSION(1, 0, true,leg,today); CHECK_INCLUSION(1, 1, true,leg,today); CHECK_INCLUSION(1, 2, false,leg,today); CHECK_INCLUSION(2, 1, true,leg,today); CHECK_INCLUSION(2, 2, true,leg,today); CHECK_INCLUSION(2, 3, false,leg,today); // case 5: do include reference-date payments, override at // today's date Settings.includeReferenceDateEvents = true; Settings.includeTodaysCashFlows = false; CHECK_INCLUSION(0, 0, false,leg,today); CHECK_INCLUSION(0, 1, false,leg,today); CHECK_INCLUSION(1, 0, true,leg,today); CHECK_INCLUSION(1, 1, true,leg,today); CHECK_INCLUSION(1, 2, false,leg,today); CHECK_INCLUSION(2, 1, true,leg,today); CHECK_INCLUSION(2, 2, true,leg,today); CHECK_INCLUSION(2, 3, false,leg,today); // no discount to make calculations easier InterestRate no_discount = new InterestRate(0.0, new Actual365Fixed(), Compounding.Continuous, Frequency.Annual); // no override Settings.includeTodaysCashFlows = null; CHECK_NPV(false, 2.0,no_discount,leg,today); CHECK_NPV(true, 3.0,no_discount,leg,today); // override Settings.includeTodaysCashFlows = false; CHECK_NPV(false, 2.0,no_discount,leg,today); CHECK_NPV(true, 2.0,no_discount,leg,today); }
//! Basis-point value /*! Obtained by setting dy = 0.0001 in the 2nd-order Taylor series expansion. */ public static double basisPointValue(Leg leg,InterestRate yield,bool includeSettlementDateFlows, Date settlementDate = null,Date npvDate = null) { if (leg.empty()) return 0.0; if (settlementDate == null) settlementDate = Settings.evaluationDate(); if (npvDate == null) npvDate = settlementDate; double npv = CashFlows.npv(leg, yield,includeSettlementDateFlows,settlementDate, npvDate); double modifiedDuration = CashFlows.duration(leg, yield, Duration.Type.Modified,includeSettlementDateFlows, settlementDate, npvDate); double convexity = CashFlows.convexity(leg, yield, includeSettlementDateFlows, settlementDate, npvDate); double delta = -modifiedDuration*npv; double gamma = (convexity/100.0)*npv; double shift = 0.0001; delta *= shift; gamma *= shift*shift; return delta + 0.5*gamma; }
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.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 }
public void testBrazilianCached() { //("Testing Brazilian public bond prices against cached values..."); CommonVars vars = new CommonVars(); double faceAmount = 1000.0; double redemption = 100.0; Date issueDate = new Date(1, Month.January, 2007); Date today = new Date(6, Month.June, 2007); Settings.setEvaluationDate(today); // NTN-F maturity dates InitializedList<Date> maturityDates = new InitializedList<Date>(6); maturityDates[0] = new Date(1, Month.January, 2008); maturityDates[1] = new Date(1, Month.January, 2010); maturityDates[2] = new Date(1, Month.July, 2010); maturityDates[3] = new Date(1, Month.January, 2012); maturityDates[4] = new Date(1, Month.January, 2014); maturityDates[5] = new Date(1, Month.January, 2017); // NTN-F yields InitializedList<double> yields = new InitializedList<double>(6); yields[0] = 0.114614; yields[1] = 0.105726; yields[2] = 0.105328; yields[3] = 0.104283; yields[4] = 0.103218; yields[5] = 0.102948; // NTN-F prices InitializedList<double> prices = new InitializedList<double>(6); prices[0] = 1034.63031372; prices[1] = 1030.09919487; prices[2] = 1029.98307160; prices[3] = 1028.13585068; prices[4] = 1028.33383817; prices[5] = 1026.19716497; int settlementDays = 1; vars.faceAmount = 1000.0; // The tolerance is high because Andima truncate yields double tolerance = 1.0e-4; InitializedList<InterestRate> couponRates = new InitializedList<InterestRate>(1); couponRates[0] = new InterestRate(0.1, new Thirty360(), Compounding.Compounded, Frequency.Annual); for (int bondIndex = 0; bondIndex < maturityDates.Count; bondIndex++) { // plain InterestRate yield = new InterestRate(yields[bondIndex], new Business252(new Brazil()), Compounding.Compounded, Frequency.Annual); Schedule schedule = new Schedule(new Date(1, Month.January, 2007), maturityDates[bondIndex], new Period(Frequency.Semiannual), new Brazil(Brazil.Market.Settlement), BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); FixedRateBond bond = new FixedRateBond(settlementDays, faceAmount, schedule, couponRates, BusinessDayConvention.Following, redemption, issueDate); double cachedPrice = prices[bondIndex]; double price = vars.faceAmount * (bond.cleanPrice(yield.rate(), yield.dayCounter(), yield.compounding(), yield.frequency(), today) + bond.accruedAmount(today)) / 100; if (Math.Abs(price - cachedPrice) > tolerance) { Assert.Fail("failed to reproduce cached price:\n" + " calculated: " + price + "\n" + " expected: " + cachedPrice + "\n" + " error: " + (price - cachedPrice) + "\n" ); } } }
////! Internal rate of return. ///*! The IRR is the interest rate at which the NPV of the cash flows equals the given market price. The function verifies // the theoretical existance of an IRR and numerically establishes the IRR to the desired precision. */ //public static double irr(List<CashFlow> cashflows, double marketPrice, DayCounter dayCounter, Compounding compounding, // Frequency frequency, Date settlementDate, double accuracy, int maxIterations, double guess) { // if (settlementDate == null) // settlementDate = Settings.evaluationDate(); // // depending on the sign of the market price, check that cash flows of the opposite sign have been specified (otherwise // // IRR is nonsensical.) // int lastSign = Math.Sign(-marketPrice), // signChanges = 0; // foreach (CashFlow cf in cashflows.Where(cf => !cf.hasOccurred(settlementDate))) { // int thisSign = Math.Sign(cf.amount()); // if (lastSign * thisSign < 0) // sign change // signChanges++; // if (thisSign != 0) // lastSign = thisSign; // } // if (!(signChanges > 0)) // throw new ApplicationException("the given cash flows cannot result in the given market price due to their sign"); // /* The following is commented out due to the lack of a QL_WARN macro // if (signChanges > 1) { // Danger of non-unique solution // // Check the aggregate cash flows (Norstrom) // Real aggregateCashFlow = marketPrice; // signChanges = 0; // for (Size i = 0; i < cashflows.size(); ++i) { // Real nextAggregateCashFlow = // aggregateCashFlow + cashflows[i]->amount(); // if (aggregateCashFlow * nextAggregateCashFlow < 0.0) // signChanges++; // aggregateCashFlow = nextAggregateCashFlow; // } // if (signChanges > 1) // QL_WARN( "danger of non-unique solution"); // }; // */ // //Brent solver; // NewtonSafe solver = new NewtonSafe(); // solver.setMaxEvaluations(maxIterations); // return solver.solve(new IrrFinder(cashflows, marketPrice, dayCounter, compounding, frequency, settlementDate), // accuracy, guess, guess / 10.0); // } //! Cash-flow duration. /*! The simple duration of a string of cash flows is defined as \f[ D_{\mathrm{simple}} = \frac{\sum t_i c_i B(t_i)}{\sum c_i B(t_i)} \f] where \f$ c_i \f$ is the amount of the \f$ i \f$-th cash flow, \f$ t_i \f$ is its payment time, and \f$ B(t_i) \f$ is the corresponding discount according to the passed yield. The modified duration is defined as \f[ D_{\mathrm{modified}} = -\frac{1}{P} \frac{\partial P}{\partial y} \f] where \f$ P \f$ is the present value of the cash flows according to the given IRR \f$ y \f$. The Macaulay duration is defined for a compounded IRR as \f[ D_{\mathrm{Macaulay}} = \left( 1 + \frac{y}{N} \right) D_{\mathrm{modified}} \f] where \f$ y \f$ is the IRR and \f$ N \f$ is the number of cash flows per year. */ public static double duration(List<CashFlow> cashflows, InterestRate rate, Duration.Type type = Duration.Type.Modified, Date settlementDate = null) { if (cashflows.Count == 0) return 0.0; if (settlementDate == null) settlementDate = Settings.evaluationDate(); switch (type) { case Duration.Type.Simple: return simpleDuration(cashflows, rate, settlementDate); case Duration.Type.Modified: return modifiedDuration(cashflows, rate, settlementDate); case Duration.Type.Macaulay: return macaulayDuration(cashflows, rate, settlementDate); default: throw new ArgumentException("unknown duration type"); } }
public void testConversions() { InterestRateData[] cases = { // data from "Option Pricing Formulas", Haug, pag.181-182 // Rate,Compounding, Frequency, Time, Compounding2, Frequency2, Rate2, precision new InterestRateData(0.0800, Compounding.Compounded, Frequency.Quarterly, 1.00, Compounding.Continuous, Frequency.Annual, 0.0792, 4), new InterestRateData(0.1200, Compounding.Continuous, Frequency.Annual, 1.00, Compounding.Compounded, Frequency.Annual, 0.1275, 4), new InterestRateData(0.0800, Compounding.Compounded, Frequency.Quarterly, 1.00, Compounding.Compounded, Frequency.Annual, 0.0824, 4), new InterestRateData(0.0700, Compounding.Compounded, Frequency.Quarterly, 1.00, Compounding.Compounded, Frequency.Semiannual, 0.0706, 4), // undocumented, but reasonable :) new InterestRateData(0.0100, Compounding.Compounded, Frequency.Annual, 1.00, Compounding.Simple, Frequency.Annual, 0.0100, 4), new InterestRateData(0.0200, Compounding.Simple, Frequency.Annual, 1.00, Compounding.Compounded, Frequency.Annual, 0.0200, 4), new InterestRateData(0.0300, Compounding.Compounded, Frequency.Semiannual, 0.50, Compounding.Simple, Frequency.Annual, 0.0300, 4), new InterestRateData(0.0400, Compounding.Simple, Frequency.Annual, 0.50, Compounding.Compounded, Frequency.Semiannual, 0.0400, 4), new InterestRateData(0.0500, Compounding.Compounded, Frequency.EveryFourthMonth, 1.0/3, Compounding.Simple, Frequency.Annual, 0.0500, 4), new InterestRateData(0.0600, Compounding.Simple, Frequency.Annual, 1.0/3, Compounding.Compounded, Frequency.EveryFourthMonth, 0.0600, 4), new InterestRateData(0.0500, Compounding.Compounded, Frequency.Quarterly, 0.25, Compounding.Simple, Frequency.Annual, 0.0500, 4), new InterestRateData(0.0600, Compounding.Simple, Frequency.Annual, 0.25, Compounding.Compounded, Frequency.Quarterly, 0.0600, 4), new InterestRateData(0.0700, Compounding.Compounded, Frequency.Bimonthly, 1.0/6, Compounding.Simple, Frequency.Annual, 0.0700, 4), new InterestRateData(0.0800, Compounding.Simple, Frequency.Annual, 1.0/6, Compounding.Compounded, Frequency.Bimonthly, 0.0800, 4), new InterestRateData(0.0900, Compounding.Compounded, Frequency.Monthly, 1.0/12, Compounding.Simple, Frequency.Annual, 0.0900, 4), new InterestRateData(0.1000, Compounding.Simple, Frequency.Annual, 1.0/12, Compounding.Compounded, Frequency.Monthly, 0.1000, 4), new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual, 0.25, Compounding.Simple, Frequency.Annual, 0.0300, 4), new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual, 0.25, Compounding.Simple, Frequency.Semiannual, 0.0300, 4), new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual, 0.25, Compounding.Simple, Frequency.Quarterly, 0.0300, 4), new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual, 0.50, Compounding.Simple, Frequency.Annual, 0.0300, 4), new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual, 0.50, Compounding.Simple, Frequency.Semiannual, 0.0300, 4), new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual, 0.75, Compounding.Compounded, Frequency.Semiannual, 0.0300, 4), new InterestRateData(0.0400, Compounding.Simple, Frequency.Semiannual, 0.25, Compounding.SimpleThenCompounded, Frequency.Quarterly, 0.0400, 4), new InterestRateData(0.0400, Compounding.Simple, Frequency.Semiannual, 0.25, Compounding.SimpleThenCompounded, Frequency.Semiannual, 0.0400, 4), new InterestRateData(0.0400, Compounding.Simple, Frequency.Semiannual, 0.25, Compounding.SimpleThenCompounded, Frequency.Annual, 0.0400, 4), new InterestRateData(0.0400, Compounding.Compounded, Frequency.Quarterly, 0.50, Compounding.SimpleThenCompounded, Frequency.Quarterly, 0.0400, 4), new InterestRateData(0.0400, Compounding.Simple, Frequency.Semiannual, 0.50, Compounding.SimpleThenCompounded, Frequency.Semiannual, 0.0400, 4), new InterestRateData(0.0400, Compounding.Simple, Frequency.Semiannual, 0.50, Compounding.SimpleThenCompounded, Frequency.Annual, 0.0400, 4), new InterestRateData(0.0400, Compounding.Compounded, Frequency.Quarterly, 0.75, Compounding.SimpleThenCompounded, Frequency.Quarterly, 0.0400, 4), new InterestRateData(0.0400, Compounding.Compounded, Frequency.Semiannual, 0.75, Compounding.SimpleThenCompounded, Frequency.Semiannual, 0.0400, 4), new InterestRateData(0.0400, Compounding.Simple, Frequency.Semiannual, 0.75, Compounding.SimpleThenCompounded, Frequency.Annual, 0.0400, 4) }; Rounding roundingPrecision; double r3; double r2; Date d1 = Date.Today; Date d2; InterestRate ir; InterestRate ir2; InterestRate ir3; InterestRate expectedIR; double compoundf; double error; double disc; for (int i = 0; i < cases.Length-1 ; i++) { ir = new InterestRate(cases[i].r, new Actual360(), cases[i].comp, cases[i].freq); d2 = d1 + new Period((int)(360 * cases[i].t + 0.5) ,TimeUnit.Days); roundingPrecision = new Rounding(cases[i].precision); // check that the compound factor is the inverse of the discount factor compoundf = ir.compoundFactor(d1, d2); disc = ir.discountFactor(d1, d2); error = Math.Abs(disc - 1.0 / compoundf); if (error > 1e-15) Assert.Fail(ir + " 1.0/compound_factor: " + 1.0 / compoundf); // check that the equivalent InterestRate with *same* daycounter, // compounding, and frequency is the *same* InterestRate ir2 = ir.equivalentRate(d1, d2, ir.dayCounter(), ir.compounding(), ir.frequency()); error = Math.Abs(ir.rate() - ir2.rate()); if (error > 1e-15) Assert.Fail("original interest rate: " + ir + " equivalent interest rate: " + ir2 + " rate error: " + error); if (ir.dayCounter() != ir2.dayCounter()) Assert.Fail("day counter error original interest rate: " + ir + " equivalent interest rate: " + ir2); if (ir.compounding() != ir2.compounding()) Assert.Fail("compounding error original interest rate: " + ir + " equivalent interest rate: " + ir2); if (ir.frequency() != ir2.frequency()) Assert.Fail("frequency error original interest rate: " + ir + " equivalent interest rate: " + ir2); // check that the equivalent rate with *same* daycounter, // compounding, and frequency is the *same* rate r2 = ir.equivalentRate(d1, d2, ir.dayCounter(), ir.compounding(), ir.frequency()).rate(); error = Math.Abs(ir.rate() - r2); if (error > 1e-15) Assert.Fail("original rate: " + ir + " equivalent rate: " + r2 + " error: " + error); // check that the equivalent InterestRate with *different* // compounding, and frequency is the *expected* InterestRate ir3 = ir.equivalentRate(d1, d2, ir.dayCounter(), cases[i].comp2, cases[i].freq2); expectedIR = new InterestRate(cases[i].expected, ir.dayCounter(), cases[i].comp2, cases[i].freq2); r3 = roundingPrecision.Round(ir3.rate()); error = Math.Abs(r3 - expectedIR.rate()); if (error > 1.0e-17) Assert.Fail("original interest rate: " + ir + " calculated equivalent interest rate: " + ir3 + " truncated equivalent rate: " + r3 + " expected equivalent interest rate: " + expectedIR + " rate error: " + error); if (ir3.dayCounter() != expectedIR.dayCounter()) Assert.Fail("day counter error original interest rate: " + ir3 + " equivalent interest rate: " + expectedIR); if (ir3.compounding() != expectedIR.compounding()) Assert.Fail("compounding error original interest rate: " + ir3 + " equivalent interest rate: " + expectedIR); if (ir3.frequency() != expectedIR.frequency()) Assert.Fail("frequency error original interest rate: " + ir3 + " equivalent interest rate: " + expectedIR); // check that the equivalent rate with *different* // compounding, and frequency is the *expected* rate r3 = ir.equivalentRate(d1, d2, ir.dayCounter(), cases[i].comp2, cases[i].freq2).rate(); r3 = roundingPrecision.Round(r3); error = Math.Abs(r3 - cases[i].expected); if (error > 1.0e-17) Assert.Fail("calculated equivalent rate: " + r3 + " expected equivalent rate: " + cases[i].expected + " error: " + error); } }
//! Cash-flow convexity /*! The convexity of a string of cash flows is defined as \f[ C = \frac{1}{P} \frac{\partial^2 P}{\partial y^2} \f] where \f$ P \f$ is the present value of the cash flows according to the given IRR \f$ y \f$. */ public static double convexity(List<CashFlow> cashflows, InterestRate rate, Date settlementDate = null) { if (cashflows.Count == 0) return 0.0; if (settlementDate == null) settlementDate = Settings.evaluationDate(); DayCounter dayCounter = rate.dayCounter(); double P = 0; double d2Pdy2 = 0; double y = rate.rate(); int N = (int)rate.frequency(); foreach (CashFlow cashflow in cashflows.Where(cashflow => !cashflow.hasOccurred(settlementDate))) { double t = dayCounter.yearFraction(settlementDate, cashflow.date()); double c = cashflow.amount(); double B = rate.discountFactor(t); P += c * B; switch (rate.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 + y / N) * (1 + y / 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 + y / N) * (1 + y / N)); break; default: throw new ArgumentException("unknown compounding convention (" + rate.compounding() + ")"); } } // no cashflows if (P == 0) return 0; return d2Pdy2 / P; }
//! Yield value of a basis point /*! The yield value of a one basis point change in price is the derivative of the yield with respect to the price multiplied by 0.01 */ public static double yieldValueBasisPoint(Leg leg, InterestRate yield, bool includeSettlementDateFlows, Date settlementDate = null, Date npvDate = null) { if (leg.empty()) return 0.0; if (settlementDate == null) settlementDate = Settings.evaluationDate(); if (npvDate == null) npvDate = settlementDate; double npv = CashFlows.npv(leg, yield, includeSettlementDateFlows, settlementDate, npvDate); double modifiedDuration = CashFlows.duration(leg, yield, Duration.Type.Modified, includeSettlementDateFlows, settlementDate, npvDate); double shift = 0.01; return (1.0/(-npv*modifiedDuration))*shift; }
//! Basis-point value /*! Obtained by setting dy = 0.0001 in the 2nd-order Taylor series expansion. */ public static double basisPointValue(List<CashFlow> leg, InterestRate y, Date settlementDate) { if (leg.Count == 0) return 0.0; double shift = 0.0001; double dirtyPrice = CashFlows.npv(leg, y, settlementDate); double modifiedDuration = CashFlows.duration(leg, y, Duration.Type.Modified, settlementDate); double convexity = CashFlows.convexity(leg, y, settlementDate); double delta = -modifiedDuration*dirtyPrice; double gamma = (convexity/100.0)*dirtyPrice; delta *= shift; gamma *= shift*shift; return delta + 0.5*gamma; }
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); }
//! Yield value of a basis point /*! The yield value of a one basis point change in price is the derivative of the yield with respect to the price multiplied by 0.01 */ public static double yieldValueBasisPoint(List<CashFlow> leg, InterestRate y, Date settlementDate) { if (leg.Count == 0) return 0.0; double shift = 0.01; double dirtyPrice = CashFlows.npv(leg, y, settlementDate); double modifiedDuration = CashFlows.duration(leg, y, Duration.Type.Modified, settlementDate); return (1.0/(-dirtyPrice*modifiedDuration))*shift; }
public override double derivative(double y) { InterestRate yield = new InterestRate(y, dayCounter_, compounding_, frequency_); return modifiedDuration(leg_, yield,includeSettlementDateFlows_,settlementDate_, npvDate_); }
protected override void performCalculations() { Date fixingDate = calendar_.advance(valueDate_, -settlementDays_, TimeUnit.Days); forwardRate_ = new InterestRate(index_.fixing(fixingDate), index_.dayCounter(), Compounding.Simple, Frequency.Once); underlyingSpotValue_ = spotValue(); underlyingIncome_ = 0.0; base.performCalculations(); }
// NPV of the cash flows. // The NPV is the sum of the cash flows, each 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 npv(Leg leg, InterestRate yield, bool includeSettlementDateFlows, Date settlementDate = null, Date npvDate = null) { if (leg.empty()) return 0.0; if (settlementDate == null) settlementDate = Settings.evaluationDate(); if (npvDate == null) npvDate = settlementDate; double npv = 0.0; double discount = 1.0; Date lastDate = npvDate; Date refStartDate, refEndDate; for (int i=0; i<leg.Count; ++i) { if (leg[i].hasOccurred(settlementDate, includeSettlementDateFlows)) continue; Date couponDate = leg[i].date(); double amount = leg[i].amount(); if (leg[i].tradingExCoupon(settlementDate)) { amount = 0.0; } Coupon coupon = leg[i] as Coupon; if (coupon != null ) { refStartDate = coupon.refPeriodStart; refEndDate = coupon.refPeriodEnd; } else { if (lastDate == npvDate) { // we don't have a previous coupon date, // so we fake it refStartDate = couponDate - new Period(1,TimeUnit.Years); } else { refStartDate = lastDate; } refEndDate = couponDate; } double b = yield.discountFactor(lastDate, couponDate, refStartDate, refEndDate); discount *= b; lastDate = couponDate; npv += amount * discount; } return npv; }
// Handle<YieldTermStructure> discountCurve = Handle<YieldTermStructure>()); public ForwardRateAgreement(Date valueDate, Date maturityDate, Position.Type type, double strikeForwardRate, double notionalAmount, IborIndex index, Handle<YieldTermStructure> discountCurve) : base(index.dayCounter(), index.fixingCalendar(), index.businessDayConvention(), index.fixingDays(), new Payoff(), valueDate, maturityDate, discountCurve) { fraType_ = type; notionalAmount_ = notionalAmount; index_ = index; if (notionalAmount <= 0.0) throw new ApplicationException("notional Amount must be positive"); // do I adjust this ? // valueDate_ = calendar_.adjust(valueDate_,businessDayConvention_); Date fixingDate = calendar_.advance(valueDate_, -settlementDays_, TimeUnit.Days); forwardRate_ = new InterestRate(index.fixing(fixingDate), index.dayCounter(), Compounding.Simple, Frequency.Once); strikeForwardRate_ = new InterestRate(strikeForwardRate, index.dayCounter(), Compounding.Simple, Frequency.Once); double strike = notionalAmount_ * strikeForwardRate_.compoundFactor(valueDate_, maturityDate_); payoff_ = new ForwardTypePayoff(fraType_, strike); // incomeDiscountCurve_ is irrelevant to an FRA incomeDiscountCurve_ = discountCurve_; // income is irrelevant to FRA - set it to zero underlyingIncome_ = 0.0; index_.registerWith(update); }
//! Cash-flow duration. /*! The simple duration of a string of cash flows is defined as \f[ D_{\mathrm{simple}} = \frac{\sum t_i c_i B(t_i)}{\sum c_i B(t_i)} \f] where \f$ c_i \f$ is the amount of the \f$ i \f$-th cash flow, \f$ t_i \f$ is its payment time, and \f$ B(t_i) \f$ is the corresponding discount according to the passed yield. The modified duration is defined as \f[ D_{\mathrm{modified}} = -\frac{1}{P} \frac{\partial P}{\partial y} \f] where \f$ P \f$ is the present value of the cash flows according to the given IRR \f$ y \f$. The Macaulay duration is defined for a compounded IRR as \f[ D_{\mathrm{Macaulay}} = \left( 1 + \frac{y}{N} \right) D_{\mathrm{modified}} \f] where \f$ y \f$ is the IRR and \f$ N \f$ is the number of cash flows per year. */ public static double duration(Leg leg, InterestRate rate, Duration.Type type, bool includeSettlementDateFlows, Date settlementDate = null, Date npvDate = null) { if (leg.empty()) return 0.0; if (settlementDate == null) settlementDate = Settings.evaluationDate(); if (npvDate == null) npvDate = settlementDate; switch (type) { case Duration.Type.Simple: return simpleDuration(leg, rate, includeSettlementDateFlows, settlementDate, npvDate); case Duration.Type.Modified: return modifiedDuration(leg, rate, includeSettlementDateFlows, settlementDate, npvDate); case Duration.Type.Macaulay: return macaulayDuration(leg, rate, includeSettlementDateFlows, settlementDate, npvDate); default: Utils.QL_FAIL("unknown duration type"); break; } return 0.0; }
public override double derivative(double x) { InterestRate y = new InterestRate(x, dayCounter_, compounding_, frequency_); return(modifiedDuration(cashflows_, y, settlementDate_)); }