/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.equity;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.equity.variance.pricing.AffineDividends;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurveAffineDividends;
import com.opengamma.analytics.financial.model.interestrate.curve.ForwardCurveYieldImplied;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.math.curve.ConstantDoublesCurve;
import com.opengamma.core.id.ExternalSchemes;
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.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueRequirementNames;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.analytics.model.curve.forward.ForwardCurveValuePropertyNames;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdentifiable;
import com.opengamma.id.ExternalScheme;
import com.opengamma.util.money.Currency;
/**
* Function produces a FORWARD_CURVE given YIELD_CURVE and Equity MARKET_VALUE
* Simple implementation does not include any Dividend treatment
*/
public class EquityForwardCurveFunction extends AbstractFunction.NonCompiledInvoker {
private static final Set<ExternalScheme> s_validSchemes = ImmutableSet.of(ExternalSchemes.BLOOMBERG_TICKER, ExternalSchemes.BLOOMBERG_TICKER_WEAK, ExternalSchemes.ACTIVFEED_TICKER);
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
final ValueProperties properties = createValueProperties()
.with(ForwardCurveValuePropertyNames.PROPERTY_FORWARD_CURVE_CALCULATION_METHOD, ForwardCurveValuePropertyNames.PROPERTY_YIELD_CURVE_IMPLIED_METHOD)
.withAny(ValuePropertyNames.CURVE_CURRENCY)
.withAny(ValuePropertyNames.CURVE)
.withAny(ValuePropertyNames.CURVE_CALCULATION_CONFIG)
.withAny(ValuePropertyNames.DIVIDEND_TYPE)
.get();
return Collections.singleton(new ValueSpecification(ValueRequirementNames.FORWARD_CURVE, target.toSpecification(), properties));
}
// REVIEW Andrew 2012-01-17 -- Can we make the target type of this SECURITY, or even EQUITY_SECURITY ?
@Override
public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
if (target.getValue() instanceof ExternalIdentifiable) {
final ExternalId identifier = ((ExternalIdentifiable) target.getValue()).getExternalId();
return s_validSchemes.contains(identifier.getScheme());
}
return false;
}
@Override
public boolean canHandleMissingInputs() {
// dividend yield may not be available
return true;
}
/* Spot value of the equity index or name */
private ValueRequirement getSpotRequirement(final ComputationTarget target) {
return new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, target.getUniqueId());
}
/* Funding curve of the equity's currency */
private ValueRequirement getFundingCurveRequirement(final Currency ccy, final String curveName, final String curveCalculationConfig) {
final ValueProperties fundingProperties = ValueProperties.builder() // Note that createValueProperties is _not_ used - otherwise engine can't find the requirement
.with(ValuePropertyNames.CURVE, curveName)
.with(ValuePropertyNames.CURVE_CALCULATION_CONFIG, curveCalculationConfig)
.get();
return new ValueRequirement(ValueRequirementNames.YIELD_CURVE, ComputationTargetSpecification.of(ccy), fundingProperties);
}
@Override
/* If a requirement is not found, return null, and go looking for a default */
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
final Set<ValueRequirement> requirements = new HashSet<>();
final ValueProperties constraints = desiredValue.getConstraints();
// Spot Requirement
requirements.add(getSpotRequirement(target));
// Funding Curve Currency
final Set<String> ccyConstraint = constraints.getValues(ValuePropertyNames.CURVE_CURRENCY);
if (ccyConstraint == null || ccyConstraint.size() != 1) {
return null;
}
final Currency currency = Currency.of(ccyConstraint.iterator().next());
// Funding Curve Name
final Set<String> fundingCurveNameSet = constraints.getValues(ValuePropertyNames.CURVE);
if (fundingCurveNameSet == null || fundingCurveNameSet.size() != 1) {
return null;
}
final String fundingCurveName = fundingCurveNameSet.iterator().next();
final Set<String> curveCalculationConfigs = constraints.getValues(ValuePropertyNames.CURVE_CALCULATION_CONFIG);
if (curveCalculationConfigs == null || curveCalculationConfigs.size() != 1) {
return null;
}
final String curveCalculationConfig = Iterables.getOnlyElement(curveCalculationConfigs);
// Funding Curve Requirement
requirements.add(getFundingCurveRequirement(currency, fundingCurveName, curveCalculationConfig));
// Dividend Requirements depend on type
final Set<String> dividendTypeSet = constraints.getValues(ValuePropertyNames.DIVIDEND_TYPE);
if (dividendTypeSet == null || dividendTypeSet.size() != 1) {
return null;
}
final String dividendType = dividendTypeSet.iterator().next();
if (ValuePropertyNames.DIVIDEND_TYPE_DISCRETE.equalsIgnoreCase(dividendType)) {
requirements.add(new ValueRequirement(ValueRequirementNames.AFFINE_DIVIDENDS, ComputationTargetType.PRIMITIVE, target.getUniqueId()));
} else if (ValuePropertyNames.DIVIDEND_TYPE_CONTINUOUS.equalsIgnoreCase(dividendType)) {
requirements.add(new ValueRequirement(MarketDataRequirementNames.DIVIDEND_YIELD, ComputationTargetType.PRIMITIVE, target.getUniqueId()));
}
return requirements;
}
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
// desiredValues is defined by getResults. In our case, a singleton
final ValueRequirement desiredValue = Iterables.getOnlyElement(desiredValues);
// Spot
final Double spot = (Double) inputs.getValue(MarketDataRequirementNames.MARKET_VALUE);
if (spot == null) {
throw new OpenGammaRuntimeException("Failed to get spot value requirement: " + target.getName());
}
final String curveCalculationConfig = desiredValue.getConstraint(ValuePropertyNames.CURVE_CALCULATION_CONFIG);
// Curve Currency
final String ccyName = desiredValue.getConstraint(ValuePropertyNames.CURVE_CURRENCY);
if (ccyName == null) {
throw new OpenGammaRuntimeException("Failed to find " + ValuePropertyNames.CURVE_CURRENCY);
}
// Funding
final String fundingCurveName = desiredValue.getConstraint(ValuePropertyNames.CURVE);
final Object fundingCurveObject = inputs.getValue(ValueRequirementNames.YIELD_CURVE);
if (fundingCurveObject == null) {
throw new OpenGammaRuntimeException("Failed to get funding curve requirement");
}
final YieldCurve fundingCurve = (YieldCurve) fundingCurveObject;
// Dividend type: Discrete or Continuous
final String dividendType = desiredValue.getConstraint(ValuePropertyNames.DIVIDEND_TYPE);
boolean isContinuousDividends = ValuePropertyNames.DIVIDEND_TYPE_CONTINUOUS.equalsIgnoreCase(dividendType);
// Compute ForwardCurve
final ForwardCurve forwardCurve;
if (isContinuousDividends) {
// Cost of Carry - if no dividend yield available set 0 cost of carry
final Double dividendYieldObject = (Double) inputs.getValue(MarketDataRequirementNames.DIVIDEND_YIELD);
final double dividendYield = dividendYieldObject == null ? 0.0 : dividendYieldObject.doubleValue();
final YieldCurve costOfCarryCurve = YieldCurve.from(ConstantDoublesCurve.from(dividendYield, "CostOfCarry"));
forwardCurve = new ForwardCurveYieldImplied(spot, fundingCurve, costOfCarryCurve);
} else {
Object discreteDividendsInput = inputs.getValue(ValueRequirementNames.AFFINE_DIVIDENDS);
if ((discreteDividendsInput != null) && (discreteDividendsInput instanceof AffineDividends)) {
final AffineDividends discreteDividends = (AffineDividends) discreteDividendsInput;
forwardCurve = new ForwardCurveAffineDividends(spot, fundingCurve, discreteDividends);
} else {
forwardCurve = new ForwardCurveYieldImplied(spot, fundingCurve, YieldCurve.from(ConstantDoublesCurve.from(0.0, "CostOfCarry")));
}
}
final ValueProperties properties = createValueProperties()
.with(ForwardCurveValuePropertyNames.PROPERTY_FORWARD_CURVE_CALCULATION_METHOD, ForwardCurveValuePropertyNames.PROPERTY_YIELD_CURVE_IMPLIED_METHOD)
.with(ValuePropertyNames.CURVE, fundingCurveName)
.with(ValuePropertyNames.CURVE_CURRENCY, ccyName)
.with(ValuePropertyNames.CURVE_CALCULATION_CONFIG, curveCalculationConfig)
.with(ValuePropertyNames.DIVIDEND_TYPE, dividendType)
.get();
final ValueSpecification resultSpec = new ValueSpecification(ValueRequirementNames.FORWARD_CURVE, target.toSpecification(), properties);
return Collections.singleton(new ComputedValue(resultSpec, forwardCurve));
}
@Override
public ComputationTargetType getTargetType() {
return ComputationTargetType.PRIMITIVE;
}
}