/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame.marketdata;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.forex.method.FXMatrix;
import com.opengamma.analytics.financial.provider.calculator.discounting.ParSpreadMarketQuoteCurveSensitivityDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.ParSpreadMarketQuoteDiscountingCalculator;
import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle;
import com.opengamma.analytics.financial.provider.curve.multicurve.MulticurveDiscountBuildingRepository;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount;
import com.opengamma.analytics.financial.provider.description.interestrate.ProviderUtils;
import com.opengamma.financial.analytics.curve.AbstractCurveDefinition;
import com.opengamma.financial.analytics.curve.CurveConstructionConfiguration;
import com.opengamma.financial.analytics.curve.CurveDefinition;
import com.opengamma.financial.analytics.curve.CurveGroupConfiguration;
import com.opengamma.financial.analytics.curve.credit.CurveSpecificationBuilder;
import com.opengamma.financial.analytics.ircurve.strips.CurveNode;
import com.opengamma.financial.analytics.ircurve.strips.RateFutureNode;
import com.opengamma.sesame.CurveNodeConverterFn;
import com.opengamma.sesame.CurveNodeId;
import com.opengamma.sesame.CurveNodeInstrumentDefinitionFactory;
import com.opengamma.sesame.MulticurveBundle;
import com.opengamma.sesame.TenorCurveNodeId;
import com.opengamma.sesame.marketdata.builders.MarketDataBuilder;
import com.opengamma.sesame.marketdata.scenarios.CyclePerturbations;
import com.opengamma.timeseries.date.DateTimeSeries;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.result.Result;
import com.opengamma.util.time.Tenor;
import com.opengamma.util.tuple.Pair;
/**
* Market data builder for multicurve bundles.
*/
public class MulticurveMarketDataBuilder
extends AbstractMulticurveMarketDataBuilder<MulticurveBundle>
implements MarketDataBuilder {
private static final ParSpreadMarketQuoteDiscountingCalculator DISCOUNTING_CALCULATOR =
ParSpreadMarketQuoteDiscountingCalculator.getInstance();
private static final ParSpreadMarketQuoteCurveSensitivityDiscountingCalculator CURVE_SENSITIVITY_CALCULATOR =
ParSpreadMarketQuoteCurveSensitivityDiscountingCalculator.getInstance();
private final MulticurveDiscountBuildingRepository _curveBuilder;
/**
* @param curveSpecBuilder for building curve specifications
* @param curveNodeConverter for converting curve node instruments to derivatives
* @param definitionFactory creates instrument definitions for curve node instruments
* @param curveBuilder analytics object for building and calibrating the curve bundle
*/
public MulticurveMarketDataBuilder(CurveSpecificationBuilder curveSpecBuilder,
CurveNodeConverterFn curveNodeConverter,
CurveNodeInstrumentDefinitionFactory definitionFactory,
MulticurveDiscountBuildingRepository curveBuilder) {
super(curveSpecBuilder, curveNodeConverter, definitionFactory);
_curveBuilder = ArgumentChecker.notNull(curveBuilder, "curveBuilder");
}
@Override
public Set<MarketDataRequirement> getTimeSeriesRequirements(
TimeSeriesRequirement requirement,
Map<MarketDataId<?>, DateTimeSeries<LocalDate, ?>> suppliedData) {
// TODO implement getTimeSeriesRequirements()
throw new UnsupportedOperationException("getTimeSeriesRequirements not implemented");
}
@Override
public Map<SingleValueRequirement, Result<?>> buildSingleValues(MarketDataBundle marketDataBundle,
ZonedDateTime valuationTime,
Set<SingleValueRequirement> requirements,
MarketDataSource marketDataSource,
CyclePerturbations cyclePerturbations) {
ImmutableMap.Builder<SingleValueRequirement, Result<?>> results = ImmutableMap.builder();
for (SingleValueRequirement requirement : requirements) {
MulticurveId marketDataId = (MulticurveId) requirement.getMarketDataId();
CurveConstructionConfiguration curveConfig = marketDataId.resolveConfig();
Result<MulticurveBundle> bundleResult =
buildBundle(marketDataBundle, valuationTime, curveConfig, requirement, cyclePerturbations);
results.put(requirement, bundleResult);
}
return results.build();
}
@Override
public Map<TimeSeriesRequirement, Result<? extends DateTimeSeries<LocalDate, ?>>> buildTimeSeries(
MarketDataBundle marketDataBundle,
Set<TimeSeriesRequirement> requirements,
MarketDataSource marketDataSource,
CyclePerturbations cyclePerturbations) {
// TODO implement this
return Collections.emptyMap();
}
@Override
public Class<MulticurveId> getKeyType() {
return MulticurveId.class;
}
@Override
CurveConstructionConfiguration getCurveConfig(SingleValueRequirement requirement) {
MulticurveId marketDataId = (MulticurveId) requirement.getMarketDataId();
return marketDataId.resolveConfig();
}
@Override
Set<MarketDataRequirement> getParentBundleRequirements(SingleValueRequirement requirement,
CurveConstructionConfiguration curveConfig) {
ImmutableSet.Builder<MarketDataRequirement> parentBundleRequirements = ImmutableSet.builder();
for (String parentBundleName : curveConfig.getExogenousConfigurations()) {
MulticurveId curveBundleId = MulticurveId.of(parentBundleName);
parentBundleRequirements.add(SingleValueRequirement.of(curveBundleId, requirement.getMarketDataTime()));
}
return parentBundleRequirements.build();
}
/**
* Builds a multicurve bundle
*
* @param marketDataBundle the market data
* @param valuationTime the valuation time for which the curve bundle should be built
* @param bundleConfig the configuration for the multicurve bundle
* @param cyclePerturbations the perturbations that should be applied to the market data for this calculation cycle
* @return a multicurve bundle built from the configuration
*/
private Result<MulticurveBundle> buildBundle(
MarketDataBundle marketDataBundle,
ZonedDateTime valuationTime,
CurveConstructionConfiguration bundleConfig,
MarketDataRequirement bundleRequirement,
CyclePerturbations cyclePerturbations) {
Map<String, List<? extends CurveNodeId>> nodeIds = nodeIds(bundleConfig);
Set<Currency> currencies = getCurrencies(bundleConfig, valuationTime);
FxMatrixId fxMatrixKey = FxMatrixId.of(currencies);
Result<FXMatrix> fxMatrixResult = marketDataBundle.get(fxMatrixKey, FXMatrix.class);
if (!fxMatrixResult.isSuccess()) {
return Result.failure(fxMatrixResult);
}
FXMatrix fxMatrix = fxMatrixResult.getValue();
MulticurveProviderDiscount parentBundle = createParentBundle(marketDataBundle, bundleConfig, fxMatrix);
IntermediateResults intermediateResults =
buildIntermediateResults(
marketDataBundle,
valuationTime,
bundleConfig,
bundleRequirement,
cyclePerturbations);
Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> calibratedCurves =
_curveBuilder.makeCurvesFromDerivatives(
intermediateResults.getCurveBundles(),
parentBundle,
intermediateResults.getCurrenciesByCurveName(),
intermediateResults.getIborIndexByCurveName(),
intermediateResults.getOnIndexByCurveName(),
DISCOUNTING_CALCULATOR,
CURVE_SENSITIVITY_CALCULATOR);
return Result.success(new MulticurveBundle(calibratedCurves.getFirst(), nodeIds, calibratedCurves.getSecond()));
}
/**
* Creates the parent curve bundle for a curve bundle.
*
* @param marketDataBundle the market data
* @param bundleConfig the curve bundle configuration
* @param fxMatrix the FX rates for the currencies used by the curve
* @return the curve bundle's parent bundle
*/
private static MulticurveProviderDiscount createParentBundle(MarketDataBundle marketDataBundle,
CurveConstructionConfiguration bundleConfig,
FXMatrix fxMatrix) {
Set<MulticurveProviderDiscount> parentBundles = new HashSet<>();
for (String parentBundleName : bundleConfig.getExogenousConfigurations()) {
MulticurveId dependencyKey = MulticurveId.of(parentBundleName);
MulticurveBundle bundle = marketDataBundle.get(dependencyKey, MulticurveBundle.class).getValue();
parentBundles.add(bundle.getMulticurveProvider());
}
if (parentBundles.isEmpty()) {
return new MulticurveProviderDiscount(fxMatrix);
} else {
MulticurveProviderDiscount mergedBundle = ProviderUtils.mergeDiscountingProviders(parentBundles);
return ProviderUtils.mergeDiscountingProviders(mergedBundle, fxMatrix);
}
}
/**
* Builds a map of curve name to a list of curve node IDs which identify the nodes by tenor or futures expiry.
*/
private static Map<String, List<? extends CurveNodeId>> nodeIds(CurveConstructionConfiguration multicurveConfig) {
ImmutableMap.Builder<String, List<? extends CurveNodeId>> nodeMapBuilder = ImmutableMap.builder();
for (CurveGroupConfiguration groupConfig : multicurveConfig.getCurveGroups()) {
Set<AbstractCurveDefinition> curveDefinitions = groupConfig.resolveTypesForCurves().keySet();
for (AbstractCurveDefinition abstractCurveDefinition : curveDefinitions) {
if (abstractCurveDefinition instanceof CurveDefinition) {
CurveDefinition curveDefinition = (CurveDefinition) abstractCurveDefinition;
String curveName = curveDefinition.getName();
SortedSet<CurveNode> nodes = curveDefinition.getNodes();
ImmutableList.Builder<CurveNodeId> nodeListBuilder = ImmutableList.builder();
for (CurveNode node : nodes) {
if (node instanceof RateFutureNode) {
// TODO create FuturesExpiryCurveNodeId and add to nodeListBuilder
// RateFutureNode futureNode = (RateFutureNode) node;
throw new OpenGammaRuntimeException("Future nodes are not supported");
} else {
Tenor tenor = node.getResolvedMaturity();
TenorCurveNodeId nodeId = TenorCurveNodeId.of(tenor);
nodeListBuilder.add(nodeId);
}
}
nodeMapBuilder.put(curveName, nodeListBuilder.build());
}
}
}
return nodeMapBuilder.build();
}
}