/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.volatilityswap; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import java.util.LinkedHashMap; import java.util.Map; import org.testng.annotations.Test; import com.opengamma.analytics.financial.forex.method.FXMatrix; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.index.IndexON; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.model.volatility.surface.SmileDeltaTermStructureParametersStrikeInterpolation; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.financial.provider.description.volatilityswap.CarrLeeFXData; import com.opengamma.analytics.math.curve.ConstantDoublesCurve; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.financial.convention.frequency.PeriodFrequency; import com.opengamma.util.money.Currency; import com.opengamma.util.test.TestGroup; import com.opengamma.util.tuple.Pairs; /** * Test class for all of the Greek calculators based on finite difference method */ @Test(groups = TestGroup.UNIT) public class VolatilitySwapFiniteDifferenceGreeksTest { /** * */ public void newlyIssuedSwapTest() { final double spot = 1.5; final double timeToExpiry = 0.5; final double timeFromInception = 0.; final double dr = 0.01; final double fr = 0.005; final double bump = 1.e-5; final double bumpVol = 1.e-7; final CarrLeeFXVolatilitySwapCalculator baseCal = new CarrLeeFXVolatilitySwapCalculator(); final VolatilitySwapFiniteDifferenceGreeksCalculator cal = new VolatilitySwapFiniteDifferenceGreeksCalculator(); final CarrLeeFXVolatilitySwapCalculator baseCalRange = new CarrLeeFXVolatilitySwapCalculator(40, new double[] {0.8 * spot, 1.2 * spot }); final VolatilitySwapFiniteDifferenceGreeksCalculator calRange = new VolatilitySwapFiniteDifferenceGreeksCalculator(bump, baseCalRange); final double[] timeSet = new double[] {timeToExpiry * 0.5, timeToExpiry }; final int nTime = timeSet.length; final double[] delta = new double[] {0.10, 0.25 }; final int nVols = 2 * delta.length + 1; final double[][] volatility = new double[nTime][nVols]; final double[] volSmile = new double[] {9. / 100., 8. / 100., 7.5 / 100., 7.2 / 100., 7.85 / 100. }; for (int i = 0; i < nTime; ++i) { System.arraycopy(volSmile, 0, volatility[i], 0, nVols); } final Interpolator1D interp = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); final SmileDeltaTermStructureParametersStrikeInterpolation smile = new SmileDeltaTermStructureParametersStrikeInterpolation(timeSet, delta, volatility, interp); final Currency base = Currency.EUR; final Currency counter = Currency.USD; final Map<Currency, YieldAndDiscountCurve> discountingCurves = new LinkedHashMap<>(); discountingCurves.put(Currency.EUR, new YieldCurve("domestic", ConstantDoublesCurve.from(dr))); discountingCurves.put(Currency.USD, new YieldCurve("foreign", ConstantDoublesCurve.from(fr))); final FXMatrix fxMatrix = new FXMatrix(base, counter, spot); final MulticurveProviderDiscount curves = new MulticurveProviderDiscount(discountingCurves, new LinkedHashMap<IborIndex, YieldAndDiscountCurve>(), new LinkedHashMap<IndexON, YieldAndDiscountCurve>(), fxMatrix); final CarrLeeFXData data = new CarrLeeFXData(Pairs.of(base, counter), smile, curves); final FXVolatilitySwap swap = new FXVolatilitySwap(-timeFromInception, timeToExpiry, PeriodFrequency.DAILY, timeToExpiry, spot, 1, base, base, counter, 252); final double[] greeks = cal.getFXVolatilitySwapGreeks(swap, data); final double[] greeksRange = calRange.getFXVolatilitySwapGreeks(swap, data); /** * Tests with bumped data */ final VolatilitySwapCalculatorResultWithStrikes baseResult = (VolatilitySwapCalculatorResultWithStrikes) swap.accept(baseCal, data); final double baseFV = baseResult.getFairValue(); final VolatilitySwapCalculatorResultWithStrikes baseResultRange = (VolatilitySwapCalculatorResultWithStrikes) swap.accept(baseCalRange, data); final double baseFVRange = baseResultRange.getFairValue(); final double bumpedSpot = spot + bump; final FXMatrix bumpedFxMatrix = new FXMatrix(base, counter, bumpedSpot); final MulticurveProviderDiscount spotBumpedCurves = new MulticurveProviderDiscount(discountingCurves, new LinkedHashMap<IborIndex, YieldAndDiscountCurve>(), new LinkedHashMap<IndexON, YieldAndDiscountCurve>(), bumpedFxMatrix); final CarrLeeFXData spotBumpedData = new CarrLeeFXData(Pairs.of(base, counter), smile, spotBumpedCurves); final double spotBumpedFV = swap.accept(baseCal, spotBumpedData).getFairValue(); final double spotBumpedFVRange = swap.accept(baseCalRange, spotBumpedData).getFairValue(); final double[][] bumpedVolatility = new double[nTime][nVols]; final double[] bumpedVolSmile = new double[nVols]; for (int i = 0; i < nVols; ++i) { /* * Note interpolation is linear, but strike range affected */ bumpedVolSmile[i] = volSmile[i] + bumpVol; } for (int i = 0; i < nTime; ++i) { System.arraycopy(bumpedVolSmile, 0, bumpedVolatility[i], 0, nVols); } final SmileDeltaTermStructureParametersStrikeInterpolation volBumpedSmile = new SmileDeltaTermStructureParametersStrikeInterpolation(timeSet, delta, bumpedVolatility, interp); final CarrLeeFXData volBumpedData = new CarrLeeFXData(Pairs.of(base, counter), volBumpedSmile, curves); final double volBumpedFV = swap.accept(baseCal, volBumpedData).getFairValue(); final double volBumpedFVRange = swap.accept(baseCalRange, volBumpedData).getFairValue(); final double bumpedTimeToExpiry = timeToExpiry - 1.0 / 252.0; final FXVolatilitySwap timeBumpedSwap = new FXVolatilitySwap(-timeFromInception, bumpedTimeToExpiry, PeriodFrequency.DAILY, bumpedTimeToExpiry, spot, 1, base, base, counter, 252); final double timeBumpedFV = timeBumpedSwap.accept(baseCal, data).getFairValue(); final double timeBumpedFVRange = timeBumpedSwap.accept(baseCalRange, data).getFairValue(); assertEquals((spotBumpedFV - baseFV) / bump, greeks[0], 1.e-12); assertEquals((volBumpedFV - baseFV) / bumpVol / 100., greeks[1], 1.e-2);//Approximation assertEquals(timeBumpedFV - baseFV, greeks[2], 1.e-12); assertEquals((spotBumpedFVRange - baseFVRange) / bump, greeksRange[0], 1.e-12); assertEquals((volBumpedFVRange - baseFVRange) / bumpVol / 100., greeksRange[1], 1.e-2);//Approximation assertEquals(timeBumpedFVRange - baseFVRange, greeksRange[2], 1.e-12); /** * Consistency with separate methods */ final CarrLeeFXVolatilitySwapDeltaCalculator del = new CarrLeeFXVolatilitySwapDeltaCalculator(); final CarrLeeFXVolatilitySwapVegaCalculator veg = new CarrLeeFXVolatilitySwapVegaCalculator(); final CarrLeeFXVolatilitySwapThetaCalculator the = new CarrLeeFXVolatilitySwapThetaCalculator(); assertEquals(swap.accept(del, data), greeks[0], 1.e-12); assertEquals(swap.accept(veg, data), greeks[1], 1.e-12); assertEquals(swap.accept(the, data), greeks[2], 1.e-12); final CarrLeeFXVolatilitySwapDeltaCalculator delRange = new CarrLeeFXVolatilitySwapDeltaCalculator(bump, baseCalRange); final CarrLeeFXVolatilitySwapVegaCalculator vegRange = new CarrLeeFXVolatilitySwapVegaCalculator(bumpVol, baseCalRange); final CarrLeeFXVolatilitySwapThetaCalculator theRange = new CarrLeeFXVolatilitySwapThetaCalculator(baseCalRange); assertEquals(swap.accept(delRange, data), greeksRange[0], 1.e-12); assertEquals(swap.accept(vegRange, data), greeksRange[1], 1.e-12); assertEquals(swap.accept(theRange, data), greeksRange[2], 1.e-12); } /** * */ public void SeasonedSwapTest() { final double spot = 1.5; final double timeToExpiry = 0.5; final double timeFromInception = 0.25; final double dr = 0.01; final double fr = 0.005; final double rv = 8.1 * 8.1; final double bump = 1.e-5; final double bumpVol = 1.e-7; final CarrLeeFXVolatilitySwapCalculator baseCal = new CarrLeeFXVolatilitySwapCalculator(); final VolatilitySwapFiniteDifferenceGreeksCalculator cal = new VolatilitySwapFiniteDifferenceGreeksCalculator(); final CarrLeeFXVolatilitySwapCalculator baseCalRange = new CarrLeeFXVolatilitySwapCalculator(40, new double[] {0.8 * spot, 1.2 * spot }); final VolatilitySwapFiniteDifferenceGreeksCalculator calRange = new VolatilitySwapFiniteDifferenceGreeksCalculator(bump, baseCalRange); final double[] timeSet = new double[] {timeToExpiry * 0.5, timeToExpiry }; final int nTime = timeSet.length; final double[] delta = new double[] {0.10, 0.25 }; final int nVols = 2 * delta.length + 1; final double[][] volatility = new double[nTime][nVols]; final double[] volSmile = new double[] {9. / 100., 8. / 100., 7.5 / 100., 7.2 / 100., 7.85 / 100. }; for (int i = 0; i < nTime; ++i) { System.arraycopy(volSmile, 0, volatility[i], 0, nVols); } final Interpolator1D interp = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); final SmileDeltaTermStructureParametersStrikeInterpolation smile = new SmileDeltaTermStructureParametersStrikeInterpolation(timeSet, delta, volatility, interp); final Currency base = Currency.EUR; final Currency counter = Currency.USD; final Map<Currency, YieldAndDiscountCurve> discountingCurves = new LinkedHashMap<>(); discountingCurves.put(Currency.EUR, new YieldCurve("domestic", ConstantDoublesCurve.from(dr))); discountingCurves.put(Currency.USD, new YieldCurve("foreign", ConstantDoublesCurve.from(fr))); final FXMatrix fxMatrix = new FXMatrix(base, counter, spot); final MulticurveProviderDiscount curves = new MulticurveProviderDiscount(discountingCurves, new LinkedHashMap<IborIndex, YieldAndDiscountCurve>(), new LinkedHashMap<IndexON, YieldAndDiscountCurve>(), fxMatrix); final CarrLeeFXData data = new CarrLeeFXData(Pairs.of(base, counter), smile, curves, rv); final FXVolatilitySwap swap = new FXVolatilitySwap(-timeFromInception, timeToExpiry, PeriodFrequency.DAILY, timeToExpiry, spot, 1, base, base, counter, 252); final double[] greeks = cal.getFXVolatilitySwapGreeks(swap, data); final double[] greeksRange = calRange.getFXVolatilitySwapGreeks(swap, data); /** * Tests with bumped data */ final VolatilitySwapCalculatorResultWithStrikes baseResult = (VolatilitySwapCalculatorResultWithStrikes) swap.accept(baseCal, data); final double baseFV = baseResult.getFairValue(); final VolatilitySwapCalculatorResultWithStrikes baseResultRange = (VolatilitySwapCalculatorResultWithStrikes) swap.accept(baseCalRange, data); final double baseFVRange = baseResultRange.getFairValue(); final double bumpedSpot = spot + bump; final FXMatrix bumpedFxMatrix = new FXMatrix(base, counter, bumpedSpot); final MulticurveProviderDiscount spotBumpedCurves = new MulticurveProviderDiscount(discountingCurves, new LinkedHashMap<IborIndex, YieldAndDiscountCurve>(), new LinkedHashMap<IndexON, YieldAndDiscountCurve>(), bumpedFxMatrix); final CarrLeeFXData spotBumpedData = new CarrLeeFXData(Pairs.of(base, counter), smile, spotBumpedCurves, rv); final double spotBumpedFV = swap.accept(baseCal, spotBumpedData).getFairValue(); final double spotBumpedFVRange = swap.accept(baseCalRange, spotBumpedData).getFairValue(); final double[][] bumpedVolatility = new double[nTime][nVols]; final double[] bumpedVolSmile = new double[nVols]; for (int i = 0; i < nVols; ++i) { /* * Note interpolation is linear, but strike range affected */ bumpedVolSmile[i] = volSmile[i] + bumpVol; } for (int i = 0; i < nTime; ++i) { System.arraycopy(bumpedVolSmile, 0, bumpedVolatility[i], 0, nVols); } final SmileDeltaTermStructureParametersStrikeInterpolation volBumpedSmile = new SmileDeltaTermStructureParametersStrikeInterpolation(timeSet, delta, bumpedVolatility, interp); final CarrLeeFXData volBumpedData = new CarrLeeFXData(Pairs.of(base, counter), volBumpedSmile, curves, rv); final double volBumpedFV = swap.accept(baseCal, volBumpedData).getFairValue(); final double volBumpedFVRange = swap.accept(baseCalRange, volBumpedData).getFairValue(); final double bumpedTimeToExpiry = timeToExpiry - 1.0 / 252.0; final FXVolatilitySwap timeBumpedSwap = new FXVolatilitySwap(-timeFromInception - 1.0 / 252.0, bumpedTimeToExpiry, PeriodFrequency.DAILY, bumpedTimeToExpiry, spot, 1, base, base, counter, 252); final double timeBumpedFV = timeBumpedSwap.accept(baseCal, data).getFairValue(); final double timeBumpedFVRange = timeBumpedSwap.accept(baseCalRange, data).getFairValue(); assertEquals((spotBumpedFV - baseFV) / bump, greeks[0], 1.e-12); assertEquals((volBumpedFV - baseFV) / bumpVol / 100., greeks[1], 1.e-1);//Approximation assertEquals(timeBumpedFV - baseFV, greeks[2], 1.e-12); assertEquals((spotBumpedFVRange - baseFVRange) / bump, greeksRange[0], 1.e-12); assertEquals((volBumpedFVRange - baseFVRange) / bumpVol / 100., greeksRange[1], 1.e-1);//Approximation assertEquals(timeBumpedFVRange - baseFVRange, greeksRange[2], 1.e-12); /** * Consistency with separate methods */ final CarrLeeFXVolatilitySwapDeltaCalculator del = new CarrLeeFXVolatilitySwapDeltaCalculator(); final CarrLeeFXVolatilitySwapVegaCalculator veg = new CarrLeeFXVolatilitySwapVegaCalculator(); final CarrLeeFXVolatilitySwapThetaCalculator the = new CarrLeeFXVolatilitySwapThetaCalculator(); assertEquals(swap.accept(del, data), greeks[0], 1.e-12); assertEquals(swap.accept(veg, data), greeks[1], 1.e-12); assertEquals(swap.accept(the, data), greeks[2], 1.e-12); final CarrLeeFXVolatilitySwapDeltaCalculator delRange = new CarrLeeFXVolatilitySwapDeltaCalculator(bump, baseCalRange); final CarrLeeFXVolatilitySwapVegaCalculator vegRange = new CarrLeeFXVolatilitySwapVegaCalculator(bumpVol, baseCalRange); final CarrLeeFXVolatilitySwapThetaCalculator theRange = new CarrLeeFXVolatilitySwapThetaCalculator(baseCalRange); assertEquals(swap.accept(delRange, data), greeksRange[0], 1.e-12); assertEquals(swap.accept(vegRange, data), greeksRange[1], 1.e-12); assertEquals(swap.accept(theRange, data), greeksRange[2], 1.e-12); } public void hashCodeAndEqualsTest() { final CarrLeeFXVolatilitySwapCalculator baseCal = new CarrLeeFXVolatilitySwapCalculator(); final CarrLeeFXVolatilitySwapCalculator baseCalRange = new CarrLeeFXVolatilitySwapCalculator(40, new double[] {0.8, 1.2 }); final VolatilitySwapFiniteDifferenceGreeksCalculator greeksDef = new VolatilitySwapFiniteDifferenceGreeksCalculator(); final VolatilitySwapFiniteDifferenceGreeksCalculator greeksBase = new VolatilitySwapFiniteDifferenceGreeksCalculator(1.e-5); final VolatilitySwapFiniteDifferenceGreeksCalculator greeksBaseCal = new VolatilitySwapFiniteDifferenceGreeksCalculator(1.e-5, baseCal); final VolatilitySwapFiniteDifferenceGreeksCalculator greeksRangeCal = new VolatilitySwapFiniteDifferenceGreeksCalculator(1.e-5, baseCalRange); final VolatilitySwapFiniteDifferenceGreeksCalculator greeksBump = new VolatilitySwapFiniteDifferenceGreeksCalculator(1.e-7); assertTrue(greeksDef.equals(greeksDef)); assertEquals(greeksDef.hashCode(), greeksBase.hashCode()); assertTrue(greeksDef.equals(greeksBase)); assertTrue(greeksBase.equals(greeksDef)); assertEquals(greeksDef.hashCode(), greeksBaseCal.hashCode()); assertTrue(greeksDef.equals(greeksBaseCal)); assertTrue(greeksBaseCal.equals(greeksDef)); assertFalse(greeksDef.equals(greeksRangeCal)); assertFalse(greeksDef.equals(greeksBump)); assertFalse(greeksDef.equals(new double[] {})); assertFalse(greeksDef.equals(null)); final CarrLeeFXVolatilitySwapDeltaCalculator delDef = new CarrLeeFXVolatilitySwapDeltaCalculator(); final CarrLeeFXVolatilitySwapDeltaCalculator delBase = new CarrLeeFXVolatilitySwapDeltaCalculator(1.e-5); final CarrLeeFXVolatilitySwapDeltaCalculator delBaseCal = new CarrLeeFXVolatilitySwapDeltaCalculator(1.e-5, baseCal); final CarrLeeFXVolatilitySwapDeltaCalculator delRangeCal = new CarrLeeFXVolatilitySwapDeltaCalculator(1.e-5, baseCalRange); final CarrLeeFXVolatilitySwapDeltaCalculator delBump = new CarrLeeFXVolatilitySwapDeltaCalculator(1.e-7); assertTrue(delDef.equals(delDef)); assertEquals(delDef.hashCode(), delBase.hashCode()); assertTrue(delDef.equals(delBase)); assertTrue(delBase.equals(delDef)); assertEquals(delDef.hashCode(), delBaseCal.hashCode()); assertTrue(delDef.equals(delBaseCal)); assertTrue(delBaseCal.equals(delDef)); assertFalse(delDef.equals(delRangeCal)); assertFalse(delDef.equals(delBump)); assertFalse(delDef.equals(new double[] {})); assertFalse(delDef.equals(null)); final CarrLeeFXVolatilitySwapVegaCalculator vegDef = new CarrLeeFXVolatilitySwapVegaCalculator(); final CarrLeeFXVolatilitySwapVegaCalculator vegBase = new CarrLeeFXVolatilitySwapVegaCalculator(1.e-7); final CarrLeeFXVolatilitySwapVegaCalculator vegBaseCal = new CarrLeeFXVolatilitySwapVegaCalculator(1.e-7, baseCal); final CarrLeeFXVolatilitySwapVegaCalculator vegRangeCal = new CarrLeeFXVolatilitySwapVegaCalculator(1.e-7, baseCalRange); final CarrLeeFXVolatilitySwapVegaCalculator vegBump = new CarrLeeFXVolatilitySwapVegaCalculator(1.e-5); assertTrue(vegDef.equals(vegDef)); assertEquals(vegDef.hashCode(), vegBase.hashCode()); assertTrue(vegDef.equals(vegBase)); assertTrue(vegBase.equals(vegDef)); assertEquals(vegDef.hashCode(), vegBaseCal.hashCode()); assertTrue(vegDef.equals(vegBaseCal)); assertTrue(vegBaseCal.equals(vegDef)); assertFalse(vegDef.equals(vegRangeCal)); assertFalse(vegDef.equals(vegBump)); assertFalse(vegDef.equals(new double[] {})); assertFalse(vegDef.equals(null)); final CarrLeeFXVolatilitySwapThetaCalculator theDef = new CarrLeeFXVolatilitySwapThetaCalculator(); final CarrLeeFXVolatilitySwapThetaCalculator theBaseCal = new CarrLeeFXVolatilitySwapThetaCalculator(baseCal); final CarrLeeFXVolatilitySwapThetaCalculator theRangeCal = new CarrLeeFXVolatilitySwapThetaCalculator(baseCalRange); assertTrue(theDef.equals(theDef)); assertEquals(theDef.hashCode(), theBaseCal.hashCode()); assertTrue(theDef.equals(theBaseCal)); assertTrue(theBaseCal.equals(theDef)); assertFalse(theDef.equals(theRangeCal)); assertFalse(theDef.equals(new double[] {})); assertFalse(theDef.equals(null)); } }