/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame;
import static com.opengamma.util.result.FailureStatus.MISSING_DATA;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.forex.method.FXMatrix;
import com.opengamma.analytics.financial.legalentity.LegalEntity;
import com.opengamma.analytics.financial.legalentity.LegalEntityFilter;
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.IssuerProviderDiscount;
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.marketdatasnapshot.SnapshotDataBundle;
import com.opengamma.financial.analytics.curve.AbstractCurveDefinition;
import com.opengamma.financial.analytics.curve.AbstractCurveSpecification;
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.InterpolatedCurveSpecification;
import com.opengamma.financial.analytics.curve.IssuerCurveTypeConfiguration;
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.id.ExternalIdBundle;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.result.Result;
import com.opengamma.util.result.SuccessStatus;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Pairs;
/**
* Issuer provider function using interpolated (pre-fitted) curves.
*/
public class InterpolatedIssuerBundleFn implements IssuerProviderBundleFn {
private final CurveSpecificationFn _curveSpecificationProvider;
private final CurveSpecificationMarketDataFn _curveSpecificationMarketDataProvider;
public InterpolatedIssuerBundleFn(CurveSpecificationFn curveSpecificationProvider,
CurveSpecificationMarketDataFn curveSpecificationMarketDataProvider) {
_curveSpecificationProvider = ArgumentChecker.notNull(curveSpecificationProvider, "curveSpecificationProvider");
_curveSpecificationMarketDataProvider =
ArgumentChecker.notNull(curveSpecificationMarketDataProvider, "curveSpecificationMarketDataProvider");
}
@Override
public Result<IssuerProviderBundle> generateBundle(Environment env, CurveConstructionConfiguration curveConfig) {
boolean valid = true;
ZonedDateTime now = env.getValuationTime();
IssuerProviderDiscount curveBundle = new IssuerProviderDiscount(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());
int n = specification.getNodes().size();
double[] times = new double[n];
double[] yields = new double[n];
double[][] jacobian = new double[n][n];
boolean isYield = false;
int i = 0;
int compoundPeriodsPerYear = 0;
int nNodesForCurve = specification.getNodes().size();
for (CurveNodeWithIdentifier node: specification.getNodes()) {
CurveNode curveNode = node.getCurveNode();
if (curveNode instanceof ContinuouslyCompoundedRateNode) {
if (i == 0) {
isYield = true;
} else {
if (!isYield) {
throw new OpenGammaRuntimeException("Was expecting only continuously-compounded rate nodes; have " + curveNode);
}
}
} else if (curveNode instanceof DiscountFactorNode) {
if (i == 0) {
isYield = false;
} else {
if (isYield) {
throw new OpenGammaRuntimeException("Was expecting only discount factor nodes; have " + 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);
}
Double marketValue = snapshot.getDataPoint(node.getIdentifier());
if (marketValue == null) {
throw new OpenGammaRuntimeException("Could not get market value for " + node);
}
times[i] = TimeCalculator.getTimeBetween(now, now.plus(curveNode.getResolvedMaturity().getPeriod()));
yields[i] = marketValue;
jacobian[i][i] = 1;
i++;
}
Interpolator1D interpolator =
CombinedInterpolatorExtrapolatorFactory.getInterpolator(specification.getInterpolatorName(),
specification.getLeftExtrapolatorName(),
specification.getRightExtrapolatorName());
String curveName = curve.getName();
InterpolatedDoublesCurve rawCurve = InterpolatedDoublesCurve.from(times, 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 IssuerCurveTypeConfiguration) {
IssuerCurveTypeConfiguration issuer = (IssuerCurveTypeConfiguration) type;
curveBundle.setCurve(Pairs.<Object, LegalEntityFilter<LegalEntity>>of(issuer.getKeys(), issuer.getFilters()), discountCurve);
curveBundle.getMulticurveProvider().setCurve(Currency.of(curveName.substring(0, 3)), discountCurve);
}
}
unitMap.put(curveName, Pairs.of(totalNodes, nNodesForCurve));
unitBundles.put(curveName, Pairs.of(new CurveBuildingBlock(unitMap), new DoubleMatrix2D(jacobian)));
totalNodes += nNodesForCurve;
} else {
valid = false;
}
} else {
valid = false;
}
}
}
if (valid) {
return Result.success(new IssuerProviderBundle(curveBundle, new CurveBuildingBlockBundle(unitBundles)));
} else {
// todo - supply some useful information in the failure message!
return Result.failure(MISSING_DATA, "Unable to get intermediate data");
}
}
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;
}
}