/**
* 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.option;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.equity.EquityOptionBlackPresentValueCalculator;
import com.opengamma.analytics.financial.equity.StaticReplicationDataBundle;
import com.opengamma.analytics.financial.equity.option.EquityIndexOption;
import com.opengamma.analytics.financial.interestrate.NodeYieldSensitivityCalculator;
import com.opengamma.analytics.financial.interestrate.PresentValueNodeSensitivityCalculator;
import com.opengamma.analytics.financial.interestrate.YieldCurveBundle;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.FunctionCompilationContext;
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.ircurve.InterpolatedYieldCurveSpecificationWithSecurities;
import com.opengamma.financial.analytics.model.YieldCurveNodeSensitivitiesHelper;
import com.opengamma.financial.security.FinancialSecurityUtils;
import com.opengamma.util.money.Currency;
import com.opengamma.util.tuple.DoublesPair;
/**
* This Function provides the sensitivity to the discount rate. <p>
* We have two dates of interest, expiry and settlement.
* Sensitivity to the expiry rate might be implicit in the estimation of the underlying equity's forward, but we don't include this here.
* The sensitivity to settlement rate is in the discounting, the ZeroBond price: PV = Z(t,S) * C(F,K,sig,T) <p>
* We use chain rule to distribute closed-form model sensitivity across the curve
*/
public class EquityVanillaBarrierOptionFundingCurveSensitivitiesFunction extends EquityVanillaBarrierOptionBlackFunction {
/** The present value calculator */
private static final EquityOptionBlackPresentValueCalculator PV_CALCULATOR = EquityOptionBlackPresentValueCalculator.getInstance();
/**
* Default constructor
*/
public EquityVanillaBarrierOptionFundingCurveSensitivitiesFunction() {
super(ValueRequirementNames.YIELD_CURVE_NODE_SENSITIVITIES);
}
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
final Set<ValueRequirement> requirements = super.getRequirements(context, target, desiredValue);
if (requirements == null) {
return null;
}
// Get Funding Curve Name
final Set<String> fundingCurves = desiredValue.getConstraints().getValues(ValuePropertyNames.CURVE);
if (fundingCurves == null || fundingCurves.size() != 1) {
return null;
}
final String fundingCurveName = fundingCurves.iterator().next();
requirements.add(getCurveSpecRequirement(FinancialSecurityUtils.getCurrency(target.getSecurity()), fundingCurveName));
return requirements;
}
// Need to do this to get labels for the output
private ValueRequirement getCurveSpecRequirement(final Currency currency, final String curveName) {
final ValueProperties properties = ValueProperties.builder().with(ValuePropertyNames.CURVE, curveName).get();
return new ValueRequirement(ValueRequirementNames.YIELD_CURVE_SPEC, ComputationTargetType.PRIMITIVE, currency.getUniqueId(), properties);
}
@Override
protected Set<ComputedValue> computeValues(final Set<EquityIndexOption> vanillaOptions, final StaticReplicationDataBundle market, final FunctionInputs inputs,
final Set<ValueRequirement> desiredValues, final ComputationTargetSpecification targetSpec, final ValueProperties resultProperties) {
final ValueSpecification resultSpec = new ValueSpecification(getValueRequirementNames()[0], targetSpec, resultProperties);
final ValueRequirement desiredValue = Iterables.getOnlyElement(desiredValues);
final String fundingCurveName = desiredValue.getConstraint(ValuePropertyNames.CURVE);
final Object fundingObject = inputs.getValue(ValueRequirementNames.YIELD_CURVE);
if (fundingObject == null) {
throw new OpenGammaRuntimeException("Could not get Funding Curve");
}
final YieldAndDiscountCurve fundingCurve = (YieldAndDiscountCurve) fundingObject;
// Put curve into a bundle
final YieldCurveBundle curveBundle = new YieldCurveBundle();
curveBundle.setCurve(fundingCurveName, fundingCurve);
if (!(fundingCurve instanceof YieldCurve)) {
throw new IllegalArgumentException("Can only handle YieldCurve");
}
// 4. Compute sensitivity to discount rate, then distribute across curve's nodes
final DoubleMatrix1D sensVector;
if (((YieldCurve) fundingCurve).getCurve() instanceof InterpolatedDoublesCurve) {
// Compute the sum of the underlying vanillas' present values
double pv = 0.0;
for (final EquityIndexOption derivative : vanillaOptions) {
pv += PV_CALCULATOR.visitEquityIndexOption(derivative, market);
}
final double settlementTime = vanillaOptions.iterator().next().getTimeToSettlement(); // All share the same dates
if (settlementTime < 0.0) {
throw new OpenGammaRuntimeException("EquityBarrierOptionSecurity has already settled.");
}
final double rhoSettle = -1 * settlementTime * pv;
// We use PresentValueNodeSensitivityCalculator to distribute this risk across the curve
final NodeYieldSensitivityCalculator distributor = PresentValueNodeSensitivityCalculator.getDefaultInstance();
// What's left is to package up the inputs to the distributor, a YieldCurveBundle and a Map of Sensitivities
final Map<String, List<DoublesPair>> curveSensMap = new HashMap<>();
curveSensMap.put(fundingCurveName, Lists.newArrayList(DoublesPair.of(settlementTime, rhoSettle)));
sensVector = distributor.curveToNodeSensitivities(curveSensMap, curveBundle);
} else {
throw new IllegalArgumentException("YieldCurveNodeSensitivities currently available only for Funding Curve backed by a InterpolatedDoublesCurve");
}
// Build up InstrumentLabelledSensitivities for the Curve
final Object curveSpecObject = inputs.getValue(ValueRequirementNames.YIELD_CURVE_SPEC);
if (curveSpecObject == null) {
throw new OpenGammaRuntimeException("Curve specification was null");
}
final InterpolatedYieldCurveSpecificationWithSecurities curveSpec = (InterpolatedYieldCurveSpecificationWithSecurities) curveSpecObject;
return YieldCurveNodeSensitivitiesHelper.getInstrumentLabelledSensitivitiesForCurve(fundingCurveName, curveBundle, sensVector, curveSpec, resultSpec);
}
}