/** * 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 java.time.LocalDate; import java.time.Period; import java.util.function.Function; import com.opengamma.strata.basics.date.DayCount; import com.opengamma.strata.basics.date.DayCounts; import com.opengamma.strata.basics.schedule.StubConvention; import com.opengamma.strata.collect.ArgChecker; import com.opengamma.strata.math.impl.rootfinding.BracketRoot; import com.opengamma.strata.math.impl.rootfinding.BrentSingleRootFinder; import com.opengamma.strata.math.impl.rootfinding.RealSingleRootFinder; /** * This should be viewed as "proof of concept" code, since it used the code that has date logic mixed with the analytics (this was to * mimic the structure of the ISDA c code). This should <b>not</b> be used for production credit (hazard) curve calibration/bootstrapping, * ISDACompliantCreditCurveCalibrator should be used. * */ public class IsdaCompliantCurveCalibrator { private static final DayCount ACT_365 = DayCounts.ACT_365F; private static final BracketRoot BRACKER = new BracketRoot(); private static final RealSingleRootFinder ROOTFINDER = new BrentSingleRootFinder(); private static final IsdaCompliantPresentValueCreditDefaultSwap PRICER = new IsdaCompliantPresentValueCreditDefaultSwap(); public IsdaCompliantDateCreditCurve calibrateHazardCurve( LocalDate today, LocalDate stepinDate, LocalDate valueDate, LocalDate startDate, LocalDate[] endDates, double[] couponRates, boolean payAccOnDefault, Period tenor, StubConvention stubType, boolean protectStart, IsdaCompliantDateYieldCurve yieldCurve, double recoveryRate) { ArgChecker.notNull(today, "null today"); ArgChecker.notNull(stepinDate, "null stepinDate"); ArgChecker.notNull(valueDate, "null valueDate"); ArgChecker.notNull(startDate, "null startDate"); ArgChecker.noNulls(endDates, "null endDates"); ArgChecker.notEmpty(couponRates, "no or null couponRates"); ArgChecker.notNull(tenor, "null tenor"); ArgChecker.notNull(stubType, "null stubType"); ArgChecker.notNull(yieldCurve, "null yieldCurve"); ArgChecker.inRange(recoveryRate, 0d, 1d, "recoveryRate"); ArgChecker.isFalse(valueDate.isBefore(today), "Require valueDate >= today"); ArgChecker.isFalse(stepinDate.isBefore(today), "Require stepin >= today"); int n = endDates.length; ArgChecker.isTrue(n == couponRates.length, "length of couponRates does not match endDates"); // use continuous premiums as initial guess double[] guess = new double[n]; double[] t = new double[n]; double lgd = 1 - recoveryRate; for (int i = 0; i < n; i++) { guess[i] = couponRates[i] / lgd; t[i] = ACT_365.yearFraction(today, endDates[i]); } IsdaCompliantDateCreditCurve hazardCurve = new IsdaCompliantDateCreditCurve(today, endDates, guess); for (int i = 0; i < n; i++) { CDSPricer func = new CDSPricer(i, today, stepinDate, valueDate, startDate, endDates[i], couponRates[i], protectStart, payAccOnDefault, tenor, stubType, recoveryRate, yieldCurve, hazardCurve); double[] bracket = BRACKER.getBracketedPoints(func, 0.9 * guess[i], 1.1 * guess[i], 0.0, Double.POSITIVE_INFINITY); double zeroRate = ROOTFINDER.getRoot(func, bracket[0], bracket[1]); hazardCurve = hazardCurve.withRate(zeroRate, i); } return hazardCurve; } private class CDSPricer implements Function<Double, Double> { private final int _index; private final LocalDate _today; private final LocalDate _stepinDate; private final LocalDate _valueDate; private final LocalDate _startDate; private final LocalDate _endDate; private final double _couponRate; private final boolean _protectStart; private final boolean _payAccOnDefault; private final Period _tenor; private final StubConvention _stubType; private final double _rr; private IsdaCompliantDateYieldCurve _yieldCurve; private IsdaCompliantDateCreditCurve _hazardCurve; public CDSPricer( int index, LocalDate today, LocalDate stepinDate, LocalDate valueDate, LocalDate startDate, LocalDate endDate, double couponRate, boolean protectStart, boolean payAccOnDefault, Period tenor, StubConvention stubType, double rr, IsdaCompliantDateYieldCurve yieldCurve, IsdaCompliantDateCreditCurve hazardCurve) { _index = index; _today = today; _stepinDate = stepinDate; _valueDate = valueDate; _startDate = startDate; _endDate = endDate; _couponRate = couponRate; _protectStart = protectStart; _payAccOnDefault = payAccOnDefault; _tenor = tenor; _stubType = stubType; _rr = rr; _yieldCurve = yieldCurve; _hazardCurve = hazardCurve; } @Override public Double apply(Double x) { // TODO this direct access is unpleasant IsdaCompliantDateCreditCurve hazardCurve = _hazardCurve.withRate(x, _index); double rpv01 = PRICER.pvPremiumLegPerUnitSpread(_today, _stepinDate, _valueDate, _startDate, _endDate, _payAccOnDefault, _tenor, _stubType, _yieldCurve, hazardCurve, _protectStart, CdsPriceType.CLEAN); double protectLeg = PRICER.calculateProtectionLeg(_today, _stepinDate, _valueDate, _startDate, _endDate, _yieldCurve, hazardCurve, _rr, _protectStart); double pv = protectLeg - _couponRate * rpv01; return pv; } } }