private double calculateSinglePeriodAccrualOnDefault( CdsCoupon coupon, double effectiveStart, double[] integrationPoints, YieldTermStructure yieldCurve, PiecewiseconstantHazardRate creditCurve) { double start = Math.Max(coupon.getEffStart(), effectiveStart); if (start >= coupon.getEffEnd()) { return(0.0); //this coupon has already expired } double[] knots = DoublesScheduleGenerator.truncateSetInclusive(start, coupon.getEffEnd(), integrationPoints); double t = knots[0]; double ht0 = creditCurve.getRT_(t); double rt0 = yieldCurve.getRT_(t); double b0 = Math.Exp(-rt0 - ht0); // this is the risky discount factor double t0 = t - coupon.getEffStart() + _omega; double pv = 0.0; int nItems = knots.Length; for (int j = 1; j < nItems; ++j) { t = knots[j]; double ht1 = creditCurve.getRT_(t); double rt1 = yieldCurve.getRT_(t); double b1 = Math.Exp(-rt1 - ht1); double dt = knots[j] - knots[j - 1]; double dht = ht1 - ht0; double drt = rt1 - rt0; double dhrt = dht + drt; double tPV; double t1 = t - coupon.getEffStart() + _omega; if (Math.Abs(dhrt) < 1e-5) { tPV = dht * b0 * (t0 * Maths.Epsilon.epsilon(-dhrt) + dt * Maths.Epsilon.epsilonP(-dhrt)); } else { tPV = dht / dhrt * (t0 * b0 - t1 * b1 + dt / dhrt * (b0 - b1)); } t0 = t1; pv += tPV; ht0 = ht1; rt0 = rt1; b0 = b1; } return(coupon.getYFRatio() * pv); }
private double calculateSinglePeriodAccrualOnDefault(int paymentIndex, PiecewiseconstantHazardRate creditCurve) { double[] knots = _premLegIntPoints[paymentIndex]; if (knots == null) { return(0.0); } double[] df = _premDF[paymentIndex]; double[] deltaT = _premDt[paymentIndex]; double[] rt = _rt[paymentIndex]; double accRate = _accRate[paymentIndex]; double accStart = _offsetAccStart[paymentIndex]; double t = knots[0]; double ht0 = creditCurve.getRT_(t); double rt0 = rt[0]; double b0 = df[0] * Math.Exp(-ht0); double t0 = t - accStart + _omega; double pv = 0.0; int nItems = knots.Length; for (int j = 1; j < nItems; ++j) { t = knots[j]; double ht1 = creditCurve.getRT_(t); double rt1 = rt[j]; double b1 = df[j] * Math.Exp(-ht1); double dt = deltaT[j - 1]; double dht = ht1 - ht0; double drt = rt1 - rt0; double dhrt = dht + drt + 1e-50; // to keep consistent with ISDA c code double tPV; if (Math.Abs(dhrt) < 1e-5) { tPV = dht * dt * b0 * Epsilon.epsilonP(-dhrt); } else { tPV = dht * dt / dhrt * ((b0 - b1) / dhrt - b1); } } return(accRate * pv); }
public virtual double[] pvAndSense(PiecewiseconstantHazardRate creditCurve) { double pv = _riskLessValue * Math.Exp(-creditCurve.getRT_(_effEnd)); double pvSense = -pv *creditCurve.getSingleNodeRTSensitivity(_effEnd, _creditCurveKnot); return(new double[] { pv, pvSense }); }
public double rpv01(PiecewiseconstantHazardRate creditCurve, CdsPriceType cleanOrDirty) { double pv = 0.0; for (int i = 0; i < _nPayments; i++) { CdsCoupon c = _cds.getCoupon(i); double q = Math.Exp(-creditCurve.getRT_(c.getEffEnd())); pv += c.getYearFrac() * _paymentDF[i] * q; } if (_cds.isPayAccOnDefault()) { double accPV = 0.0; for (int i = 0; i < _nPayments; i++) { accPV += calculateSinglePeriodAccrualOnDefault(i, creditCurve); } pv += accPV; } pv /= _valuationDF; if (cleanOrDirty == CdsPriceType.CLEAN) { pv -= _cds.getAccruedYearFraction(); } return(pv); }
public double protectionLeg(PiecewiseconstantHazardRate creditCurve) { double ht0 = creditCurve.getRT_(_proLegIntPoints[0]); double rt0 = _proYieldCurveRT[0]; double b0 = _proDF[0] * Math.Exp(-ht0); double pv = 0.0; for (int i = 1; i < _nProPoints; ++i) { double ht1 = creditCurve.getRT_(_proLegIntPoints[i]); double rt1 = _proYieldCurveRT[i]; double b1 = _proDF[i] * Math.Exp(-ht1); double dht = ht1 - ht0; double drt = rt1 - rt0; double dhrt = dht + drt; // this is equivalent to the ISDA code without explicitly calculating the time step - it also handles the limit double dPV; if (Math.Abs(dhrt) < 1e-5) { dPV = dht * b0 * Epsilon.epsilon(-dhrt); } else { dPV = (b0 - b1) * dht / dhrt; } pv += dPV; ht0 = ht1; rt0 = rt1; b0 = b1; } pv *= _lgdDF; // multiply by LGD and adjust to valuation date return(pv); }
/** * Compute the present value of the protection leg with a notional of 1, which is given by the integral * $\frac{1-R}{P(T_{v})} \int_{T_a} ^{T_b} P(t) \frac{dQ(t)}{dt} dt$ where $P(t)$ and $Q(t)$ are the discount * and survival curves respectively, $T_a$ and $T_b$ are the start and end of the protection respectively, * $T_v$ is the valuation time (all measured from $t = 0$, 'today') and $R$ is the recovery rate. * * @param cds the analytic description of a CDS traded at a certain time * @param yieldCurve the yield (or discount) curve * @param creditCurve the credit (or survival) curve * @param valuationTime the valuation time, if time is zero, leg is valued today, * value often quoted for cash-settlement date * @return the value of the protection leg (on a unit notional) */ public double annuity( CDS cds, YieldTermStructure yieldCurve, PiecewiseconstantHazardRate creditCurve, CdsPriceType cleanOrDirty, double valuationTime) { double pv = dirtyAnnuity(cds, yieldCurve, creditCurve); double valDF = Math.Exp(-yieldCurve.getRT_(valuationTime)); if (cleanOrDirty == CdsPriceType.CLEAN) { double csTime = cds.getCashSettleTime(); double protStart = cds.getEffectiveProtectionStart(); double csDF = valuationTime == csTime ? valDF :Math.Exp(-yieldCurve.getRT_(csTime)); double q = protStart == 0 ? 1.0 : Math.Exp(-creditCurve.getRT_(protStart)); double acc = cds.getAccruedYearFraction(); pv -= acc * csDF * q; //subtract the accrued risky discounted to today } pv /= valDF; //roll forward to valuation date return(pv); }
/** * The value of the full (or dirty) annuity (or RPV01 - the premium leg per unit of coupon) today (t=0). * The cash flows from premium payments and accrual-on-default are risky discounted to t=0 * The actual value of the leg is this multiplied by the notional and the fractional coupon * (i.e. coupon in basis points divided by 10,000). * <p> * This is valid for both spot and forward starting CDS. * * @param cds the analytic description of a CDS traded at a certain time * @param yieldCurve the yield (or discount) curve * @param creditCurve the credit (or survival) curve * @return the full (or dirty) annuity valued today. <b>Note</b> what is usually quoted is the clean annuity */ public double dirtyAnnuity(CDS cds, YieldTermStructure yt, PiecewiseconstantHazardRate hazard) { DateTime tradedate = cds.tradedate; List <DateTime> Jumps = yt.jumpDates_; DateTime settlementDate = tradedate.AddDays(0); double recoveryrate = cds.Recovery; DateTime Stepindate = tradedate.AddDays(1); double coupon = cds.PremiumRate; OMLib.Conventions.DayCount.Actual360 dc = new OMLib.Conventions.DayCount.Actual360(); CdsCoupon[] cf = cds.getCoupons(); double notional = cds.Notional; double ita = (double)365 / 360; double totalNPV = 0.0; for (int i = 0; i < cf.Length; ++i) { totalNPV += cf[i].getYearFrac() * notional * Math.Exp(-hazard.getRT_(cf[i].getEffEnd())) * Math.Exp(-yt.getRT_(cf[i].getPaymentTime())); } double start = cds.getNumPayments() == 1 ? cds.getEffectiveProtectionStart() : cds.getAccStart(); double[] integrationSchedule = DoublesScheduleGenerator.getIntegrationsPoints(start, cds.getProtectionEnd(), yt, hazard); double accPV = 0.0; for (int i = 0; i < cf.Length; ++i) { accPV += calculateSinglePeriodAccrualOnDefault(cf[i], cds.getEffectiveProtectionStart(), integrationSchedule, yt, hazard); } totalNPV += accPV; return(totalNPV); }
//------------------------------------------------------------------------- public double pv(PiecewiseconstantHazardRate creditCurve) { return(_riskLessValue * Math.Exp(-creditCurve.getRT_(_effEnd))); }
private double calculateSinglePeriodAccrualOnDefaultCreditSensitivity( CdsCoupon coupon, double effStart, double[] integrationPoints, YieldTermStructure yieldCurve, PiecewiseconstantHazardRate creditCurve, int creditCurveNode) { double start = Math.Max(coupon.getEffStart(), effStart); if (start >= coupon.getEffEnd()) { return(0.0); } double[] knots = DoublesScheduleGenerator.truncateSetInclusive(start, coupon.getEffEnd(), integrationPoints); double t = knots[0]; double ht0 = creditCurve.getRT_(t); double rt0 = yieldCurve.getRT_(t); double p0 = Math.Exp(-rt0); double q0 = Math.Exp(-ht0); double b0 = p0 * q0; // this is the risky discount factor double dqdr0 = creditCurve.getSingleNodeDiscountFactorSensitivity(t, creditCurveNode); double t0 = t - coupon.getEffStart() + _omega; double pvSense = 0.0; int nItems = knots.Length; for (int j = 1; j < nItems; ++j) { t = knots[j]; double ht1 = creditCurve.getRT_(t); double rt1 = yieldCurve.getRT_(t); double p1 = Math.Exp(-rt1); double q1 = Math.Exp(-ht1); double b1 = p1 * q1; double dqdr1 = creditCurve.getSingleNodeDiscountFactorSensitivity(t, creditCurveNode); double dt = knots[j] - knots[j - 1]; double dht = ht1 - ht0; double drt = rt1 - rt0; double dhrt = dht + drt + 1e-50; // to keep consistent with ISDA c code double tPvSense; // TODO once the maths is written up in a white paper, check these formula again, // since tests again finite difference could miss some subtle error if (_formula == AccrualOnDefaultFormulae.MARKIT_FIX) { if (Math.Abs(dhrt) < 1e-5) { double eP = Maths.Epsilon.epsilonP(-dhrt); double ePP = Maths.Epsilon.epsilonPP(-dhrt); double dPVdq0 = p0 * dt * ((1 + dht) * eP - dht * ePP); double dPVdq1 = b0 * dt / q1 * (-eP + dht * ePP); tPvSense = dPVdq0 * dqdr0 + dPVdq1 * dqdr1; } else { double w1 = (b0 - b1) / dhrt; double w2 = w1 - b1; double w3 = dht / dhrt; double w4 = dt / dhrt; double w5 = (1 - w3) * w2; double dPVdq0 = w4 / q0 * (w5 + w3 * (b0 - w1)); double dPVdq1 = w4 / q1 * (w5 + w3 * (b1 * (1 + dhrt) - w1)); tPvSense = dPVdq0 * dqdr0 - dPVdq1 * dqdr1; } } else { double t1 = t - coupon.getEffStart() + _omega; if (Math.Abs(dhrt) < 1e-5) { double e = Maths.Epsilon.epsilon(-dhrt); double eP = Maths.Epsilon.epsilonP(-dhrt); double ePP = Maths.Epsilon.epsilonPP(-dhrt); double w1 = t0 * e + dt * eP; double w2 = t0 * eP + dt * ePP; double dPVdq0 = p0 * ((1 + dht) * w1 - dht * w2); double dPVdq1 = b0 / q1 * (-w1 + dht * w2); tPvSense = dPVdq0 * dqdr0 + dPVdq1 * dqdr1; } else { double w1 = dt / dhrt; double w2 = dht / dhrt; double w3 = (t0 + w1) * b0 - (t1 + w1) * b1; double w4 = (1 - w2) / dhrt; double w5 = w1 / dhrt * (b0 - b1); double dPVdq0 = w4 * w3 / q0 + w2 * ((t0 + w1) * p0 - w5 / q0); double dPVdq1 = w4 * w3 / q1 + w2 * ((t1 + w1) * p1 - w5 / q1); tPvSense = dPVdq0 * dqdr0 - dPVdq1 * dqdr1; } t0 = t1; } pvSense += tPvSense; ht0 = ht1; rt0 = rt1; p0 = p1; q0 = q1; b0 = b1; dqdr0 = dqdr1; } return(coupon.getYFRatio() * pvSense); }
/** * The sensitivity of the PV of the protection leg to the zero rate of a given node (knot) of the yield curve. * * @param cds the analytic description of a CDS traded at a certain time * @param yieldCurve the yield (or discount) curve * @param creditCurve the credit (or survival) curve * @param yieldCurveNode the yield curve node * @return the sensitivity (on a unit notional) */ public double protectionLegYieldSensitivity( CDS cds, YieldTermStructure yieldCurve, PiecewiseconstantHazardRate creditCurve, int yieldCurveNode) { if ((yieldCurveNode != 0 && cds.getProtectionEnd() <= yieldCurve.t[yieldCurveNode - 1]) || (yieldCurveNode != creditCurve.getNumberOfKnots() - 1 && cds.getEffectiveProtectionStart() >= yieldCurve.t[yieldCurveNode + 1])) { return(0.0); // can't have any sensitivity in this case } if (cds.getProtectionEnd() <= 0.0) { //short cut already expired CDSs return(0.0); } double[] integrationSchedule = DoublesScheduleGenerator.getIntegrationsPoints( cds.getEffectiveProtectionStart(), cds.getProtectionEnd(), yieldCurve, creditCurve); double t = integrationSchedule[0]; double ht0 = creditCurve.getRT_(t); double rt0 = yieldCurve.getRT_(t); double dpdr0 = yieldCurve.getSingleNodeDiscountFactorSensitivity(t, yieldCurveNode); double q0 = Math.Exp(-ht0); double p0 = Math.Exp(-rt0); double pvSense = 0.0; int n = integrationSchedule.Length; for (int i = 1; i < n; ++i) { t = integrationSchedule[i]; double ht1 = creditCurve.getRT_(t); double dpdr1 = yieldCurve.getSingleNodeDiscountFactorSensitivity(t, yieldCurveNode); double rt1 = yieldCurve.getRT_(t); double q1 = Math.Exp(-ht1); double p1 = Math.Exp(-rt1); if (dpdr0 == 0.0 && dpdr1 == 0.0) { ht0 = ht1; rt0 = rt1; p0 = p1; q0 = q1; continue; } double hBar = ht1 - ht0; double fBar = rt1 - rt0; double fhBar = hBar + fBar; double dPVSense; double e = Maths.Epsilon.epsilon(-fhBar); double eP = Maths.Epsilon.epsilonP(-fhBar); double dPVdp0 = q0 * hBar * (e - eP); double dPVdp1 = hBar * p0 * q0 / p1 * eP; dPVSense = dPVdp0 * dpdr0 + dPVdp1 * dpdr1; pvSense += dPVSense; ht0 = ht1; dpdr0 = dpdr1; rt0 = rt1; p0 = p1; q0 = q1; } pvSense *= cds.getLGD(); // Compute the discount factor discounting the upfront payment made on the cash settlement date back to the valuation date double df = Math.Exp(-yieldCurve.getRT_(cds.getCashSettleTime())); pvSense /= df; //TODO this was put in quickly the get the right sensitivity to the first node double dfSense = yieldCurve.getSingleNodeDiscountFactorSensitivity(cds.getCashSettleTime(), yieldCurveNode); if (dfSense != 0.0) { double pro = protectionLeg(cds, yieldCurve, creditCurve); pvSense -= pro / df * dfSense; } return(pvSense); }
public double protectionLegCreditSensitivity( CDS cds, YieldTermStructure yieldCurve, PiecewiseconstantHazardRate creditCurve, int creditCurveNode) { if ((creditCurveNode != 0 && cds.getProtectionEnd() <= creditCurve.getTimeAtIndex(creditCurveNode - 1)) || (creditCurveNode != creditCurve.t.Count - 1 && cds.getEffectiveProtectionStart() >= creditCurve.getTimeAtIndex(creditCurveNode + 1))) { return(0.0); // can't have any sensitivity in this case } if (cds.getProtectionEnd() <= 0.0) { //short cut already expired CDSs return(0.0); } double[] integrationSchedule = DoublesScheduleGenerator.getIntegrationsPoints( cds.getEffectiveProtectionStart(), cds.getProtectionEnd(), yieldCurve, creditCurve); double t = integrationSchedule[0]; double ht0 = creditCurve.getRT_(t); double rt0 = yieldCurve.getRT_(t); double dqdr0 = creditCurve.getSingleNodeDiscountFactorSensitivity(t, creditCurveNode); double q0 = Math.Exp(-ht0); double p0 = Math.Exp(-rt0); double pvSense = 0.0; int n = integrationSchedule.Length; for (int i = 1; i < n; ++i) { t = integrationSchedule[i]; double ht1 = creditCurve.getRT_(t); double dqdr1 = creditCurve.getSingleNodeDiscountFactorSensitivity(t, creditCurveNode); double rt1 = yieldCurve.getRT_(t); double q1 = Math.Exp(-ht1); double p1 = Math.Exp(-rt1); if (dqdr0 == 0.0 && dqdr1 == 0.0) { ht0 = ht1; rt0 = rt1; p0 = p1; q0 = q1; continue; } double hBar = ht1 - ht0; double fBar = rt1 - rt0; double fhBar = hBar + fBar; double dPVSense; if (Math.Abs(fhBar) < 1e-5) { double e = Maths.Epsilon.epsilon(-fhBar); double eP = Maths.Epsilon.epsilonP(-fhBar); double dPVdq0 = p0 * ((1 + hBar) * e - hBar * eP); double dPVdq1 = -p0 * q0 / q1 * (e - hBar * eP); dPVSense = dPVdq0 * dqdr0 + dPVdq1 * dqdr1; } else { double w = fBar / fhBar * (p0 * q0 - p1 * q1); dPVSense = ((w / q0 + hBar * p0) / fhBar) * dqdr0 - ((w / q1 + hBar * p1) / fhBar) * dqdr1; } pvSense += dPVSense; ht0 = ht1; dqdr0 = dqdr1; rt0 = rt1; p0 = p1; q0 = q1; } pvSense *= cds.getLGD(); // Compute the discount factor discounting the upfront payment made on the cash settlement date back to the valuation date double df = Math.Exp(-yieldCurve.getRT_(cds.getCashSettleTime())); pvSense /= df; return(pvSense); }
public double calculateSinglePeriodAccrualOnDefault(CDS cds, YieldTermStructure yieldCurve, PiecewiseconstantHazardRate creditCurve) { double Acc = 0; List <double> Jumps = yieldCurve.t; DateTime tradedate = cds.tradedate; DateTime settlementDate = tradedate.AddDays(cds.Cashsettlement); double recoveryrate = cds.Recovery; DateTime Stepindate = tradedate.AddDays(1); double notional = cds.Notional; double coupon = cds.PremiumRate; DateTime lastpayment = cds.formerpaymentdate; DateTime effectiveStart = tradedate.AddDays(1); CdsCoupon[] cc = cds.getCoupons(); for (int i = 0; i < cds.getNumPayments(); ++i) { //Accured on default double t_0 = (i > 0) ? cc[i].getEffStart():0; double T = cc[i].getEffEnd(); List <double> knots = new List <double>(); knots.Add(t_0); for (int j = 0; j < Jumps.Count; j++) { if ((Jumps[j] < T) && (t_0 < Jumps[j])) { knots.Add(Jumps[j]); } } knots.Add(T); double t = knots[0]; double ht0 = creditCurve.getRT_(t); double rt0 = yieldCurve.getRT_(t); double b0 = Math.Exp(-rt0 - ht0); // this is the risky discount factor OMLib.Conventions.DayCount.Actual365 dc = new OMLib.Conventions.DayCount.Actual365(); double t0; if (i == 0) { t0 = knots[0] - cc[0].getEffStart(); } else { t0 = knots[0] - cc[i].getEffStart(); } double pv = 0.0; int nItems = knots.Count; for (int j = 1; j < nItems; ++j) { t = knots[j]; double ht1 = creditCurve.getRT_(t); double rt1 = yieldCurve.getRT_(t); double b1 = Math.Exp(-rt1 - ht1); double dt = knots[j] - knots[j - 1]; double dht = ht1 - ht0; double drt = rt1 - rt0; double dhrt = dht + drt + 1e-50; double tPV; double t1; if (i == 0) { t1 = knots[j] - cc[0].getEffStart(); } else { t1 = knots[j] - cc[i].getEffStart(); } if (Math.Abs(dhrt) < 1e-5) { tPV = dht * b0 * (t0 * (Math.Exp(-dhrt) - 1) / (-dhrt) + dt * ((-dhrt - 1) * (Math.Exp(-dhrt) - 1) - dhrt) / (dhrt * dhrt)); } else { tPV = dht / dhrt * (t0 * b0 - t1 * b1 + dt / dhrt * (b0 - b1)); } t0 = t1; pv += tPV; ht0 = ht1; rt0 = rt1; b0 = b1; } Acc += pv; } return(Acc); }
public double protectionLeg(CDS cds, YieldTermStructure yt, PiecewiseconstantHazardRate hazard, double valuationTime) { List <double> Jumps = yt.t; List <double> tenor = hazard.t; List <double> result = new List <double>(); int index = 0, indexj = 0, lastIndex = 0; while (index < Jumps.Count || indexj < tenor.Count) { if (lastIndex > 0) { if (index >= Jumps.Count) { if (!DateTime.Equals(result.Last(), tenor[indexj])) { result.Add(tenor[indexj]); lastIndex++; } indexj++; continue; } if (indexj >= tenor.Count) { if (!DateTime.Equals(result.Last(), Jumps[index])) { result.Add(Jumps[index]); lastIndex++; } index++; continue; } } double smallestVal = tenor.Last(); // Choose the smaller of a or b if (Jumps[index] < tenor[indexj]) { smallestVal = Jumps[index++]; } else { smallestVal = tenor[indexj++]; } // Don't insert duplicates if (lastIndex > 0) { if (result.Last() != smallestVal) { result.Add(smallestVal); lastIndex++; } } else { result.Add(smallestVal); lastIndex++; } } DateTime tradedate = cds.tradedate; DateTime settlementDate = tradedate.AddDays((int)valuationTime * 365); double recoveryrate = cds.Recovery; DateTime Stepindate = tradedate.AddDays(1); OMLib.Conventions.DayCount.Actual360 dc = new OMLib.Conventions.DayCount.Actual360(); CdsCoupon[] cf = cds.getCoupons(); double notional = cds.Notional; DateTime t0 = tradedate; double T = cf.Last().getEffEnd(); List <double> JumpNodes = new List <double>(); JumpNodes.Add(0); for (int j = 0; j < result.Count; j++) { if (result[j] < T) { JumpNodes.Add(result[j]); } } JumpNodes.Add(T); double ht0 = hazard.getRT_(JumpNodes[0]); double rt0 = yt.getRT_(JumpNodes[0]); double b0 = Math.Exp(-ht0 - rt0); // risky discount factor double pv = 0.0; double dPV = 0.0; for (int i = 1; i < JumpNodes.Count; ++i) { double ht1 = hazard.getRT_(JumpNodes[i]); double rt1 = yt.getRT_(JumpNodes[i]); double b1 = Math.Exp(-ht1 - rt1); double dht = ht1 - ht0; double drt = rt1 - rt0; double dhrt = dht + drt; // The formula has been modified from ISDA (but is equivalent) to avoid log(exp(x)) and explicitly // calculating the time step - it also handles the limit if (Math.Abs(dhrt) < 1e-5) { dPV = dht * b0 * Maths.Epsilon.epsilon(-dhrt) / (-dhrt); } else { dPV = (b0 - b1) * dht / dhrt; } pv += dPV; ht0 = ht1; rt0 = rt1; b0 = b1; } pv = pv * notional * (1 - recoveryrate); return(pv / yt.discount(settlementDate)); }