/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.provider.sensitivity.parameter; import it.unimi.dsi.fastutil.ints.IntArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitor; import com.opengamma.analytics.financial.provider.description.interestrate.ParameterProviderInterface; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.ForwardSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyParameterSensitivity; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; import com.opengamma.util.tuple.DoublesPair; import com.opengamma.util.tuple.Pairs; /** * For an instrument, computes the sensitivity of a multi-currency value (often the present value) to the * parameters used in the curve. * The meaning of "parameters" will depend of the way the curve is stored (interpolated zero-coupon rates, * function parameters, etc.). * The sensitivity is computed for the underlying curves up to one level deep, not further (see reference * documentation for more explanation). * The return format is a MultipleCurrencyParameterSensitivity object. * <p> * Reference: Sensitivity computation, OpenGamma Documentation n. 23, September 2014. * @param <DATA_TYPE> Data type extending ParameterProviderInterface. */ public class ParameterSensitivityUnderlyingParameterCalculator<DATA_TYPE extends ParameterProviderInterface> extends ParameterSensitivityParameterAbstractCalculator<DATA_TYPE> { /** * Constructor * @param curveSensitivityCalculator The curve sensitivity calculator. */ public ParameterSensitivityUnderlyingParameterCalculator( final InstrumentDerivativeVisitor<DATA_TYPE, MultipleCurrencyMulticurveSensitivity> curveSensitivityCalculator) { super(curveSensitivityCalculator); } @Override public MultipleCurrencyParameterSensitivity pointToParameterSensitivity(final MultipleCurrencyMulticurveSensitivity sensitivity, final DATA_TYPE parameterMulticurves, final Set<String> sensicurveNamesSet) { ArgumentChecker.notNull(sensitivity, "sensitivity"); ArgumentChecker.notNull(parameterMulticurves, "multicurves parameter"); ArgumentChecker.notNull(sensicurveNamesSet, "curves set"); // TODO: The first part depends only of the multicurves and curvesSet, not the sensitivity. Should it be refactored and done only once? final Set<String> multicurveNamesSet = parameterMulticurves.getMulticurveProvider().getAllNames(); final String[] multicurveNamesArray = multicurveNamesSet.toArray(new String[0]); // Implementation note: Check sensicurve are in multicurve ArgumentChecker.isTrue(multicurveNamesSet.containsAll(sensicurveNamesSet), "curve in the names set not in the multi-curve provider"); final int nbMultiCurve = multicurveNamesSet.size(); // Populate the name names and numbers for the curves in the multicurve final LinkedHashMap<String, Integer> multicurveNum = new LinkedHashMap<>(); for (int loopcur = 0; loopcur < nbMultiCurve; loopcur++) { // loop over all curves in multicurves multicurveNum.put(multicurveNamesArray[loopcur], loopcur); } final int[] nbNewParameters = new int[nbMultiCurve]; final int[] nbParameters = new int[nbMultiCurve]; // Implementation note: nbNewParameters - number of new parameters in the curve, parameters not // from an underlying curve which is another curve of the bundle. for (int loopcur = 0; loopcur < nbMultiCurve; loopcur++) { // loop over all curves in multicurves nbParameters[loopcur] = parameterMulticurves.getMulticurveProvider().getNumberOfParameters(multicurveNamesArray[loopcur]); nbNewParameters[loopcur] = nbParameters[loopcur]; } for (int loopcur = 0; loopcur < nbMultiCurve; loopcur++) { // loop over all curves in multicurves final List<String> underlyingCurveNames = parameterMulticurves.getMulticurveProvider().getUnderlyingCurvesNames(multicurveNamesArray[loopcur]); for (final String u : underlyingCurveNames) { final Integer i = multicurveNum.get(u); if (i != null) { nbNewParameters[loopcur] -= nbNewParameters[i]; // Only one level: a curve used as an underlying can not have an underlying itself. } } } final int[][] indexOtherMulticurve = new int[nbMultiCurve][]; // Implementation note: indexOtherMultiCurve - for each curve in the multi-curve, the index of the underlying curves in the same set final int[] startOwnParameter = new int[nbMultiCurve]; // Implementation note: The start index of the parameters of the own (new) parameters. final int[][] startUnderlyingParameter = new int[nbMultiCurve][]; // Implementation note: The start index of the parameters of the underlying curves for (int loopcur = 0; loopcur < nbMultiCurve; loopcur++) { // loop over all curves in multicurves final List<String> underlyingCurveNames = parameterMulticurves.getMulticurveProvider().getUnderlyingCurvesNames(multicurveNamesArray[loopcur]); final IntArrayList indexOtherMulticurveList = new IntArrayList(); for (final String u : underlyingCurveNames) { final Integer i = multicurveNum.get(u); if (i != null) { indexOtherMulticurveList.add(i); } } indexOtherMulticurve[loopcur] = indexOtherMulticurveList.toIntArray(); } for (int loopcurve = 0; loopcurve < nbMultiCurve; loopcurve++) { // loop over all curves in multicurves int loopstart = 0; final int num = multicurveNum.get(multicurveNamesArray[loopcurve]); final IntArrayList startUnderlyingParamList = new IntArrayList(); final List<String> underlyingCurveNames = parameterMulticurves.getMulticurveProvider().getUnderlyingCurvesNames(multicurveNamesArray[loopcurve]); for (final String u : underlyingCurveNames) { final Integer i = multicurveNum.get(u); if (i != null) { startUnderlyingParamList.add(loopstart); loopstart += nbNewParameters[i]; // Implementation note: Rely on underlying curves being first and then the new parameters } } startOwnParameter[num] = loopstart; startUnderlyingParameter[num] = startUnderlyingParamList.toIntArray(); } // Implementation note: Compute the "dirty" sensitivity, i.e. the sensitivity to all the parameters in each curve. // The underlying are taken into account in the "clean" step. Set<Currency> ccySet = sensitivity.getCurrencies(); Currency[] ccyArray = ccySet.toArray(new Currency[0]); int nbCcy = ccySet.size(); double[][][] sensiDirty = new double[nbCcy][nbMultiCurve][]; for (int loopccy = 0; loopccy < nbCcy; loopccy++) { final Map<String, List<DoublesPair>> sensitivityDsc = sensitivity.getSensitivity(ccyArray[loopccy]).getYieldDiscountingSensitivities(); final Map<String, List<ForwardSensitivity>> sensitivityFwd = sensitivity.getSensitivity(ccyArray[loopccy]).getForwardSensitivities(); for (int loopcurve = 0; loopcurve < nbMultiCurve; loopcurve++) { // loop over all curves sensiDirty[loopccy][loopcurve] = new double[nbParameters[loopcurve]]; final double[] sDsc1Name = parameterMulticurves.parameterSensitivity(multicurveNamesArray[loopcurve], sensitivityDsc.get(multicurveNamesArray[loopcurve])); final double[] sFwd1Name = parameterMulticurves.parameterForwardSensitivity(multicurveNamesArray[loopcurve], sensitivityFwd.get(multicurveNamesArray[loopcurve])); for (int loopp = 0; loopp < nbParameters[loopcurve]; loopp++) { sensiDirty[loopccy][loopcurve][loopp] = sDsc1Name[loopp] + sFwd1Name[loopp]; } } } // Implementation note: "clean" the sensitivity, i.e. add the parts on the same curves together. double[][][] sensiClean = new double[nbCcy][nbMultiCurve][]; for (int loopccy = 0; loopccy < nbCcy; loopccy++) { for (int loopcurve = 0; loopcurve < nbMultiCurve; loopcurve++) { // loop over all curves sensiClean[loopccy][loopcurve] = new double[nbNewParameters[loopcurve]]; } } for (int loopccy = 0; loopccy < nbCcy; loopccy++) { for (int loopcurve = 0; loopcurve < nbMultiCurve; loopcurve++) { // loop over all curves // Direct sensitivity for (int loopi = 0; loopi < nbNewParameters[loopcurve]; loopi++) { sensiClean[loopccy][loopcurve][loopi] += sensiDirty[loopccy][loopcurve][startOwnParameter[loopcurve] + loopi]; } // Underlying (indirect) sensitivity for (int loopu = 0; loopu < startUnderlyingParameter[loopcurve].length; loopu++) { for (int loopi = 0; loopi < nbNewParameters[indexOtherMulticurve[loopcurve][loopu]]; loopi++) { sensiClean[loopccy][indexOtherMulticurve[loopcurve][loopu]][loopi] += sensiDirty[loopccy][loopcurve][startUnderlyingParameter[loopcurve][loopu] + loopi]; } } } } MultipleCurrencyParameterSensitivity result = new MultipleCurrencyParameterSensitivity(); for (int loopccy = 0; loopccy < nbCcy; loopccy++) { // loop on all currencies for (int loopcurve = 0; loopcurve < nbMultiCurve; loopcurve++) { // loop over all curves result = result.plus(Pairs.of(multicurveNamesArray[loopcurve], ccyArray[loopccy]), new DoubleMatrix1D(sensiClean[loopccy][loopcurve])); } } return result; } @Override public MultipleCurrencyParameterSensitivity pointToParameterSensitivity( final MultipleCurrencyMulticurveSensitivity sensitivity, final DATA_TYPE parameterMulticurves) { ArgumentChecker.notNull(parameterMulticurves, "multicurves parameter"); return pointToParameterSensitivity(sensitivity, parameterMulticurves, parameterMulticurves.getMulticurveProvider().getAllCurveNames()); } }