/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model.volatility.surface.black.pure; import static com.opengamma.engine.value.SurfaceAndCubePropertyNames.PRICE_QUOTE; import static com.opengamma.engine.value.SurfaceAndCubePropertyNames.PROPERTY_SURFACE_QUOTE_TYPE; import static com.opengamma.engine.value.SurfaceAndCubePropertyNames.PROPERTY_SURFACE_UNITS; import static com.opengamma.engine.value.ValuePropertyNames.CURVE; import static com.opengamma.engine.value.ValuePropertyNames.CURVE_CALCULATION_CONFIG; import static com.opengamma.engine.value.ValuePropertyNames.CURVE_CURRENCY; import static com.opengamma.engine.value.ValuePropertyNames.SURFACE; import static com.opengamma.financial.analytics.model.InstrumentTypeProperties.EQUITY_OPTION; import static com.opengamma.financial.analytics.model.InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE; import static com.opengamma.financial.analytics.volatility.surface.SurfaceAndCubeQuoteType.CALL_AND_PUT_STRIKE; import java.util.Collections; import java.util.Set; import org.threeten.bp.LocalDate; import org.threeten.bp.ZonedDateTime; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.equity.variance.pricing.AffineDividends; import com.opengamma.analytics.financial.equity.variance.pricing.EquityVolatilityToPureVolatilitySurfaceConverter; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.volatility.surface.PureImpliedVolatilitySurface; import com.opengamma.analytics.financial.model.volatility.surface.VolatilitySurfaceInterpolator; import com.opengamma.core.marketdatasnapshot.VolatilitySurfaceData; import com.opengamma.core.value.MarketDataRequirementNames; import com.opengamma.engine.ComputationTarget; import com.opengamma.engine.ComputationTargetSpecification; import com.opengamma.engine.function.AbstractFunction; import com.opengamma.engine.function.FunctionCompilationContext; import com.opengamma.engine.function.FunctionExecutionContext; import com.opengamma.engine.function.FunctionInputs; import com.opengamma.engine.target.ComputationTargetType; import com.opengamma.engine.value.ComputedValue; import com.opengamma.engine.value.ValueProperties; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueRequirementNames; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.financial.analytics.model.FutureOptionExpiries; import com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyUtils; import com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfaceUtils; import com.opengamma.financial.analytics.volatility.surface.FunctionalVolatilitySurfaceData; import com.opengamma.util.async.AsynchronousExecution; import com.opengamma.util.money.Currency; import com.opengamma.util.tuple.Triple; /** * */ public abstract class PureBlackVolatilitySurfaceFunction extends AbstractFunction.NonCompiledInvoker { /** Property describing the treatment of dividends */ public static final String PROPERTY_DIVIDEND_TREATMENT = "DividendTreatment"; private static final String X_LABEL = "Expiry (years)"; private static final String Y_LABEL = "Moneyness"; @Override public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) throws AsynchronousExecution { final LocalDate date = ZonedDateTime.now(executionContext.getValuationClock()).toLocalDate(); final ValueRequirement desiredValue = Iterables.getOnlyElement(desiredValues); final Object spotObject = inputs.getValue(MarketDataRequirementNames.MARKET_VALUE); if (spotObject == null) { throw new OpenGammaRuntimeException("Could not get spot value"); } final Object curveObject = inputs.getValue(ValueRequirementNames.YIELD_CURVE); if (curveObject == null) { throw new OpenGammaRuntimeException("Could not get yield curve"); } final Object volatilitySurfaceObject = inputs.getValue(ValueRequirementNames.VOLATILITY_SURFACE_DATA); if (volatilitySurfaceObject == null) { throw new OpenGammaRuntimeException("Could not get volatility surface"); } final Object interpolatorObject = inputs.getValue(ValueRequirementNames.BLACK_VOLATILITY_SURFACE_INTERPOLATOR); if (interpolatorObject == null) { throw new OpenGammaRuntimeException("Could not get surface interpolator"); } final double spot = (Double) spotObject; final YieldAndDiscountCurve curve = (YieldAndDiscountCurve) curveObject; final AffineDividends dividends = getDividends(inputs); @SuppressWarnings("unchecked") final VolatilitySurfaceData<Object, Object> volatilitySurfaceData = (VolatilitySurfaceData<Object, Object>) volatilitySurfaceObject; final Triple<double[], double[][], double[][]> strikesAndValues = BlackVolatilitySurfaceUtils.getStrippedStrikesAndValues(volatilitySurfaceData); final double[] expiryNumber = strikesAndValues.getFirst(); final double[] expiries = getExpiries(expiryNumber, date); final double[][] strikes = strikesAndValues.getSecond(); final double[][] prices = strikesAndValues.getThird(); final VolatilitySurfaceInterpolator surfaceInterpolator = (VolatilitySurfaceInterpolator) interpolatorObject; final PureImpliedVolatilitySurface pureSurface = EquityVolatilityToPureVolatilitySurfaceConverter.getConvertedSurface(spot, curve, dividends, expiries, strikes, prices, surfaceInterpolator); final FunctionalVolatilitySurfaceData surfaceData = new FunctionalVolatilitySurfaceData(pureSurface, X_LABEL, expiries[0], expiries[expiries.length - 1], 25, Y_LABEL, 0.25, 1.75, 50, 0, 0.6); final ValueProperties properties = getResultProperties(desiredValue); return Collections.singleton(new ComputedValue(new ValueSpecification(ValueRequirementNames.PURE_VOLATILITY_SURFACE, target.toSpecification(), properties), surfaceData)); } @Override public ComputationTargetType getTargetType() { return ComputationTargetType.PRIMITIVE; } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) { final ValueProperties properties = getResultProperties(); return Collections.singleton(new ValueSpecification(ValueRequirementNames.PURE_VOLATILITY_SURFACE, target.toSpecification(), properties)); } @Override public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) { final ValueProperties constraints = desiredValue.getConstraints(); final Set<String> curveNames = constraints.getValues(CURVE); if (curveNames == null || curveNames.size() != 1) { return null; } final Set<String> curveCalculationConfigs = constraints.getValues(CURVE_CALCULATION_CONFIG); if (curveCalculationConfigs == null || curveCalculationConfigs.size() != 1) { return null; } final Set<String> surfaceNames = constraints.getValues(SURFACE); if (surfaceNames == null || surfaceNames.size() != 1) { return null; } final Set<String> currencies = constraints.getValues(CURVE_CURRENCY); if (currencies == null || currencies.size() != 1) { return null; } final String surfaceName = Iterables.getOnlyElement(surfaceNames) + "_PRICE"; final ComputationTargetSpecification targetSpec = target.toSpecification(); final Currency currency = Currency.of(Iterables.getOnlyElement(currencies)); final ValueRequirement curveRequirement = getCurveRequirement(currency, desiredValue); final ValueRequirement volatilitySurfaceRequirement = getVolatilityDataRequirement(targetSpec, surfaceName); final ValueRequirement spotRequirement = new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, targetSpec); final ValueRequirement interpolatorRequirement = getInterpolatorRequirement(targetSpec, desiredValue); return Sets.newHashSet(curveRequirement, volatilitySurfaceRequirement, spotRequirement, interpolatorRequirement); } protected abstract ValueProperties getResultProperties(); protected abstract ValueProperties getResultProperties(final ValueRequirement desiredValue); protected abstract AffineDividends getDividends(FunctionInputs inputs); private ValueRequirement getVolatilityDataRequirement(final ComputationTargetSpecification target, final String surfaceName) { final ValueProperties properties = ValueProperties.builder() .with(SURFACE, surfaceName) .with(PROPERTY_SURFACE_INSTRUMENT_TYPE, EQUITY_OPTION) .with(PROPERTY_SURFACE_QUOTE_TYPE, CALL_AND_PUT_STRIKE) .with(PROPERTY_SURFACE_UNITS, PRICE_QUOTE).get(); return new ValueRequirement(ValueRequirementNames.VOLATILITY_SURFACE_DATA, target, properties); } private ValueRequirement getCurveRequirement(final Currency currency, final ValueRequirement desiredValue) { final String discountingCurve = desiredValue.getConstraint(CURVE); final String curveCalculationConfig = desiredValue.getConstraint(CURVE_CALCULATION_CONFIG); final ValueProperties properties = ValueProperties.builder() .with(CURVE, discountingCurve) .with(CURVE_CALCULATION_CONFIG, curveCalculationConfig).get(); return new ValueRequirement(ValueRequirementNames.YIELD_CURVE, ComputationTargetType.CURRENCY.specification(currency), properties); } private ValueRequirement getInterpolatorRequirement(final ComputationTargetSpecification target, final ValueRequirement desiredValue) { final ValueProperties properties = BlackVolatilitySurfacePropertyUtils.addVolatilityInterpolatorProperties(ValueProperties.builder().get(), desiredValue).get(); return new ValueRequirement(ValueRequirementNames.BLACK_VOLATILITY_SURFACE_INTERPOLATOR, target, properties); } private double[] getExpiries(final double[] expiryNumber, final LocalDate date) { final int n = expiryNumber.length; final double[] expiries = new double[n]; for (int i = 0; i < n; i++) { expiries[i] = FutureOptionExpiries.EQUITY.getFutureOptionTtm((int) expiryNumber[i], date); } return expiries; } }