public PremiumLegElement(double protectionStart, CdsCoupon coupon, YieldTermStructure yieldCurve, int creditCurveKnot, double[] knots, AccrualOnDefaultFormulae formula) : base(coupon, yieldCurve, creditCurveKnot) { _coupon = coupon; _creditCurveKnot = creditCurveKnot; _formula = formula; if (formula == AccrualOnDefaultFormulae.ORIGINAL_ISDA) { _omega = 1.0 / 730; } else { _omega = 0.0; } _knots = DoublesScheduleGenerator.truncateSetInclusive(Math.Max(_coupon.getEffStart(), protectionStart), _coupon.getEffEnd(), knots); _n = _knots.Length; _rt = new double[_n]; _p = new double[_n]; for (int i = 0; i < _n; i++) { _rt[i] = yieldCurve.getRT_(_knots[i]); _p[i] = Math.Exp(-_rt[i]); } }
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); }
public CDS(double Coupon, double notional, DateTime maturity, DateTime firstpaymentday, DateTime tradedate, DateTime formerpaymentday, string frequency, double recovery, int settlement, int Cashsettlement) { // Product Setup OMLib.Conventions.DayCount.Actual360 AccuralDCC = new OMLib.Conventions.DayCount.Actual360(); OMLib.Conventions.DayCount.Actual365 curveDCC = new OMLib.Conventions.DayCount.Actual365(); Calendar calendar = new UnitedStates(); formerpaymentday = calendar.adjust(formerpaymentday, BusinessDayConvention.Following); int accrued = AccuralDCC.DayCount(formerpaymentday, calendar.adjust(tradedate, BusinessDayConvention.Following)) + 1; this.accruedday = accrued; this.marketvalue = new double(); this.accruedamt = notional * Coupon * accrued / 360; this.Notional = notional; this._payAccOnDefault = true; OMLib.Conventions.BusinessDayConvention convention = new OMLib.Conventions.BusinessDayConvention(Enums.BusinessDayConvention.ModifiedFollowing, tradedate); this.tradedate = calendar.adjust(tradedate, BusinessDayConvention.ModifiedFollowing); /*convention.AdjustedDate;*/ this.Recovery = recovery; convention = new OMLib.Conventions.BusinessDayConvention(Enums.BusinessDayConvention.ModifiedFollowing, firstpaymentday); this.firstpaymentdate = CdsAnalyticFactory.getNextIMMDate(tradedate); /*convention.AdjustedDate;*/ convention = new OMLib.Conventions.BusinessDayConvention(Enums.BusinessDayConvention.ModifiedFollowing, formerpaymentday); this.formerpaymentdate = CdsAnalyticFactory.getPrevIMMDate(tradedate);//calendar.adjust(formerpaymentday,BusinessDayConvention.ModifiedFollowing); /*convention.AdjustedDate;*/ this.Maturity = maturity; this.PremiumRate = Coupon; this.Frequency = frequency; this.Cashsettlement = Cashsettlement; DateTime valueDate = calendar.adjust(tradedate.AddDays(Cashsettlement)); convention = new OMLib.Conventions.BusinessDayConvention(Enums.BusinessDayConvention.ModifiedFollowing, tradedate.AddDays(3)); this.evalDate = calendar.adjust(tradedate.AddDays(settlement), BusinessDayConvention.ModifiedFollowing); /*convention.AdjustedDate;*/ this.Payment_Schedule = PremiumDates(this.Maturity, CdsAnalyticFactory.getNextIMMDate(tradedate), this.Frequency); QLNet.Calendar.OrthodoxImpl cal = new Calendar.OrthodoxImpl(); IsdaPremiumLegSchedule paymentSchedule = new IsdaPremiumLegSchedule(formerpaymentdate, maturity, payment_interval, StubConvention.SHORT_INITIAL, QLNet.BusinessDayConvention.ModifiedFollowing, cal, true); _coupons = CdsCoupon.makeCoupons(tradedate, paymentSchedule, true, ACT_360, ACT_365); OMLib.Conventions.DayCount.Actual365 CurveDCC = new OMLib.Conventions.DayCount.Actual365(); DateTime effectiveStartDate = tradedate; _accStart = DateTime.Compare(formerpaymentdate, tradedate) < 0 ?-CurveDCC.YearFraction(formerpaymentdate, tradedate) : CurveDCC.YearFraction(tradedate, formerpaymentdate); _cashSettlementTime = CurveDCC.YearFraction(tradedate, valueDate); _effectiveProtectionStart = DateTime.Compare(effectiveStartDate, tradedate) < 0 ? -CurveDCC.YearFraction(effectiveStartDate, tradedate) : CurveDCC.YearFraction(tradedate, effectiveStartDate); _protectionEnd = CurveDCC.YearFraction(tradedate, maturity); DateTime accStart = paymentSchedule.getAccStartDate(0); }
public double pvPremiumLegCreditSensitivity( CDS cds, YieldTermStructure yieldCurve, PiecewiseconstantHazardRate creditCurve, int creditCurveNode) { if (cds.getProtectionEnd() <= 0.0) { //short cut already expired CDSs return(0.0); } int n = cds.getNumPayments(); double pvSense = 0.0; for (int i = 0; i < n; i++) { CdsCoupon c = cds.getCoupon(i); double paymentTime = c.getPaymentTime(); double creditObsTime = c.getEffEnd(); double dqdh = creditCurve.getSingleNodeDiscountFactorSensitivity(creditObsTime, creditCurveNode); if (dqdh == 0) { continue; } double p = Math.Exp(-yieldCurve.getRT_(paymentTime)); pvSense += c.getYearFrac() * p * dqdh; } if (cds.isPayAccOnDefault()) { double start = cds.getNumPayments() == 1 ? cds.getEffectiveProtectionStart() : cds.getAccStart(); double[] integrationSchedule = DoublesScheduleGenerator.getIntegrationsPoints(start, cds.getProtectionEnd(), yieldCurve, creditCurve); double accPVSense = 0.0; for (int i = 0; i < n; i++) { accPVSense += calculateSinglePeriodAccrualOnDefaultCreditSensitivity( cds.getCoupon(i), cds.getEffectiveProtectionStart(), integrationSchedule, yieldCurve, creditCurve, creditCurveNode); } pvSense += accPVSense; } double df = Math.Exp(-yieldCurve.getRT_(cds.getCashSettleTime())); pvSense /= df; return(pvSense); }
public CouponOnlyElement(CdsCoupon coupon, YieldTermStructure yieldCurve, int creditCurveKnot) { _riskLessValue = coupon.getYearFrac() * Math.Exp(-yieldCurve.getRT_(coupon.getPaymentTime())); _effEnd = coupon.getEffEnd(); _creditCurveKnot = creditCurveKnot; }
public CreditCurveCalibrator(MultiCdsAnalytic multiCDS, YieldTermStructure yieldCurve, AccrualOnDefaultFormulae formula) { _nCDS = multiCDS.getNumMaturities(); _t = new double[_nCDS]; _lgd = new double[_nCDS]; _unitAccured = new double[_nCDS]; for (int i = 0; i < _nCDS; i++) { _t[i] = multiCDS.getProtectionEnd(i); _lgd[i] = multiCDS.getLGD(); _unitAccured[i] = multiCDS.getAccruedPremiumPerUnitSpread(i); } _valuationDF = Math.Exp(-yieldCurve.getRT_(multiCDS.getCashSettleTime())); //This is the global set of knots - it will be truncated down for the various leg elements //TODO this will not match ISDA C for forward starting (i.e. accStart > tradeDate) CDS, and will give different answers //if the Markit 'fix' is used in that case double[] knots = DoublesScheduleGenerator.getIntegrationsPoints( multiCDS.getEffectiveProtectionStart(), _t[_nCDS - 1], yieldCurve.t.ToArray(), _t.ToArray()); //The protection leg _protElems = new ProtectionLegElement[_nCDS]; for (int i = 0; i < _nCDS; i++) { _protElems[i] = new ProtectionLegElement( i == 0 ? multiCDS.getEffectiveProtectionStart() : _t[i - 1], _t[i], yieldCurve, i, knots); } _cds2CouponsMap = new int[_nCDS][]; _cdsCouponsUpdateMap = new int[_nCDS][]; _knot2CouponsMap = new int[_nCDS][]; List <CdsCoupon> allCoupons = new List <CdsCoupon>(_nCDS + multiCDS.getTotalPayments() - 1); allCoupons.AddRange(multiCDS.getStandardCoupons().ToList()); allCoupons.Add(multiCDS.getTerminalCoupon(_nCDS - 1)); int[] temp = new int[multiCDS.getTotalPayments()]; for (int i = 0; i < multiCDS.getTotalPayments(); i++) { temp[i] = i; } _cds2CouponsMap[_nCDS - 1] = temp; //complete the list of unique coupons and fill out the cds2CouponsMap for (int i = 0; i < _nCDS - 1; i++) { CdsCoupon c = multiCDS.getTerminalCoupon(i); int nPayments = Math.Max(0, multiCDS.getPaymentIndexForMaturity(i)) + 1; _cds2CouponsMap[i] = new int[nPayments]; for (int jj = 0; jj < nPayments - 1; jj++) { _cds2CouponsMap[i][jj] = jj; } //because of business-day adjustment, a terminal coupon can be identical to a standard coupon, //in which case it is not added again int index = allCoupons.IndexOf(c); if (index == -1) { index = allCoupons.Count; allCoupons.Add(c); } _cds2CouponsMap[i][nPayments - 1] = index; } //loop over the coupons to populate the couponUpdateMap _nCoupons = allCoupons.Count; int[] sizes = new int[_nCDS]; int[] map = new int[_nCoupons]; for (int i = 0; i < _nCoupons; i++) { CdsCoupon c = allCoupons[i]; int index = Array.BinarySearch(_t, c.getEffEnd()); if (index < 0) { index = -(index + 1); } sizes[index]++; map[i] = index; } //make the protection leg elements if (multiCDS.isPayAccOnDefault()) { _premElems = new PremiumLegElement[_nCoupons]; for (int i = 0; i < _nCoupons; i++) { _premElems[i] = new PremiumLegElement(multiCDS.getEffectiveProtectionStart(), allCoupons[i], yieldCurve, map[i], knots, formula); } } else { _premElems = new CouponOnlyElement[_nCoupons]; for (int i = 0; i < _nCoupons; i++) { _premElems[i] = new CouponOnlyElement(allCoupons[i], yieldCurve, map[i]); } } //sort a map from coupon to curve node, to a map from curve node to coupons for (int i = 0; i < _nCDS; i++) { _knot2CouponsMap[i] = new int[sizes[i]]; } int[] indexes = new int[_nCDS]; for (int i = 0; i < _nCoupons; i++) { int index = map[i]; _knot2CouponsMap[index][indexes[index]++] = i; } //the cdsCouponsUpdateMap is the intersection of the cds2CouponsMap and knot2CouponsMap for (int i = 0; i < _nCDS; i++) { _cdsCouponsUpdateMap[i] = intersection(_knot2CouponsMap[i], _cds2CouponsMap[i]); } }
public CreditCurveCalibrator(CDS[] cds, YieldTermStructure yieldCurve, AccrualOnDefaultFormulae formula) { _nCDS = cds.Length; Boolean payAccOnDefault = cds[0].isPayAccOnDefault(); double accStart = cds[0].getAccStart(); double effectProtStart = cds[0].getEffectiveProtectionStart(); double cashSettleTime = cds[0].getCashSettleTime(); _t = new double[_nCDS]; _t[0] = cds[0].getProtectionEnd(); //Check all the CDSs match for (int i = 1; i < _nCDS; i++) { _t[i] = cds[i].getProtectionEnd(); } _valuationDF = Math.Exp(-yieldCurve.getRT_(cashSettleTime)); _lgd = new double[_nCDS]; _unitAccured = new double[_nCDS]; for (int i = 0; i < _nCDS; i++) { _lgd[i] = cds[i].getLGD(); _unitAccured[i] = cds[i].getAccruedYearFraction(); } //This is the global set of knots - it will be truncated down for the various leg elements //TODO this will not match ISDA C for forward starting (i.e. accStart > tradeDate) CDS, and will give different answers //if the Markit 'fix' is used in that case double[] knots = DoublesScheduleGenerator. getIntegrationsPoints(effectProtStart, _t[_nCDS - 1], yieldCurve.t.ToArray(), _t); //The protection leg _protElems = new ProtectionLegElement[_nCDS]; for (int i = 0; i < _nCDS; i++) { _protElems[i] = new ProtectionLegElement(i == 0 ? effectProtStart : _t[i - 1], _t[i], yieldCurve, i, knots); } _cds2CouponsMap = new int[_nCDS][]; _cdsCouponsUpdateMap = new int[_nCDS][]; _knot2CouponsMap = new int[_nCDS][]; int nPaymentsFinalCDS = cds[_nCDS - 1].getNumPayments(); List <CdsCoupon> allCoupons = new List <CdsCoupon>(_nCDS + nPaymentsFinalCDS - 1); allCoupons.AddRange(cds[_nCDS - 1].getCoupons()); int[] temp = new int[nPaymentsFinalCDS]; for (int i = 0; i < nPaymentsFinalCDS; i++) { temp[i] = i; } _cds2CouponsMap[_nCDS - 1] = temp; //complete the list of unique coupons and fill out the cds2CouponsMap for (int i = 0; i < _nCDS - 1; i++) { CdsCoupon[] c = cds[i].getCoupons(); int nPayments = c.Length; _cds2CouponsMap[i] = new int[nPayments]; for (int k = 0; k < nPayments; k++) { int index = -1; for (int j = 0; j < allCoupons.Count; j++) { if (allCoupons[j].Equals(c[k])) { index = j; break; } } if (index == -1) { index = allCoupons.Count; allCoupons.Add(c[k]); } _cds2CouponsMap[i][k] = index; } } //loop over the coupons to populate the couponUpdateMap _nCoupons = allCoupons.Count; int[] sizes = new int[_nCDS]; int[] map = new int[_nCoupons]; for (int i = 0; i < _nCoupons; i++) { CdsCoupon c = allCoupons[i]; int index = Array.BinarySearch(_t, c.getEffEnd()); if (index < 0) { index = -(index + 1); } sizes[index]++; map[i] = index; } //make the protection leg elements if (payAccOnDefault) { _premElems = new PremiumLegElement[_nCoupons]; for (int i = 0; i < _nCoupons; i++) { _premElems[i] = new PremiumLegElement(effectProtStart, allCoupons[i], yieldCurve, map[i], knots, formula); } } else { _premElems = new CouponOnlyElement[_nCoupons]; for (int i = 0; i < _nCoupons; i++) { _premElems[i] = new CouponOnlyElement(allCoupons[i], yieldCurve, map[i]); } } //sort a map from coupon to curve node, to a map from curve node to coupons for (int i = 0; i < _nCDS; i++) { _knot2CouponsMap[i] = new int[sizes[i]]; } int[] indexes = new int[_nCDS]; for (int i = 0; i < _nCoupons; i++) { int index = map[i]; _knot2CouponsMap[index][indexes[index]++] = i; } //the cdsCouponsUpdateMap is the intersection of the cds2CouponsMap and knot2CouponsMap for (int i = 0; i < _nCDS; i++) { _cdsCouponsUpdateMap[i] = intersection(_knot2CouponsMap[i], _cds2CouponsMap[i]); } }
/** * Set up a strip of increasing maturity CDSs that have some coupons in common. The trade date, step-in date and valuation date and * accrual start date are all common, as is the payment frequency. The maturities are expressed as integer multiples of the * payment interval from a reference date (the next IMM date after the trade date for standard CDSs) - this guarantees that premiums * will be the same across several CDSs. * @param tradeDate The trade date * @param stepinDate (aka Protection Effective sate or assignment date). Date when party assumes ownership. This is usually T+1. This is when protection * (and risk) starts in terms of the model. Note, this is sometimes just called the Effective Date, however this can cause * confusion with the legal effective date which is T-60 or T-90. * @param cashSettlementDate The cash settlement date. The date that values are PVed to. Is is normally today + 3 business days. * @param accStartDate Accrual Start Date. This is when the CDS nominally starts in terms of premium payments. i.e. the number * of days in the first period (and thus the amount of the first premium payment) is counted from this date. * @param maturityReferanceDate A reference date that maturities are measured from. For standard CDSSs, this is the next IMM date after * the trade date, so the actually maturities will be some fixed periods after this. * @param maturityIndexes The maturities are fixed integer multiples of the payment interval, so for 6M, 1Y and 2Y tenors with a 3M * payment interval, would require 2, 4, and 8 as the indices * @param payAccOnDefault Is the accrued premium paid in the event of a default * @param paymentInterval The nominal step between premium payments (e.g. 3 months, 6 months). * @param stubType the stub convention * @param protectStart If protectStart = true, then protections starts at the beginning of the day, otherwise it is at the end. * @param recoveryRate The recovery rate * @param businessdayAdjustmentConvention How are adjustments for non-business days made * @param calendar HolidayCalendar defining what is a non-business day * @param accrualDayCount Day count used for accrual * @param curveDayCount Day count used on curve (NOTE ISDA uses ACT/365 and it is not recommended to change this) */ public MultiCdsAnalytic( DateTime tradeDate, DateTime stepinDate, DateTime cashSettlementDate, DateTime accStartDate, DateTime maturityReferanceDate, int[] maturityIndexes, Boolean payAccOnDefault, int paymentInterval, StubConvention stubType, Boolean protectStart, double recoveryRate, QLNet.BusinessDayConvention businessdayAdjustmentConvention, QLNet.Calendar calendar, Enums.DayCount accrualDayCount, Enums.DayCount curveDayCount) { OMLib.Conventions.DayCount.Thirty360 swapDCC = new OMLib.Conventions.DayCount.Thirty360(); OMLib.Conventions.DayCount.Actual360 moneyMarketDCC = new OMLib.Conventions.DayCount.Actual360(); OMLib.Conventions.DayCount.Actual365 curveDCC = new OMLib.Conventions.DayCount.Actual365(); _nMaturities = maturityIndexes.Length; _payAccOnDefault = payAccOnDefault; _accStart = DateTime.Compare(accStartDate, tradeDate) < 0 ? -curveDCC.YearFraction(accStartDate, tradeDate) : curveDCC.YearFraction(tradeDate, accStartDate); DateTime temp = DateTime.Compare(stepinDate, accStartDate) > 0 ? stepinDate : accStartDate; DateTime effectiveStartDate = protectStart ? temp.AddDays(-1) : temp; _cashSettlementTime = curveDCC.YearFraction(tradeDate, cashSettlementDate); _effectiveProtectionStart = curveDCC.YearFraction(tradeDate, effectiveStartDate); _lgd = 1 - recoveryRate; DateTime[] maturities = new DateTime[_nMaturities]; _protectionEnd = new double[_nMaturities]; int period = paymentInterval; for (int i = 0; i < _nMaturities; i++) { int tStep = period * maturityIndexes[i]; maturities[i] = maturityReferanceDate.AddMonths(tStep); _protectionEnd[i] = curveDCC.YearFraction(tradeDate, maturities[i]); } IsdaPremiumLegSchedule fullPaymentSchedule = new IsdaPremiumLegSchedule(accStartDate, maturities[_nMaturities - 1], period, stubType, businessdayAdjustmentConvention, calendar, protectStart); //remove already expired coupons IsdaPremiumLegSchedule paymentSchedule = fullPaymentSchedule.truncateSchedule(stepinDate); int couponOffset = fullPaymentSchedule.getNumPayments() - paymentSchedule.getNumPayments(); _totalPayments = paymentSchedule.getNumPayments(); _standardCoupons = new CdsCoupon[_totalPayments - 1]; for (int i = 0; i < (_totalPayments - 1); i++) { //The last coupon is actually a terminal coupon, so not included here _standardCoupons[i] = new CdsCoupon( tradeDate, paymentSchedule.getAccPaymentDateTriplet(i), protectStart, accrualDayCount, curveDayCount); } //find the terminal coupons _terminalCoupons = new CdsCoupon[_nMaturities]; _matIndexToPayments = new int[_nMaturities]; _accruedDays = new int[_nMaturities]; _accrued = new double[_nMaturities]; long secondJulianDate = stepinDate.Ticks; for (int i = 0; i < _nMaturities; i++) { int index = fullPaymentSchedule.getNominalPaymentDateIndex(maturities[i]); //maturity is unadjusted, but if protectionStart=true (i.e. standard CDS) there is effectively an extra day of accrued interest DateTime accEnd = protectStart ? maturities[i].AddDays(1) : maturities[i]; _terminalCoupons[i] = new CdsCoupon( tradeDate, fullPaymentSchedule.getAccStartDate(index), accEnd, fullPaymentSchedule.getPaymentDate(index), protectStart); _matIndexToPayments[i] = index - couponOffset; //This will only matter for the edge case when the trade date is 1 day before maturity DateTime tDate2 = _matIndexToPayments[i] < 0 ? fullPaymentSchedule.getAccStartDate(couponOffset - 1) : paymentSchedule.getAccStartDate(0); long firstJulianDate = tDate2.Ticks; _accruedDays[i] = secondJulianDate > firstJulianDate ? (int)(secondJulianDate - firstJulianDate) : 0; _accrued[i] = DateTime.Compare(tDate2, stepinDate) < 0 ? swapDCC.YearFraction(tDate2, stepinDate) : 0.0; } }
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); }