/** * 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 static org.testng.AssertJUnit.assertTrue; import java.util.Map.Entry; import org.testng.annotations.Test; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.datasets.CalendarTarget; import com.opengamma.analytics.financial.instrument.future.InterestRateFutureOptionMarginSecurityDefinition; import com.opengamma.analytics.financial.instrument.future.InterestRateFutureSecurityDefinition; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.index.IndexIborMaster; import com.opengamma.analytics.financial.interestrate.future.derivative.InterestRateFutureOptionMarginSecurity; import com.opengamma.analytics.financial.interestrate.sensitivity.PresentValueBlackSTIRFuturesCubeSensitivity; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackFunctionData; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackPriceFunction; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption; import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository; import com.opengamma.analytics.financial.provider.description.MulticurveProviderDiscountDataSets; import com.opengamma.analytics.financial.provider.description.StandardDataSetsBlack; import com.opengamma.analytics.financial.provider.description.interestrate.BlackSTIRFuturesExpLogMoneynessProvider; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.math.surface.InterpolatedDoublesSurface; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.util.test.TestGroup; import com.opengamma.util.time.DateUtils; import com.opengamma.util.tuple.Triple; /** * Test. */ @Test(groups = TestGroup.UNIT) public class STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethodTest { /** Option on STIR futures */ private static final IndexIborMaster MASTER_IBOR_INDEX = IndexIborMaster.getInstance(); private static final IborIndex EURIBOR3M = MASTER_IBOR_INDEX.getIndex("EURIBOR3M"); private static final ZonedDateTime LAST_TRADE_DATE = DateUtils.getUTCDate(2014, 12, 15); private static final double NOTIONAL = 1000000.0; // 1m private static final double FUTURE_FACTOR = 0.25; private static final String NAME = "ERZ4"; private static final Calendar TARGET = new CalendarTarget("TARGET"); private static final InterestRateFutureSecurityDefinition ERZ4_DEFINITION = new InterestRateFutureSecurityDefinition(LAST_TRADE_DATE, EURIBOR3M, NOTIONAL, FUTURE_FACTOR, NAME, TARGET); private static final ZonedDateTime EXPIRY_DATE = DateUtils.getUTCDate(2014, 11, 17); private static final double STRIKE_09825 = 0.9825; private static final boolean IS_CALL = true; private static final InterestRateFutureOptionMarginSecurityDefinition CALL_ERZ4_09825_DEFINITION = new InterestRateFutureOptionMarginSecurityDefinition(ERZ4_DEFINITION, EXPIRY_DATE, STRIKE_09825, IS_CALL); private static final InterestRateFutureOptionMarginSecurityDefinition PUT_ERZ4_09825_DEFINITION = new InterestRateFutureOptionMarginSecurityDefinition(ERZ4_DEFINITION, EXPIRY_DATE, STRIKE_09825, !IS_CALL); /** Black surface expiry/log-moneyness */ final private static InterpolatedDoublesSurface BLACK_SURFACE_LOGMONEY = StandardDataSetsBlack.blackSurfaceExpiryLogMoneyness(); /** EUR curves */ final private static MulticurveProviderDiscount MULTICURVE = MulticurveProviderDiscountDataSets.createMulticurveEUR(); final private static BlackSTIRFuturesExpLogMoneynessProvider MULTICURVE_BLACK = new BlackSTIRFuturesExpLogMoneynessProvider(MULTICURVE, BLACK_SURFACE_LOGMONEY, EURIBOR3M); private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2014, 4, 10); private static final InterestRateFutureOptionMarginSecurity CALL_ERZ4_09825 = CALL_ERZ4_09825_DEFINITION.toDerivative(REFERENCE_DATE); private static final InterestRateFutureOptionMarginSecurity PUT_ERZ4_09825 = PUT_ERZ4_09825_DEFINITION.toDerivative(REFERENCE_DATE); /** Methods and calculators */ private static final InterestRateFutureSecurityDiscountingMethod METHOD_FUTURE = InterestRateFutureSecurityDiscountingMethod.getInstance(); private static final InterestRateFutureOptionMarginSecurityBlackSTIRFuturesMethod METHOD_OPT = InterestRateFutureOptionMarginSecurityBlackSTIRFuturesMethod.getInstance(); private static final BlackPriceFunction BLACK_FUNCTION = new BlackPriceFunction(); /** Tolerances */ private static final double TOLERANCE_RATE = 1.0E-10; private static final double TOLERANCE_DELTA = 1.0E-8; public void impliedVolatility() { final double priceFutures = METHOD_FUTURE.price(CALL_ERZ4_09825.getUnderlyingFuture(), MULTICURVE); final double rateFutures = 1.0d - priceFutures; final double rateStrike = 1.0d - STRIKE_09825; final double logmoney = Math.log(rateStrike / rateFutures); final double expiry = CALL_ERZ4_09825.getExpirationTime(); final double ivExpected = BLACK_SURFACE_LOGMONEY.getZValue(expiry, logmoney); final double ivComputed = METHOD_OPT.impliedVolatility(CALL_ERZ4_09825, MULTICURVE_BLACK); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: impliedVolatility", ivExpected, ivComputed, TOLERANCE_RATE); } public void futurePrice() { final double priceExpected = METHOD_FUTURE.price(CALL_ERZ4_09825.getUnderlyingFuture(), MULTICURVE); final double priceComputed = METHOD_OPT.underlyingFuturesPrice(CALL_ERZ4_09825, MULTICURVE_BLACK); final double priceComputed2 = METHOD_OPT.underlyingFuturesPrice(CALL_ERZ4_09825, MULTICURVE); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: underlying futures price", priceExpected, priceComputed, TOLERANCE_RATE); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: underlying futures price", priceExpected, priceComputed2, TOLERANCE_RATE); } public void priceFromFuturesPrice() { final double priceFutures = 0.9875; final double rateFutures = 1.0d - priceFutures; final double rateStrike = 1.0d - STRIKE_09825; final EuropeanVanillaOption option = new EuropeanVanillaOption(rateStrike, CALL_ERZ4_09825.getExpirationTime(), !CALL_ERZ4_09825.isCall()); final double logmoney = Math.log(rateStrike / rateFutures); final double expiry = CALL_ERZ4_09825.getExpirationTime(); final double volatility = BLACK_SURFACE_LOGMONEY.getZValue(expiry, logmoney); final BlackFunctionData dataBlack = new BlackFunctionData(rateFutures, 1.0, volatility); final double priceExpected = BLACK_FUNCTION.getPriceFunction(option).evaluate(dataBlack); final double priceComputed = METHOD_OPT.price(CALL_ERZ4_09825, MULTICURVE_BLACK, priceFutures); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: underlying futures price", priceExpected, priceComputed, TOLERANCE_RATE); } public void priceFromCurves() { final double priceFutures = METHOD_FUTURE.price(CALL_ERZ4_09825.getUnderlyingFuture(), MULTICURVE); final double priceExpected = METHOD_OPT.price(CALL_ERZ4_09825, MULTICURVE_BLACK, priceFutures); final double priceComputed = METHOD_OPT.price(CALL_ERZ4_09825, MULTICURVE_BLACK); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: price", priceExpected, priceComputed, TOLERANCE_RATE); } public void putCallParity() { final double priceFutures = METHOD_FUTURE.price(CALL_ERZ4_09825.getUnderlyingFuture(), MULTICURVE); final double priceCallComputed = METHOD_OPT.price(CALL_ERZ4_09825, MULTICURVE_BLACK); final double pricePutComputed = METHOD_OPT.price(PUT_ERZ4_09825, MULTICURVE_BLACK); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: put call parity price", priceCallComputed - pricePutComputed, priceFutures - STRIKE_09825, TOLERANCE_RATE); } public void priceBlackSensitivity() { final double priceFutures = METHOD_FUTURE.price(CALL_ERZ4_09825.getUnderlyingFuture(), MULTICURVE); final double rateFutures = 1.0d - priceFutures; final double rateStrike = 1.0d - STRIKE_09825; final EuropeanVanillaOption option = new EuropeanVanillaOption(rateStrike, CALL_ERZ4_09825.getExpirationTime(), !CALL_ERZ4_09825.isCall()); final double logmoney = Math.log(rateStrike / rateFutures); final double expiry = CALL_ERZ4_09825.getExpirationTime(); final double volatility = BLACK_SURFACE_LOGMONEY.getZValue(expiry, logmoney); final BlackFunctionData dataBlack = new BlackFunctionData(rateFutures, 1.0, volatility); final double[] priceAD = BLACK_FUNCTION.getPriceAdjoint(option, dataBlack); final double vega = priceAD[2]; final PresentValueBlackSTIRFuturesCubeSensitivity vegaComputed = METHOD_OPT.priceBlackSensitivity(CALL_ERZ4_09825, MULTICURVE_BLACK); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: Black parameters sensitivity", vega, vegaComputed.getSensitivity().toSingleValue(), TOLERANCE_DELTA); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: Black parameters sensitivity", 1, vegaComputed.getSensitivity().getMap().size()); final Entry<Triple<Double, Double, Double>, Double> point = vegaComputed.getSensitivity().getMap().entrySet().iterator().next(); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: Black parameters sensitivity", CALL_ERZ4_09825.getExpirationTime(), point.getKey().getFirst(), TOLERANCE_RATE); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: Black parameters sensitivity", CALL_ERZ4_09825.getUnderlyingFuture().getTradingLastTime() - CALL_ERZ4_09825.getExpirationTime(), point.getKey().getSecond(), TOLERANCE_RATE); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: Black parameters sensitivity", CALL_ERZ4_09825.getStrike(), point.getKey().getThird(), TOLERANCE_RATE); } public void theoreticalDelta() { final double priceFutures = METHOD_FUTURE.price(CALL_ERZ4_09825.getUnderlyingFuture(), MULTICURVE); final double rateFutures = 1.0d - priceFutures; final double rateStrike = 1.0d - STRIKE_09825; final EuropeanVanillaOption option = new EuropeanVanillaOption(rateStrike, CALL_ERZ4_09825.getExpirationTime(), !CALL_ERZ4_09825.isCall()); final double logmoney = Math.log(rateStrike / rateFutures); final double expiry = CALL_ERZ4_09825.getExpirationTime(); final double volatility = BLACK_SURFACE_LOGMONEY.getZValue(expiry, logmoney); final BlackFunctionData dataBlack = new BlackFunctionData(rateFutures, 1.0, volatility); final double[] priceAD = BLACK_FUNCTION.getPriceAdjoint(option, dataBlack); final double deltaCallExpected = -priceAD[1]; final double deltaCallComputed = METHOD_OPT.deltaUnderlyingPrice(CALL_ERZ4_09825, MULTICURVE_BLACK); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: delta", deltaCallExpected, deltaCallComputed, TOLERANCE_DELTA); assertTrue("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: delta", (0.0d < deltaCallComputed) && (deltaCallComputed < 1.0d)); final double deltaPutComputed = METHOD_OPT.deltaUnderlyingPrice(PUT_ERZ4_09825, MULTICURVE_BLACK); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: delta", deltaCallExpected - 1.0d, deltaPutComputed, TOLERANCE_DELTA); } public void theoreticalGamma() { final double priceFutures = METHOD_FUTURE.price(CALL_ERZ4_09825.getUnderlyingFuture(), MULTICURVE); final double rateFutures = 1.0d - priceFutures; final double rateStrike = 1.0d - STRIKE_09825; final EuropeanVanillaOption option = new EuropeanVanillaOption(rateStrike, CALL_ERZ4_09825.getExpirationTime(), !CALL_ERZ4_09825.isCall()); final double logmoney = Math.log(rateStrike / rateFutures); final double expiry = CALL_ERZ4_09825.getExpirationTime(); final double volatility = BLACK_SURFACE_LOGMONEY.getZValue(expiry, logmoney); final BlackFunctionData dataBlack = new BlackFunctionData(rateFutures, 1.0, volatility); final double[] firstDerivs = new double[3]; final double[][] secondDerivs = new double[3][3]; BLACK_FUNCTION.getPriceAdjoint2(option, dataBlack, firstDerivs, secondDerivs); final double gammaCallExpected = secondDerivs[0][0]; final double gammaCallComputed = METHOD_OPT.gammaUnderlyingPrice(CALL_ERZ4_09825, MULTICURVE_BLACK); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: gamma", gammaCallExpected, gammaCallComputed, TOLERANCE_DELTA); assertTrue("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: gamma", (0.0d < gammaCallComputed)); } public void theoreticalVega() { final double priceFutures = METHOD_FUTURE.price(CALL_ERZ4_09825.getUnderlyingFuture(), MULTICURVE); final double rateFutures = 1.0d - priceFutures; final double rateStrike = 1.0d - STRIKE_09825; final EuropeanVanillaOption option = new EuropeanVanillaOption(rateStrike, CALL_ERZ4_09825.getExpirationTime(), !CALL_ERZ4_09825.isCall()); final double logmoney = Math.log(rateStrike / rateFutures); final double expiry = CALL_ERZ4_09825.getExpirationTime(); final double volatility = BLACK_SURFACE_LOGMONEY.getZValue(expiry, logmoney); final BlackFunctionData dataBlack = new BlackFunctionData(rateFutures, 1.0, volatility); final double[] priceAD = BLACK_FUNCTION.getPriceAdjoint(option, dataBlack); final double vegaCallExpected = priceAD[2]; final double vegaCallComputed = METHOD_OPT.vegaUnderlyingPrice(CALL_ERZ4_09825, MULTICURVE_BLACK); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: vega", vegaCallExpected, vegaCallComputed, TOLERANCE_DELTA); assertTrue("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: vega", 0.0d < vegaCallComputed); } public void theoreticalTheta() { final double priceFutures = METHOD_FUTURE.price(CALL_ERZ4_09825.getUnderlyingFuture(), MULTICURVE); final double rateFutures = 1.0d - priceFutures; final double rateStrike = 1.0d - STRIKE_09825; final double logmoney = Math.log(rateStrike / rateFutures); final double expiry = CALL_ERZ4_09825.getExpirationTime(); final double volatility = BLACK_SURFACE_LOGMONEY.getZValue(expiry, logmoney); final double rate = -Math.log(MULTICURVE.getMulticurveProvider().getDiscountFactor(CALL_ERZ4_09825.getCurrency(), CALL_ERZ4_09825.getExpirationTime())) / CALL_ERZ4_09825.getExpirationTime(); final double thetaCallExpected = BlackFormulaRepository.theta(rateFutures, rateStrike, CALL_ERZ4_09825.getExpirationTime(), volatility, !CALL_ERZ4_09825.isCall(), rate); final double thetaCallComputed = METHOD_OPT.thetaUnderlyingPrice(CALL_ERZ4_09825, MULTICURVE_BLACK); assertEquals("STIRFuturesOptionMarginSecurityBlackExpLogMoneynessMethod: theta", thetaCallExpected, thetaCallComputed, TOLERANCE_DELTA); } }