/** * Copyright (C) 2015 - 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.BondFuturesDataSets; import com.opengamma.analytics.financial.instrument.future.BondFuturesOptionPremiumSecurityDefinition; import com.opengamma.analytics.financial.instrument.future.BondFuturesOptionPremiumTransactionDefinition; import com.opengamma.analytics.financial.instrument.future.BondFuturesSecurityDefinition; import com.opengamma.analytics.financial.interestrate.future.calculator.DeltaBlackBondFuturesCalculator; import com.opengamma.analytics.financial.interestrate.future.calculator.GammaBlackBondFuturesCalculator; import com.opengamma.analytics.financial.interestrate.future.calculator.ThetaBlackBondFuturesCalculator; import com.opengamma.analytics.financial.interestrate.future.calculator.VegaBlackBondFuturesCalculator; import com.opengamma.analytics.financial.interestrate.future.derivative.BondFuturesOptionPremiumTransaction; import com.opengamma.analytics.financial.interestrate.future.derivative.BondFuturesSecurity; import com.opengamma.analytics.financial.legalentity.LegalEntity; import com.opengamma.analytics.financial.provider.calculator.blackbondfutures.PresentValueBlackBondFuturesOptionCalculator; import com.opengamma.analytics.financial.provider.calculator.discounting.PresentValueCurveSensitivityDiscountingCalculator; import com.opengamma.analytics.financial.provider.description.interestrate.BlackBondFuturesExpStrikeProvider; import com.opengamma.analytics.financial.provider.description.interestrate.IssuerProviderDiscount; import com.opengamma.analytics.financial.provider.description.interestrate.ParameterProviderInterface; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyParameterSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.parameter.ParameterSensitivityParameterCalculator; import com.opengamma.analytics.financial.util.AssertSensitivityObjects; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.analytics.math.surface.InterpolatedDoublesSurface; 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; /** * End-to-end test used for demo and integration checks. Previous run hard-coded numbers. */ public class BondFuturesOptionPremiumBlackExpStrikeE2ETest { /** Bond future option: JGB */ private static final Currency JPY = Currency.JPY; private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2015, 5, 12); private static final BondFuturesSecurityDefinition JBM5_DEFINITION = BondFuturesDataSets.JBM5_DEFINITION; private static final BondFuturesSecurity JBM5 = JBM5_DEFINITION.toDerivative(REFERENCE_DATE); private static final BondFuturesSecurityDefinition JBU5_DEFINITION = BondFuturesDataSets.JBU5_DEFINITION; private static final BondFuturesSecurity JBU5 = JBU5_DEFINITION.toDerivative(REFERENCE_DATE); private static final double NOTIONAL = JBM5_DEFINITION.getNotional(); private static final double STRIKE_147 = 1.47; // To be close to ATM for the data set used. private static final double STRIKE_146_5 = 1.465; // To be close to ATM for the data set used. private static final ZonedDateTime EXPIRY_DATE_M_OPT = DateUtils.getUTCDate(2015, 5, 31); private static final ZonedDateTime EXPIRY_DATE_N_OPT = DateUtils.getUTCDate(2015, 6, 30); private static final boolean IS_CALL = true; private static final BondFuturesOptionPremiumSecurityDefinition CALL_JBM_147_DEFINITION = new BondFuturesOptionPremiumSecurityDefinition(JBM5_DEFINITION, EXPIRY_DATE_M_OPT, STRIKE_147, IS_CALL); private static final BondFuturesOptionPremiumSecurityDefinition PUT_JBM_147_DEFINITION = new BondFuturesOptionPremiumSecurityDefinition(JBM5_DEFINITION, EXPIRY_DATE_M_OPT, STRIKE_147, !IS_CALL); private static final BondFuturesOptionPremiumSecurityDefinition PUT_JBN_146_5_DEFINITION = new BondFuturesOptionPremiumSecurityDefinition(JBU5_DEFINITION, EXPIRY_DATE_N_OPT, STRIKE_146_5, !IS_CALL); private static final int QUANTITY = 100; private static final ZonedDateTime PREMIUM_DATE = DateUtils.getUTCDate(2015, 5, 13); private static final ZonedDateTime PREMIUM_DATE_2 = DateUtils.getUTCDate(2015, 5, 6); private static final double PREMIUM_UNIT_CALL_JBM_147 = 0.0030; private static final double PREMIUM_UNIT_PUT_JBM_147 = 0.0046; private static final BondFuturesOptionPremiumTransactionDefinition CALL_JBM_147_TRA_DEFINITION = new BondFuturesOptionPremiumTransactionDefinition(CALL_JBM_147_DEFINITION, QUANTITY, PREMIUM_DATE, -QUANTITY * NOTIONAL * PREMIUM_UNIT_CALL_JBM_147); private static final BondFuturesOptionPremiumTransaction CALL_JBM_147_TRA = CALL_JBM_147_TRA_DEFINITION.toDerivative(REFERENCE_DATE); private static final BondFuturesOptionPremiumTransactionDefinition PUT_JBM_147_TRA_DEFINITION = new BondFuturesOptionPremiumTransactionDefinition(PUT_JBM_147_DEFINITION, QUANTITY, PREMIUM_DATE, -QUANTITY * NOTIONAL * PREMIUM_UNIT_PUT_JBM_147); private static final BondFuturesOptionPremiumTransaction PUT_JBM_147_TRA = PUT_JBM_147_TRA_DEFINITION.toDerivative(REFERENCE_DATE); private static final BondFuturesOptionPremiumTransactionDefinition PUT_JBN_146_5_TRA_DEFINITION = new BondFuturesOptionPremiumTransactionDefinition(PUT_JBN_146_5_DEFINITION, -QUANTITY, PREMIUM_DATE_2, 0); private static final BondFuturesOptionPremiumTransaction PUT_JBN_146_5_TRA = PUT_JBN_146_5_TRA_DEFINITION.toDerivative(REFERENCE_DATE); /** Black surface expiry/strike */ final private static InterpolatedDoublesSurface BLACK_SURFACE_EXP_STRIKE = BondFuturesOptionPremiumE2EDataSet.BLACK_SURFACE_BND_EXP_STRIKE; /** Curves for a specific issuer name */ private static final IssuerProviderDiscount ISSUER_SPECIFIC_MULTICURVES = BondFuturesOptionPremiumE2EDataSet.ISSUER_SPECIFIC_MULTICURVE_JP; /** The legal entity */ private static final LegalEntity LEGAL_ENTITY_JAPAN = BondFuturesOptionPremiumE2EDataSet.JP_GOVT; /** The Black bond futures provider **/ private static final BlackBondFuturesExpStrikeProvider BLACK_EXP_STRIKE_BNDFUT = new BlackBondFuturesExpStrikeProvider(ISSUER_SPECIFIC_MULTICURVES, BLACK_SURFACE_EXP_STRIKE, LEGAL_ENTITY_JAPAN); /** Methods and calculators */ private static final BondFuturesOptionPremiumSecurityBlackBondFuturesMethod METHOD_OPT_SEC = BondFuturesOptionPremiumSecurityBlackBondFuturesMethod.getInstance(); private static final BondFuturesOptionPremiumTransactionBlackBondFuturesMethod METHOD_OPT_TRA = BondFuturesOptionPremiumTransactionBlackBondFuturesMethod.getInstance(); private static final BondFuturesSecurityDiscountingMethod METHOD_FUTURES = BondFuturesSecurityDiscountingMethod.getInstance(); private static final PresentValueCurveSensitivityDiscountingCalculator PVCSDC = PresentValueCurveSensitivityDiscountingCalculator.getInstance(); private static final ParameterSensitivityParameterCalculator<ParameterProviderInterface> PSC = new ParameterSensitivityParameterCalculator<>(PVCSDC); /** Tolerances */ private static final double TOLERANCE_PRICE = 1.0E-6; private static final double TOLERANCE_PV = 1.0E-2; private static final double TOLERANCE_PV_APPROX = 1.0E+3; private static final double TOLERANCE_PV_DELTA = 1.0E+2; private static final double BP1 = 1.0E-4; @Test public void futurePrice() { double futuresPriceM5Expected = 1.46883113; double futuresPriceM5 = METHOD_FUTURES.price(JBM5, ISSUER_SPECIFIC_MULTICURVES); assertEquals(futuresPriceM5Expected, futuresPriceM5, TOLERANCE_PRICE); double futuresPriceU5Expected = 1.46222860; double futuresPriceU5 = METHOD_FUTURES.price(JBU5, ISSUER_SPECIFIC_MULTICURVES); assertEquals(futuresPriceU5Expected, futuresPriceU5, TOLERANCE_PRICE); } /* Present value for each option. */ @Test public void presentValue() { double pvCallM147Expected = 5878108.4498; // PV includes premium and option value MultipleCurrencyAmount pvCallM147Computed = METHOD_OPT_TRA.presentValue(CALL_JBM_147_TRA, BLACK_EXP_STRIKE_BNDFUT); assertEquals(pvCallM147Expected, pvCallM147Computed.getAmount(JPY), TOLERANCE_PV); double pvPutM147Expected = 1566468.8540; MultipleCurrencyAmount pvPutM147Computed = METHOD_OPT_TRA.presentValue(PUT_JBM_147_TRA, BLACK_EXP_STRIKE_BNDFUT); assertEquals(pvPutM147Expected, pvPutM147Computed.getAmount(JPY), TOLERANCE_PV); double pvPutU1465Expected = -81090395.9457; // Short the option MultipleCurrencyAmount pvPutU1465Computed = METHOD_OPT_TRA.presentValue(PUT_JBN_146_5_TRA, BLACK_EXP_STRIKE_BNDFUT); assertEquals(pvPutU1465Expected, pvPutU1465Computed.getAmount(JPY), TOLERANCE_PV); MultipleCurrencyAmount pvPutU1465Calculator = PUT_JBN_146_5_TRA .accept(PresentValueBlackBondFuturesOptionCalculator.getInstance(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(pvPutU1465Expected, pvPutU1465Calculator.getAmount(JPY), TOLERANCE_PV); } /* Sensitivity to the zero-coupon rates of the curves. */ @Test public void presentValueParameterSensitivity() { MultipleCurrencyMulticurveSensitivity pvptsCallM147 = METHOD_OPT_TRA.presentValueCurveSensitivity(CALL_JBM_147_TRA, BLACK_EXP_STRIKE_BNDFUT); MultipleCurrencyParameterSensitivity pvpsCallM147Computed = PSC.pointToParameterSensitivity(pvptsCallM147, BLACK_EXP_STRIKE_BNDFUT).multipliedBy(BP1); // ZR sensi to 1 bp double[] deltaDscCM = {41391.172, 30092.939, 0.000, 0.000, 0.000, 0.000, 0.000}; double[] deltaGovtCM = {-1699.265, -5064.843, -30751.921, -66818.039, -4351376.994, -159578.927, 0.000}; // Check with delta AssertSensitivityObjects.assertEquals("BondFuturesOptionPremiumBlackExpStrike - end-to-end test", pvpsCallM147Computed, ps(deltaDscCM, deltaGovtCM), TOLERANCE_PV_DELTA); MultipleCurrencyMulticurveSensitivity pvptsPutU1465 = METHOD_OPT_TRA.presentValueCurveSensitivity(PUT_JBN_146_5_TRA, BLACK_EXP_STRIKE_BNDFUT); MultipleCurrencyParameterSensitivity pvpsPutU1465Computed = PSC.pointToParameterSensitivity(pvptsPutU1465, BLACK_EXP_STRIKE_BNDFUT).multipliedBy(BP1); // ZR sensi to 1 bp double[] deltaDscPN = {510.164, 172102.284, 121028.501, 0.000, 0.000, 0.000, 0.000}; double[] deltaGovtPN = {-1932.522, -5617.214, -34020.187, -73950.143, -5118826.798, -689566.008, 0.000}; AssertSensitivityObjects.assertEquals("BondFuturesOptionPremiumBlackExpStrike - end-to-end test", pvpsPutU1465Computed, ps(deltaDscPN, deltaGovtPN), TOLERANCE_PV_DELTA); } /* Delta of the PV. */ @Test public void presentValueDelta() { double deltaPutM147Expected = -5433547105.474; double deltaPutM147Computed = METHOD_OPT_TRA.presentValueDelta(PUT_JBM_147_TRA, BLACK_EXP_STRIKE_BNDFUT); assertEquals(deltaPutM147Expected, deltaPutM147Computed, TOLERANCE_PV); } /* Gamma of the PV. */ @Test public void presentValueGamma() { double gammaPutM147Expected = 381729633582.165; double gammaPutM147Computed = METHOD_OPT_TRA.presentValueGamma(PUT_JBM_147_TRA, BLACK_EXP_STRIKE_BNDFUT); assertEquals(gammaPutM147Expected, gammaPutM147Computed, TOLERANCE_PV); } /* Vega of the PV. */ @Test public void presentValueVega() { double vegaPutM147Expected = 1328991007.568; double vegaPutM147Computed = METHOD_OPT_TRA.presentValueVega(PUT_JBM_147_TRA, BLACK_EXP_STRIKE_BNDFUT); assertEquals(vegaPutM147Expected, vegaPutM147Computed, TOLERANCE_PV); } /* Check that the delta/gamma expansion approximate the change of PV */ @Test public void presentValueDeltaGammaExpansion() { double priceFutures = METHOD_FUTURES.price(JBU5, ISSUER_SPECIFIC_MULTICURVES); double delta = METHOD_OPT_TRA.presentValueDelta(PUT_JBN_146_5_TRA, BLACK_EXP_STRIKE_BNDFUT); double gamma = METHOD_OPT_TRA.presentValueGamma(PUT_JBN_146_5_TRA, BLACK_EXP_STRIKE_BNDFUT); double pv0 = METHOD_OPT_TRA.presentValue(PUT_JBN_146_5_TRA, BLACK_EXP_STRIKE_BNDFUT).getAmount(JPY); double shift = 1.0E-3; // 10 bp double pvShifted = METHOD_OPT_TRA.presentValueFromUnderlyingPrice( PUT_JBN_146_5_TRA, BLACK_EXP_STRIKE_BNDFUT, priceFutures + shift).getAmount(JPY); assertEquals(pvShifted, pv0 + delta * shift + 0.5 * gamma * shift * shift, TOLERANCE_PV_APPROX); } /* Check that the delta/gamma expansion approximate the change of PV */ @Test public void presentValueVegaExpansion() { double vega = METHOD_OPT_TRA.presentValueVega(PUT_JBN_146_5_TRA, BLACK_EXP_STRIKE_BNDFUT); double pv0 = METHOD_OPT_TRA.presentValue(PUT_JBN_146_5_TRA, BLACK_EXP_STRIKE_BNDFUT).getAmount(JPY); double shift = 5.0E-4; // 5bp out of ~3% vol InterpolatedDoublesSurface blackSurfaceShifted = BondFuturesOptionPremiumE2EDataSet.blackSurfaceBndExpStrike(shift); BlackBondFuturesExpStrikeProvider providerVolShifted = new BlackBondFuturesExpStrikeProvider(ISSUER_SPECIFIC_MULTICURVES, blackSurfaceShifted, LEGAL_ENTITY_JAPAN); double pvShifted = METHOD_OPT_TRA.presentValue(PUT_JBN_146_5_TRA, providerVolShifted).getAmount(JPY); assertEquals(pvShifted, pv0 + vega * shift, TOLERANCE_PV_APPROX); } @Test public void delta() { double deltaPutM147Expected = -0.54335471; double deltaPutM147Computed = METHOD_OPT_SEC.delta(PUT_JBM_147_TRA.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(deltaPutM147Expected, deltaPutM147Computed, TOLERANCE_PRICE); double deltaPutM147Calculator = PUT_JBM_147_TRA.getUnderlyingOption() .accept(DeltaBlackBondFuturesCalculator.getInstance(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(deltaPutM147Expected, deltaPutM147Calculator, TOLERANCE_PRICE); double deltaPutM1465Expected = -0.56391967; double deltaPutM1465Computed = METHOD_OPT_SEC.delta(PUT_JBN_146_5_TRA.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(deltaPutM1465Expected, deltaPutM1465Computed, TOLERANCE_PRICE); double deltaPutM1465Calculator = PUT_JBN_146_5_TRA.getUnderlyingOption() .accept(DeltaBlackBondFuturesCalculator.getInstance(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(deltaPutM1465Expected, deltaPutM1465Calculator, TOLERANCE_PRICE); } @Test public void gamma() { double gammaPutM147Expected = 38.17296336; double gammaPutM147Computed = METHOD_OPT_SEC.gamma(PUT_JBM_147_TRA.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(gammaPutM147Expected, gammaPutM147Computed, TOLERANCE_PRICE); double gammaPutM147Calculator = PUT_JBM_147_TRA.getUnderlyingOption() .accept(GammaBlackBondFuturesCalculator.getInstance(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(gammaPutM147Expected, gammaPutM147Calculator, TOLERANCE_PRICE); double gammaPutM1465Expected = 23.70913668; double gammaPutM1465Computed = METHOD_OPT_SEC.gamma(PUT_JBN_146_5_TRA.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(gammaPutM1465Expected, gammaPutM1465Computed, TOLERANCE_PRICE); double gammaPutM1465Calculator = PUT_JBN_146_5_TRA.getUnderlyingOption() .accept(GammaBlackBondFuturesCalculator.getInstance(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(gammaPutM1465Expected, gammaPutM1465Calculator, TOLERANCE_PRICE); } @Test public void vega() { double vegaPutM147Expected = 0.13289910; double vegaPutM147Computed = METHOD_OPT_SEC.vega(PUT_JBM_147_TRA.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(vegaPutM147Expected, vegaPutM147Computed, TOLERANCE_PRICE); double vegaPutM147Calculator = PUT_JBM_147_TRA.getUnderlyingOption() .accept(VegaBlackBondFuturesCalculator.getInstance(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(vegaPutM147Expected, vegaPutM147Calculator, TOLERANCE_PRICE); double vegaPutM1465Expected = 0.2109653; double vegaPutM1465Computed = METHOD_OPT_SEC.vega(PUT_JBN_146_5_TRA.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(vegaPutM1465Expected, vegaPutM1465Computed, TOLERANCE_PRICE); double vegaPutM1465Calculator = PUT_JBN_146_5_TRA.getUnderlyingOption() .accept(VegaBlackBondFuturesCalculator.getInstance(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(vegaPutM1465Expected, vegaPutM1465Calculator, TOLERANCE_PRICE); } @Test public void theta() { double thetaPutM147Expected = -0.03957085; double thetaPutM147Computed = METHOD_OPT_SEC.theta(PUT_JBM_147_TRA.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(thetaPutM147Expected, thetaPutM147Computed, TOLERANCE_PRICE); double thetaPutM147Calculator = PUT_JBM_147_TRA.getUnderlyingOption() .accept(ThetaBlackBondFuturesCalculator.getInstance(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(thetaPutM147Expected, thetaPutM147Calculator, TOLERANCE_PRICE); double thetaPutM1465Expected = -0.02435502; double thetaPutM1465Computed = METHOD_OPT_SEC.theta(PUT_JBN_146_5_TRA.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(thetaPutM1465Expected, thetaPutM1465Computed, TOLERANCE_PRICE); double thetaPutM1465Calculator = PUT_JBN_146_5_TRA.getUnderlyingOption() .accept(ThetaBlackBondFuturesCalculator.getInstance(), BLACK_EXP_STRIKE_BNDFUT); assertEquals(thetaPutM1465Expected, thetaPutM1465Calculator, TOLERANCE_PRICE); } // create a parameter sensitivity from the arrays private MultipleCurrencyParameterSensitivity ps(double[] deltaDsc, double[] deltaGovt) { LinkedHashMap<Pair<String, Currency>, DoubleMatrix1D> sensitivity = new LinkedHashMap<>(); sensitivity.put(ObjectsPair.of(ISSUER_SPECIFIC_MULTICURVES.getMulticurveProvider().getName(JPY), JPY), new DoubleMatrix1D(deltaDsc)); sensitivity.put(ObjectsPair.of(ISSUER_SPECIFIC_MULTICURVES.getName(LEGAL_ENTITY_JAPAN), JPY), new DoubleMatrix1D( deltaGovt)); return new MultipleCurrencyParameterSensitivity(sensitivity); } }