/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.provider.calculator.discounting; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import java.util.HashMap; import java.util.List; import java.util.Set; import org.testng.annotations.Test; import org.threeten.bp.Period; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.forex.datasets.StandardDataSetsEURUSDForex; import com.opengamma.analytics.financial.instrument.index.GeneratorAttributeIR; import com.opengamma.analytics.financial.instrument.index.GeneratorSwapFixedIbor; import com.opengamma.analytics.financial.instrument.index.GeneratorSwapFixedIborMaster; import com.opengamma.analytics.financial.instrument.index.GeneratorSwapIborIbor; import com.opengamma.analytics.financial.instrument.index.GeneratorSwapIborIborMaster; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.swap.SwapFixedIborDefinition; import com.opengamma.analytics.financial.instrument.swap.SwapIborIborDefinition; import com.opengamma.analytics.financial.interestrate.payments.derivative.Coupon; import com.opengamma.analytics.financial.interestrate.swap.derivative.Swap; import com.opengamma.analytics.financial.interestrate.swap.derivative.SwapFixedCoupon; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.financial.schedule.ScheduleCalculator; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.matrix.DoubleMatrix2D; import com.opengamma.analytics.tutorial.datasets.AnalysisMarketDataJPYSets; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.financial.convention.calendar.MondayToFridayCalendar; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; import com.opengamma.util.test.TestGroup; import com.opengamma.util.time.DateUtils; import com.opengamma.util.tuple.Pair; /** * Tests the zero-coupon rate cross-gamma calculator for multi-curve. */ @Test(groups = TestGroup.UNIT) public class CrossGammaMultiCurveCalculatorTest { private static final Calendar TYO = new MondayToFridayCalendar("TYO"); private static final ZonedDateTime CALIBRATION_DATE = DateUtils.getUTCDate(2014, 8, 2); private static final GeneratorSwapFixedIborMaster GENERATOR_IRS_MASTER = GeneratorSwapFixedIborMaster.getInstance(); private static final GeneratorSwapIborIborMaster GENERATOR_BS_MASTER = GeneratorSwapIborIborMaster.getInstance(); /** Instrument description */ private static final GeneratorSwapFixedIbor JPY6MLIBOR6M = GENERATOR_IRS_MASTER.getGenerator("JPY6MLIBOR6M", TYO); private static final GeneratorSwapIborIbor JPYLIBOR3MLIBOR6M = GENERATOR_BS_MASTER.getGenerator("JPYLIBOR3MLIBOR6M", TYO); private static final IborIndex JPYLIBOR6M = JPY6MLIBOR6M.getIborIndex(); private static final Period SWAP_TENOR = Period.ofYears(10); private static final ZonedDateTime SETTLEMENT_DATE = ScheduleCalculator.getAdjustedDate(CALIBRATION_DATE, JPYLIBOR6M.getSpotLag(), TYO); private static final double NOTIONAL = 100000000; //100m private static final double RATE_FIXED = 0.025; private static final double SPREAD_BS = 0.0005; private static final SwapFixedIborDefinition SWAP_FIXED_IBOR_DEFINITION = SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, JPY6MLIBOR6M, NOTIONAL, RATE_FIXED, true); private static final SwapFixedCoupon<Coupon> SWAP_FIXED_IBOR = SWAP_FIXED_IBOR_DEFINITION.toDerivative(CALIBRATION_DATE); private static final SwapIborIborDefinition SWAP_IBOR_IBOR_DEFINITION = JPYLIBOR3MLIBOR6M.generateInstrument(CALIBRATION_DATE, SPREAD_BS, NOTIONAL, new GeneratorAttributeIR(SWAP_TENOR)); /** Calculators */ private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance(); private static final PresentValueCurveSensitivityDiscountingCalculator PVCSDC = PresentValueCurveSensitivityDiscountingCalculator.getInstance(); private static final CrossGammaMultiCurveCalculator CGC = new CrossGammaMultiCurveCalculator(PVCSDC); /** Curves */ private static final Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> MULTICURVE_PAIR = AnalysisMarketDataJPYSets.getMulticurveJPY(); private static final MulticurveProviderDiscount MULTICURVE = MULTICURVE_PAIR.getFirst(); /** Constants */ private static final double SHIFT = 1.0E-4; private static final double TOLERANCE_PV_GAMMA = 2.0E+0; private static final double TOLERANCE_PV_GAMMA_RELATIF = 6.0E-4; /** Default size of bump: 1 basis point. */ private static final double BP1 = 1.0E-4; /** Default size of bump: 1 basis point. */ private static final DoubleMatrix2D GAMMA_CROSS_CURVES = CGC.calculateCrossGammaCrossCurve(SWAP_FIXED_IBOR, MULTICURVE); @Test public void constructor() { CrossGammaMultiCurveCalculator cgcShift = new CrossGammaMultiCurveCalculator(BP1 * 10, PVCSDC); DoubleMatrix2D gammaCrossCurve10 = cgcShift.calculateCrossGammaCrossCurve(SWAP_FIXED_IBOR, MULTICURVE); int nbNode = GAMMA_CROSS_CURVES.getNumberOfColumns(); boolean unchanged = true; for (int i = 0; i < nbNode; i++) { for (int j = 0; j < nbNode; j++) { unchanged = unchanged && (Math.abs(GAMMA_CROSS_CURVES.getEntry(i, j) - gammaCrossCurve10.getEntry(i, j)) > TOLERANCE_PV_GAMMA); } } assertFalse("CrossGammaMultiCurveCalculator", unchanged); // Changing the shift is changing the results. } @Test(expectedExceptions = IllegalArgumentException.class) public void multicurrencies() { MulticurveProviderDiscount multicurveMulticcy = StandardDataSetsEURUSDForex.getCurvesEUROisUSDOis().getFirst(); CGC.calculateCrossGammaIntraCurve(SWAP_FIXED_IBOR, multicurveMulticcy); } @Test public void crossGammaCrossCurveIrs() { final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(CALIBRATION_DATE); HashMap<String, DoubleMatrix2D> gammaIntraCurve = CGC.calculateCrossGammaIntraCurve(swap, MULTICURVE); // Checking that overlap is coherent Set<String> names = MULTICURVE.getAllNames(); int submatrixsize = 0; for (String name : names) { DoubleMatrix2D gamma = gammaIntraCurve.get(name); YieldAndDiscountCurve curve = MULTICURVE.getCurve(name); ArgumentChecker.isTrue(curve instanceof YieldCurve, "curve should be YieldCurve"); YieldCurve yieldCurve = (YieldCurve) curve; ArgumentChecker.isTrue(yieldCurve.getCurve() instanceof InterpolatedDoublesCurve, "Yield curve should be based on InterpolatedDoublesCurve"); InterpolatedDoublesCurve interpolatedCurve = (InterpolatedDoublesCurve) yieldCurve.getCurve(); int nbNode = interpolatedCurve.getXDataAsPrimitive().length; for (int i = 0; i < nbNode; i++) { for (int j = 0; j < nbNode; j++) { double ic = gamma.getEntry(i, j); double cc = GAMMA_CROSS_CURVES.getEntry(submatrixsize + i, submatrixsize + j); assertTrue("CrossGammaMultiCurveCalculator - cross-gamma cross-curves - " + i + " - " + j + " / " + ic + " - " + cc, (Math.abs(ic / cc - 1) < TOLERANCE_PV_GAMMA_RELATIF) || // If relative difference is small enough (Math.abs(ic - cc) < TOLERANCE_PV_GAMMA)); // If absolute difference is small enough } } submatrixsize += nbNode; } } @Test public void crossGammaIntraCurveIrs() { final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(CALIBRATION_DATE); crossGammaIntraCurve(swap); } @Test public void crossGammaIntraCurveBs() { final Swap<Coupon, Coupon> swap = SWAP_IBOR_IBOR_DEFINITION.toDerivative(CALIBRATION_DATE); crossGammaIntraCurve(swap); } private void crossGammaIntraCurve(Swap<?, ?> swap) { HashMap<String, DoubleMatrix2D> gammaMap = CGC.calculateCrossGammaIntraCurve(swap, MULTICURVE); Set<String> names = MULTICURVE.getAllNames(); for (String name : names) { // Start curves Set<Currency> ccys = MULTICURVE.getCurrencies(); List<Currency> nameCcys = MULTICURVE.getCurrencyForName(name); List<IborIndex> nameIbors = MULTICURVE.getIborIndexForName(name); ArgumentChecker.isTrue(ccys.size() == 1, "only one currency allowed for multi-curve gamma"); Currency ccy = ccys.iterator().next(); YieldAndDiscountCurve curve = MULTICURVE.getCurve(name); ArgumentChecker.isTrue(curve instanceof YieldCurve, "curve should be YieldCurve"); YieldCurve yieldCurve = (YieldCurve) curve; ArgumentChecker.isTrue(yieldCurve.getCurve() instanceof InterpolatedDoublesCurve, "Yield curve should be based on InterpolatedDoublesCurve"); InterpolatedDoublesCurve interpolatedCurve = (InterpolatedDoublesCurve) yieldCurve.getCurve(); double[] y = interpolatedCurve.getYDataAsPrimitive(); double[] x = interpolatedCurve.getXDataAsPrimitive(); int nbNode = y.length; double[][] gammaComputed = gammaMap.get(name).getData(); double[][] gammaExpected = new double[nbNode][nbNode]; for (int i = 0; i < nbNode; i++) { // Start node for (int j = 0; j < nbNode; j++) { double[][] pv = new double[2][2]; for (int pmi = 0; pmi < 2; pmi++) { for (int pmj = 0; pmj < 2; pmj++) { final double[] yieldBumpedPP = y.clone(); yieldBumpedPP[i] += ((pmi == 0) ? SHIFT : -SHIFT); yieldBumpedPP[j] += ((pmj == 0) ? SHIFT : -SHIFT); final YieldAndDiscountCurve curveBumped = new YieldCurve(name, new InterpolatedDoublesCurve(x, yieldBumpedPP, interpolatedCurve.getInterpolator(), true)); MulticurveProviderDiscount providerBumped = new MulticurveProviderDiscount(); for (Currency loopccy : MULTICURVE.getCurrencies()) { if (nameCcys.contains(loopccy)) { providerBumped.setCurve(loopccy, curveBumped); } else { providerBumped.setCurve(loopccy, MULTICURVE.getCurve(loopccy)); } } for (IborIndex loopibor : MULTICURVE.getIndexesIbor()) { if (nameIbors.contains(loopibor)) { providerBumped.setCurve(loopibor, curveBumped); } else { providerBumped.setCurve(loopibor, MULTICURVE.getCurve(loopibor)); } } pv[pmi][pmj] = swap.accept(PVDC, providerBumped).getAmount(ccy); } } gammaExpected[i][j] = (pv[0][0] - pv[1][0] - pv[0][1] + pv[1][1]) / (2 * SHIFT * 2 * SHIFT); } } // End node for (int i = 0; i < nbNode; i++) { // Start assert for (int j = 0; j < nbNode; j++) { if (Math.abs(gammaExpected[i][j]) > 1 || Math.abs(gammaComputed[i][j]) > 1) { // Check only the meaningful numbers assertTrue("CrossGammaSingleCurveCalculator - " + i + " - " + j + " / " + gammaExpected[i][j] + " != " + gammaComputed[i][j], (Math.abs(gammaExpected[i][j] / gammaComputed[i][j] - 1) < TOLERANCE_PV_GAMMA_RELATIF) || // If relative difference is small enough (Math.abs(gammaExpected[i][j] - gammaComputed[i][j]) < TOLERANCE_PV_GAMMA)); // If absolute difference is small enough } } } // End assert } // End curves } }