/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.credit.isda; import static com.opengamma.strata.math.impl.util.Epsilon.epsilon; import static com.opengamma.strata.math.impl.util.Epsilon.epsilonP; import static com.opengamma.strata.math.impl.util.Epsilon.epsilonPP; import java.util.Arrays; import com.opengamma.strata.collect.ArgChecker; /** * */ public class PremiumLegElement extends CouponOnlyElement { private final CdsCoupon _coupon; private final AccrualOnDefaultFormulae _formula; private final double _omega; private final int _creditCurveKnot; private final double[] _knots; private final double[] _rt; private final double[] _p; private final int _n; public PremiumLegElement( double protectionStart, CdsCoupon coupon, IsdaCompliantYieldCurve yieldCurve, int creditCurveKnot, double[] knots, AccrualOnDefaultFormulae formula) { super(coupon, yieldCurve, creditCurveKnot); ArgChecker.notNull(coupon, "coupon"); _coupon = coupon; _creditCurveKnot = creditCurveKnot; _formula = formula; if (formula == AccrualOnDefaultFormulae.ORIGINAL_ISDA) { _omega = 1. / 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]); } } @Override public double[] pvAndSense(IsdaCompliantCreditCurve creditCurve) { double[] pv = super.pvAndSense(creditCurve); double[] aod = new double[2]; if (_formula == AccrualOnDefaultFormulae.MARKIT_FIX) { aod = accOnDefaultMarkitFix(creditCurve); } else { aod = accOnDefault(creditCurve); } return new double[] {pv[0] + aod[0], pv[1] + aod[1]}; } private double[] accOnDefault(IsdaCompliantCreditCurve creditCurve) { double t = _knots[0]; double[] htAndSense = creditCurve.getRTandSensitivity(t, _creditCurveKnot); double ht0 = htAndSense[0]; double rt0 = _rt[0]; double p0 = _p[0]; double q0 = Math.exp(-ht0); double b0 = p0 * q0; // this is the risky discount factor double dqdr0 = -htAndSense[1] * q0; double t0 = t - _coupon.getEffStart() + _omega; double pv = 0.0; double pvSense = 0.0; for (int j = 1; j < _n; ++j) { t = _knots[j]; htAndSense = creditCurve.getRTandSensitivity(t, _creditCurveKnot); double ht1 = htAndSense[0]; double rt1 = _rt[j]; double p1 = _p[j]; double q1 = Math.exp(-ht1); double b1 = p1 * q1; double dqdr1 = -htAndSense[1] * q1; double dt = _knots[j] - _knots[j - 1]; double dht = ht1 - ht0; double drt = rt1 - rt0; double dhrt = dht + drt; double tPV; double tPvSense; double t1 = t - _coupon.getEffStart() + _omega; if (Math.abs(dhrt) < 1e-5) { double e = epsilon(-dhrt); double eP = epsilonP(-dhrt); double ePP = epsilonPP(-dhrt); double w1 = t0 * e + dt * eP; double w2 = t0 * eP + dt * ePP; double dPVdq0 = p0 * ((1 + dhrt) * w1 - dht * w2); double dPVdq1 = b0 / q1 * (-w1 + dht * w2); tPV = dht * b0 * w1; 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); tPV = dht / dhrt * (t0 * b0 - t1 * b1 + dt / dhrt * (b0 - b1)); tPvSense = dPVdq0 * dqdr0 - dPVdq1 * dqdr1; } t0 = t1; pv += tPV; pvSense += tPvSense; ht0 = ht1; rt0 = rt1; p0 = p1; q0 = q1; b0 = b1; dqdr0 = dqdr1; } return new double[] {_coupon.getYFRatio() * pv, _coupon.getYFRatio() * pvSense}; } private double[] accOnDefaultMarkitFix(IsdaCompliantCreditCurve creditCurve) { double t = _knots[0]; double[] htAndSense = creditCurve.getRTandSensitivity(t, _creditCurveKnot); double ht0 = htAndSense[0]; double rt0 = _rt[0]; double p0 = _p[0]; double q0 = Math.exp(-ht0); double b0 = p0 * q0; // this is the risky discount factor double dqdr0 = -htAndSense[1] * q0; double pv = 0.0; double pvSense = 0.0; for (int j = 1; j < _n; ++j) { t = _knots[j]; htAndSense = creditCurve.getRTandSensitivity(t, _creditCurveKnot); double ht1 = htAndSense[0]; double rt1 = _rt[j]; double p1 = _p[j]; double q1 = Math.exp(-ht1); double b1 = p1 * q1; double dqdr1 = -htAndSense[1] * q1; double dt = _knots[j] - _knots[j - 1]; double dht = ht1 - ht0; double drt = rt1 - rt0; double dhrt = dht + drt; double tPV; double tPvSense; if (Math.abs(dhrt) < 1e-5) { double eP = epsilonP(-dhrt); double ePP = epsilonPP(-dhrt); tPV = dht * dt * b0 * eP; 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)); tPV = dt * w3 * w2; tPvSense = dPVdq0 * dqdr0 - dPVdq1 * dqdr1; } pv += tPV; pvSense += tPvSense; ht0 = ht1; rt0 = rt1; p0 = p1; q0 = q1; b0 = b1; dqdr0 = dqdr1; } return new double[] {_coupon.getYFRatio() * pv, _coupon.getYFRatio() * pvSense}; } //------------------------------------------------------------------------- @Override public int hashCode() { int prime = 31; int result = super.hashCode(); result = prime * result + ((_coupon == null) ? 0 : _coupon.hashCode()); result = prime * result + _creditCurveKnot; result = prime * result + ((_formula == null) ? 0 : _formula.hashCode()); result = prime * result + Arrays.hashCode(_knots); result = prime * result + _n; long temp; temp = Double.doubleToLongBits(_omega); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + Arrays.hashCode(_p); result = prime * result + Arrays.hashCode(_rt); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } PremiumLegElement other = (PremiumLegElement) obj; if (_coupon == null) { if (other._coupon != null) { return false; } } else if (!_coupon.equals(other._coupon)) { return false; } if (_creditCurveKnot != other._creditCurveKnot) { return false; } if (_formula != other._formula) { return false; } if (!Arrays.equals(_knots, other._knots)) { return false; } if (_n != other._n) { return false; } if (Double.doubleToLongBits(_omega) != Double.doubleToLongBits(other._omega)) { return false; } if (!Arrays.equals(_p, other._p)) { return false; } if (!Arrays.equals(_rt, other._rt)) { return false; } return true; } }