/** * 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.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang.ArrayUtils; import org.threeten.bp.Period; import com.opengamma.analytics.financial.forex.method.PresentValueForexBlackVolatilityNodeSensitivityDataBundle; import com.opengamma.analytics.financial.forex.method.PresentValueForexBlackVolatilityQuoteSensitivityDataBundle; import com.opengamma.analytics.financial.interestrate.sensitivity.PresentValueSwaptionSurfaceSensitivity; import com.opengamma.analytics.math.matrix.DoubleMatrix2D; import com.opengamma.analytics.util.amount.SurfaceValue; import com.opengamma.financial.analytics.DoubleLabelledMatrix2D; import com.opengamma.financial.analytics.DoubleLabelledMatrix3D; import com.opengamma.financial.analytics.volatility.surface.VolatilitySurfaceDefinition; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.time.Tenor; import com.opengamma.util.tuple.DoublesPair; import com.opengamma.util.tuple.Pair; import com.opengamma.util.tuple.Pairs; /** * Contains utility methods that vega output from the analytics libraries into objects that * can be transported and displayed by the engine. */ public class VegaMatrixUtils { private static final DecimalFormat FX_OPTION_FORMATTER = new DecimalFormat("##"); private static final DecimalFormat IR_FUTURE_OPTION_FORMATTER = new DecimalFormat("##.###"); private static final DecimalFormat DELTA_FORMATTER = new DecimalFormat("##"); /** * Returns a bucketed FX option vega matrix with delta / expiry axes. * @param vegas The vegas, not null * @return A labelled vega matrix. */ public static DoubleLabelledMatrix2D getVegaFXMatrix(final PresentValueForexBlackVolatilityNodeSensitivityDataBundle vegas) { ArgumentChecker.notNull(vegas, "vegas"); final double[] expiries = vegas.getExpiries().getData(); final double[] delta = vegas.getDelta().getData(); final double[][] vega = vegas.getVega().getData(); final int nDelta = delta.length; final int nExpiries = expiries.length; final Double[] rowValues = new Double[nExpiries]; final String[] rowLabels = new String[nExpiries]; final Double[] columnValues = new Double[nDelta]; final String[] columnLabels = new String[nDelta]; final double[][] values = new double[nDelta][nExpiries]; for (int i = 0; i < nDelta; i++) { columnValues[i] = delta[i]; columnLabels[i] = "P" + DELTA_FORMATTER.format(delta[i] * 100) + " " + vegas.getCurrencyPair().getFirst() + "/" + vegas.getCurrencyPair().getSecond(); for (int j = 0; j < nExpiries; j++) { if (i == 0) { rowValues[j] = expiries[j]; rowLabels[j] = VegaMatrixUtils.getFXVolatilityFormattedExpiry(expiries[j]); } values[i][j] = vega[j][i]; } } return new DoubleLabelledMatrix2D(rowValues, rowLabels, columnValues, columnLabels, values); } /** * Returns a bucketed FX option vega matrix with the same axes as the volatility quotes (i.e. ATM, risk-reversal and butterfly quotes) * @param vegas The vegas, not null * @return A labelled vega matrix */ public static DoubleLabelledMatrix2D getVegaFXQuoteMatrix(final PresentValueForexBlackVolatilityQuoteSensitivityDataBundle vegas) { ArgumentChecker.notNull(vegas, "vegas"); final double[] expiries = vegas.getExpiries(); final double[] delta = vegas.getDelta(); final double[][] vega = vegas.getVega(); final int nDelta = delta.length; final int nExpiries = expiries.length; final Double[] rowValues = new Double[nExpiries]; final String[] rowLabels = new String[nExpiries]; final Double[] columnValues = new Double[nDelta]; final String[] columnLabels = new String[nDelta]; final double[][] values = new double[nDelta][nExpiries]; columnLabels[0] = "ATM " + " " + vegas.getCurrencyPair().getFirst() + "/" + vegas.getCurrencyPair().getSecond(); columnValues[0] = 0.; final int n = (nDelta - 1) / 2; for (int i = 0; i < n; i++) { columnLabels[1 + i] = "RR " + FX_OPTION_FORMATTER.format(delta[i] * 100) + " " + vegas.getCurrencyPair().getFirst() + "/" + vegas.getCurrencyPair().getSecond(); columnValues[1 + i] = 1. + i; columnLabels[n + 1 + i] = "B " + FX_OPTION_FORMATTER.format(delta[i] * 100) + " " + vegas.getCurrencyPair().getFirst() + "/" + vegas.getCurrencyPair().getSecond(); columnValues[n + 1 + i] = n + 1. + i; } for (int j = 0; j < nExpiries; j++) { rowValues[j] = expiries[j]; rowLabels[j] = getFXVolatilityFormattedExpiry(expiries[j]); } for (int i = 0; i < nDelta; i++) { for (int j = 0; j < nExpiries; j++) { values[i][j] = vega[j][i]; } } return new DoubleLabelledMatrix2D(rowValues, rowLabels, columnValues, columnLabels, values); } /** * Returns a bucketed interest rate future option vega matrix with strike / expiry axes. * @param definition The volatility surface, not null * @param matrix The vega matrix, not null * @param expiryValues The expiries, not null * @return A labelled vega matrix. */ public static DoubleLabelledMatrix2D getVegaIRFutureOptionQuoteMatrix(final VolatilitySurfaceDefinition<?, ?> definition, final DoubleMatrix2D matrix, final double[] expiryValues) { ArgumentChecker.notNull(definition, "definition"); ArgumentChecker.notNull(matrix, "matrix"); ArgumentChecker.notNull(expiryValues, "expiry values"); final int columns = matrix.getNumberOfRows(); ArgumentChecker.isTrue(columns == expiryValues.length, "Did not have same number of columns as expiries"); final int rows = matrix.getNumberOfColumns(); final Double[] rowValues = new Double[rows]; final Double[] columnValues = new Double[columns]; final Object[] rowLabels = new Object[rows]; final Object[] columnLabels = new Object[columns]; final double[][] values = new double[rows][columns]; final Object[] strikes = definition.getYs(); final Object[] nFutureOption = definition.getXs(); for (int i = 0; i < rows; i++) { final double strike = ((Number) strikes[i]).doubleValue(); rowValues[i] = strike; rowLabels[i] = IR_FUTURE_OPTION_FORMATTER.format(strike); for (int j = 0; j < columns; j++) { if (i == 0) { final int n = ((Number) nFutureOption[j]).intValue(); columnValues[j] = Double.valueOf(n); columnLabels[j] = n; } values[i][j] = matrix.getEntry(j, i); } } return new DoubleLabelledMatrix2D(columnValues, columnLabels, rowValues, rowLabels, values); } /** * Returns a bucketed swaption vega cube with swaption expiry / swap maturity / distance from ATM axes. * @param fittedPoints The points in the swaption volatility cube, not null * @param matrices a map from swaption expiry to vega matrix, not null * @return A labelled vega cube */ public static DoubleLabelledMatrix3D getVegaSwaptionCubeQuoteMatrix(final Map<Pair<Tenor, Tenor>, Double[]> fittedPoints, final Map<Double, DoubleMatrix2D> matrices) { ArgumentChecker.notNull(fittedPoints, "fitted points"); ArgumentChecker.notNull(matrices, "matrices"); final List<Double> xKeysList = new ArrayList<>(); final List<Double> xLabelsList = new ArrayList<>(); final List<Double> yKeysList = new ArrayList<>(); final List<Tenor> yLabelsList = new ArrayList<>(); final List<Double> zKeysList = new ArrayList<>(); final List<Tenor> zLabelsList = new ArrayList<>(); for (final Entry<Pair<Tenor, Tenor>, Double[]> entry : fittedPoints.entrySet()) { final double swapMaturity = getTime(entry.getKey().getFirst()); if (!zKeysList.contains(swapMaturity)) { zKeysList.add(swapMaturity); zLabelsList.add(entry.getKey().getFirst()); } final double swaptionExpiry = getTime(entry.getKey().getSecond()); if (!yKeysList.contains(swaptionExpiry)) { yKeysList.add(swaptionExpiry); yLabelsList.add(entry.getKey().getSecond()); } if (xKeysList.size() == 0) { final Double[] relativeStrikesArray = entry.getValue(); for (final Double relativeStrike : relativeStrikesArray) { xKeysList.add(relativeStrike); xLabelsList.add(relativeStrike); } } } final Double[] xKeys = xKeysList.toArray(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY); final Double[] xLabels = xLabelsList.toArray(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY); final Double[] yKeys = yKeysList.toArray(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY); final Tenor[] yLabels = yLabelsList.toArray(new Tenor[yLabelsList.size()]); final Double[] zKeys = zKeysList.toArray(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY); final Tenor[] zLabels = zLabelsList.toArray(new Tenor[zLabelsList.size()]); final double[][][] values = new double[zKeys.length][xKeys.length][yKeys.length]; for (int i = 0; i < zKeys.length; i++) { values[i] = matrices.get(zKeys[i]).toArray(); } return new DoubleLabelledMatrix3D(xKeys, xLabels, yKeys, yLabels, zKeys, zLabels, values); } /** * Returns a bucketed swaption atm vega matrix with swaption expiry / swap maturity axes. * @param vegas a map from swaption expiry, maturity to vega, not null * @return A labelled vega matrix */ public static DoubleLabelledMatrix2D getVegaSwaptionMatrix(final PresentValueSwaptionSurfaceSensitivity vegas) { ArgumentChecker.notNull(vegas, "vegas"); final HashMap<DoublesPair, Double> vegaMap = vegas.getSensitivity().getMap(); final List<Double> xKeysList = new ArrayList<>(); final List<Double> xLabelsList = new ArrayList<>(); final List<Double> yKeysList = new ArrayList<>(); final List<Double> yLabelsList = new ArrayList<>(); for (final Entry<DoublesPair, Double> entry : vegaMap.entrySet()) { final double swapExpiry = entry.getKey().getFirst(); if (!xKeysList.contains(swapExpiry)) { xKeysList.add(swapExpiry); xLabelsList.add(swapExpiry); } final double swaptionMaturity = entry.getKey().getSecond(); if (!yKeysList.contains(swaptionMaturity)) { yKeysList.add(swaptionMaturity); yLabelsList.add(swaptionMaturity); } } final Double[] xKeys = xKeysList.toArray(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY); final Double[] xLabels = xLabelsList.toArray(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY); final int nExpiries = xLabels.length; final Double[] yKeys = yKeysList.toArray(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY); final Double[] yLabels = yLabelsList.toArray(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY); final int nMaturities = yLabels.length; final double[][] values = new double[nMaturities][nExpiries]; for (int i = 0; i < nExpiries; i++) { for (int j = 0; j < nMaturities; j++) { DoublesPair key = DoublesPair.of(xKeys[i].doubleValue(), yKeys[j].doubleValue()); Double value = vegaMap.get(key); values[j][i] = value == null ? 0.0 : value; } } return new DoubleLabelledMatrix2D(xKeys, xLabels, yKeys, yLabels, values); } public static String getFXVolatilityFormattedExpiry(final double expiry) { if (expiry < 1. / 54) { final int days = (int) Math.ceil((365 * expiry)); return days + "D"; } if (expiry < 1. / 13) { final int weeks = (int) Math.ceil((52 * expiry)); return weeks + "W"; } if (expiry < 0.95) { final int months = (int) Math.ceil((12 * expiry)); return months + "M"; } return ((int) Math.ceil(expiry)) + "Y"; } private static double getTime(final Tenor tenor) { //TODO this should be moved into a utils class final Period period = tenor.getPeriod(); final double months = period.toTotalMonths(); return months / 12.; } }