/**
* 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.assertTrue;
import org.testng.annotations.Test;
import org.threeten.bp.Period;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.analytics.financial.instrument.index.GeneratorSwapFixedIbor;
import com.opengamma.analytics.financial.instrument.index.GeneratorSwapFixedIborMaster;
import com.opengamma.analytics.financial.instrument.index.IborIndex;
import com.opengamma.analytics.financial.instrument.swap.SwapFixedIborDefinition;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Coupon;
import com.opengamma.analytics.financial.interestrate.swap.derivative.SwapFixedCoupon;
import com.opengamma.analytics.financial.model.interestrate.curve.DiscountCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.financial.provider.description.MulticurveProviderDiscountDataSets;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity;
import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve;
import com.opengamma.financial.convention.calendar.Calendar;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.time.DateUtils;
/**
* Tests the zero-coupon rate cross-gamma calculator for single curve.
*/
@Test(groups = TestGroup.UNIT)
public class CrossGammaSingleCurveCalculatorTest {
private static final Calendar NYC = MulticurveProviderDiscountDataSets.getUSDCalendar();
private static final GeneratorSwapFixedIborMaster GENERATOR_SWAP_MASTER = GeneratorSwapFixedIborMaster.getInstance();
/** Instrument description */
private static final GeneratorSwapFixedIbor USD6MLIBOR3M = GENERATOR_SWAP_MASTER.getGenerator("USD6MLIBOR3M", NYC);
private static final Period SWAP_TENOR = Period.ofYears(10);
private static final ZonedDateTime SETTLEMENT_DATE = DateUtils.getUTCDate(2012, 5, 17);
private static final double NOTIONAL = 100000000; //100m
private static final double RATE_FIXED = 0.025;
private static final SwapFixedIborDefinition SWAP_FIXED_IBOR_DEFINITION =
SwapFixedIborDefinition.from(SETTLEMENT_DATE, SWAP_TENOR, USD6MLIBOR3M, NOTIONAL, RATE_FIXED, true);
/** Data */
private static final MulticurveProviderDiscount SINGLE_CURVE_ZC =
MulticurveProviderDiscountDataSets.createSingleCurveZcUsd();
private static final MulticurveProviderDiscount SINGLE_CURVE_DF =
MulticurveProviderDiscountDataSets.createSingleCurveDfUsd();
private static final MulticurveProviderDiscount SINGLE_CURVE_PERF =
MulticurveProviderDiscountDataSets.createSingleCurvePerformanceUsd();
/** Calculators */
private static final double SHIFT = 1.0E-4;
private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance();
private static final PresentValueCurveSensitivityDiscountingCalculator PVCSDC =
PresentValueCurveSensitivityDiscountingCalculator.getInstance();
private static final CrossGammaSingleCurveCalculator CGC = new CrossGammaSingleCurveCalculator(SHIFT, PVCSDC);
/** Constants */
private static final double TOLERANCE_PV_GAMMA_MIN = 1.0E+1;
private static final double TOLERANCE_PV_GAMMA = 2.0E+0;
private static final double TOLERANCE_PV_GAMMA_RELATIF = 5.0E-4;
@Test
public void crossGammaZc() {
ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14);
SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate);
checkCrossGamma(swap, true, SINGLE_CURVE_ZC);
}
@Test
public void crossGammaDf() {
ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14);
SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate);
checkCrossGamma(swap, false, SINGLE_CURVE_DF);
}
private void checkCrossGamma(SwapFixedCoupon<Coupon> swap, boolean isZc, MulticurveProviderDiscount singleCurve) {
String name = singleCurve.getAllNames().iterator().next();
Currency ccy = singleCurve.getCurrencyForName(name).get(0);
YieldAndDiscountCurve curve = singleCurve.getCurve(name);
if (isZc) {
ArgumentChecker.isTrue(curve instanceof YieldCurve, "curve should be YieldCurve");
} else {
ArgumentChecker.isTrue(curve instanceof DiscountCurve, "curve should be DiscountCurve");
}
InterpolatedDoublesCurve interpolatedCurve;
if(isZc) {
YieldCurve yieldCurve = (YieldCurve) curve;
ArgumentChecker.isTrue(yieldCurve.getCurve() instanceof InterpolatedDoublesCurve,
"Yield curve should be based on InterpolatedDoublesCurve");
interpolatedCurve = (InterpolatedDoublesCurve) yieldCurve.getCurve();
} else {
DiscountCurve discountCurve = (DiscountCurve) curve;
ArgumentChecker.isTrue(discountCurve.getCurve() instanceof InterpolatedDoublesCurve,
"Yield curve should be based on InterpolatedDoublesCurve");
interpolatedCurve = (InterpolatedDoublesCurve) discountCurve.getCurve();
}
double[] y = interpolatedCurve.getYDataAsPrimitive();
double[] x = interpolatedCurve.getXDataAsPrimitive();
int nbNode = y.length;
double[][] gammaComputed = CGC.calculateCrossGamma(swap, singleCurve).getData();
double[][] gammaExpected = new double[nbNode][nbNode];
for (int i = 0; i < nbNode; i++) {
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[] parametersBumpedPP = y.clone();
parametersBumpedPP[i] += ((pmi == 0) ? SHIFT : -SHIFT);
parametersBumpedPP[j] += ((pmj == 0) ? SHIFT : -SHIFT);
YieldAndDiscountCurve curveBumped;
if (isZc) {
curveBumped = new YieldCurve(name,
new InterpolatedDoublesCurve(x, parametersBumpedPP, interpolatedCurve.getInterpolator(), true));
} else {
curveBumped = new DiscountCurve(name,
new InterpolatedDoublesCurve(x, parametersBumpedPP, interpolatedCurve.getInterpolator(), true));
}
MulticurveProviderDiscount providerBumped = new MulticurveProviderDiscount();
for (Currency loopccy : singleCurve.getCurrencies()) {
providerBumped.setCurve(loopccy, curveBumped);
}
for (IborIndex loopibor : singleCurve.getIndexesIbor()) {
providerBumped.setCurve(loopibor, curveBumped);
}
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);
}
}
double[] deltaExpected = new double[nbNode];
for (int i = 0; i < nbNode; i++) {
double[] pv = new double[2];
for (int pmi = 0; pmi < 2; pmi++) {
final double[] parametersBumpedPP = y.clone();
parametersBumpedPP[i] += ((pmi == 0) ? SHIFT : -SHIFT);
YieldAndDiscountCurve curveBumped;
if (isZc) {
curveBumped = new YieldCurve(name,
new InterpolatedDoublesCurve(x, parametersBumpedPP, interpolatedCurve.getInterpolator(), true));
} else {
curveBumped = new DiscountCurve(name,
new InterpolatedDoublesCurve(x, parametersBumpedPP, interpolatedCurve.getInterpolator(), true));
}
MulticurveProviderDiscount providerBumped = new MulticurveProviderDiscount();
for (Currency loopccy : singleCurve.getCurrencies()) {
providerBumped.setCurve(loopccy, curveBumped);
}
for (IborIndex loopibor : singleCurve.getIndexesIbor()) {
providerBumped.setCurve(loopibor, curveBumped);
}
pv[pmi] = swap.accept(PVDC, providerBumped).getAmount(ccy);
}
deltaExpected[i] = (pv[0] - pv[1]) / (2 * SHIFT);
}
for (int i = 0; i < nbNode; i++) {
for (int j = 0; j < nbNode; j++) {
if (Math.abs(gammaExpected[i][j]) > TOLERANCE_PV_GAMMA_MIN ||
Math.abs(gammaComputed[i][j]) > TOLERANCE_PV_GAMMA_MIN) { // 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
}
}
}
}
@Test
public void gammaSumOfColumns() {
double shift = 1.0E-5;
CrossGammaSingleCurveCalculator gammaCalculator = new CrossGammaSingleCurveCalculator(shift, PVCSDC);
final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14);
final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate);
double[][] gammaCross = gammaCalculator.calculateCrossGamma(swap, SINGLE_CURVE_ZC).getData();
int nbNode = gammaCross.length;
double[] gammaSumOfColumnsExpected = new double[gammaCross.length];
for (int i = 0; i < nbNode; i++) {
for (int j = 0; j < nbNode; j++) {
gammaSumOfColumnsExpected[i] += gammaCross[i][j];
}
}
double[] gammaSumOfColumnsComputed = gammaCalculator.calculateSumOfColumnsGamma(swap, SINGLE_CURVE_ZC);
for (int i = 0; i < nbNode; i++) {
if (Math.abs(gammaSumOfColumnsExpected[i]) > 1 || Math.abs(gammaSumOfColumnsExpected[i]) > 1) { // Check only the meaningful numbers
assertTrue("CrossGammaSingleCurveCalculator - " + i + " / " + gammaSumOfColumnsExpected[i] + " - " +
gammaSumOfColumnsComputed[i],
(Math.abs(gammaSumOfColumnsExpected[i] / gammaSumOfColumnsComputed[i] - 1) < TOLERANCE_PV_GAMMA_RELATIF) || // If relative difference is small enough
(Math.abs(gammaSumOfColumnsExpected[i] - gammaSumOfColumnsComputed[i]) < TOLERANCE_PV_GAMMA)); // If absolute difference is small enough
}
}
}
@SuppressWarnings("unused")
@Test(enabled = false)
public void performance() {
long startTime, endTime;
int nbTest = 1000;
int nbRep = 4;
Currency usd = USD6MLIBOR3M.getCurrency();
double pvTotal = 0;
double gammaTotal = 0;
final ZonedDateTime referenceDate = DateUtils.getUTCDate(2012, 5, 14);
final SwapFixedCoupon<Coupon> swap = SWAP_FIXED_IBOR_DEFINITION.toDerivative(referenceDate);
for (int looprep = 0; looprep < nbRep; looprep++) { // Start repetitions
startTime = System.currentTimeMillis();
for (int looptest = 0; looptest < nbTest; looptest++) {
pvTotal += swap.accept(PVDC, SINGLE_CURVE_PERF).getAmount(usd);
}
endTime = System.currentTimeMillis();
System.out.println(nbTest + " pv swap: " + (endTime - startTime) + " ms - " + pvTotal);
// Performance note: PV: 09-Mar-2015: On Mac Book Pro 2.6 GHz Intel Core i7: 20 ms for 1000 swaps.
startTime = System.currentTimeMillis();
for (int looptest = 0; looptest < nbTest; looptest++) {
MultipleCurrencyMulticurveSensitivity pvcs = swap.accept(PVCSDC, SINGLE_CURVE_PERF);
}
endTime = System.currentTimeMillis();
System.out.println(nbTest + " pvcs swap: " + (endTime - startTime) + " ms");
// Performance note: PVCS: 09-Mar-2015: On Mac Book Pro 2.6 GHz Intel Core i7: 55 ms for 1000 swaps.
startTime = System.currentTimeMillis();
for (int looptest = 0; looptest < nbTest; looptest++) {
double[][] gammaComputed = CGC.calculateCrossGamma(swap, SINGLE_CURVE_PERF).getData();
gammaTotal += gammaComputed[0][0];
}
endTime = System.currentTimeMillis();
System.out.println(nbTest + " cross-gamma swap: " + (endTime - startTime) + " ms - " + gammaTotal);
// Performance note: PVCS: 09-Mar-2015: On Mac Book Pro 2.6 GHz Intel Core i7: 1850 ms for 1000 swaps.
startTime = System.currentTimeMillis();
for (int looptest = 0; looptest < nbTest; looptest++) {
double[] gammaSumOfColumnsComputed = CGC.calculateSumOfColumnsGamma(swap, SINGLE_CURVE_PERF);
gammaTotal += gammaSumOfColumnsComputed[0];
}
endTime = System.currentTimeMillis();
System.out.println(nbTest + " cross-gamma swap: " + (endTime - startTime) + " ms - "+ gammaTotal);
// Performance note: PVCS: 09-Mar-2015: On Mac Book Pro 2.6 GHz Intel Core i7: 180 ms for 1000 swaps.
} // End repetitions
}
}