/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.math.interpolation.Interpolator2D;
import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.math.matrix.MatrixAlgebra;
import com.opengamma.analytics.math.matrix.MatrixAlgebraFactory;
import com.opengamma.financial.analytics.volatility.surface.VolatilitySurfaceDefinition;
import com.opengamma.util.tuple.DoublesPair;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Pairs;
/**
*
*/
public class SABRVegaCalculationUtils {
private static final MatrixAlgebra ALGEBRA = MatrixAlgebraFactory.OG_ALGEBRA;
private static final DoublesPairComparator COMPARATOR = new DoublesPairComparator();
public static DoubleMatrix2D getVegaSurface(final double alpha, final double rho, final double nu, final Map<Double, Interpolator1DDataBundle> alphaDataBundle,
final Map<Double, Interpolator1DDataBundle> rhoDataBundle, final Map<Double, Interpolator1DDataBundle> nuDataBundle,
final Map<DoublesPair, DoubleMatrix2D> inverseJacobians, final DoublesPair expiryMaturity, final Interpolator2D nodeSensitivityCalculator,
final Map<Double, List<Double>> fittedDataPoints, final VolatilitySurfaceDefinition<Object, Object> definition) {
final Map<Double, List<Pair<Double, Double>>> alphaGridNodeSensitivities =
SABRVegaCalculationUtils.getMaturityExpiryValueMap(nodeSensitivityCalculator.getNodeSensitivitiesForValue(alphaDataBundle, expiryMaturity));
final Map<Double, List<Pair<Double, Double>>> nuGridNodeSensitivities =
SABRVegaCalculationUtils.getMaturityExpiryValueMap(nodeSensitivityCalculator.getNodeSensitivitiesForValue(nuDataBundle, expiryMaturity));
final Map<Double, List<Pair<Double, Double>>> rhoGridNodeSensitivities =
SABRVegaCalculationUtils.getMaturityExpiryValueMap(nodeSensitivityCalculator.getNodeSensitivitiesForValue(rhoDataBundle, expiryMaturity));
final Map<Double, List<Pair<Double, DoubleMatrix2D>>> orderedInverseJacobian = getMaturityExpiryValueMap(inverseJacobians);
//TODO having to know the order of the parameters is not good - change the result that is returned from the fit
final DoubleMatrix2D alphaResult = getVegaSurfaceForParameter(alpha, alphaGridNodeSensitivities, orderedInverseJacobian, 0, fittedDataPoints, definition);
final DoubleMatrix2D rhoResult = getVegaSurfaceForParameter(rho, rhoGridNodeSensitivities, orderedInverseJacobian, 1, fittedDataPoints, definition);
final DoubleMatrix2D nuResult = getVegaSurfaceForParameter(nu, nuGridNodeSensitivities, orderedInverseJacobian, 2, fittedDataPoints, definition);
return (DoubleMatrix2D) ALGEBRA.add(alphaResult, ALGEBRA.add(nuResult, rhoResult));
}
public static Map<Double, DoubleMatrix2D> getVegaCube(final double alpha, final double rho, final double nu, final Map<Double, Interpolator1DDataBundle> alphaDataBundle,
final Map<Double, Interpolator1DDataBundle> rhoDataBundle, final Map<Double, Interpolator1DDataBundle> nuDataBundle,
final Map<DoublesPair, DoubleMatrix2D> inverseJacobians, final DoublesPair expiryMaturity, final Interpolator2D nodeSensitivityCalculator) {
final Map<Double, List<Pair<Double, Double>>> alphaGridNodeSensitivities =
SABRVegaCalculationUtils.getMaturityExpiryValueMap(nodeSensitivityCalculator.getNodeSensitivitiesForValue(alphaDataBundle, expiryMaturity));
final Map<Double, List<Pair<Double, Double>>> nuGridNodeSensitivities =
SABRVegaCalculationUtils.getMaturityExpiryValueMap(nodeSensitivityCalculator.getNodeSensitivitiesForValue(nuDataBundle, expiryMaturity));
final Map<Double, List<Pair<Double, Double>>> rhoGridNodeSensitivities =
SABRVegaCalculationUtils.getMaturityExpiryValueMap(nodeSensitivityCalculator.getNodeSensitivitiesForValue(rhoDataBundle, expiryMaturity));
final Map<Double, List<Pair<Double, DoubleMatrix2D>>> orderedInverseJacobian = getMaturityExpiryValueMap(inverseJacobians);
//TODO having to know the order of the parameters is not good - change the result that is returned from the fit
final Map<Double, DoubleMatrix2D> alphaResult = getVegaCubeForParameter(alpha, alphaGridNodeSensitivities, orderedInverseJacobian, 0);
final Map<Double, DoubleMatrix2D> rhoResult = getVegaCubeForParameter(rho, rhoGridNodeSensitivities, orderedInverseJacobian, 1);
final Map<Double, DoubleMatrix2D> nuResult = getVegaCubeForParameter(nu, nuGridNodeSensitivities, orderedInverseJacobian, 2);
if (!alphaResult.keySet().equals(nuResult.keySet())) {
throw new OpenGammaRuntimeException("Did not have the same number of maturities in the nu results as in the alpha results");
}
if (!alphaResult.keySet().equals(rhoResult.keySet())) {
throw new OpenGammaRuntimeException("Did not have the same number of maturities in the rho results as in the alpha results");
}
final Map<Double, DoubleMatrix2D> result = new HashMap<>();
for (final Map.Entry<Double, DoubleMatrix2D> alphaEntry : alphaResult.entrySet()) {
result.put(alphaEntry.getKey(), (DoubleMatrix2D) ALGEBRA.add(alphaEntry.getValue(), ALGEBRA.add(nuResult.get(alphaEntry.getKey()), rhoResult.get(alphaEntry.getKey()))));
}
return result;
}
private static <T> Map<Double, List<Pair<Double, T>>> getMaturityExpiryValueMap(final Map<DoublesPair, T> data) {
final TreeMap<DoublesPair, T> sorted = new TreeMap<>(COMPARATOR);
sorted.putAll(data);
final Map<Double, List<Pair<Double, T>>> result = new TreeMap<>();
for (final Map.Entry<DoublesPair, T> entry : sorted.entrySet()) {
final double maturity = entry.getKey().second;
if (!result.containsKey(maturity)) {
final List<Pair<Double, T>> expiryValue = new ArrayList<>();
expiryValue.add(Pairs.of(entry.getKey().first, entry.getValue()));
result.put(maturity, expiryValue);
} else {
final List<Pair<Double, T>> expiryValue = result.get(maturity);
expiryValue.add(Pairs.of(entry.getKey().first, entry.getValue()));
}
}
return result;
}
private static DoubleMatrix2D getVegaSurfaceForParameter(final double parameter, final Map<Double, List<Pair<Double, Double>>> gridNodeSensitivities,
final Map<Double, List<Pair<Double, DoubleMatrix2D>>> inverseJacobians, final int parameterNumber, final Map<Double, List<Double>> fittedDataPoints,
final VolatilitySurfaceDefinition<Object, Object> definition) {
if (inverseJacobians.size() != 1) {
throw new OpenGammaRuntimeException("Cannot handle volatility cubes");
}
final List<Pair<Double, Double>> gns = gridNodeSensitivities.values().iterator().next();
final List<Pair<Double, DoubleMatrix2D>> invJac = inverseJacobians.values().iterator().next();
final int rows = gns.size();
final double[][] result = new double[rows][];
for (int i = 0; i < rows; i++) {
final Pair<Double, Double> expirySensitivity = gns.get(i);
final double expiry = expirySensitivity.getFirst();
final List<Double> fittedDataPointsForExpiry = fittedDataPoints.get(expiry);
final List<Object> allDataPointsForExpiry = Arrays.asList(definition.getYs());
final int totalStrikes = allDataPointsForExpiry.size();
final double sensitivity = expirySensitivity.getSecond();
final Pair<Double, DoubleMatrix2D> expiryMatrix = invJac.get(i);
if (Double.doubleToLongBits(expiry) != Double.doubleToLongBits(expiryMatrix.getFirst())) {
throw new OpenGammaRuntimeException("Should never happen");
}
final DoubleMatrix2D m = expiryMatrix.getSecond();
result[i] = new double[totalStrikes];
for (int j = 0; j < m.getNumberOfColumns(); j++) {
final double temp = m.getEntry(parameterNumber, j) * sensitivity * parameter;
final int k = totalStrikes - allDataPointsForExpiry.indexOf(fittedDataPointsForExpiry.get(j)) - 1;
result[i][k] = temp;
}
}
return new DoubleMatrix2D(result);
}
private static Map<Double, DoubleMatrix2D> getVegaCubeForParameter(final double parameter, final Map<Double, List<Pair<Double, Double>>> gridNodeSensitivities,
final Map<Double, List<Pair<Double, DoubleMatrix2D>>> inverseJacobians, final int parameterNumber) {
final Map<Double, DoubleMatrix2D> vega = new TreeMap<>();
for (final Map.Entry<Double, List<Pair<Double, Double>>> entry : gridNodeSensitivities.entrySet()) {
final List<Pair<Double, Double>> gns = entry.getValue();
final List<Pair<Double, DoubleMatrix2D>> invJac = inverseJacobians.get(entry.getKey());
final int rows = gns.size();
final double[][] result = new double[rows][];
for (int i = 0; i < rows; i++) {
final Pair<Double, Double> expirySensitivity = gns.get(i);
final double expiry = expirySensitivity.getFirst();
final double sensitivity = expirySensitivity.getSecond();
final Pair<Double, DoubleMatrix2D> expiryMatrix = invJac.get(i);
if (Double.doubleToLongBits(expiry) != Double.doubleToLongBits(expiryMatrix.getFirst())) {
throw new OpenGammaRuntimeException("Should never happen");
}
final DoubleMatrix2D m = expiryMatrix.getSecond();
result[i] = new double[m.getNumberOfColumns()];
for (int j = 0; j < m.getNumberOfColumns(); j++) {
result[i][j] = m.getEntry(parameterNumber, j) * sensitivity * parameter;
}
}
vega.put(entry.getKey(), new DoubleMatrix2D(result));
}
return vega;
}
private static final class DoublesPairComparator implements Comparator<DoublesPair> {
public DoublesPairComparator() {
}
@Override
public int compare(final DoublesPair p1, final DoublesPair p2) {
if (Double.compare(p1.second, p2.second) == 0) {
return Double.compare(p1.first, p2.first);
}
return Double.compare(p1.second, p2.second);
}
}
}