/*
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.curve;
import static com.opengamma.engine.value.ValuePropertyNames.CURVE;
import static com.opengamma.engine.value.ValueRequirementNames.CURVE_DEFINITION;
import static com.opengamma.engine.value.ValueRequirementNames.CURVE_MARKET_DATA;
import static com.opengamma.engine.value.ValueRequirementNames.CURVE_SPECIFICATION;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.threeten.bp.Clock;
import org.threeten.bp.LocalDate;
import org.threeten.bp.Period;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.DataNotFoundException;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDACompliantYieldCurve;
import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDACompliantYieldCurveBuild;
import com.opengamma.analytics.financial.credit.isdastandardmodel.ISDAInstrumentTypes;
import com.opengamma.core.convention.Convention;
import com.opengamma.core.convention.ConventionSource;
import com.opengamma.core.marketdatasnapshot.SnapshotDataBundle;
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.OpenGammaExecutionContext;
import com.opengamma.financial.analytics.curve.CurveSpecification;
import com.opengamma.financial.analytics.ircurve.strips.CashNode;
import com.opengamma.financial.analytics.ircurve.strips.CurveNodeWithIdentifier;
import com.opengamma.financial.analytics.ircurve.strips.SwapNode;
import com.opengamma.financial.analytics.model.cds.ISDAFunctionConstants;
import com.opengamma.financial.convention.DepositConvention;
import com.opengamma.financial.convention.IborIndexConvention;
import com.opengamma.financial.convention.SwapFixedLegConvention;
import com.opengamma.financial.convention.VanillaIborLegConvention;
import com.opengamma.financial.convention.daycount.DayCount;
import com.opengamma.financial.convention.daycount.DayCounts;
import com.opengamma.id.ExternalId;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.async.AsynchronousExecution;
/**
* Produces an ISDA compatible yield curve
*/
// non compiled for now to allow dynamic config db lookup
public class ISDACompliantCurveFunction extends AbstractFunction.NonCompiledInvoker {
/** ISDA fixes yield curve daycout to Act/365 */
private static final DayCount ACT_365 = DayCounts.ACT_365;
/** the curve configuration name */
private final String _curveName;
/**
* The name of the curve produced by this function.
* @param curveName The curve name, not null
*/
public ISDACompliantCurveFunction(final String curveName) {
ArgumentChecker.notNull(curveName, "curve name");
_curveName = curveName;
}
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target,
final Set<ValueRequirement> desiredValues) throws AsynchronousExecution {
final Clock snapshotClock = executionContext.getValuationClock();
final ValueRequirement desiredValue = desiredValues.iterator().next();
final ZonedDateTime now = ZonedDateTime.now(snapshotClock);
final ConventionSource conventionSource = OpenGammaExecutionContext.getConventionSource(executionContext);
final CurveSpecification specification = (CurveSpecification) inputs.getValue(ValueRequirementNames.CURVE_SPECIFICATION);
final SnapshotDataBundle snapshot = (SnapshotDataBundle) inputs.getValue(ValueRequirementNames.CURVE_MARKET_DATA);
final LocalDate spotDate = (!desiredValue.getConstraints().getValues(ISDAFunctionConstants.ISDA_CURVE_DATE).isEmpty())
? LocalDate.parse(desiredValue.getConstraint(ISDAFunctionConstants.ISDA_CURVE_DATE))
: now.toLocalDate();
DepositConvention cashConvention = null;
VanillaIborLegConvention floatLegConvention = null;
SwapFixedLegConvention fixLegConvention = null;
IborIndexConvention liborConvention = null;
final int nNodes = specification.getNodes().size();
final double[] marketDataForCurve = new double[nNodes];
final ISDAInstrumentTypes[] instruments = new ISDAInstrumentTypes[nNodes];
final Period[] tenors = new Period[nNodes];
int k = 0;
for (final CurveNodeWithIdentifier node : specification.getNodes()) {
final Double marketData = snapshot.getDataPoint(node.getIdentifier());
if (marketData == null) {
throw new OpenGammaRuntimeException("Could not get market data for " + node.getIdentifier());
}
marketDataForCurve[k] = marketData;
tenors[k] = node.getCurveNode().getResolvedMaturity().getPeriod();
if (node.getCurveNode() instanceof CashNode) {
instruments[k] = ISDAInstrumentTypes.MoneyMarket;
final ExternalId cashConventionId = ((CashNode) node.getCurveNode()).getConvention();
if (cashConvention == null) {
try {
cashConvention = conventionSource.getSingle(cashConventionId, DepositConvention.class);
} catch (DataNotFoundException ex) {
// ignore, continue around loop
}
} else if (!cashConvention.getExternalIdBundle().contains(cashConventionId)) {
throw new OpenGammaRuntimeException("Got 2 types of cash convention: " + cashConvention.getExternalIdBundle() + " " + cashConventionId);
}
} else if (node.getCurveNode() instanceof SwapNode) {
instruments[k] = ISDAInstrumentTypes.Swap;
final ExternalId payConventionId = ((SwapNode) node.getCurveNode()).getPayLegConvention();
final Convention payConvention = conventionSource.getSingle(payConventionId);
final ExternalId receiveConventionId = ((SwapNode) node.getCurveNode()).getReceiveLegConvention();
final Convention receiveConvention = conventionSource.getSingle(receiveConventionId);
if (payConvention instanceof VanillaIborLegConvention) { // float leg
if (floatLegConvention == null) {
floatLegConvention = (VanillaIborLegConvention) payConvention;
} else if (!floatLegConvention.getExternalIdBundle().contains(payConventionId)) {
throw new OpenGammaRuntimeException("Got 2 types of float leg convention: " + payConvention.getExternalIdBundle() + " " + payConventionId);
}
} else if (payConvention instanceof SwapFixedLegConvention) {
if (fixLegConvention == null) {
fixLegConvention = (SwapFixedLegConvention) payConvention;
} else if (!fixLegConvention.getExternalIdBundle().contains(payConventionId)) {
throw new OpenGammaRuntimeException("Got 2 types of fixed leg convention: " + payConvention.getExternalIdBundle() + " " + payConventionId);
}
} else {
throw new OpenGammaRuntimeException("Unexpected swap convention type: " + payConvention);
}
if (receiveConvention instanceof VanillaIborLegConvention) { // float leg
if (floatLegConvention == null) {
floatLegConvention = (VanillaIborLegConvention) receiveConvention;
} else if (!floatLegConvention.getExternalIdBundle().contains(receiveConventionId)) {
throw new OpenGammaRuntimeException("Got 2 types of float leg convention: " + receiveConvention.getExternalIdBundle() + " " + receiveConventionId);
}
} else if (receiveConvention instanceof SwapFixedLegConvention) {
if (fixLegConvention == null) {
fixLegConvention = (SwapFixedLegConvention) receiveConvention;
} else if (!fixLegConvention.getExternalIdBundle().contains(receiveConventionId)) {
throw new OpenGammaRuntimeException("Got 2 types of fixed leg convention: " + receiveConvention.getExternalIdBundle() + " " + receiveConventionId);
}
} else {
throw new OpenGammaRuntimeException("Unexpected swap convention type: " + receiveConvention);
}
} else {
throw new OpenGammaRuntimeException("Can't handle node type " + node.getCurveNode().getClass().getSimpleName() + " at node " + node);
}
k++;
}
ArgumentChecker.notNull(cashConvention, "Cash convention");
ArgumentChecker.notNull(floatLegConvention, "Floating leg convention");
ArgumentChecker.notNull(fixLegConvention, "Fixed leg convention");
liborConvention = conventionSource.getSingle(floatLegConvention.getIborIndexConvention(), IborIndexConvention.class);
ArgumentChecker.notNull(liborConvention, floatLegConvention.getIborIndexConvention().toString());
final ISDACompliantYieldCurve yieldCurve = ISDACompliantYieldCurveBuild.build(spotDate, spotDate, instruments, tenors, marketDataForCurve, cashConvention.getDayCount(),
fixLegConvention.getDayCount(), fixLegConvention.getPaymentTenor().getPeriod(), ACT_365, liborConvention.getBusinessDayConvention());
final ValueProperties properties = desiredValue.getConstraints().copy()
.with(ISDAFunctionConstants.ISDA_CURVE_DATE, spotDate.toString())
.get();
final ValueSpecification spec = new ValueSpecification(ValueRequirementNames.YIELD_CURVE, target.toSpecification(), properties);
return Collections.singleton(new ComputedValue(spec, yieldCurve));
}
@Override
public ComputationTargetType getTargetType() {
return ComputationTargetType.NULL;
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
final ValueProperties properties = createValueProperties()
.with(ValuePropertyNames.CURVE, _curveName)
.with(ISDAFunctionConstants.ISDA_IMPLEMENTATION, ISDAFunctionConstants.ISDA_IMPLEMENTATION_NEW)
.with(ValuePropertyNames.CURVE_CALCULATION_METHOD, ISDAFunctionConstants.ISDA_METHOD_NAME)
.withAny(ISDAFunctionConstants.ISDA_CURVE_DATE)
.get();
return Collections.singleton(new ValueSpecification(ValueRequirementNames.YIELD_CURVE,
target.toSpecification(),
properties));
}
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext compilationContext, final ComputationTarget target, final ValueRequirement desiredValue) {
final Set<ValueRequirement> requirements = new HashSet<>();
final ValueProperties properties = ValueProperties.builder()
.with(CURVE, _curveName)
.get();
requirements.add(new ValueRequirement(CURVE_DEFINITION, ComputationTargetSpecification.NULL, properties));
requirements.add(new ValueRequirement(CURVE_MARKET_DATA, ComputationTargetSpecification.NULL, properties));
requirements.add(new ValueRequirement(CURVE_SPECIFICATION, ComputationTargetSpecification.NULL, properties));
return requirements;
}
}