/** * Copyright (C) 2012 - 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.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; 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.derivative.BondFuturesOptionPremiumSecurity; import com.opengamma.analytics.financial.interestrate.future.derivative.BondFuturesOptionPremiumTransaction; import com.opengamma.analytics.financial.interestrate.payments.provider.PaymentFixedDiscountingMethod; import com.opengamma.analytics.financial.legalentity.LegalEntity; import com.opengamma.analytics.financial.provider.description.IssuerProviderDiscountDataSets; import com.opengamma.analytics.financial.provider.description.StandardDataSetsBlack; import com.opengamma.analytics.financial.provider.description.interestrate.BlackBondFuturesExpStrikeProvider; import com.opengamma.analytics.financial.provider.description.interestrate.IssuerProviderDiscount; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MulticurveSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity; import com.opengamma.analytics.financial.util.AssertSensitivityObjects; import com.opengamma.analytics.math.surface.InterpolatedDoublesSurface; import com.opengamma.util.money.Currency; import com.opengamma.util.money.MultipleCurrencyAmount; import com.opengamma.util.test.TestGroup; import com.opengamma.util.time.DateUtils; import com.opengamma.util.tuple.DoublesPair; /** * Tests related to the pricing methods for bond future options transaction with up-front premium payment. */ @Test(groups = TestGroup.UNIT) public class BondFuturesOptionPremiumTransactionBlackExpStrikeMethodTest { /** Bond future option: JGB */ private static final Currency JPY = Currency.JPY; private static final BondFuturesSecurityDefinition JBM5_DEFINITION = BondFuturesDataSets.JBM5_DEFINITION; private static final double NOTIONAL = JBM5_DEFINITION.getNotional(); private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2015, 5, 12); private static final double STRIKE_147 = 1.47; // To be close to ATM for the data set used. private static final ZonedDateTime EXPIRY_DATE_OPT = DateUtils.getUTCDate(2015, 5, 31); private static final boolean IS_CALL = true; private static final BondFuturesOptionPremiumSecurityDefinition CALL_JB_147_DEFINITION = new BondFuturesOptionPremiumSecurityDefinition(JBM5_DEFINITION, EXPIRY_DATE_OPT, STRIKE_147, IS_CALL); private static final BondFuturesOptionPremiumSecurityDefinition PUT_JB_147_DEFINITION = new BondFuturesOptionPremiumSecurityDefinition(JBM5_DEFINITION, EXPIRY_DATE_OPT, STRIKE_147, !IS_CALL); private static final BondFuturesOptionPremiumSecurity CALL_JB_147 = CALL_JB_147_DEFINITION.toDerivative(REFERENCE_DATE); private static final BondFuturesOptionPremiumSecurity PUT_JB_147 = PUT_JB_147_DEFINITION.toDerivative(REFERENCE_DATE); private static final int QUANTITY = 1234; private static final ZonedDateTime PREMIUM_DATE = DateUtils.getUTCDate(2015, 5, 13); private static final double PREMIUM_UNIT_CALL = 10.5; private static final double PREMIUM_UNIT_PUT = 15.6; private static final BondFuturesOptionPremiumTransactionDefinition BOND_FUTURE_OPTION_TRA_CALL_DEFINITION = new BondFuturesOptionPremiumTransactionDefinition(CALL_JB_147_DEFINITION, QUANTITY, PREMIUM_DATE, -QUANTITY * PREMIUM_UNIT_CALL); private static final BondFuturesOptionPremiumTransaction BOND_FUTURE_OPTION_TRA_CALL = BOND_FUTURE_OPTION_TRA_CALL_DEFINITION.toDerivative(REFERENCE_DATE); private static final BondFuturesOptionPremiumTransactionDefinition BOND_FUTURE_OPTION_TRA_PUT_DEFINITION = new BondFuturesOptionPremiumTransactionDefinition(PUT_JB_147_DEFINITION, QUANTITY, PREMIUM_DATE, -QUANTITY * PREMIUM_UNIT_PUT); private static final BondFuturesOptionPremiumTransaction BOND_FUTURE_OPTION_TRA_PUT = BOND_FUTURE_OPTION_TRA_PUT_DEFINITION.toDerivative(REFERENCE_DATE); /** Black surface expiry/strike */ final private static InterpolatedDoublesSurface BLACK_SURFACE_EXP_STRIKE = StandardDataSetsBlack.BLACK_SURFACE_BND_EXP_STRIKE; /** Curves for a specific issuer name */ private static final IssuerProviderDiscount ISSUER_SPECIFIC_MULTICURVES = IssuerProviderDiscountDataSets.ISSUER_SPECIFIC_MULTICURVE_JP; /** The legal entity */ private static final LegalEntity LEGAL_ENTITY_JAPAN = IssuerProviderDiscountDataSets.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 PaymentFixedDiscountingMethod METHOD_PAY_FIXED = PaymentFixedDiscountingMethod.getInstance(); /** Tolerances */ private static final double TOLERANCE_PV = 1.0E-2; private static final double TOLERANCE_PV_DELTA = 1.0E+2; @Test public void presentValueFromCurves() { MultipleCurrencyAmount pvCallComputed = METHOD_OPT_TRA.presentValue(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT); double priceCall = METHOD_OPT_SEC.price(BOND_FUTURE_OPTION_TRA_CALL.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); MultipleCurrencyAmount pvCallPremium = METHOD_PAY_FIXED.presentValue(BOND_FUTURE_OPTION_TRA_CALL.getPremium(), ISSUER_SPECIFIC_MULTICURVES.getMulticurveProvider()); assertEquals("BondFuturesOptionPremiumTransactionBlackExpStrikeMethod: present value", pvCallComputed.getAmount(JPY), pvCallPremium.getAmount(JPY) + priceCall * QUANTITY * JBM5_DEFINITION.getNotional(), TOLERANCE_PV); MultipleCurrencyAmount pvPutComputed = METHOD_OPT_TRA.presentValue(BOND_FUTURE_OPTION_TRA_PUT, BLACK_EXP_STRIKE_BNDFUT); MultipleCurrencyAmount pvPutPremium = METHOD_PAY_FIXED.presentValue(BOND_FUTURE_OPTION_TRA_PUT.getPremium(), BLACK_EXP_STRIKE_BNDFUT.getMulticurveProvider()); double priceFut = METHOD_FUTURES.price(BOND_FUTURE_OPTION_TRA_CALL.getUnderlyingOption().getUnderlyingFuture(), ISSUER_SPECIFIC_MULTICURVES); double df = ISSUER_SPECIFIC_MULTICURVES.getMulticurveProvider().getDiscountFactor(JBM5_DEFINITION.getCurrency(), BOND_FUTURE_OPTION_TRA_CALL.getUnderlyingOption().getExpirationTime()); assertEquals("BondFuturesOptionPremiumTransactionBlackExpStrikeMethod: option price from future price", df * (priceFut - STRIKE_147) * QUANTITY * JBM5_DEFINITION.getNotional(), (pvCallComputed.getAmount(JPY) - pvCallPremium.getAmount(JPY)) - (pvPutComputed.getAmount(JPY) - pvPutPremium.getAmount(JPY)), TOLERANCE_PV); } @Test public void presentValueFromFuturesPrice() { final MultipleCurrencyAmount pvComputed = METHOD_OPT_TRA.presentValue(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT); final double priceCall = METHOD_OPT_SEC.price(BOND_FUTURE_OPTION_TRA_CALL.getUnderlyingOption(), BLACK_EXP_STRIKE_BNDFUT); final MultipleCurrencyAmount pvPremium = METHOD_PAY_FIXED.presentValue(BOND_FUTURE_OPTION_TRA_CALL.getPremium(), BLACK_EXP_STRIKE_BNDFUT.getMulticurveProvider()); assertEquals("BondFuturesOptionPremiumTransactionBlackExpStrikeMethod: present value", pvComputed.getAmount(JPY), pvPremium.getAmount(JPY) + priceCall * QUANTITY * JBM5_DEFINITION.getNotional(), TOLERANCE_PV); double priceFut = METHOD_FUTURES.price(BOND_FUTURE_OPTION_TRA_CALL.getUnderlyingOption().getUnderlyingFuture(), ISSUER_SPECIFIC_MULTICURVES); MultipleCurrencyAmount pv2 = METHOD_OPT_TRA .presentValueFromUnderlyingPrice(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT, priceFut); assertEquals("BondFuturesOptionPremiumTransactionBlackExpStrikeMethod: present value", pv2.getAmount(JPY), pvComputed.getAmount(JPY), TOLERANCE_PV); } @Test public void presentValueCurveSensitivityFromCurves() { MultipleCurrencyMulticurveSensitivity pvcsCallComputed = METHOD_OPT_TRA.presentValueCurveSensitivity(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT).cleaned(); MulticurveSensitivity pcsCall = METHOD_OPT_SEC .priceCurveSensitivity(CALL_JB_147, BLACK_EXP_STRIKE_BNDFUT); MultipleCurrencyMulticurveSensitivity pvcsCallPremium = METHOD_PAY_FIXED.presentValueCurveSensitivity( BOND_FUTURE_OPTION_TRA_CALL.getPremium(), BLACK_EXP_STRIKE_BNDFUT.getMulticurveProvider()); MultipleCurrencyMulticurveSensitivity pvcsCallExpected = pvcsCallPremium.plus( MultipleCurrencyMulticurveSensitivity.of(JPY, pcsCall.multipliedBy(JBM5_DEFINITION.getNotional() * QUANTITY))) .cleaned(); AssertSensitivityObjects.assertEquals( "BondFutureOptionPremiumTransactionBlackSurfaceMethod: present value curve sensitivity", pvcsCallExpected, pvcsCallComputed, TOLERANCE_PV_DELTA); MultipleCurrencyMulticurveSensitivity pvcsPutComputed = METHOD_OPT_TRA.presentValueCurveSensitivity(BOND_FUTURE_OPTION_TRA_PUT, BLACK_EXP_STRIKE_BNDFUT).cleaned(); MultipleCurrencyMulticurveSensitivity pvcsPutPremium = METHOD_PAY_FIXED.presentValueCurveSensitivity(BOND_FUTURE_OPTION_TRA_PUT.getPremium(), BLACK_EXP_STRIKE_BNDFUT.getMulticurveProvider()); double df = ISSUER_SPECIFIC_MULTICURVES.getMulticurveProvider().getDiscountFactor(JBM5_DEFINITION.getCurrency(), BOND_FUTURE_OPTION_TRA_CALL.getUnderlyingOption().getExpirationTime()); MultipleCurrencyMulticurveSensitivity pvcsFutQu = MultipleCurrencyMulticurveSensitivity.of(JPY, METHOD_FUTURES.priceCurveSensitivity( PUT_JB_147.getUnderlyingFuture(), BLACK_EXP_STRIKE_BNDFUT) .multipliedBy(QUANTITY * df * NOTIONAL).cleaned(TOLERANCE_PV_DELTA)); double priceFut = METHOD_FUTURES.price(BOND_FUTURE_OPTION_TRA_CALL.getUnderlyingOption().getUnderlyingFuture(), ISSUER_SPECIFIC_MULTICURVES); Map<String, List<DoublesPair>> mapDsc = new HashMap<>(); List<DoublesPair> listDiscounting = new ArrayList<>(); listDiscounting.add(DoublesPair.of(BOND_FUTURE_OPTION_TRA_PUT.getUnderlyingOption().getExpirationTime(), -BOND_FUTURE_OPTION_TRA_PUT.getUnderlyingOption().getExpirationTime() * df)); mapDsc.put(ISSUER_SPECIFIC_MULTICURVES.getMulticurveProvider().getName(JPY), listDiscounting); MultipleCurrencyMulticurveSensitivity dfDr = MultipleCurrencyMulticurveSensitivity.of(JPY, MulticurveSensitivity.ofYieldDiscounting(mapDsc)); MultipleCurrencyMulticurveSensitivity pvcsCallPut = pvcsCallComputed.plus(pvcsCallPremium.multipliedBy(-1)) .plus(pvcsPutPremium.plus(pvcsPutComputed.multipliedBy(-1))) .cleaned(TOLERANCE_PV_DELTA); AssertSensitivityObjects.assertEquals( "BondFuturesOptionPremiumTransactionBlackExpStrikeMethod: present value curve sensitivity", (pvcsFutQu.plus(dfDr.multipliedBy((priceFut - STRIKE_147) * QUANTITY * NOTIONAL))).cleaned(), pvcsCallPut.cleaned(), TOLERANCE_PV_DELTA); } @Test public void presentValueDelta() { double delta = METHOD_OPT_SEC.delta(CALL_JB_147, BLACK_EXP_STRIKE_BNDFUT); double pvDelta = METHOD_OPT_TRA.presentValueDelta(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT); assertEquals(delta * QUANTITY * NOTIONAL, pvDelta, TOLERANCE_PV); } @Test public void presentValueGamma() { double gamma = METHOD_OPT_SEC.gamma(CALL_JB_147, BLACK_EXP_STRIKE_BNDFUT); double pvGamma = METHOD_OPT_TRA.presentValueGamma(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT); assertEquals(gamma * QUANTITY * NOTIONAL, pvGamma, TOLERANCE_PV); } @Test public void presentValueVega() { double vega = METHOD_OPT_SEC.vega(CALL_JB_147, BLACK_EXP_STRIKE_BNDFUT); double pvVega = METHOD_OPT_TRA.presentValueVega(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT); assertEquals(vega * QUANTITY * NOTIONAL, pvVega, TOLERANCE_PV); } @Test public void expansionDeltaGamma() { double pvDelta = METHOD_OPT_TRA.presentValueDelta(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT); double pvGamma = METHOD_OPT_TRA.presentValueGamma(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT); double shift = 2.0E-5; double price = METHOD_FUTURES.price(CALL_JB_147.getUnderlyingFuture(), ISSUER_SPECIFIC_MULTICURVES); MultipleCurrencyAmount pvInit = METHOD_OPT_TRA .presentValueFromUnderlyingPrice(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT, price); MultipleCurrencyAmount pvShifted = METHOD_OPT_TRA .presentValueFromUnderlyingPrice(BOND_FUTURE_OPTION_TRA_CALL, BLACK_EXP_STRIKE_BNDFUT, price + shift); double pvExpanded = pvDelta * shift + 0.5 * pvGamma * (shift * shift); assertEquals(pvShifted.getAmount(JPY) - pvInit.getAmount(JPY), pvExpanded, 1.0E+0); } }