/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.credit.isdastandardmodel; import static com.opengamma.financial.convention.businessday.BusinessDayDateUtils.addWorkDays; import org.testng.annotations.Test; import org.threeten.bp.LocalDate; import org.threeten.bp.Period; import cern.jet.random.engine.MersenneTwister; import com.opengamma.analytics.math.FunctionUtils; import com.opengamma.analytics.math.MathException; import com.opengamma.analytics.math.linearalgebra.CholeskyDecompositionCommons; import com.opengamma.analytics.math.linearalgebra.CholeskyDecompositionResult; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.analytics.math.matrix.DoubleMatrix2D; import com.opengamma.analytics.math.matrix.MatrixAlgebra; import com.opengamma.analytics.math.matrix.OGMatrixAlgebra; import com.opengamma.analytics.math.statistics.distribution.NormalDistribution; import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.test.TestGroup; /** * This tests the time to calibrate the yield and credit curves. By default the tests are disabled. */ @Test(groups = TestGroup.UNIT) public class CalibrationTimingTest extends ISDABaseTest { private static ProbabilityDistribution<Double> NORMAL = new NormalDistribution(0, 1, new MersenneTwister(MersenneTwister.DEFAULT_SEED)); private static final MatrixAlgebra MA = new OGMatrixAlgebra(); private static final CholeskyDecompositionCommons CHOLESKY = new CholeskyDecompositionCommons(); private static final int NUM_YIELD_CURVE_POINTS = 20; private static final Period SWAP_INTERVAL = Period.ofMonths(6); private static final ISDAInstrumentTypes[] YC_INST_TYPES; private static final Period[] YC_INST_TENOR; private static final double[] YC_MARKET_RATES = new double[] {0.00340055550701297, 0.00636929056400781, 0.0102617798438113, 0.0135851258907251, 0.0162809551414651, 0.020583125112332, 0.0227369218210212, 0.0251978805237614, 0.0273223815467694, 0.0310882447627048, 0.0358397743454067, 0.036047665095421, 0.0415916567616181, 0.044066373237682, 0.046708518178509, 0.0491196954851753, 0.0529297239911766, 0.0562025436376854, 0.0589772202773522, 0.0607471217692999 }; private static final DoubleMatrix2D YC_COVAR; private static final DoubleMatrix2D YC_COVAR_SQR; private static final int NUM_CREDIT_CURVE_POINTS = 11; private static final LocalDate[] CC_DATES = new LocalDate[] {LocalDate.of(2013, 9, 20), LocalDate.of(2013, 12, 20), LocalDate.of(2014, 3, 20), LocalDate.of(2014, 6, 20), LocalDate.of(2014, 9, 20), LocalDate.of(2015, 9, 20), LocalDate.of(2016, 9, 20), LocalDate.of(2017, 9, 20), LocalDate.of(2018, 9, 20), LocalDate.of(2020, 9, 20), LocalDate.of(2023, 9, 20) }; private static final DoubleMatrix2D CC_COVAR; private static final DoubleMatrix2D CC_COVAR_SQR; private static final LocalDate TODAY = LocalDate.of(2013, 6, 4); private static final LocalDate STEPIN_DATE = TODAY.plusDays(1); // aka effective date private static final LocalDate SPOTDATE = addWorkDays(TODAY, 3, DEFAULT_CALENDAR); // 3 working days on private static final LocalDate VALUEDATE = SPOTDATE; private static final LocalDate STARTDATE = TODAY; // have protection start now. private static final double[] MARKET_CREDIT_SPREADS = new double[] {40, 45, 50, 55, 70, 90, 130, 130, 130, 120, 115, 105, 90 }; static { // setup yield curve stuff YC_INST_TYPES = new ISDAInstrumentTypes[NUM_YIELD_CURVE_POINTS]; YC_INST_TENOR = new Period[NUM_YIELD_CURVE_POINTS]; final int[] mmMonths = new int[] {1, 2, 3, 6, 9, 12 }; final int[] swapYears = new int[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20, 25, 30 }; final int nMoneyMarket = 6; final int nSwaps = 14; // check ArgumentChecker.isTrue(mmMonths.length == nMoneyMarket, "mmMonths"); ArgumentChecker.isTrue(swapYears.length == nSwaps, "swapYears"); for (int i = 0; i < nMoneyMarket; i++) { YC_INST_TYPES[i] = ISDAInstrumentTypes.MoneyMarket; YC_INST_TENOR[i] = Period.ofMonths(mmMonths[i]); } for (int i = nMoneyMarket; i < NUM_YIELD_CURVE_POINTS; i++) { YC_INST_TYPES[i] = ISDAInstrumentTypes.Swap; YC_INST_TENOR[i] = Period.ofYears(swapYears[i - nMoneyMarket]); } final LocalDate[] ycMatDates = new LocalDate[NUM_YIELD_CURVE_POINTS]; final double[] ycMatTimes = new double[NUM_YIELD_CURVE_POINTS]; for (int i = 0; i < NUM_YIELD_CURVE_POINTS; i++) { ycMatDates[i] = SPOTDATE.plus(YC_INST_TENOR[i]); ycMatTimes[i] = ACT365F.getDayCountFraction(SPOTDATE, ycMatDates[i]); } final double ycDecorrelation = -0.2; final double ycVar = 1 / 36.; double[][] temp = new double[NUM_YIELD_CURVE_POINTS][NUM_YIELD_CURVE_POINTS]; for (int i = 0; i < NUM_YIELD_CURVE_POINTS; i++) { temp[i][i] = ycVar * FunctionUtils.square(YC_MARKET_RATES[i]); for (int j = 0; j < i; j++) { final double t1 = ycMatTimes[i]; final double t2 = ycMatTimes[j]; final double rho = t1 >= t2 ? Math.pow(t1 / t2, ycDecorrelation) : Math.pow(t2 / t1, ycDecorrelation); temp[i][j] = rho * ycVar * YC_MARKET_RATES[i] * YC_MARKET_RATES[j]; } } for (int i = 0; i < NUM_YIELD_CURVE_POINTS; i++) { for (int j = i + 1; j < NUM_YIELD_CURVE_POINTS; j++) { temp[i][j] = temp[j][i]; } } YC_COVAR = new DoubleMatrix2D(temp); CholeskyDecompositionResult res = CHOLESKY.evaluate(YC_COVAR); YC_COVAR_SQR = res.getL(); // setup credit curve stuff final double[] coupons = new double[NUM_CREDIT_CURVE_POINTS]; final int n = coupons.length; for (int i = 0; i < n; i++) { coupons[i] = MARKET_CREDIT_SPREADS[i] / 10000.0; } final double[] ccMatTimes = new double[NUM_CREDIT_CURVE_POINTS]; for (int i = 0; i < NUM_CREDIT_CURVE_POINTS; i++) { ccMatTimes[i] = ACT365F.getDayCountFraction(TODAY, CC_DATES[i]); } final double ccDecorrelation = -0.01; final double ccVar = 1 / 50.; temp = new double[NUM_CREDIT_CURVE_POINTS][NUM_CREDIT_CURVE_POINTS]; for (int i = 0; i < NUM_CREDIT_CURVE_POINTS; i++) { temp[i][i] = ccVar * FunctionUtils.square(coupons[i]); for (int j = 0; j < i; j++) { final double t1 = ccMatTimes[i]; final double t2 = ccMatTimes[j]; final double rho = t1 >= t2 ? Math.pow(t1 / t2, ccDecorrelation) : Math.pow(t2 / t1, ccDecorrelation); temp[i][j] = rho * ccVar * coupons[i] * coupons[j]; } } for (int i = 0; i < NUM_CREDIT_CURVE_POINTS; i++) { for (int j = i + 1; j < NUM_CREDIT_CURVE_POINTS; j++) { temp[i][j] = temp[j][i]; } } CC_COVAR = new DoubleMatrix2D(temp); res = CHOLESKY.evaluate(CC_COVAR); CC_COVAR_SQR = res.getL(); } @Test(enabled = false) public void yieldCurvePeturbTest() { System.out.println("CalibrationTimingTest - set enabled=false before push"); final DoubleMatrix1D base = new DoubleMatrix1D(YC_MARKET_RATES); final int nSims = 10000; final ISDACompliantYieldCurveBuild ycBuilder = new ISDACompliantYieldCurveBuild(SPOTDATE, YC_INST_TYPES, YC_INST_TENOR, ACT360, D30360, SWAP_INTERVAL, MOD_FOLLOWING); final long startTime = System.nanoTime(); int failed = 0; for (int count = 0; count < nSims; count++) { final double[] temp = new double[NUM_YIELD_CURVE_POINTS]; for (int i = 0; i < NUM_YIELD_CURVE_POINTS; i++) { temp[i] = NORMAL.nextRandom(); } final DoubleMatrix1D z = new DoubleMatrix1D(temp); final DoubleMatrix1D w = (DoubleMatrix1D) MA.multiply(YC_COVAR_SQR, z); final DoubleMatrix1D peturbedRates = (DoubleMatrix1D) MA.add(base, w); // System.out.println(peturbedRates); try { @SuppressWarnings("unused") final ISDACompliantCurve yieldCurve = ycBuilder.build(peturbedRates.getData()); } catch (final MathException e) { failed++; } } final double totalTime = (System.nanoTime() - startTime) / 1e9; System.out.println("total time for " + nSims + " yield Curves: " + totalTime + "s. Failed to build " + failed + " curves (" + ((100. * failed) / nSims) + "%)"); } @Test(enabled = false) public void creditCurvePeturbTest() { System.out.println("CalibrationTimingTest - set enabled=false before push"); final ISDACompliantYieldCurve yieldCurve = ISDACompliantYieldCurveBuild.build(SPOTDATE, YC_INST_TYPES, YC_INST_TENOR, YC_MARKET_RATES, ACT360, D30360, SWAP_INTERVAL, MOD_FOLLOWING); final Period tenor = Period.ofMonths(3); final StubType stubType = StubType.FRONTSHORT; final boolean payAccOndefault = true; final boolean protectionStart = true; final double recovery = 0.4; final double[] coupons = new double[NUM_CREDIT_CURVE_POINTS]; final CDSAnalytic[] cds = new CDSAnalytic[NUM_CREDIT_CURVE_POINTS]; for (int i = 0; i < NUM_CREDIT_CURVE_POINTS; i++) { cds[i] = new CDSAnalytic(TODAY, STEPIN_DATE, VALUEDATE, STARTDATE, CC_DATES[i], payAccOndefault, tenor, stubType, protectionStart, recovery); coupons[i] = MARKET_CREDIT_SPREADS[i] / 10000.0; } final DoubleMatrix1D base = new DoubleMatrix1D(coupons); final int nSims = 10000; final long startTime = System.nanoTime(); int failed = 0; for (int count = 0; count < nSims; count++) { final double[] temp = new double[NUM_CREDIT_CURVE_POINTS]; for (int i = 0; i < NUM_CREDIT_CURVE_POINTS; i++) { temp[i] = NORMAL.nextRandom(); } final DoubleMatrix1D z = new DoubleMatrix1D(temp); final DoubleMatrix1D w = (DoubleMatrix1D) MA.multiply(CC_COVAR_SQR, z); final DoubleMatrix1D peturbedSpreads = (DoubleMatrix1D) MA.add(base, w); // System.out.println(peturbedRates); try { @SuppressWarnings("unused") final ISDACompliantCreditCurve creditCurve = CREDIT_CURVE_BUILDER.calibrateCreditCurve(cds, peturbedSpreads.getData(), yieldCurve); } catch (final MathException e) { failed++; } } final double totalTime = (System.nanoTime() - startTime) / 1e9; System.out.println("total time for " + nSims + " credit Curves: " + totalTime + "s. Failed to build " + failed + " curves (" + ((100. * failed) / nSims) + "%)"); } }