/** * 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 java.util.Arrays; /** * */ public class ProtectionLegElement { private final double[] _knots; private final double[] _rt; private final double[] _p; private final int _n; private final int _creditCurveKnot; public ProtectionLegElement( double start, double end, IsdaCompliantYieldCurve yieldCurve, int creditCurveKnot, double[] knots) { _knots = DoublesScheduleGenerator.truncateSetInclusive(start, end, 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]); } _creditCurveKnot = creditCurveKnot; } //------------------------------------------------------------------------- public double[] pvAndSense(IsdaCompliantCreditCurve creditCurve) { double t = _knots[0]; double[] htAndSense = creditCurve.getRTandSensitivity(t, _creditCurveKnot); double ht0 = htAndSense[0]; double rt0 = _rt[0]; double q0 = Math.exp(-ht0); double dqdh0 = -htAndSense[1] * q0; double p0 = _p[0]; double b0 = p0 * q0; // risky discount factor double pv = 0.0; double pvSense = 0.0; for (int i = 1; i < _n; ++i) { t = _knots[i]; htAndSense = creditCurve.getRTandSensitivity(t, _creditCurveKnot); double ht1 = htAndSense[0]; double rt1 = _rt[i]; double q1 = Math.exp(-ht1); double p1 = _p[i]; double b1 = p1 * q1; double dqdh1 = -htAndSense[1] * q1; 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 double dPV; double dPVSense; if (Math.abs(dhrt) < 1e-5) { double e = epsilon(-dhrt); double eP = epsilonP(-dhrt); dPV = dht * b0 * e; double dPVdq0 = p0 * ((1 + dht) * e - dht * eP); double dPVdq1 = -p0 * q0 / q1 * (e - dht * eP); dPVSense = dPVdq0 * dqdh0 + dPVdq1 * dqdh1; } else { double w1 = (b0 - b1) / dhrt; dPV = dht * w1; double w = drt * w1; dPVSense = ((w / q0 + dht * p0) / dhrt) * dqdh0 - ((w / q1 + dht * p1) / dhrt) * dqdh1; } pv += dPV; pvSense += dPVSense; ht0 = ht1; dqdh0 = dqdh1; rt0 = rt1; p0 = p1; q0 = q1; b0 = b1; } return new double[] {pv, pvSense}; } //------------------------------------------------------------------------- @Override public int hashCode() { int prime = 31; int result = 1; result = prime * result + _creditCurveKnot; result = prime * result + Arrays.hashCode(_knots); result = prime * result + _n; 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 (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ProtectionLegElement other = (ProtectionLegElement) obj; if (_creditCurveKnot != other._creditCurveKnot) { return false; } if (!Arrays.equals(_knots, other._knots)) { return false; } if (_n != other._n) { return false; } if (!Arrays.equals(_p, other._p)) { return false; } if (!Arrays.equals(_rt, other._rt)) { return false; } return true; } }