/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.equity.option; import static com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory.getInterpolator; import static org.testng.AssertJUnit.assertEquals; import java.util.ArrayList; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.ExerciseDecisionType; import com.opengamma.analytics.financial.equity.EquityOptionBlackPresentValueCalculator; import com.opengamma.analytics.financial.equity.EquityOptionBlackScholesRhoCalculator; import com.opengamma.analytics.financial.equity.EquityOptionBlackScholesThetaCalculator; import com.opengamma.analytics.financial.equity.EquityOptionBlackSpotDeltaCalculator; import com.opengamma.analytics.financial.equity.EquityOptionBlackSpotGammaCalculator; import com.opengamma.analytics.financial.equity.EquityOptionBlackVegaCalculator; import com.opengamma.analytics.financial.equity.EqyOptBjerksundStenslandGreekCalculator; import com.opengamma.analytics.financial.equity.EqyOptBjerksundStenslandPresentValueCalculator; import com.opengamma.analytics.financial.equity.StaticReplicationDataBundle; import com.opengamma.analytics.financial.greeks.Greek; import com.opengamma.analytics.financial.greeks.GreekResultCollection; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.model.option.pricing.analytic.BjerksundStenslandModel; import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository; import com.opengamma.analytics.financial.model.volatility.surface.BlackVolatilitySurfaceStrike; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolator; 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.surface.InterpolatedDoublesSurface; import com.opengamma.analytics.util.time.TimeCalculator; import com.opengamma.financial.convention.daycount.DayCounts; /** * */ @SuppressWarnings("javadoc") public class EquityE2ETestMaster { // Calculators for European options protected static final EquityOptionBlackPresentValueCalculator PV_EUROPEAN = EquityOptionBlackPresentValueCalculator .getInstance(); protected static final EquityOptionBlackScholesRhoCalculator RHO_EUROPEAN = EquityOptionBlackScholesRhoCalculator .getInstance(); protected static final EquityOptionBlackSpotDeltaCalculator DELTA_EUROPEAN = EquityOptionBlackSpotDeltaCalculator .getInstance(); protected static final EquityOptionBlackSpotGammaCalculator GAMMA_EUROPEAN = EquityOptionBlackSpotGammaCalculator .getInstance(); protected static final EquityOptionBlackScholesThetaCalculator THETA_EUROPEAN = EquityOptionBlackScholesThetaCalculator .getInstance(); protected static final EquityOptionBlackVegaCalculator VEGA_EUROPEAN = EquityOptionBlackVegaCalculator.getInstance(); // Calculators for American options protected static final EqyOptBjerksundStenslandPresentValueCalculator PV_AMERICAN = EqyOptBjerksundStenslandPresentValueCalculator .getInstance(); protected static final EqyOptBjerksundStenslandGreekCalculator GREEKS_AMERICAN = EqyOptBjerksundStenslandGreekCalculator .getInstance(); // yield curve private static final double[] SINGLE_CURVE_TIME = new double[] {0.002739726, 0.093150685, 0.257534247, 0.515068493, 1.005479452, 2.009416873, 3.005479452, 4.005479452, 5.005479452, 6.006684632, 7.010958904, 8.008219178, 9.005479452, 10.00668463, 12.00547945, 15.00547945, 20.00547945 }; private static final double[] SINGLE_CURVE_RATE = new double[] {0.001774301, 0.000980829, 0.000940143, 0.001061566, 0.001767578, 0.005373189, 0.009795971, 0.013499667, 0.016397755, 0.018647803, 0.020528999, 0.022002859, 0.023322553, 0.024538027, 0.026482704, 0.028498622, 0.030369559 }; private static final String SINGLE_CURVE_NAME = "Single Curve"; private static final Interpolator1D YIELD_INTERPOLATOR = CombinedInterpolatorExtrapolatorFactory.getInterpolator( Interpolator1DFactory.DOUBLE_QUADRATIC, Interpolator1DFactory.LINEAR_EXTRAPOLATOR, Interpolator1DFactory.LINEAR_EXTRAPOLATOR); private static final InterpolatedDoublesCurve INTERPOLATED_CURVE = InterpolatedDoublesCurve.from(SINGLE_CURVE_TIME, SINGLE_CURVE_RATE, YIELD_INTERPOLATOR); protected static final YieldAndDiscountCurve SINGLE_CURVE = new YieldCurve(SINGLE_CURVE_NAME, INTERPOLATED_CURVE); // tools for vol surface private static final BjerksundStenslandModel AMERICAN_MODEL = new BjerksundStenslandModel(); private final static CombinedInterpolatorExtrapolator EXPIRY_INTERPOLATOR = getInterpolator( Interpolator1DFactory.DOUBLE_QUADRATIC, Interpolator1DFactory.LINEAR_EXTRAPOLATOR, Interpolator1DFactory.LINEAR_EXTRAPOLATOR); private static final CombinedInterpolatorExtrapolator STRIKE_INTERPOLATOR = getInterpolator( Interpolator1DFactory.DOUBLE_QUADRATIC, Interpolator1DFactory.LINEAR_EXTRAPOLATOR, Interpolator1DFactory.LINEAR_EXTRAPOLATOR); protected static final String[] COMPUTE_VALUES = new String[] {"pv per contract", "pv", "delta", "gamma", "theta", "rho", "vega", "position delta", "position gamma", "position theta", "position rho", "position vega" }; protected static final double TOL = 1.0e-10; /** * @param spot The underlying spot * @param timeToExpiries The time to expiry * @param strikes The market strikes * @param marketPrices The market prices * @param isCalls True if call * @param forwardCurve The forward curve * @param isAmerican True if American * @return BlackVolatilitySurfaceStrike */ protected BlackVolatilitySurfaceStrike createSurface(double spot, double[] timeToExpiries, double[][] strikes, double[][] marketPrices, boolean[][] isCalls, ForwardCurve forwardCurve, ExerciseDecisionType exerciseType) { int nExpiry = timeToExpiries.length; ArrayList<Double> expiryList = new ArrayList<>(); ArrayList<Double> strikeList = new ArrayList<>(); ArrayList<Double> impliedVolList = new ArrayList<>(); for (int i = 0; i < nExpiry; ++i) { double expiry = timeToExpiries[i]; int nStrikes = strikes[i].length; for (int j = 0; j < nStrikes; ++j) { if (!Double.isNaN(marketPrices[i][j])) { double strike = strikes[i][j]; expiryList.add(expiry); strikeList.add(strike); double interestRate = SINGLE_CURVE.getInterestRate(expiry); double costOfCarry = Math.log(forwardCurve.getForward(expiry) / spot) / expiry; boolean isCall = isCalls[i][j]; double impliedVol; if (exerciseType == ExerciseDecisionType.AMERICAN) { impliedVol = AMERICAN_MODEL.impliedVolatility(marketPrices[i][j], spot, strike, interestRate, costOfCarry, expiry, isCall); } else { double df = SINGLE_CURVE.getDiscountFactor(expiry); double fwdPrice = marketPrices[i][j] / df; double fwd = forwardCurve.getForward(expiry); impliedVol = BlackFormulaRepository.impliedVolatility(fwdPrice, fwd, strike, expiry, isCall); } impliedVolList.add(impliedVol); } } } InterpolatedDoublesSurface surface = new InterpolatedDoublesSurface(expiryList, strikeList, impliedVolList, new GridInterpolator2D(EXPIRY_INTERPOLATOR, STRIKE_INTERPOLATOR)); return new BlackVolatilitySurfaceStrike(surface); } protected double[] toDateToDouble(ZonedDateTime baseDate, ZonedDateTime[] targetDates) { int nDates = targetDates.length; double[] res = new double[nDates]; for (int i = 0; i < nDates; ++i) { res[i] = TimeCalculator.getTimeBetween(baseDate, targetDates[i], DayCounts.ACT_365); } return res; } protected void assertOptionResult(InstrumentDerivative targetInstrument, double notional, StaticReplicationDataBundle data, double[] expected) { double unitAmount; ExerciseDecisionType exerciseType; if (targetInstrument instanceof EquityOption) { EquityOption targetOption = (EquityOption) targetInstrument; unitAmount = targetOption.getUnitAmount(); exerciseType = targetOption.getExerciseType(); } else if (targetInstrument instanceof EquityIndexOption) { EquityIndexOption targetOption = (EquityIndexOption) targetInstrument; unitAmount = targetOption.getUnitAmount(); exerciseType = targetOption.getExerciseType(); } else { throw new IllegalArgumentException("instrument should be EquityOption or EquityIndexOption"); } Double pvPerContract; Double pv; Double delta; Double gamma; Double theta; Double rho; Double vega; if (exerciseType == ExerciseDecisionType.AMERICAN) { pvPerContract = targetInstrument.accept(PV_AMERICAN, data); pv = targetInstrument.accept(PV_AMERICAN, data) * notional; GreekResultCollection greeks = targetInstrument.accept(GREEKS_AMERICAN, data); delta = greeks.get(Greek.DELTA); gamma = greeks.get(Greek.GAMMA); theta = greeks.get(Greek.THETA); rho = greeks.get(Greek.RHO); vega = greeks.get(Greek.VEGA); } else { pvPerContract = targetInstrument.accept(PV_EUROPEAN, data); pv = targetInstrument.accept(PV_EUROPEAN, data) * notional; delta = targetInstrument.accept(DELTA_EUROPEAN, data); gamma = targetInstrument.accept(GAMMA_EUROPEAN, data); theta = targetInstrument.accept(THETA_EUROPEAN, data); rho = targetInstrument.accept(RHO_EUROPEAN, data); vega = targetInstrument.accept(VEGA_EUROPEAN, data); } double positionDelta = delta * unitAmount * notional; double positionGamma = gamma * unitAmount * notional; double positionTheta = theta * unitAmount * notional; double positionRho = rho * unitAmount * notional; double positionVega = vega * unitAmount * notional; double[] res = new double[] {pvPerContract, pv, delta, gamma, theta, rho, vega, positionDelta, positionGamma, positionTheta, positionRho, positionVega }; assertEqualsArray(COMPUTE_VALUES, expected, res, TOL); } private void assertEqualsArray(String[] messages, double[] expected, double[] result, double relativeTol) { int nData = messages.length; for (int i = 0; i < nData; ++i) { assertEqualsRelative(messages[i], expected[i], result[i], relativeTol); } } private void assertEqualsRelative(String message, double expected, double result, double relativeTol) { double tol = Math.max(1.0, Math.abs(expected)) * relativeTol; assertEquals(message, expected, result, tol); } }