/** * 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.Arrays; 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.financial.convention.frequency.PeriodFrequency; import com.opengamma.util.money.Currency; import com.opengamma.util.test.TestGroup; import com.opengamma.util.tuple.Pairs; /** * */ @Test(groups = TestGroup.UNIT) public class CarrLeeFXVolatilitySwapCalculatorTest { /** * */ @Test void sampleDataTest() { final CarrLeeFXVolatilitySwapCalculator cal = new CarrLeeFXVolatilitySwapCalculator(); final double spot = 0.8; final double timeToExpiry = 0.49; final double timeFromInception = 0.12; final double dr = 0.05; final double fr = 0.02; final double realizedVar = 6.7 * 6.7; final double[] timeToExpiration = new double[] {0.01, 0.1, 0.3 }; final int nTime = timeToExpiration.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.1 / 100., 6.9 / 100., 6.45 / 100., 7.22 / 100. }; for (int i = 0; i < nTime; ++i) { System.arraycopy(volSmile, 0, volatility[i], 0, nVols); } final SmileDeltaTermStructureParametersStrikeInterpolation smile = new SmileDeltaTermStructureParametersStrikeInterpolation(timeToExpiration, delta, volatility); 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, realizedVar); final FXVolatilitySwap swap = new FXVolatilitySwap(-timeFromInception, timeToExpiry, PeriodFrequency.DAILY, timeToExpiry, spot, 1, base, base, counter, 252); assertEquals(6.861525317073218, swap.accept(cal, data).getFairValue(), 1e-10); final FXVolatilitySwap swap1 = new FXVolatilitySwap(0., timeToExpiry, PeriodFrequency.DAILY, timeToExpiry, spot, 1, base, base, counter, 252); final FXVolatilitySwap swap2 = new FXVolatilitySwap(1.e-4, timeToExpiry, PeriodFrequency.DAILY, timeToExpiry, spot, 1, base, base, counter, 252); assertEquals(swap1.accept(cal, data).getFairValue(), swap2.accept(cal, data).getFairValue(), 1.e-6); } /** * */ @SuppressWarnings("unused") @Test public void flatVolSmileTest() { final CarrLeeFXVolatilitySwapCalculator cal = new CarrLeeFXVolatilitySwapCalculator(); final double eps = 1.e-3; final double spot = 1.3; final double timeToExpiry = 0.24; final double timeFromInception = 0.12; final double dr = 0.; final double fr = 0.; final double realizedVar = 6. * 6.; final double[] timeToExpiration = new double[] {0.01, 0.1, 0.3 }; final int nTime = timeToExpiration.length; final double[] delta = new double[] {0.10, 0.25 }; final int nVols = 2 * delta.length + 1; final double[][] volatility = new double[nTime][nVols]; for (int i = 0; i < nTime; ++i) { Arrays.fill(volatility[i], 0.06); } final Currency base = Currency.EUR; final Currency counter = Currency.USD; final SmileDeltaTermStructureParametersStrikeInterpolation smile = new SmileDeltaTermStructureParametersStrikeInterpolation(timeToExpiration, delta, volatility); 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, realizedVar); final FXVolatilitySwap swap = new FXVolatilitySwap(-timeFromInception, timeToExpiry, PeriodFrequency.DAILY, timeToExpiry, spot, 1, base, base, counter, 252); assertEquals(6., swap.accept(cal, data).getFairValue(), eps); final FXVolatilitySwap swap1 = new FXVolatilitySwap(0., timeToExpiry, PeriodFrequency.DAILY, timeToExpiry, spot, 1, base, base, counter, 252); final FXVolatilitySwap swap2 = new FXVolatilitySwap(1.e-4, timeToExpiry, PeriodFrequency.DAILY, timeToExpiry, spot, 1, base, base, counter, 252); assertEquals(swap1.accept(cal, data).getFairValue(), swap2.accept(cal, data).getFairValue(), 1.e-6); final double[] strikeRange = new double[] {spot * 0.8, spot * 1.2 }; final CarrLeeFXVolatilitySwapCalculator cal1 = new CarrLeeFXVolatilitySwapCalculator(150, strikeRange); swap.accept(cal1, data); assertEquals(6., swap.accept(cal1, data).getFairValue(), eps * 10.0); /* * Error test */ try { new CarrLeeFXVolatilitySwapCalculator(0.1, 0.1, 10); throw new RuntimeException(); } catch (final Exception e) { assertEquals("-1 < lowestPutDelta < 0 should be true", e.getMessage()); } try { new CarrLeeFXVolatilitySwapCalculator(-2.1, 0.1, 10); throw new RuntimeException(); } catch (final Exception e) { assertEquals("-1 < lowestPutDelta < 0 should be true", e.getMessage()); } try { new CarrLeeFXVolatilitySwapCalculator(-0.1, -0.1, 10); throw new RuntimeException(); } catch (final Exception e) { assertEquals("0 < highestCallDelta < 1 should be true", e.getMessage()); } try { new CarrLeeFXVolatilitySwapCalculator(-0.1, 3.1, 10); throw new RuntimeException(); } catch (final Exception e) { assertEquals("0 < highestCallDelta < 1 should be true", e.getMessage()); } try { new CarrLeeFXVolatilitySwapCalculator(-0.1, 0.1, 1); throw new RuntimeException(); } catch (final Exception e) { assertEquals("numPoints should be greater than 2", e.getMessage()); } try { new CarrLeeFXVolatilitySwapCalculator(1, new double[] {1.1, 1.4 }); throw new RuntimeException(); } catch (final Exception e) { assertEquals("numPoints should be greater than 2", e.getMessage()); } try { new CarrLeeFXVolatilitySwapCalculator(10, new double[] {1.1, 1.4, 1.5 }); throw new RuntimeException(); } catch (final Exception e) { assertEquals("length of strikeRange should be 2", e.getMessage()); } try { new CarrLeeFXVolatilitySwapCalculator(10, new double[] {1.4, 1.1 }); throw new RuntimeException(); } catch (final Exception e) { assertEquals("upper bound should be greater than lower bound", e.getMessage()); } try { final FXVolatilitySwap swapNegativeTime = new FXVolatilitySwap(0., timeToExpiry, PeriodFrequency.DAILY, -timeToExpiry, spot, 1, base, base, counter, 252); swapNegativeTime.accept(cal, data); throw new RuntimeException(); } catch (final Exception e) { assertEquals("timeToExpiry should be positive", e.getMessage()); } try { final FXMatrix fxMatrixNegativeSpot = new FXMatrix(base, counter, -spot); final MulticurveProviderDiscount curvesNegativeSpot = new MulticurveProviderDiscount(discountingCurves, new LinkedHashMap<IborIndex, YieldAndDiscountCurve>(), new LinkedHashMap<IndexON, YieldAndDiscountCurve>(), fxMatrixNegativeSpot); final CarrLeeFXData dataNegativeSpot = new CarrLeeFXData(Pairs.of(base, counter), smile, curvesNegativeSpot, realizedVar); swap.accept(cal, dataNegativeSpot); throw new RuntimeException(); } catch (final Exception e) { assertEquals("spot should be positive", e.getMessage()); } try { final CarrLeeFXVolatilitySwapCalculator calBadRange = new CarrLeeFXVolatilitySwapCalculator(10, new double[] {10.1, 10.4 }); swap.accept(calBadRange, data); throw new RuntimeException(); } catch (final Exception e) { assertEquals("forward is outside of strike range", e.getMessage()); } try { final CarrLeeFXVolatilitySwapCalculator calBadRange = new CarrLeeFXVolatilitySwapCalculator(10, new double[] {0.1, 0.3 }); swap.accept(calBadRange, data); throw new RuntimeException(); } catch (final Exception e) { assertEquals("forward is outside of strike range", e.getMessage()); } } /** * */ public void hashCodeAndEquals() { final CarrLeeFXVolatilitySwapCalculator defaultCal = new CarrLeeFXVolatilitySwapCalculator(); final CarrLeeFXVolatilitySwapCalculator baseCal = new CarrLeeFXVolatilitySwapCalculator(-0.1, 0.1, 50); final CarrLeeFXVolatilitySwapCalculator lowerCal = new CarrLeeFXVolatilitySwapCalculator(-0.2, 0.1, 50); final CarrLeeFXVolatilitySwapCalculator upperCal = new CarrLeeFXVolatilitySwapCalculator(-0.1, 0.3, 50); final CarrLeeFXVolatilitySwapCalculator pointsCal = new CarrLeeFXVolatilitySwapCalculator(-0.1, 0.1, 150); final CarrLeeFXVolatilitySwapCalculator strikeBaseCal = new CarrLeeFXVolatilitySwapCalculator(50, new double[] {0.5, 1.5 }); final CarrLeeFXVolatilitySwapCalculator strikeIdCal = new CarrLeeFXVolatilitySwapCalculator(50, new double[] {0.5, 1.5 }); final CarrLeeFXVolatilitySwapCalculator strikeCal = new CarrLeeFXVolatilitySwapCalculator(50, new double[] {0.5, 2.5 }); assertTrue(defaultCal.equals(defaultCal)); assertTrue(defaultCal.hashCode() == baseCal.hashCode()); assertTrue(defaultCal.equals(baseCal)); assertTrue(baseCal.equals(defaultCal)); assertFalse(defaultCal.equals(lowerCal)); assertFalse(defaultCal.equals(upperCal)); assertFalse(defaultCal.equals(pointsCal)); assertFalse(defaultCal.equals(strikeBaseCal)); assertFalse(strikeBaseCal.equals(strikeCal)); assertTrue(strikeBaseCal.hashCode() == strikeIdCal.hashCode()); assertTrue(strikeBaseCal.equals(strikeIdCal)); assertTrue(strikeIdCal.equals(strikeBaseCal)); assertFalse(defaultCal.equals(null)); assertFalse(defaultCal.equals(new double[] {})); } }