/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.forex.option.black;
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 java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.threeten.bp.Clock;
import org.threeten.bp.ZonedDateTime;
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.YieldCurveWithBlackForexTermStructureBundle;
import com.opengamma.analytics.financial.model.volatility.curve.BlackForexTermStructureParameters;
import com.opengamma.analytics.financial.model.volatility.surface.SmileDeltaTermStructureParametersStrikeInterpolation;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory;
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.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.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.InstrumentTypeProperties;
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.ObjectsPair;
import com.opengamma.util.tuple.Pair;
/**
* @deprecated This function uses deprecated functions from the analytics library.
*/
@Deprecated
public abstract class FXOptionBlackTermStructureFunction extends AbstractFunction.NonCompiledInvoker {
/** The name of the calculation method */
public static final String BLACK_TERM_STRUCTURE_METHOD = "BlackTermStructure";
private final String _valueRequirementName;
public FXOptionBlackTermStructureFunction(final String valueRequirementName) {
ArgumentChecker.notNull(valueRequirementName, "value requirement name");
_valueRequirementName = valueRequirementName;
}
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
final Clock snapshotClock = executionContext.getValuationClock();
final ZonedDateTime now = ZonedDateTime.now(snapshotClock);
final FinancialSecurity security = (FinancialSecurity) target.getSecurity();
final Currency putCurrency = security.accept(ForexVisitors.getPutCurrencyVisitor());
final Currency callCurrency = security.accept(ForexVisitors.getCallCurrencyVisitor());
final ValueRequirement desiredValue = desiredValues.iterator().next();
final String putCurveName = desiredValue.getConstraint(PUT_CURVE);
final String callCurveName = desiredValue.getConstraint(CALL_CURVE);
final String surfaceName = desiredValue.getConstraint(ValuePropertyNames.SURFACE);
final String putCurveConfig = desiredValue.getConstraint(PUT_CURVE_CALC_CONFIG);
final String callCurveConfig = desiredValue.getConstraint(CALL_CURVE_CALC_CONFIG);
final String interpolatorName = desiredValue.getConstraint(InterpolatedDataProperties.X_INTERPOLATOR_NAME);
final String leftExtrapolatorName = desiredValue.getConstraint(InterpolatedDataProperties.LEFT_X_EXTRAPOLATOR_NAME);
final String rightExtrapolatorName = desiredValue.getConstraint(InterpolatedDataProperties.RIGHT_X_EXTRAPOLATOR_NAME);
final Object baseQuotePairsObject = inputs.getValue(ValueRequirementNames.CURRENCY_PAIRS);
if (baseQuotePairsObject == null) {
throw new OpenGammaRuntimeException("Could not get base/quote pair data");
}
final CurrencyPairs baseQuotePairs = (CurrencyPairs) baseQuotePairsObject;
final String fullPutCurveName = putCurveName + "_" + putCurrency.getCode();
final String fullCallCurveName = callCurveName + "_" + callCurrency.getCode();
final YieldAndDiscountCurve putFundingCurve = getCurve(inputs, putCurrency, putCurveName, putCurveConfig);
final YieldAndDiscountCurve callFundingCurve = getCurve(inputs, callCurrency, callCurveName, callCurveConfig);
final YieldAndDiscountCurve[] curves;
final Map<String, Currency> curveCurrency = new HashMap<>();
curveCurrency.put(fullPutCurveName, putCurrency);
curveCurrency.put(fullCallCurveName, callCurrency);
final InstrumentDefinition<?> definition = security.accept(new ForexSecurityConverter(baseQuotePairs));
final String[] allCurveNames;
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 + ")");
}
if (baseQuotePair.getBase().equals(putCurrency)) { // To get Base/quote in market standard order.
ccy1 = putCurrency;
ccy2 = callCurrency;
curves = new YieldAndDiscountCurve[] {putFundingCurve, callFundingCurve };
allCurveNames = new String[] {fullPutCurveName, fullCallCurveName };
spot = (Double) spotObject;
} else {
curves = new YieldAndDiscountCurve[] {callFundingCurve, putFundingCurve };
allCurveNames = new String[] {fullCallCurveName, fullPutCurveName };
ccy1 = callCurrency;
ccy2 = putCurrency;
spot = 1. / (Double) spotObject;
}
final InstrumentDerivative fxOption = definition.toDerivative(now, allCurveNames);
final YieldCurveBundle yieldCurves = new YieldCurveBundle(allCurveNames, curves);
final Interpolator1D interpolator = CombinedInterpolatorExtrapolatorFactory.getInterpolator(interpolatorName, leftExtrapolatorName, rightExtrapolatorName);
final ValueRequirement fxVolatilitySurfaceRequirement = getSurfaceRequirement(surfaceName, putCurrency, callCurrency, interpolatorName, leftExtrapolatorName, rightExtrapolatorName);
final Object volatilitySurfaceObject = inputs.getValue(fxVolatilitySurfaceRequirement);
if (volatilitySurfaceObject == null) {
throw new OpenGammaRuntimeException("Could not get " + fxVolatilitySurfaceRequirement);
}
final FXMatrix fxMatrix = new FXMatrix(ccy1, ccy2, spot);
final ValueProperties.Builder properties = getResultProperties(target, desiredValue, baseQuotePair);
final ValueSpecification spec = new ValueSpecification(_valueRequirementName, target.toSpecification(), properties.get());
final YieldCurveBundle curvesWithFX = new YieldCurveBundle(fxMatrix, curveCurrency, yieldCurves.getCurvesMap());
final ObjectsPair<Currency, Currency> currencyPair = Pair.of(ccy1, ccy2);
BlackForexTermStructureParameters termStructure;
if (volatilitySurfaceObject instanceof SmileDeltaTermStructureParametersStrikeInterpolation) {
final SmileDeltaTermStructureParametersStrikeInterpolation smiles = (SmileDeltaTermStructureParametersStrikeInterpolation) volatilitySurfaceObject;
termStructure = smiles.toTermStructureOnlyData(interpolator);
} else {
termStructure = (BlackForexTermStructureParameters) volatilitySurfaceObject;
}
final YieldCurveWithBlackForexTermStructureBundle flatData = new YieldCurveWithBlackForexTermStructureBundle(curvesWithFX, termStructure, currencyPair);
return getResult(fxOption, flatData, target, desiredValues, inputs, spec, executionContext);
}
@Override
public ComputationTargetType getTargetType() {
return FinancialSecurityTypes.FX_OPTION_SECURITY.or(FinancialSecurityTypes.FX_BARRIER_OPTION_SECURITY).or(FinancialSecurityTypes.FX_DIGITAL_OPTION_SECURITY)
.or(FinancialSecurityTypes.NON_DELIVERABLE_FX_OPTION_SECURITY).or(FinancialSecurityTypes.NON_DELIVERABLE_FX_DIGITAL_OPTION_SECURITY);
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
final ValueProperties.Builder properties = getResultProperties(target);
return Collections.singleton(new ValueSpecification(_valueRequirementName, target.toSpecification(), properties.get()));
}
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
final FinancialSecurity security = (FinancialSecurity) target.getSecurity();
final ValueProperties constraints = desiredValue.getConstraints();
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) {
return null;
}
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 String putCurveName = putCurveNames.iterator().next();
final String callCurveName = callCurveNames.iterator().next();
final String putCurveCalculationConfig = putCurveCalculationConfigs.iterator().next();
final String callCurveCalculationConfig = callCurveCalculationConfigs.iterator().next();
final String surfaceName = surfaceNames.iterator().next();
final String interpolatorName = interpolatorNames.iterator().next();
final String leftExtrapolatorName = leftExtrapolatorNames.iterator().next();
final String rightExtrapolatorName = rightExtrapolatorNames.iterator().next();
final Currency putCurrency = security.accept(ForexVisitors.getPutCurrencyVisitor());
final Currency callCurrency = security.accept(ForexVisitors.getCallCurrencyVisitor());
final ValueRequirement putFundingCurve = getCurveRequirement(putCurveName, putCurrency, putCurveCalculationConfig);
final ValueRequirement callFundingCurve = getCurveRequirement(callCurveName, callCurrency, 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, ComputationTargetSpecification.NULL);
return Sets.newHashSet(putFundingCurve, callFundingCurve, fxVolatilitySurface, spotRequirement, pairQuoteRequirement);
}
protected abstract ValueProperties.Builder getResultProperties(final ComputationTarget target);
protected abstract ValueProperties.Builder getResultProperties(final ComputationTarget target, final ValueRequirement desiredValue, final CurrencyPair baseQuotePair);
//TODO clumsy. Push the execute() method down into the functions and have getDerivative() and getData() methods
protected abstract Set<ComputedValue> getResult(final InstrumentDerivative forex, final YieldCurveWithBlackForexTermStructureBundle data, final ComputationTarget target,
final Set<ValueRequirement> desiredValues, final FunctionInputs inputs, final ValueSpecification spec, final FunctionExecutionContext executionContext);
protected static ValueRequirement getCurveRequirement(final String curveName, final Currency currency, final String curveCalculationConfigName) {
final ValueProperties.Builder properties = ValueProperties.builder()
.with(ValuePropertyNames.CURVE, curveName)
.with(ValuePropertyNames.CURVE_CALCULATION_CONFIG, curveCalculationConfigName);
return new ValueRequirement(ValueRequirementNames.YIELD_CURVE, ComputationTargetType.CURRENCY.specification(currency), properties.get());
}
protected static ValueRequirement getSurfaceRequirement(final String surfaceName, final Currency putCurrency, final Currency callCurrency,
final String interpolatorName, final String leftExtrapolatorName, final String rightExtrapolatorName) {
final ValueProperties surfaceProperties = ValueProperties.builder()
.with(ValuePropertyNames.SURFACE, surfaceName)
.with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, InstrumentTypeProperties.FOREX)
.with(InterpolatedDataProperties.X_INTERPOLATOR_NAME, interpolatorName)
.with(InterpolatedDataProperties.LEFT_X_EXTRAPOLATOR_NAME, leftExtrapolatorName)
.with(InterpolatedDataProperties.RIGHT_X_EXTRAPOLATOR_NAME, rightExtrapolatorName)
.get();
final UnorderedCurrencyPair currenciesTarget = UnorderedCurrencyPair.of(putCurrency, callCurrency);
return new ValueRequirement(ValueRequirementNames.STANDARD_VOLATILITY_SURFACE_DATA, ComputationTargetType.UNORDERED_CURRENCY_PAIR.specification(currenciesTarget), surfaceProperties);
}
protected static YieldAndDiscountCurve getCurve(final FunctionInputs inputs, final Currency currency, final String curveName, final String curveCalculationConfig) {
final Object curveObject = inputs.getValue(getCurveRequirement(curveName, currency, curveCalculationConfig));
if (curveObject == null) {
throw new OpenGammaRuntimeException("Could not get " + curveName + " curve");
}
final YieldAndDiscountCurve curve = (YieldAndDiscountCurve) curveObject;
return curve;
}
}