All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.pricer.sensitivity.CurveSensitivityUtils Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/*
 * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.pricer.sensitivity;

import static java.util.stream.Collectors.toList;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import com.google.common.collect.ImmutableList;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.array.DoubleMatrix;
import com.opengamma.strata.market.curve.CurveParameterSize;
import com.opengamma.strata.market.param.CurrencyParameterSensitivities;
import com.opengamma.strata.market.param.CurrencyParameterSensitivity;
import com.opengamma.strata.market.param.DatedParameterMetadata;
import com.opengamma.strata.market.param.LabelDateParameterMetadata;
import com.opengamma.strata.market.param.ParameterMetadata;
import com.opengamma.strata.market.param.TenorParameterMetadata;
import com.opengamma.strata.math.impl.matrix.CommonsMatrixAlgebra;
import com.opengamma.strata.math.impl.matrix.MatrixAlgebra;
import com.opengamma.strata.product.ResolvedTrade;

/**
 * Utilities to transform sensitivities.
 */
public class CurveSensitivityUtils {
  /**
   * The matrix algebra used for matrix inversion.
   */
  private static final MatrixAlgebra MATRIX_ALGEBRA = new CommonsMatrixAlgebra();

  /**
   * Construct the inverse Jacobian matrix from the sensitivities of the trades market quotes to the curve parameters.
   * 

* All the trades and sensitivities must be in the same currency. The data should be coherent with the * market quote sensitivities passed in an order coherent with the list of curves. *

* For each trade describing the market quotes, the sensitivity provided should be the sensitivity of that * market quote to the curve parameters. * * @param curveOrder the order in which the curves should be represented in the jacobian * @param marketQuoteSensitivities the market quotes sensitivity to the curve parameters * @return inverse jacobian matrix, which correspond to the sensitivity of the parameters to the market quotes */ public static DoubleMatrix jacobianFromMarketQuoteSensitivities( List curveOrder, List marketQuoteSensitivities) { Currency ccy = marketQuoteSensitivities.get(0).getSensitivities().get(0).getCurrency(); DoubleMatrix jacobianMatrix = DoubleMatrix.ofArrayObjects( marketQuoteSensitivities.size(), marketQuoteSensitivities.size(), i -> row(curveOrder, marketQuoteSensitivities.get(i), ccy)); return MATRIX_ALGEBRA.getInverse(jacobianMatrix); } /** * Computes the row corresponding to a trade for the Jacobian matrix. * * @param curveOrder the curve order * @param parameterSensitivities the sensitivities * @param ccy the currency common to all sensitivities * @return the row */ private static DoubleArray row( List curveOrder, CurrencyParameterSensitivities parameterSensitivities, Currency ccy) { DoubleArray row = DoubleArray.EMPTY; for (CurveParameterSize curveNameAndSize : curveOrder) { Optional sensitivityOneCurve = parameterSensitivities.findSensitivity(curveNameAndSize.getName(), ccy); if (sensitivityOneCurve.isPresent()) { row = row.concat(sensitivityOneCurve.get().getSensitivity()); } else { row = row.concat(DoubleArray.filled(curveNameAndSize.getParameterCount())); } } return row; } /** * Construct the inverse Jacobian matrix from the trades and a function used to compute the sensitivities of the * market quotes to the curve parameters. *

* All the trades must be in the same currency. The trades should be coherent with the curves order. * * @param curveOrder the order in which the curves should be represented in the jacobian * @param trades the list of trades * @param sensitivityFunction the function from a trade to the market quote sensitivity to curve parameters * @return inverse jacobian matrix, which correspond to the sensitivity of the parameters to the market quotes */ public static DoubleMatrix jacobianFromMarketQuoteSensitivities( List curveOrder, List trades, Function sensitivityFunction) { List marketQuoteSensitivities = new ArrayList<>(); for (ResolvedTrade t : trades) { marketQuoteSensitivities.add(sensitivityFunction.apply(t)); } return jacobianFromMarketQuoteSensitivities(curveOrder, marketQuoteSensitivities); } /** * Re-buckets a {@link CurrencyParameterSensitivities} to a given set of dates. *

* The list of dates must be sorted in chronological order. All sensitivities are re-bucketed to the same date list. * The re-bucketing is done by linear weighting on the number of days, i.e. the sensitivities for dates outside the * extremes are fully bucketed to the extremes and for date between two re-bucketing dates, the weight on the start * date is the number days between end date and the date re-bucketed divided by the number of days between the * start and the end. * The input sensitivity should have a {@link DatedParameterMetadata} for each sensitivity. * * @param sensitivities the input sensitivities * @param targetDates the list of dates for the re-bucketing * @return the sensitivity after the re-bucketing */ public static CurrencyParameterSensitivities linearRebucketing( CurrencyParameterSensitivities sensitivities, List targetDates) { checkSortedDates(targetDates); int nbBuckets = targetDates.size(); List pmdTarget = targetDates.stream() .map(date -> LabelDateParameterMetadata.of(date, date.toString())) .collect(toList()); ImmutableList sensitivitiesList = sensitivities.getSensitivities(); List sensitivityTarget = new ArrayList<>(); for (CurrencyParameterSensitivity sensitivity : sensitivitiesList) { double[] rebucketedSensitivityAmounts = new double[nbBuckets]; DoubleArray sensitivityAmounts = sensitivity.getSensitivity(); List parameterMetadataList = sensitivity.getParameterMetadata(); for (int loopnode = 0; loopnode < sensitivityAmounts.size(); loopnode++) { ParameterMetadata nodeMetadata = parameterMetadataList.get(loopnode); ArgChecker.isTrue(nodeMetadata instanceof DatedParameterMetadata, "re-bucketing requires sensitivity date for node {} which is of type {} while 'DatedParameterMetadata' is expected", nodeMetadata.getLabel(), nodeMetadata.getClass().getName()); DatedParameterMetadata datedParameterMetadata = (DatedParameterMetadata) nodeMetadata; LocalDate nodeDate = datedParameterMetadata.getDate(); rebucketingArray(targetDates, rebucketedSensitivityAmounts, sensitivityAmounts.get(loopnode), nodeDate); } CurrencyParameterSensitivity rebucketedSensitivity = CurrencyParameterSensitivity.of( sensitivity.getMarketDataName(), pmdTarget, sensitivity.getCurrency(), DoubleArray.ofUnsafe(rebucketedSensitivityAmounts)); sensitivityTarget.add(rebucketedSensitivity); } return CurrencyParameterSensitivities.of(sensitivityTarget); } /** * Re-buckets a {@link CurrencyParameterSensitivities} to a given set of dates. *

* The list of dates must be sorted in chronological order. All sensitivities are re-bucketed to the same date list. * The re-bucketing is done by linear weighting on the number of days, i.e. the sensitivities for dates outside the * extremes are fully bucketed to the extremes and for date between two re-bucketing dates, the weight on the start * date is the number days between end date and the date re-bucketed divided by the number of days between the * start and the end. The date of the nodes can be directly in the parameter metadata - when the metadata is of the * type {@link DatedParameterMetadata} - or inferred from the sensitivity date and the tenor when the * metadata is of the type {@link TenorParameterMetadata}. Only those types of metadata are accepted. * * @param sensitivities the input sensitivities * @param targetDates the list of dates for the re-bucketing * @param sensitivityDate the date for which the sensitivities are valid * @return the sensitivity after the re-bucketing */ public static CurrencyParameterSensitivities linearRebucketing( CurrencyParameterSensitivities sensitivities, List targetDates, LocalDate sensitivityDate) { checkSortedDates(targetDates); int nbBuckets = targetDates.size(); List pmdTarget = targetDates.stream() .map(date -> LabelDateParameterMetadata.of(date, date.toString())) .collect(toList()); ImmutableList sensitivitiesList = sensitivities.getSensitivities(); List sensitivityTarget = new ArrayList<>(); for (CurrencyParameterSensitivity sensitivity : sensitivitiesList) { double[] rebucketedSensitivityAmounts = new double[nbBuckets]; DoubleArray sensitivityAmounts = sensitivity.getSensitivity(); List parameterMetadataList = sensitivity.getParameterMetadata(); for (int loopnode = 0; loopnode < sensitivityAmounts.size(); loopnode++) { ParameterMetadata nodeMetadata = parameterMetadataList.get(loopnode); ArgChecker.isTrue((nodeMetadata instanceof DatedParameterMetadata) || (nodeMetadata instanceof TenorParameterMetadata), "re-bucketing requires sensitivity date or node for node {} which is of type {}", nodeMetadata.getLabel(), nodeMetadata.getClass().getName()); LocalDate nodeDate; if (nodeMetadata instanceof DatedParameterMetadata) { DatedParameterMetadata datedParameterMetadata = (DatedParameterMetadata) nodeMetadata; nodeDate = datedParameterMetadata.getDate(); } else { TenorParameterMetadata tenorParameterMetadata = (TenorParameterMetadata) nodeMetadata; nodeDate = sensitivityDate.plus(tenorParameterMetadata.getTenor()); } rebucketingArray(targetDates, rebucketedSensitivityAmounts, sensitivityAmounts.get(loopnode), nodeDate); } CurrencyParameterSensitivity rebucketedSensitivity = CurrencyParameterSensitivity.of( sensitivity.getMarketDataName(), pmdTarget, sensitivity.getCurrency(), DoubleArray.ofUnsafe(rebucketedSensitivityAmounts)); sensitivityTarget.add(rebucketedSensitivity); } return CurrencyParameterSensitivities.of(sensitivityTarget); } /** * Re-bucket one sensitivity at a specific date and add it to an existing array. * * @param targetDates the list of dates for the re-bucketing * @param rebucketedSensitivityAmounts the array of sensitivities; the array is modified by the method * @param sensitivityAmount the value of the sensitivity at the given data * @param sensitivityDate the date associated to the amount to re-bucket */ private static void rebucketingArray( List targetDates, double[] rebucketedSensitivityAmounts, double sensitivityAmount, LocalDate sensitivityDate) { int nbBuckets = targetDates.size(); if (!sensitivityDate.isAfter(targetDates.get(0))) { rebucketedSensitivityAmounts[0] += sensitivityAmount; } else if (!sensitivityDate.isBefore(targetDates.get(nbBuckets - 1))) { rebucketedSensitivityAmounts[nbBuckets - 1] += sensitivityAmount; } else { int indexSensitivityDate = 0; while (sensitivityDate.isAfter(targetDates.get(indexSensitivityDate))) { indexSensitivityDate++; } // 'indexSensitivityDate' contains the index of the node after the sensitivity date long intervalLength = targetDates.get(indexSensitivityDate).toEpochDay() - targetDates.get(indexSensitivityDate - 1).toEpochDay(); double weight = ((double) (targetDates.get(indexSensitivityDate).toEpochDay() - sensitivityDate.toEpochDay())) / intervalLength; rebucketedSensitivityAmounts[indexSensitivityDate - 1] += weight * sensitivityAmount; rebucketedSensitivityAmounts[indexSensitivityDate] += (1.0d - weight) * sensitivityAmount; } } // Check that the dates in the list are sorted in chronological order. private static void checkSortedDates(List dates) { for (int loopdate = 0; loopdate < dates.size() - 1; loopdate++) { ArgChecker.inOrderNotEqual(dates.get(loopdate), dates.get(loopdate + 1), "first date", "following date"); } } //------------------------------------------------------------------------- // restricted constructor private CurveSensitivityUtils() { } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy