/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame.marketdata.scenarios; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import com.opengamma.DataNotFoundException; import com.opengamma.analytics.financial.instrument.index.IborIndex; import com.opengamma.analytics.financial.instrument.index.IndexDeposit; import com.opengamma.analytics.financial.instrument.index.IndexON; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.core.link.ConventionLink; import com.opengamma.core.link.SecurityLink; 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.OvernightCurveTypeConfiguration; import com.opengamma.financial.convention.IborIndexConvention; import com.opengamma.financial.convention.OvernightIndexConvention; import com.opengamma.financial.security.index.OvernightIndex; import com.opengamma.sesame.MulticurveBundle; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; /** * Metadata for a multicurve bundle, used to decide whether to apply a scenario perturbation. * <p> * Metadata can be constructed from an existing multicurve bundle or from the configuration used * to build one. * <p> * This is not intended to be general purpose for a multicurve, it is specifically intended for * use in the scenario framework. */ public final class MulticurveMetadata { private static final Logger s_logger = LoggerFactory.getLogger(MulticurveMetadata.class); private final Set<String> _curveNames; private final SetMultimap<Currency, String> _curveNamesByCurrency; private final Map<IndexDeposit, String> _curveNamesByIndex; private final Map<String, String> _curveNamesByIndexName; private final ImmutableSet<String> _discountingCurveNames; private final ImmutableSet<String> _forecastCurveNames; private MulticurveMetadata(Set<String> curveNames, SetMultimap<Currency, String> curveNamesByCurrency, Map<IndexDeposit, String> curveNamesByIndex, Map<String, String> curveNamesByIndexName, Set<String> discountingCurveNames, Set<String> forecastCurveNames) { _curveNames = curveNames; _curveNamesByCurrency = curveNamesByCurrency; _curveNamesByIndex = curveNamesByIndex; _curveNamesByIndexName = curveNamesByIndexName; _discountingCurveNames = ImmutableSet.copyOf(discountingCurveNames); _forecastCurveNames = ImmutableSet.copyOf(forecastCurveNames); } /** * Creates multicurve metadata from the configuration used to build the multicurve bundle. * * @param config the configuration for building the multicurve bundle * @return the multicurve metadata */ public static MulticurveMetadata forConfiguration(CurveConstructionConfiguration config) { ArgumentChecker.notNull(config, "config"); ImmutableSetMultimap.Builder<Currency, String> currencyBuilder = ImmutableSetMultimap.builder(); ImmutableSet.Builder<String> nameBuilder = ImmutableSet.builder(); ImmutableMap.Builder<IndexDeposit, String> indexBuilder = ImmutableMap.builder(); ImmutableMap.Builder<String, String> indexNameBuilder = ImmutableMap.builder(); Set<String> discountingCurveNames = Sets.newHashSet(); Set<String> forecastingCurveNames = Sets.newHashSet(); for (CurveGroupConfiguration groupConfig : config.getCurveGroups()) { Map<String, List<? extends CurveTypeConfiguration>> curveMap = groupConfig.getTypesForCurves(); discountingCurveNames.addAll(discountingCurves(curveMap)); forecastingCurveNames.addAll(forecastingCurves(curveMap)); for (Map.Entry<String, List<? extends CurveTypeConfiguration>> entry : curveMap.entrySet()) { String curveName = entry.getKey(); nameBuilder.add(curveName); List<? extends CurveTypeConfiguration> curveConfigs = entry.getValue(); for (Currency currency : currenciesForConfigs(curveConfigs)) { currencyBuilder.put(currency, curveName); } // it doesn't make sense for one curve to have multiple indices but the object model allows it for (IndexDeposit index : indicesForConfigs(curveConfigs)) { indexBuilder.put(index, curveName); indexNameBuilder.put(index.getName(), curveName); } } } return new MulticurveMetadata( nameBuilder.build(), currencyBuilder.build(), indexBuilder.build(), indexNameBuilder.build(), discountingCurveNames, forecastingCurveNames); } /** * Creates multicurve metadata from an existing multicurve bundle. * * @param multicurve a multicurve bundle * @return the multicurve metadata */ public static MulticurveMetadata forMulticurve(MulticurveBundle multicurve) { ArgumentChecker.notNull(multicurve, "multicurve"); MulticurveProviderDiscount curves = multicurve.getMulticurveProvider(); ImmutableSetMultimap.Builder<Currency, String> currencyBuilder = ImmutableSetMultimap.builder(); ImmutableMap.Builder<IndexDeposit, String> indexBuilder = ImmutableMap.builder(); ImmutableMap.Builder<String, String> indexNameBuilder = ImmutableMap.builder(); Set<String> discountingCurveNames = Sets.newHashSet(); Set<String> forecastingCurveNames = Sets.newHashSet(); for (Map.Entry<Currency, YieldAndDiscountCurve> entry : curves.getDiscountingCurves().entrySet()) { String curveName = entry.getValue().getName(); currencyBuilder.put(entry.getKey(), curveName); discountingCurveNames.add(curveName); } for (Map.Entry<IndexON, YieldAndDiscountCurve> entry : curves.getForwardONCurves().entrySet()) { IndexON index = entry.getKey(); String curveName = entry.getValue().getName(); currencyBuilder.put(index.getCurrency(), curveName); indexBuilder.put(index, curveName); indexNameBuilder.put(index.getName(), curveName); forecastingCurveNames.add(curveName); } for (Map.Entry<IborIndex, YieldAndDiscountCurve> entry : curves.getForwardIborCurves().entrySet()) { IborIndex index = entry.getKey(); String curveName = entry.getValue().getName(); currencyBuilder.put(index.getCurrency(), curveName); indexBuilder.put(index, curveName); indexNameBuilder.put(index.getName(), curveName); forecastingCurveNames.add(curveName); } return new MulticurveMetadata( ImmutableSet.copyOf(curves.getAllNames()), currencyBuilder.build(), indexBuilder.build(), indexNameBuilder.build(), discountingCurveNames, forecastingCurveNames); } /** * Returns all currencies for the curve configurations. * * @param curveConfigs curve type configurations * @return all currencies in the curve type configurations */ private static Set<Currency> currenciesForConfigs(List<? extends CurveTypeConfiguration> curveConfigs) { Set<Currency> currencies = new HashSet<>(); for (CurveTypeConfiguration curveConfig : curveConfigs) { if (curveConfig instanceof DiscountingCurveTypeConfiguration) { try { currencies.add(Currency.of(((DiscountingCurveTypeConfiguration) curveConfig).getReference())); } catch (IllegalArgumentException e) { s_logger.warn("Failed to parse curve config reference as currency. Config: {}", curveConfig); } } else if (curveConfig instanceof IborCurveTypeConfiguration) { try { IborIndex index = createIborIndex((IborCurveTypeConfiguration) curveConfig); currencies.add(index.getCurrency()); } catch (DataNotFoundException e) { s_logger.warn("Unable to resolve index ID '{}' for curve type config {}", ((IborCurveTypeConfiguration) curveConfig).getConvention(), curveConfig); } } else if (curveConfig instanceof OvernightCurveTypeConfiguration) { try { IndexON index = createOvernightIndex((OvernightCurveTypeConfiguration) curveConfig); currencies.add(index.getCurrency()); } catch (DataNotFoundException e) { s_logger.warn("Unable to resolve index ID '{}' for curve type config {}", ((OvernightCurveTypeConfiguration) curveConfig).getConvention(), curveConfig); } } else { s_logger.warn("Curve type config of unexpected type: " + curveConfig.getClass().getName()); } } return currencies; } /** * @param curveMap curve type configurations for each curve * @return the names of the curves set up for discounting */ private static Set<String> discountingCurves(Map<String, List<? extends CurveTypeConfiguration>> curveMap) { Set<String> discountingCurveNames = new HashSet<>(); for (Entry<String, List<? extends CurveTypeConfiguration>> curve : curveMap.entrySet()) { String curveName = curve.getKey(); List<? extends CurveTypeConfiguration> curveConfigs = curve.getValue(); for (CurveTypeConfiguration curveConfig : curveConfigs) { if (curveConfig instanceof DiscountingCurveTypeConfiguration) { discountingCurveNames.add(curveName); } } } return discountingCurveNames; } /** * @param curveMap curve type configurations for each curve * @return the names of the curves set up for forecasting */ private static Set<String> forecastingCurves(Map<String, List<? extends CurveTypeConfiguration>> curveMap) { Set<String> forecastingCurveNames = new HashSet<>(); for (Entry<String, List<? extends CurveTypeConfiguration>> curve : curveMap.entrySet()) { String curveName = curve.getKey(); List<? extends CurveTypeConfiguration> curveConfigs = curve.getValue(); for (CurveTypeConfiguration curveConfig : curveConfigs) { if (curveConfig instanceof IborCurveTypeConfiguration || curveConfig instanceof OvernightCurveTypeConfiguration) { forecastingCurveNames.add(curveName); } } } return forecastingCurveNames; } // it doesn't make sense for one curve to have multiple indices but the object model allows it private static Set<IndexDeposit> indicesForConfigs(List<? extends CurveTypeConfiguration> curveConfigs) { Set<IndexDeposit> indices = new HashSet<>(); for (CurveTypeConfiguration curveConfig : curveConfigs) { if (curveConfig instanceof IborCurveTypeConfiguration) { try { IborIndex index = createIborIndex((IborCurveTypeConfiguration) curveConfig); indices.add(index); } catch (DataNotFoundException e) { s_logger.warn("Unable to resolve index ID '{}' for curve type config {}", ((IborCurveTypeConfiguration) curveConfig).getConvention(), curveConfig); } } else if (curveConfig instanceof OvernightCurveTypeConfiguration) { try { IndexON index = createOvernightIndex((OvernightCurveTypeConfiguration) curveConfig); indices.add(index); } catch (DataNotFoundException e) { s_logger.warn("Unable to resolve index ID '{}' for curve type config {}", ((OvernightCurveTypeConfiguration) curveConfig).getConvention(), curveConfig); } } } return indices; } private static IndexON createOvernightIndex(OvernightCurveTypeConfiguration type) { OvernightIndex index = SecurityLink.resolvable(type.getConvention().toBundle(), OvernightIndex.class).resolve(); OvernightIndexConvention indexConvention = ConventionLink.resolvable(index.getConventionId(), OvernightIndexConvention.class).resolve(); return ConverterUtils.indexON(index.getName(), indexConvention); } private static IborIndex createIborIndex(IborCurveTypeConfiguration type) { com.opengamma.financial.security.index.IborIndex indexSecurity = SecurityLink.resolvable(type.getConvention(), com.opengamma.financial.security.index.IborIndex.class).resolve(); IborIndexConvention indexConvention = ConventionLink.resolvable(indexSecurity.getConventionId(), IborIndexConvention.class).resolve(); return ConverterUtils.indexIbor(indexSecurity.getName(), indexConvention, indexSecurity.getTenor()); } /** * @return the names of all curves in the multicurve bundle */ public Set<String> getCurveNames() { return _curveNames; } /** * Returns the curve names in the multicurve bundle, keyed by the curve currency. * <p> * For discounting curves this is the curve currency. For IBOR or overnight forward curves it is the * currency taken from the security representing the curve's index. * * @return the curve names in the multicurve bundle, keyed by the curve currency */ public SetMultimap<Currency, String> getCurveNamesByCurrency() { return _curveNamesByCurrency; } /** * Returns the curve names in the multicurve bundle, keyed by the curve index. * * @return the curve names in the multicurve bundle, keyed by the curve index */ public Map<IndexDeposit, String> getCurveNamesByIndex() { return _curveNamesByIndex; } /** * Returns the curve names in the multicurve bundle, keyed by the curve index name. * * @return the curve names in the multicurve bundle, keyed by the curve index name */ public Map<String, String> getCurveNamesByIndexName() { return _curveNamesByIndexName; } /** * Returns the names of the curves used for discounting * * @return the names of the curves used for discounting */ public ImmutableSet<String> getDiscountingCurveNames() { return _discountingCurveNames; } /** * Returns the names of the curves used for forecasting * * @return the names of the curves used for forecasting */ public ImmutableSet<String> getForecastingCurveNames() { return _forecastCurveNames; } }