/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.provider.description.interestrate; import java.io.Serializable; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.commons.lang.ObjectUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.legalentity.LegalEntity; import com.opengamma.analytics.financial.legalentity.LegalEntityFilter; import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.ForwardSensitivity; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; import com.opengamma.util.tuple.DoublesPair; import com.opengamma.util.tuple.Pair; /** * Class describing a provider with multi-curves and issuer-specific curves. */ public class IssuerProvider implements IssuerProviderInterface, Serializable { private static final Logger s_logger = LoggerFactory.getLogger(IssuerProvider.class); /** * The multicurve provider. */ private final MulticurveProviderInterface _multicurveProvider; /** * A map with issuer discounting curves. */ private final Map<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> _issuerCurves; /** * The set of names of all curves used in the multicurves provider. */ private Set<String> _multicurvesNames; /** * The set of all curves names. If contains the names of the multi-curves provider curves names and the issuer curves names. */ private final Set<String> _allNames = new TreeSet<>(); /** * The map between the curves names and the curves themselves. */ //TODO these aren't the best names private final Map<String, YieldAndDiscountCurve> _issuerCurvesNames = new LinkedHashMap<>(); /** * Constructs an empty multi-curve provider and issuer curves map. */ public IssuerProvider() { _multicurveProvider = new MulticurveProviderDiscount(); _issuerCurves = new LinkedHashMap<>(); setAllCurves(); } /** * Constructs a multi-curve provider with empty maps for discounting and forward curves, and an empty issuer curve map. * @param fxMatrix The FX matrix, not null */ public IssuerProvider(final FXMatrix fxMatrix) { _multicurveProvider = new MulticurveProviderDiscount(fxMatrix); _issuerCurves = new LinkedHashMap<>(); setAllCurves(); } /** * Constructs a multi-curve provider and an empty issuer curve map. * @param discountingCurves A map from currency to yield curve, not null * @param forwardIborCurves A map from ibor index to yield curve, not null * @param forwardONCurves A map from overnight index to yield curve, not null * @param fxMatrix The FX matrix, not null */ //TODO there is no guarantee that the maps are LinkedHashMaps, which could lead to unexpected behaviour public IssuerProvider(final Map<Currency, YieldAndDiscountCurve> discountingCurves, final Map<IborIndex, YieldAndDiscountCurve> forwardIborCurves, final Map<IndexON, YieldAndDiscountCurve> forwardONCurves, final FXMatrix fxMatrix) { _multicurveProvider = new MulticurveProviderDiscount(discountingCurves, forwardIborCurves, forwardONCurves, fxMatrix); _issuerCurves = new LinkedHashMap<>(); setAllCurves(); } /** * Constructs a multi-curve provider and sets the issuer curve map. The issuer curve map is not copied. * @param discountingCurves A map from currency to yield curve, not null * @param forwardIborCurves A map from ibor index to yield curve, not null * @param forwardONCurves A map from overnight index to yield curve, not null * @param issuerCurves An issuer curve map, not null * @param fxMatrix The FX matrix, not null */ //TODO there is no guarantee that the map is a LinkedHashMap, which could lead to unexpected behaviour public IssuerProvider(final Map<Currency, YieldAndDiscountCurve> discountingCurves, final Map<IborIndex, YieldAndDiscountCurve> forwardIborCurves, final Map<IndexON, YieldAndDiscountCurve> forwardONCurves, final Map<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> issuerCurves, final FXMatrix fxMatrix) { ArgumentChecker.notNull(issuerCurves, "issuer curves"); _multicurveProvider = new MulticurveProviderDiscount(discountingCurves, forwardIborCurves, forwardONCurves, fxMatrix); _issuerCurves = issuerCurves; setAllCurves(); } /** * Constructor from existing multicurve provider and issuer map. The given provider and map are used for the new provider (the same maps are used, not copied). * @param multicurve The multi-curves provider, not null * @param issuerCurves The issuer specific curves, not null */ public IssuerProvider(final MulticurveProviderInterface multicurve, final Map<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> issuerCurves) { ArgumentChecker.notNull(multicurve, "multicurve"); ArgumentChecker.notNull(issuerCurves, "issuer curves"); _multicurveProvider = multicurve; _issuerCurves = issuerCurves; setAllCurves(); } /** * Constructs a provider from an existing multi-curve provider. The maps are not copied. * @param multicurve The multi-curves provider, not null */ public IssuerProvider(final MulticurveProviderDiscount multicurve) { ArgumentChecker.notNull(multicurve, "multicurve"); _multicurveProvider = multicurve; _issuerCurves = new LinkedHashMap<>(); setAllCurves(); } @Override public MulticurveProviderInterface getMulticurveProvider() { return _multicurveProvider; } @Override public IssuerProviderInterface getIssuerProvider() { return this; } @Override public IssuerProvider copy() { final Map<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> issuerCurvesNew = new LinkedHashMap<>(_issuerCurves); return new IssuerProvider(_multicurveProvider.copy(), issuerCurvesNew); } /** * Sets all curves. */ protected void setAllCurves() { _multicurvesNames = _multicurveProvider.getAllNames(); _allNames.addAll(_multicurvesNames); for (final Map.Entry<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> entry : _issuerCurves.entrySet()) { if (entry.getValue() == null) { throw new OpenGammaRuntimeException("Curve with key " + entry.getValue() + " was null"); } _allNames.add(entry.getValue().getName()); _issuerCurvesNames.put(entry.getValue().getName(), entry.getValue()); } } @Override public double getDiscountFactor(final LegalEntity issuer, final Double time) { for (final Map.Entry<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> entry : _issuerCurves.entrySet()) { if (entry.getKey().getFirst().equals(entry.getKey().getSecond().getFilteredData(issuer))) { return entry.getValue().getDiscountFactor(time); } } s_logger.error("Could not find issuer discounting curve for {}. There are {} curve available", issuer, _issuerCurves.size()); for (final Map.Entry<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> entry : _issuerCurves.entrySet()) { s_logger.error("matching key = {}, filter {} matches = {}", entry.getKey().getFirst(), issuer, entry.getKey().getSecond().getFilteredData(issuer)); } throw new IllegalArgumentException("Issuer discounting curve not found for " + issuer); } @Override public String getName(final Pair<Object, LegalEntityFilter<LegalEntity>> issuer) { return _issuerCurves.get(issuer).getName(); } @Override public String getName(final LegalEntity issuer) { for (final Map.Entry<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> entry : _issuerCurves.entrySet()) { if (entry.getKey().getFirst().equals(entry.getKey().getSecond().getFilteredData(issuer))) { return entry.getValue().getName(); } } s_logger.error("Could not find issuer discounting curve for {}. There are {} curve available", issuer, _issuerCurves.size()); for (final Map.Entry<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> entry : _issuerCurves.entrySet()) { s_logger.error("matching key = {}, filter {} matches = {}", entry.getKey().getFirst(), issuer, entry.getKey().getSecond().getFilteredData(issuer)); } throw new IllegalArgumentException("Issuer discounting curve not found: " + issuer); } @Override public Set<String> getAllNames() { return getAllCurveNames(); } @Override public Set<String> getAllCurveNames() { return Collections.unmodifiableSortedSet(new TreeSet<>(_allNames)); } @Override public double[] parameterSensitivity(final String name, final List<DoublesPair> pointSensitivity) { if (_multicurvesNames.contains(name)) { return _multicurveProvider.parameterSensitivity(name, pointSensitivity); } final YieldAndDiscountCurve curve = _issuerCurvesNames.get(name); if (curve == null) { throw new IllegalArgumentException("Could not get curve called " + name); } final int nbParameters = curve.getNumberOfParameters(); final double[] result = new double[nbParameters]; if (pointSensitivity != null && !pointSensitivity.isEmpty()) { for (final DoublesPair timeAndS : pointSensitivity) { final double[] sensi1Point = curve.getInterestRateParameterSensitivity(timeAndS.getFirst()); for (int loopparam = 0; loopparam < nbParameters; loopparam++) { result[loopparam] += timeAndS.getSecond() * sensi1Point[loopparam]; } } } return result; } @Override public double[] parameterForwardSensitivity(final String name, final List<ForwardSensitivity> pointSensitivity) { if (_multicurvesNames.contains(name)) { return _multicurveProvider.parameterForwardSensitivity(name, pointSensitivity); } final YieldAndDiscountCurve curve = _issuerCurvesNames.get(name); if (curve == null) { throw new IllegalArgumentException("Could not get curve called " + name); } final int nbParameters = curve.getNumberOfParameters(); final double[] result = new double[nbParameters]; if (pointSensitivity != null && pointSensitivity.size() > 0) { for (final ForwardSensitivity timeAndS : pointSensitivity) { final double startTime = timeAndS.getStartTime(); final double endTime = timeAndS.getEndTime(); final double forwardBar = timeAndS.getValue(); // Implementation note: only the sensitivity to the forward is available. The sensitivity to the pseudo-discount factors need to be computed. final double dfForwardStart = curve.getDiscountFactor(startTime); final double dfForwardEnd = curve.getDiscountFactor(endTime); final double dFwddyStart = timeAndS.derivativeToYieldStart(dfForwardStart, dfForwardEnd); final double dFwddyEnd = timeAndS.derivativeToYieldEnd(dfForwardStart, dfForwardEnd); final double[] sensiPtStart = curve.getInterestRateParameterSensitivity(startTime); final double[] sensiPtEnd = curve.getInterestRateParameterSensitivity(endTime); for (int loopparam = 0; loopparam < nbParameters; loopparam++) { result[loopparam] += dFwddyStart * sensiPtStart[loopparam] * forwardBar; result[loopparam] += dFwddyEnd * sensiPtEnd[loopparam] * forwardBar; } } } return result; } @Override public Integer getNumberOfParameters(final String name) { if (_multicurvesNames.contains(name)) { return _multicurveProvider.getNumberOfParameters(name); } return _issuerCurvesNames.get(name).getNumberOfParameters(); } @Override public List<String> getUnderlyingCurvesNames(final String name) { if (_multicurvesNames.contains(name)) { return _multicurveProvider.getUnderlyingCurvesNames(name); } return _issuerCurvesNames.get(name).getUnderlyingCurvesNames(); } @Override public Set<Pair<Object, LegalEntityFilter<LegalEntity>>> getIssuers() { return _issuerCurves.keySet(); } /** * Gets all issuer curves. * @return The issuer curves */ public Map<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> getIssuerCurves() { return _issuerCurves; } /** * Gets an issuer curve. * @param key The key * @return The curve. */ public YieldAndDiscountCurve getIssuerCurve(final Pair<Object, LegalEntityFilter<LegalEntity>> key) { return _issuerCurves.get(key); } /** * Gets a named issuer curve. * @param name The name * @return The curve. */ public YieldAndDiscountCurve getIssuerCurve(final String name) { return _issuerCurvesNames.get(name); } /** * Sets the discounting curve for a given issuer. * @param issuerCcy The issuer/currency. * @param curve The yield curve used for discounting. */ public void setCurve(final Pair<Object, LegalEntityFilter<LegalEntity>> issuerCcy, final YieldAndDiscountCurve curve) { ArgumentChecker.notNull(issuerCcy, "Issuer/currency"); ArgumentChecker.notNull(curve, "curve"); if (_issuerCurves.containsKey(issuerCcy)) { throw new IllegalArgumentException("Currency discounting curve already set: " + issuerCcy.toString()); } _issuerCurves.put(issuerCcy, curve); setAllCurves(); } /** * Replaces an issuer curve. * @param ic The key of the curve to replace * @param replacement The replacement curve * @return A new provider with the curve replaced. */ public IssuerProvider withIssuerCurve(final Pair<Object, LegalEntityFilter<LegalEntity>> ic, final YieldAndDiscountCurve replacement) { final Map<Pair<Object, LegalEntityFilter<LegalEntity>>, YieldAndDiscountCurve> newIssuerCurves = new LinkedHashMap<>(_issuerCurves); newIssuerCurves.put(ic, replacement); return new IssuerProvider(_multicurveProvider, newIssuerCurves); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + _issuerCurves.hashCode(); result = prime * result + _multicurveProvider.hashCode(); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof IssuerProvider)) { return false; } final IssuerProvider other = (IssuerProvider) obj; if (!ObjectUtils.equals(_issuerCurves, other._issuerCurves)) { return false; } if (!ObjectUtils.equals(_multicurveProvider, other._multicurveProvider)) { return false; } return true; } }