/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model.forex.option.vannavolga; import static com.opengamma.financial.analytics.model.YieldCurveFunctionUtils.getCurveRequirement; import static com.opengamma.financial.analytics.model.forex.option.black.FXOptionBlackFunction.CALL_CURVE; import static com.opengamma.financial.analytics.model.forex.option.black.FXOptionBlackFunction.CALL_CURVE_CALC_CONFIG; import static com.opengamma.financial.analytics.model.forex.option.black.FXOptionBlackFunction.PUT_CURVE; import static com.opengamma.financial.analytics.model.forex.option.black.FXOptionBlackFunction.PUT_CURVE_CALC_CONFIG; import static com.opengamma.financial.analytics.model.forex.option.black.FXOptionFunctionUtils.getCurveForCurrency; import static com.opengamma.financial.analytics.model.forex.option.black.FXOptionFunctionUtils.getSurfaceRequirement; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.forex.method.FXMatrix; import com.opengamma.analytics.financial.instrument.InstrumentDefinition; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; import com.opengamma.analytics.financial.interestrate.YieldCurveBundle; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.model.option.definition.SmileDeltaParameters; import com.opengamma.analytics.financial.model.option.definition.SmileDeltaTermStructureVannaVolgaDataBundle; import com.opengamma.analytics.financial.model.volatility.surface.SmileDeltaTermStructureParameters; import com.opengamma.analytics.math.interpolation.Interpolator1D; 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.FunctionInputs; import com.opengamma.engine.target.ComputationTargetType; import com.opengamma.engine.value.ValueProperties; import com.opengamma.engine.value.ValuePropertyNames; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueRequirementNames; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.financial.analytics.conversion.ForexSecurityConverter; import com.opengamma.financial.analytics.model.InterpolatedDataProperties; import com.opengamma.financial.analytics.model.forex.ConventionBasedFXRateFunction; import com.opengamma.financial.analytics.model.forex.ForexVisitors; import com.opengamma.financial.currency.CurrencyPair; import com.opengamma.financial.currency.CurrencyPairs; import com.opengamma.financial.security.FinancialSecurity; import com.opengamma.financial.security.FinancialSecurityTypes; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; import com.opengamma.util.money.UnorderedCurrencyPair; import com.opengamma.util.tuple.Pair; import com.opengamma.util.tuple.Pairs; /** * */ public abstract class FXOptionVannaVolgaFunction extends AbstractFunction.NonCompiledInvoker { /** Property name for the delta to use */ public static final String PROPERTY_OTM_DELTA = "DeltaOTM"; /** The name of the calculation method */ public static final String VANNA_VOLGA_METHOD = "VannaVolgaMethod"; private static final Logger s_logger = LoggerFactory.getLogger(FXOptionVannaVolgaFunction.class); private final String _valueRequirementName; public FXOptionVannaVolgaFunction(final String valueRequirementName) { ArgumentChecker.notNull(valueRequirementName, "value requirement name"); _valueRequirementName = valueRequirementName; } @Override public ComputationTargetType getTargetType() { return FinancialSecurityTypes.FX_OPTION_SECURITY; } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) { final ValueProperties properties = getResultProperties(target); return Collections.singleton(new ValueSpecification(_valueRequirementName, 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> interpolatorNames = constraints.getValues(InterpolatedDataProperties.X_INTERPOLATOR_NAME); if (interpolatorNames == null || interpolatorNames.size() != 1) { return null; } final Set<String> leftExtrapolatorNames = constraints.getValues(InterpolatedDataProperties.LEFT_X_EXTRAPOLATOR_NAME); if (leftExtrapolatorNames == null || leftExtrapolatorNames.size() != 1) { return null; } final Set<String> rightExtrapolatorNames = constraints.getValues(InterpolatedDataProperties.RIGHT_X_EXTRAPOLATOR_NAME); if (rightExtrapolatorNames == null || rightExtrapolatorNames.size() != 1) { return null; } final Set<String> putCurveNames = constraints.getValues(PUT_CURVE); if (putCurveNames == null || putCurveNames.size() != 1) { return null; } final Set<String> callCurveNames = constraints.getValues(CALL_CURVE); if (callCurveNames == null || callCurveNames.size() != 1) { return null; } final Set<String> putCurveCalculationConfigs = constraints.getValues(PUT_CURVE_CALC_CONFIG); if (putCurveCalculationConfigs == null || putCurveCalculationConfigs.size() != 1) { return null; } final Set<String> callCurveCalculationConfigs = constraints.getValues(CALL_CURVE_CALC_CONFIG); if (callCurveCalculationConfigs == null || callCurveCalculationConfigs.size() != 1) { return null; } final Set<String> surfaceNames = constraints.getValues(ValuePropertyNames.SURFACE); if (surfaceNames == null || surfaceNames.size() != 1) { s_logger.error("Need one surface name; have " + surfaceNames); } final Set<String> deltas = constraints.getValues(PROPERTY_OTM_DELTA); if (deltas == null || deltas.size() != 1) { return null; } final String surfaceName = Iterables.getOnlyElement(surfaceNames); final String interpolatorName = Iterables.getOnlyElement(interpolatorNames); final String leftExtrapolatorName = Iterables.getOnlyElement(leftExtrapolatorNames); final String rightExtrapolatorName = Iterables.getOnlyElement(rightExtrapolatorNames); final String putCurveName = Iterables.getOnlyElement(putCurveNames); final String putCurveCalculationConfig = Iterables.getOnlyElement(putCurveCalculationConfigs); final String callCurveName = Iterables.getOnlyElement(callCurveNames); final String callCurveCalculationConfig = Iterables.getOnlyElement(callCurveCalculationConfigs); final FinancialSecurity security = (FinancialSecurity) target.getSecurity(); final Currency putCurrency = security.accept(ForexVisitors.getPutCurrencyVisitor()); final Currency callCurrency = security.accept(ForexVisitors.getCallCurrencyVisitor()); final ValueRequirement putFundingCurve = getCurveRequirement(ComputationTargetSpecification.of(putCurrency), putCurveName, putCurveCalculationConfig); final ValueRequirement callFundingCurve = getCurveRequirement(ComputationTargetSpecification.of(callCurrency), callCurveName, callCurveCalculationConfig); final ValueRequirement fxVolatilitySurface = getSurfaceRequirement(surfaceName, putCurrency, callCurrency, interpolatorName, leftExtrapolatorName, rightExtrapolatorName); final UnorderedCurrencyPair currencyPair = UnorderedCurrencyPair.of(putCurrency, callCurrency); final ValueRequirement spotRequirement = ConventionBasedFXRateFunction.getSpotRateRequirement(currencyPair); final ValueRequirement pairQuoteRequirement = new ValueRequirement(ValueRequirementNames.CURRENCY_PAIRS, ComputationTargetType.PRIMITIVE, currencyPair.getUniqueId()); return Sets.newHashSet(putFundingCurve, callFundingCurve, fxVolatilitySurface, spotRequirement, pairQuoteRequirement); } protected abstract ValueProperties getResultProperties(ComputationTarget target); protected abstract ValueProperties getResultProperties(ComputationTarget target, ValueRequirement desiredValue, CurrencyPair baseQuotePair); protected ValueSpecification getSpecification(final ComputationTarget target, final ValueRequirement desiredValue, final CurrencyPair baseQuotePair) { final ValueProperties properties = getResultProperties(target, desiredValue, baseQuotePair); return new ValueSpecification(_valueRequirementName, target.toSpecification(), properties); } protected String[] getCurveNames(final Currency putCurrency, final String putCurveName, final Currency callCurrency, final String callCurveName, final CurrencyPairs baseQuotePairs) { final CurrencyPair baseQuotePair = baseQuotePairs.getCurrencyPair(putCurrency, callCurrency); final String fullPutCurveName = putCurveName + "_" + putCurrency.getCode(); final String fullCallCurveName = callCurveName + "_" + callCurrency.getCode(); if (baseQuotePair.getBase().equals(putCurrency)) { // To get Base/quote in market standard order. return new String[] {fullPutCurveName, fullCallCurveName }; } return new String[] {fullCallCurveName, fullPutCurveName }; } protected InstrumentDerivative getDerivative(final FinancialSecurity security, final String[] allCurveNames, final CurrencyPairs baseQuotePairs, final ZonedDateTime now) { final InstrumentDefinition<?> definition = security.accept(new ForexSecurityConverter(baseQuotePairs)); return definition.toDerivative(now); } protected SmileDeltaTermStructureVannaVolgaDataBundle getSmiles(final Currency putCurrency, final Currency callCurrency, final String[] allCurveNames, final CurrencyPairs baseQuotePairs, final String deltaName, final FunctionInputs inputs) { final double delta = Double.parseDouble(deltaName); if (Double.compare(delta, 0.5) == 0) { throw new OpenGammaRuntimeException("Asking for OTM smile at delta = 50; this is the ATM value"); } final Object volatilitySurfaceObject = inputs.getValue(ValueRequirementNames.STANDARD_VOLATILITY_SURFACE_DATA); if (volatilitySurfaceObject == null) { throw new OpenGammaRuntimeException("Could not get volatility surface data"); } final SmileDeltaTermStructureParameters allSmiles = (SmileDeltaTermStructureParameters) volatilitySurfaceObject; final double[] uniqueDeltas = allSmiles.getDelta(); final int putDeltaIndex = Arrays.binarySearch(uniqueDeltas, delta); if (putDeltaIndex < 0) { throw new OpenGammaRuntimeException("Could not get OTM smile for delta = " + delta); } final SmileDeltaParameters[] surface = new SmileDeltaParameters[allSmiles.getNumberExpiration()]; final int atmIndex = (allSmiles.getNumberStrike() - 1) / 2; final double[] deltas = {delta }; for (int i = 0; i < allSmiles.getNumberExpiration(); i++) { final SmileDeltaParameters parameters = allSmiles.getSmileForTime(i); final double timeToExpiration = parameters.getTimeToExpiry(); final double[] volatilities = new double[3]; final double[] allVolatilities = parameters.getVolatility(); volatilities[0] = allVolatilities[putDeltaIndex]; volatilities[1] = allVolatilities[atmIndex]; volatilities[2] = allVolatilities[2 * atmIndex - putDeltaIndex]; surface[i] = new SmileDeltaParameters(timeToExpiration, deltas, volatilities); } final Interpolator1D interpolator = allSmiles.getTimeInterpolator(); final SmileDeltaTermStructureParameters resultSmiles = new SmileDeltaTermStructureParameters(surface, interpolator); final Currency ccy1; final Currency ccy2; final Object spotObject = inputs.getValue(ValueRequirementNames.SPOT_RATE); if (spotObject == null) { throw new OpenGammaRuntimeException("Could not get spot requirement"); } final double spot = (Double) spotObject; final CurrencyPair baseQuotePair = baseQuotePairs.getCurrencyPair(putCurrency, callCurrency); if (baseQuotePair == null) { throw new OpenGammaRuntimeException("Could not get base/quote pair for currency pair (" + putCurrency + ", " + callCurrency + ")"); } YieldAndDiscountCurve[] curves; final YieldAndDiscountCurve putFundingCurve = getCurveForCurrency(inputs, putCurrency); final YieldAndDiscountCurve callFundingCurve = getCurveForCurrency(inputs, callCurrency); final Map<String, Currency> curveCurrency = new HashMap<String, Currency>(); if (baseQuotePair.getBase().equals(putCurrency)) { // To get Base/quote in market standard order. ccy1 = putCurrency; ccy2 = callCurrency; curves = new YieldAndDiscountCurve[] {putFundingCurve, callFundingCurve }; curveCurrency.put(allCurveNames[0], putCurrency); curveCurrency.put(allCurveNames[1], callCurrency); } else { curves = new YieldAndDiscountCurve[] {callFundingCurve, putFundingCurve }; ccy1 = callCurrency; ccy2 = putCurrency; curveCurrency.put(allCurveNames[1], putCurrency); curveCurrency.put(allCurveNames[0], callCurrency); } final YieldCurveBundle yieldCurves = new YieldCurveBundle(allCurveNames, curves); final FXMatrix fxMatrix = new FXMatrix(ccy1, ccy2, spot); final YieldCurveBundle curvesWithFX = new YieldCurveBundle(fxMatrix, curveCurrency, yieldCurves.getCurvesMap()); final Pair<Currency, Currency> ccyPair = Pairs.of(ccy1, ccy2); return new SmileDeltaTermStructureVannaVolgaDataBundle(curvesWithFX, resultSmiles, ccyPair); } protected String getValueRequirementName() { return _valueRequirementName; } }