/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.forex.method.FXMatrix;
import com.opengamma.analytics.financial.instrument.index.IborIndex;
import com.opengamma.analytics.financial.instrument.index.IndexON;
import com.opengamma.analytics.financial.model.interestrate.curve.DiscountCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldPeriodicCurve;
import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlock;
import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount;
import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.core.link.ConventionLink;
import com.opengamma.core.link.SecurityLink;
import com.opengamma.core.marketdatasnapshot.SnapshotDataBundle;
import com.opengamma.financial.analytics.curve.AbstractCurveDefinition;
import com.opengamma.financial.analytics.curve.AbstractCurveSpecification;
import com.opengamma.financial.analytics.curve.ConverterUtils;
import com.opengamma.financial.analytics.curve.CurveConstructionConfiguration;
import com.opengamma.financial.analytics.curve.CurveGroupConfiguration;
import com.opengamma.financial.analytics.curve.CurveTypeConfiguration;
import com.opengamma.financial.analytics.curve.DiscountingCurveTypeConfiguration;
import com.opengamma.financial.analytics.curve.IborCurveTypeConfiguration;
import com.opengamma.financial.analytics.curve.InterpolatedCurveSpecification;
import com.opengamma.financial.analytics.curve.OvernightCurveTypeConfiguration;
import com.opengamma.financial.analytics.ircurve.strips.ContinuouslyCompoundedRateNode;
import com.opengamma.financial.analytics.ircurve.strips.CurveNode;
import com.opengamma.financial.analytics.ircurve.strips.CurveNodeWithIdentifier;
import com.opengamma.financial.analytics.ircurve.strips.DiscountFactorNode;
import com.opengamma.financial.analytics.ircurve.strips.PeriodicallyCompoundedRateNode;
import com.opengamma.financial.convention.IborIndexConvention;
import com.opengamma.financial.convention.OvernightIndexConvention;
import com.opengamma.financial.security.index.OvernightIndex;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.util.money.Currency;
import com.opengamma.util.result.Result;
import com.opengamma.util.result.SuccessStatus;
import com.opengamma.util.time.Tenor;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Pairs;
/**
* Multicurve bundle function using interpolated (pre-fitted) curves.
*/
public class InterpolatedMulticurveBundleFn implements DiscountingMulticurveBundleFn {
private final CurveSpecificationFn _curveSpecificationProvider;
private final CurveSpecificationMarketDataFn _curveSpecificationMarketDataProvider;
public InterpolatedMulticurveBundleFn(CurveSpecificationFn curveSpecificationProvider,
CurveSpecificationMarketDataFn curveSpecificationMarketDataProvider) {
// TODO argument checker
_curveSpecificationProvider = curveSpecificationProvider;
_curveSpecificationMarketDataProvider = curveSpecificationMarketDataProvider;
}
@Override
public Result<MulticurveBundle> generateBundle(
Environment env, CurveConstructionConfiguration curveConfig,
Map<CurveConstructionConfiguration, Result<MulticurveBundle>> requiredCurves) {
// Interim result object used to build up the complete set of
// failures rather than exiting early
Result<Boolean> result = Result.success(true);
ZonedDateTime now = env.getValuationTime();
MulticurveProviderDiscount curveBundle = new MulticurveProviderDiscount(new FXMatrix());
LinkedHashMap<String, Pair<CurveBuildingBlock, DoubleMatrix2D>> unitBundles = new LinkedHashMap<>();
/* For each group of curves */
for (CurveGroupConfiguration group: curveConfig.getCurveGroups()) {
/* For each curve definition */
for (Entry<AbstractCurveDefinition, List<? extends CurveTypeConfiguration>> entry: group.resolveTypesForCurves().entrySet()) {
LinkedHashMap<String, Pair<Integer, Integer>> unitMap = new LinkedHashMap<>();
int totalNodes = 0;
AbstractCurveDefinition curve = entry.getKey();
Result<AbstractCurveSpecification> curveSpecResult = _curveSpecificationProvider.getCurveSpecification(env, curve);
if (curveSpecResult.isSuccess()) {
InterpolatedCurveSpecification specification = (InterpolatedCurveSpecification) curveSpecResult.getValue();
Result<Map<ExternalIdBundle, Double>> marketDataResult =
_curveSpecificationMarketDataProvider.requestData(env, specification);
if (marketDataResult.getStatus() == SuccessStatus.SUCCESS) {
// todo this is temporary to allow us to get up and running fast
SnapshotDataBundle snapshot = createSnapshotDataBundle(marketDataResult.getValue());
Set<CurveNodeWithIdentifier> nodes = specification.getNodes();
int n = nodes.size();
double[] ttm = new double[n];
double[] yields = new double[n];
double[][] jacobian = new double[n][n];
int i = 0;
int compoundPeriodsPerYear = 0;
int nNodesForCurve = specification.getNodes().size();
boolean isYield = false;
for (CurveNodeWithIdentifier node: nodes) {
CurveNode curveNode = node.getCurveNode();
if (curveNode instanceof ContinuouslyCompoundedRateNode) {
if (i == 0) {
// First node - set expectation that all nodes are ContinuouslyCompoundedRateNodes
isYield = true;
} else {
if (!isYield) {
throw new OpenGammaRuntimeException("Expected only continuously-compounded rate nodes, found " + curveNode);
}
}
} else if (curveNode instanceof DiscountFactorNode) {
if (i == 0) {
// First node - set expectation that all nodes are DiscountFactorNodes
isYield = false;
} else {
if (isYield) {
throw new OpenGammaRuntimeException("Expected only discount factor nodes, found " + curveNode);
}
}
} else if (curveNode instanceof PeriodicallyCompoundedRateNode) {
if (i == 0) {
compoundPeriodsPerYear = ((PeriodicallyCompoundedRateNode) curveNode).getCompoundingPeriodsPerYear();
isYield = true;
} else {
if (!isYield) {
throw new OpenGammaRuntimeException("Was expecting only periodically compounded nodes; have " + curveNode);
}
}
} else {
throw new OpenGammaRuntimeException("Can only handle discount factor or continuously-compounded rate nodes; have " + curveNode);
}
// ttm
Tenor maturity = curveNode.getResolvedMaturity();
ttm[i] = TimeCalculator.getTimeBetween(now, now.plus(maturity.getPeriod()));
// yield
Double yield = snapshot.getDataPoint(node.getIdentifier());
if (yield == null) {
throw new OpenGammaRuntimeException("Could not get market data value for " + node);
}
yields[i] = yield;
jacobian[i][i] = 1;
i++;
}
Interpolator1D interpolator =
CombinedInterpolatorExtrapolatorFactory.getInterpolator(specification.getInterpolatorName(),
specification.getLeftExtrapolatorName(),
specification.getRightExtrapolatorName());
String curveName = curve.getName();
InterpolatedDoublesCurve rawCurve = InterpolatedDoublesCurve.from(ttm, yields, interpolator, curveName);
YieldAndDiscountCurve discountCurve;
if (compoundPeriodsPerYear != 0 && isYield) {
discountCurve = YieldPeriodicCurve.from(compoundPeriodsPerYear, rawCurve);
} else if (isYield) {
discountCurve = new YieldCurve(curveName, rawCurve);
} else {
discountCurve = new DiscountCurve(curveName, rawCurve);
}
for (CurveTypeConfiguration type: entry.getValue()) {
if (type instanceof DiscountingCurveTypeConfiguration) {
Currency currency = Currency.parse(((DiscountingCurveTypeConfiguration) type).getReference());
curveBundle.setCurve(currency, discountCurve);
} else if (type instanceof IborCurveTypeConfiguration) {
curveBundle.setCurve(createIborIndex((IborCurveTypeConfiguration) type), discountCurve);
} else if (type instanceof OvernightCurveTypeConfiguration) {
curveBundle.setCurve(createIndexON((OvernightCurveTypeConfiguration) type), discountCurve);
} else {
throw new OpenGammaRuntimeException("Unsupported curve type configuration " + type);
}
}
unitMap.put(curveName, Pairs.of(totalNodes, nNodesForCurve));
unitBundles.put(curveName, Pairs.of(new CurveBuildingBlock(unitMap), new DoubleMatrix2D(jacobian)));
totalNodes += nNodesForCurve;
} else {
result = Result.failure(result, marketDataResult);
}
} else {
result = Result.failure(result, curveSpecResult);
}
}
}
if (result.isSuccess()) {
return Result.success(new MulticurveBundle(curveBundle, new CurveBuildingBlockBundle(unitBundles)));
} else {
return Result.failure(result);
}
}
private static SnapshotDataBundle createSnapshotDataBundle(Map<ExternalIdBundle, Double> marketData) {
SnapshotDataBundle snapshotDataBundle = new SnapshotDataBundle();
for (Map.Entry<ExternalIdBundle, Double> entry : marketData.entrySet()) {
snapshotDataBundle.setDataPoint(entry.getKey(), entry.getValue());
}
return snapshotDataBundle;
}
private IndexON createIndexON(OvernightCurveTypeConfiguration type) {
OvernightIndex security = SecurityLink.resolvable(type.getConvention(), OvernightIndex.class).resolve();
OvernightIndexConvention indexConvention =
ConventionLink.resolvable(security.getConventionId(), OvernightIndexConvention.class).resolve();
return ConverterUtils.indexON(security.getName(), indexConvention);
}
private IborIndex createIborIndex(IborCurveTypeConfiguration type) {
com.opengamma.financial.security.index.IborIndex security =
SecurityLink.resolvable(type.getConvention(), com.opengamma.financial.security.index.IborIndex.class).resolve();
IborIndexConvention indexConvention =
ConventionLink.resolvable(security.getConventionId(), IborIndexConvention.class).resolve();
return ConverterUtils.indexIbor(security.getName(), indexConvention, security.getTenor());
}
@Override
public Result<ImpliedDepositCurveData> extractImpliedDepositCurveData(
Environment env, CurveConstructionConfiguration curveConfig,
Map<CurveConstructionConfiguration, Result<MulticurveBundle>> builtCurves) {
throw new UnsupportedOperationException();
}
}