/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.interestrate.future.provider; import static org.testng.AssertJUnit.assertEquals; import java.util.LinkedHashMap; import org.testng.annotations.Test; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.instrument.future.InterestRateFutureOptionMarginSecurityDefinition; import com.opengamma.analytics.financial.instrument.future.InterestRateFutureOptionMarginTransactionDefinition; import com.opengamma.analytics.financial.instrument.future.InterestRateFutureSecurityDefinition; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.interestrate.datasets.StandardDataSetsMulticurveEUR; import com.opengamma.analytics.financial.interestrate.future.derivative.InterestRateFutureOptionMarginTransaction; import com.opengamma.analytics.financial.provider.calculator.discounting.PV01CurveParametersCalculator; import com.opengamma.analytics.financial.provider.calculator.generic.MarketQuoteSensitivityBlockCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.DeltaNormalSTIRFutureOptionCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.GammaNormalSTIRFutureOptionCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.PositionDeltaNormalSTIRFutureOptionCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.PositionGammaNormalSTIRFutureOptionCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.PositionThetaNormalSTIRFutureOptionCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.PositionVegaNormalSTIRFutureOptionCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.PresentValueCurveSensitivityNormalSTIRFuturesCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.PresentValueNormalSTIRFuturesCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.ThetaNormalSTIRFutureOptionCalculator; import com.opengamma.analytics.financial.provider.calculator.normalstirfutures.VegaNormalSTIRFutureOptionCalculator; import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle; import com.opengamma.analytics.financial.provider.description.MulticurveProviderDiscountDataSets; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.financial.provider.description.interestrate.NormalSTIRFuturesExpSimpleMoneynessProviderDiscount; import com.opengamma.analytics.financial.provider.description.interestrate.NormalSTIRFuturesProviderInterface; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyParameterSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.parameter.ParameterSensitivityParameterCalculator; import com.opengamma.analytics.financial.schedule.ScheduleCalculator; import com.opengamma.analytics.financial.util.AssertSensitivityObjects; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory; import com.opengamma.analytics.math.interpolation.GridInterpolator2D; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.analytics.math.interpolation.InterpolatorTestUtil; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.analytics.math.surface.InterpolatedDoublesSurface; import com.opengamma.analytics.util.amount.ReferenceAmount; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.util.money.Currency; import com.opengamma.util.money.MultipleCurrencyAmount; import com.opengamma.util.time.DateUtils; import com.opengamma.util.tuple.ObjectsPair; import com.opengamma.util.tuple.Pair; import com.opengamma.util.tuple.Pairs; /** * E2E test for STIR futures option using volatility surface with simple moneyness on rate. */ public class STIRFuturesOptionNormalExpSimpleMoneynessMethodE2ETest { private static final BondAndSTIRFuturesE2EExamplesData DATA = new BondAndSTIRFuturesE2EExamplesData(); /* Interpolators */ private static final Interpolator1D SQUARE_FLAT = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.SQUARE_LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); private static final Interpolator1D TIME_SQUARE_FLAT = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.TIME_SQUARE, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); /* Interpolation is done along y direction first */ private static final GridInterpolator2D INTERPOLATOR_2D = new GridInterpolator2D(TIME_SQUARE_FLAT, SQUARE_FLAT); /* Volatility surface */ private static final double[] EXPIRY = DATA.getExpiry(); private static final double[] SIMPLEMONEY = DATA.getSimpleMoneyness(); private static final double[] VOL = DATA.getVolatility(); /* Curve */ private static final Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> MULTICURVE_PAIR = StandardDataSetsMulticurveEUR.getCurvesEurOisE3(); private static final MulticurveProviderDiscount MULTICURVES = MULTICURVE_PAIR.getFirst(); private static final CurveBuildingBlockBundle BLOCK = MULTICURVE_PAIR.getSecond(); private static final IborIndex[] IBOR_INDEXES = MulticurveProviderDiscountDataSets.getIndexesIborMulticurveEurUsd(); private static final IborIndex EURIBOR3M = IBOR_INDEXES[0]; private static final Currency EUR = EURIBOR3M.getCurrency(); final private static InterpolatedDoublesSurface VOL_SURFACE_SIMPLEMONEY = InterpolatedDoublesSurface.from(EXPIRY, SIMPLEMONEY, VOL, INTERPOLATOR_2D); final private static NormalSTIRFuturesExpSimpleMoneynessProviderDiscount NORMAL_MULTICURVES = new NormalSTIRFuturesExpSimpleMoneynessProviderDiscount(MULTICURVES, VOL_SURFACE_SIMPLEMONEY, EURIBOR3M, false); /* Option */ private static final Calendar TARGET = MulticurveProviderDiscountDataSets.getEURCalendar(); private static final ZonedDateTime SPOT_LAST_TRADING_DATE = DateUtils.getUTCDate(2012, 9, 19); private static final ZonedDateTime LAST_TRADING_DATE = ScheduleCalculator.getAdjustedDate(SPOT_LAST_TRADING_DATE, -EURIBOR3M.getSpotLag(), TARGET); private static final double NOTIONAL = 1000000.0; // 1m private static final double FUTURE_FACTOR = 0.25; private static final String NAME = "ERU2"; private static final double STRIKE = 0.9850; private static final InterestRateFutureSecurityDefinition ERU2_DEFINITION = new InterestRateFutureSecurityDefinition( LAST_TRADING_DATE, EURIBOR3M, NOTIONAL, FUTURE_FACTOR, NAME, TARGET); private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2010, 8, 18, 10, 0); private static final ZonedDateTime EXPIRATION_DATE = DateUtils.getUTCDate(2011, 9, 16); private static final boolean IS_CALL = true; private static final InterestRateFutureOptionMarginSecurityDefinition OPTION_ERU2_DEFINITION = new InterestRateFutureOptionMarginSecurityDefinition( ERU2_DEFINITION, EXPIRATION_DATE, STRIKE, IS_CALL); /* Transaction */ private static final int QUANTITY = 12; private static final double TRADE_PRICE = 0.0050; private static final ZonedDateTime TRADE_DATE_1 = DateUtils.getUTCDate(2010, 8, 17, 13, 00); private static final double MARGIN_PRICE = 0.0025; // Settle price for 17-Aug private static final InterestRateFutureOptionMarginTransactionDefinition TRANSACTION_1_DEFINITION = new InterestRateFutureOptionMarginTransactionDefinition( OPTION_ERU2_DEFINITION, QUANTITY, TRADE_DATE_1, TRADE_PRICE); private static final InterestRateFutureOptionMarginTransaction TRANSACTION_1 = TRANSACTION_1_DEFINITION.toDerivative( REFERENCE_DATE, MARGIN_PRICE); /* Calculator */ private static final PresentValueNormalSTIRFuturesCalculator PVNFC = PresentValueNormalSTIRFuturesCalculator.getInstance(); private static final PresentValueCurveSensitivityNormalSTIRFuturesCalculator PVCSNFC = PresentValueCurveSensitivityNormalSTIRFuturesCalculator.getInstance(); private static final ParameterSensitivityParameterCalculator<NormalSTIRFuturesProviderInterface> PSSFC = new ParameterSensitivityParameterCalculator<>(PVCSNFC); private static final MarketQuoteSensitivityBlockCalculator<NormalSTIRFuturesProviderInterface> MQSBC = new MarketQuoteSensitivityBlockCalculator<>(PSSFC); private static final PV01CurveParametersCalculator<NormalSTIRFuturesProviderInterface> PV01CPC = new PV01CurveParametersCalculator<>( PVCSNFC); private static final PositionDeltaNormalSTIRFutureOptionCalculator PDNFOC = PositionDeltaNormalSTIRFutureOptionCalculator .getInstance(); private static final PositionGammaNormalSTIRFutureOptionCalculator PGNFOC = PositionGammaNormalSTIRFutureOptionCalculator .getInstance(); private static final PositionThetaNormalSTIRFutureOptionCalculator PTNFOC = PositionThetaNormalSTIRFutureOptionCalculator .getInstance(); private static final PositionVegaNormalSTIRFutureOptionCalculator PVNFOC = PositionVegaNormalSTIRFutureOptionCalculator .getInstance(); private static final DeltaNormalSTIRFutureOptionCalculator DNFOC = DeltaNormalSTIRFutureOptionCalculator .getInstance(); private static final GammaNormalSTIRFutureOptionCalculator GNFOC = GammaNormalSTIRFutureOptionCalculator .getInstance(); private static final ThetaNormalSTIRFutureOptionCalculator TNFOC = ThetaNormalSTIRFutureOptionCalculator .getInstance(); private static final VegaNormalSTIRFutureOptionCalculator VNFOC = VegaNormalSTIRFutureOptionCalculator.getInstance(); private static final double BP1 = 1.0E-4; /** * E2E test for PV and all the risk measures. */ @Test public void E2ETest() { double tol = 1.0e-10; MultipleCurrencyAmount pv = TRANSACTION_1.accept(PVNFC, NORMAL_MULTICURVES); assertRelative("E2ETest, pv", 861850.4996823109, pv.getAmount(EUR), tol); MultipleCurrencyParameterSensitivity bucketedPv01 = MQSBC.fromInstrument(TRANSACTION_1, NORMAL_MULTICURVES, BLOCK) .multipliedBy(BP1); // market quote sensitivity, thus nonzero values for discounting curve double[] deltaDsc = new double [] {-6.699625499005507E-5, -6.699601771230529E-5, 1.1097176565222359E-10, -3.668758024917107E-9, -0.0025658905357422443, -0.004555359103668548, -0.006086354603993385, 0.17904224681155592, -0.3696466428416493, 0.026504268860874675, -7.840654172613281E-14, 9.633192325368143E-14, 3.080721153798044E-13, 2.4989987634531895E-13, 1.479886424366408E-13, 1.6993761653354066E-13 }; double[] deltaFwd = new double[] {-0.07843097744832121, 0.0016386932025324356, 7.200414013419107E-4, 0.016153849016546293, 212.57700563629456, -361.1909217716853, -2.2771335549327615E-11, 2.63862064877223E-12, 7.0640044230169935E-12, 1.4665415911199027E-12, 6.039106742059584E-12, 4.3654232078987675E-12, 3.962055990473498E-12, 4.319049695483522E-12, 3.0470051651095408E-12, 2.605778031824316E-12, 1.6592159214426629E-12 }; LinkedHashMap<Pair<String, Currency>, DoubleMatrix1D> sensitivity = new LinkedHashMap<>(); sensitivity.put(ObjectsPair.of(MULTICURVES.getName(EUR), EUR), new DoubleMatrix1D(deltaDsc)); sensitivity.put(ObjectsPair.of(MULTICURVES.getName(EURIBOR3M), EUR), new DoubleMatrix1D(deltaFwd)); MultipleCurrencyParameterSensitivity expectedbucket = new MultipleCurrencyParameterSensitivity(sensitivity); AssertSensitivityObjects.assertEquals("E2ETest, bucketed pv01", expectedbucket, bucketedPv01, tol); ReferenceAmount<Pair<String, Currency>> pv01 = TRANSACTION_1.accept(PV01CPC, NORMAL_MULTICURVES); // parameter sensitivity, thus null for discounting curve assertRelative("E2ETest, pv01", -149.20874491438397, pv01.getMap().get(Pairs.of(MULTICURVES.getName(EURIBOR3M), EUR)), tol); Double positionDelta = TRANSACTION_1.accept(PDNFOC, NORMAL_MULTICURVES); Double positionGamma = TRANSACTION_1.accept(PGNFOC, NORMAL_MULTICURVES); Double positionTheta = TRANSACTION_1.accept(PTNFOC, NORMAL_MULTICURVES); Double positionVega = TRANSACTION_1.accept(PVNFOC, NORMAL_MULTICURVES); assertRelative("E2ETest, positionDelta", 1514540.7118335292, positionDelta, tol); assertRelative("E2ETest, positionGamma", 1672748.1927204835, positionGamma, tol); assertRelative("E2ETest, positionTheta", -396583.47181617195, positionTheta, tol); assertRelative("E2ETest, positionVega", 1243371.6048490028, positionVega, tol); Double delta = TRANSACTION_1.accept(DNFOC, NORMAL_MULTICURVES); Double gamma = TRANSACTION_1.accept(GNFOC, NORMAL_MULTICURVES); Double theta = TRANSACTION_1.accept(TNFOC, NORMAL_MULTICURVES); Double vega = TRANSACTION_1.accept(VNFOC, NORMAL_MULTICURVES); assertRelative("E2ETest, delta", 0.5048469039445097, delta, tol); assertRelative("E2ETest, gamma", 0.5575827309068279, gamma, tol); assertRelative("E2ETest, theta", -0.13219449060539065, theta, tol); assertRelative("E2ETest, vega", 0.4144572016163343, vega, tol); } /** * Check data points are correctly stored. */ @Test public void nodePointTest() { double tol = 1.0e-14; /* sample points randomly chosen */ assertRelative("nodePointTest", 1.0623, VOL_SURFACE_SIMPLEMONEY.getZValue(21.0 / 365.0, -0.005), tol); assertRelative("nodePointTest", 0.7794, VOL_SURFACE_SIMPLEMONEY.getZValue(60.0 / 365.0, -0.002), tol); assertRelative("nodePointTest", 0.8938, VOL_SURFACE_SIMPLEMONEY.getZValue(120.0 / 365.0, -0.006), tol); assertRelative("nodePointTest", 0.6519, VOL_SURFACE_SIMPLEMONEY.getZValue(14.0 / 365.0, 0.0), tol); assertRelative("nodePointTest", 0.7383, VOL_SURFACE_SIMPLEMONEY.getZValue(90.0 / 365.0, 0.002), tol); assertRelative("nodePointTest", 0.7, VOL_SURFACE_SIMPLEMONEY.getZValue(180.0 / 365.0, 0.003), tol); } /** * Check flat extrapolation on volatility. */ @Test public void extrapolationTest() { double tol = 1.0e-14; /* sample points randomly chosen */ assertRelative("extrapolationTest", 1.0623, VOL_SURFACE_SIMPLEMONEY.getZValue(3.0 / 365.0, -0.01), tol); assertRelative("extrapolationTest", 1.1414, VOL_SURFACE_SIMPLEMONEY.getZValue(30.0 / 365.0, -0.015), tol); assertRelative("extrapolationTest", 0.9523, VOL_SURFACE_SIMPLEMONEY.getZValue(220.0 / 365.0, -0.012), tol); assertRelative("extrapolationTest", 0.7626, VOL_SURFACE_SIMPLEMONEY.getZValue(240.0 / 365.0, -0.002), tol); assertRelative("extrapolationTest", 0.6886, VOL_SURFACE_SIMPLEMONEY.getZValue(200.0 / 365.0, 0.007), tol); assertRelative("extrapolationTest", 0.7144, VOL_SURFACE_SIMPLEMONEY.getZValue(60.0 / 365.0, 0.006), tol); assertRelative("extrapolationTest", 0.8823, VOL_SURFACE_SIMPLEMONEY.getZValue(2.0 / 365.0, 0.008), tol); assertRelative("extrapolationTest", 1.0623, VOL_SURFACE_SIMPLEMONEY.getZValue(1.0 / 365.0, -0.004), tol); } /** * Check non-uniformly distributed date points are correctly interpolated. */ @Test public void InterpolatorTest() { double tol = 1.0e-10; double[] expiry = new double[] {22.0 / 365.0, 22.0 / 365.0, 22.0 / 365.0, 22.0 / 365.0, 22.0 / 365.0, 57.0 / 365.0, 57.0 / 365.0, 57.0 / 365.0, 57.0 / 365.0, 57.0 / 365.0 }; double[] moneyness = new double[] {-0.001086366, -0.000180979, 0.0, 0.000723589, 0.00162734, -0.001540065, -0.000633857, 0.0, 0.00027153, 0.001176098 }; double[] vol = new double[] {0.716515, 0.641929, 0.637017, 0.662312, 0.747397, 0.703106, 0.677655, 0.663821, 0.659139, 0.649632 }; InterpolatedDoublesSurface surface = InterpolatedDoublesSurface.from(expiry, moneyness, vol, INTERPOLATOR_2D); double keyMoneyness = 0.001; double computed1 = surface.getZValue(22.0 / 365.0, keyMoneyness); double ratio1 = (keyMoneyness - 0.000723589) / (0.00162734 - 0.000723589); double exp1 = Math.sqrt(0.662312 * 0.662312 * (1.0 - ratio1) + 0.747397 * 0.747397 * ratio1); InterpolatorTestUtil.assertRelative("Interpolation2DTest, moneyness", exp1, computed1, tol); double keyExpiry = 40.0; double computed2 = surface.getZValue(keyExpiry / 365.0, 0.0); double ratio2 = (keyExpiry - 22.0) / (57.0 - 22.0); double exp2 = Math.sqrt((0.637017 * 0.637017 * 22.0 * (1.0 - ratio2) + 0.663821 * 0.663821 * 57.0 * ratio2) / keyExpiry); InterpolatorTestUtil.assertRelative("Interpolation2DTest, time", exp2, computed2, tol); expiry = new double[] {22.0 / 365.0, 57.0 / 365.0, 22.0 / 365.0, 57.0 / 365.0, 22.0 / 365.0, 57.0 / 365.0, 22.0 / 365.0, 57.0 / 365.0, 22.0 / 365.0, 57.0 / 365.0 }; moneyness = new double[] {-0.001086366, -0.001540065, -0.000180979, -0.000633857, 0.0, 0.0, 0.000723589, 0.00027153, 0.00162734, 0.001176098 }; vol = new double[] {0.716515, 0.703106, 0.641929, 0.677655, 0.637017, 0.663821, 0.662312, 0.659139, 0.747397, 0.649632 }; surface = InterpolatedDoublesSurface.from(expiry, moneyness, vol, INTERPOLATOR_2D); computed1 = surface.getZValue(22.0 / 365.0, keyMoneyness); assertRelative("Interpolation2DTest, moneyness", exp1, computed1, tol); computed2 = surface.getZValue(keyExpiry / 365.0, 0.0); assertRelative("Interpolation2DTest, time", exp2, computed2, tol); } /** * Print volatility surface. */ @Test(enabled = false) public void volatilitySurfacePrintTest() { int nSample = 100; double minExpiry = EXPIRY[0] * 0.8; double maxExpiry = EXPIRY[EXPIRY.length - 1] * 1.2; double intervalExpiry = (maxExpiry - minExpiry) / (nSample - 1.0); double minMoney = SIMPLEMONEY[0] * 1.2; double maxMoney = SIMPLEMONEY[SIMPLEMONEY.length - 1] * 1.2; double intervalMoney = (maxMoney - minMoney) / (nSample - 1.0); for (int j = 0; j < nSample; ++j) { double moneyness = minMoney + intervalMoney * j; System.out.print("\t" + moneyness); } System.out.print("\n"); for (int i = 0; i < nSample; ++i) { double expiry = minExpiry + intervalExpiry * i; System.out.print(expiry); for (int j = 0; j < nSample; ++j) { double moneyness = minMoney + intervalMoney * j; System.out.print("\t" + VOL_SURFACE_SIMPLEMONEY.getZValue(expiry, moneyness)); } System.out.print("\n"); } } private void assertRelative(String message, double expected, double obtained, double relativeTol) { double ref = Math.max(Math.abs(expected), 1.0); assertEquals(message, expected, obtained, ref * relativeTol); } }