/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.calculator;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import org.testng.annotations.Test;
import com.google.common.collect.Maps;
import com.opengamma.analytics.financial.forex.method.FXMatrix;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyParameterSensitivity;
import com.opengamma.analytics.math.matrix.CommonsMatrixAlgebra;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.util.money.Currency;
import com.opengamma.util.tuple.ObjectsPair;
import com.opengamma.util.tuple.Pair;
/**
* Tests the portfolio hedging calculator, with simplified examples and then with full scale data.
*/
public class PortfolioHedgingCalculatorTest {
private static final Currency EUR = Currency.EUR;
private static final Currency USD = Currency.USD;
private static final String NAME_1 = "Dsc";
private static final String NAME_2 = "Fwd";
private static final Pair<String, Currency> NAME_1_EUR = new ObjectsPair<>(NAME_1, EUR);
private static final Pair<String, Currency> NAME_1_USD = new ObjectsPair<>(NAME_1, USD);
private static final Pair<String, Currency> NAME_2_EUR = new ObjectsPair<>(NAME_2, EUR);
private static final double EUR_USD = 1.25;
private static final FXMatrix FX_MATRIX = new FXMatrix(EUR, USD, EUR_USD);
private static final double[] SENSI_1 = {1.0, 2.0, 3.0, 0.4};
private static final int NB_SENSI_1 = SENSI_1.length;
private static final double[] SENSI_2 = {0.5, 1.0, 0.5, 1.0, 0.5};
private static final int NB_SENSI_2 = SENSI_2.length;
private static final LinkedHashSet<Pair<String, Integer>> ORDER = new LinkedHashSet<>();
static {
ORDER.add(new ObjectsPair<>(NAME_1, NB_SENSI_1));
ORDER.add(new ObjectsPair<>(NAME_2, NB_SENSI_2));
}
private static final CommonsMatrixAlgebra MATRIX = new CommonsMatrixAlgebra();
private static final double TOLERANCE = 1.0E-8;
@Test
/**
* Test the hedging portfolio with reference instruments equal to the curve construction instruments.
*/
public void exactSolution() {
final LinkedHashSet<Pair<String, Integer>> order = new LinkedHashSet<>();
order.add(new ObjectsPair<>(NAME_1, NB_SENSI_1));
final double[] sensiOpposite = new double[NB_SENSI_1];
for (int loopnode = 0; loopnode < NB_SENSI_1; loopnode++) {
sensiOpposite[loopnode] = -SENSI_1[loopnode];
}
MultipleCurrencyParameterSensitivity ps = new MultipleCurrencyParameterSensitivity();
ps = ps.plus(NAME_1_EUR, new DoubleMatrix1D(SENSI_1));
final MultipleCurrencyParameterSensitivity[] rs = new MultipleCurrencyParameterSensitivity[NB_SENSI_1];
for (int loopnode = 0; loopnode < NB_SENSI_1; loopnode++) {
rs[loopnode] = new MultipleCurrencyParameterSensitivity();
final double[] r = new double[NB_SENSI_1];
r[loopnode] = 1.0;
rs[loopnode] = rs[loopnode].plus(NAME_1_EUR, new DoubleMatrix1D(r));
}
// Unit weights
final DoubleMatrix2D w1 = new DoubleMatrix2D(NB_SENSI_1, NB_SENSI_1);
for (int loopnode = 0; loopnode < NB_SENSI_1; loopnode++) {
w1.getData()[loopnode][loopnode] = 1.0;
}
final double[] hedging1 = PortfolioHedgingCalculator.hedgeQuantity(ps, rs, w1, order, FX_MATRIX);
assertArrayEquals("PortfolioHedgingCalculator: ", sensiOpposite, hedging1, TOLERANCE);
// Non-uniform diagonal weights
final DoubleMatrix2D w2 = new DoubleMatrix2D(NB_SENSI_1, NB_SENSI_1);
for (int loopnode = 0; loopnode < NB_SENSI_1; loopnode++) {
w2.getData()[loopnode][loopnode] = loopnode + 1.0;
}
final double[] hedging2 = PortfolioHedgingCalculator.hedgeQuantity(ps, rs, w2, order, FX_MATRIX);
assertArrayEquals("PortfolioHedgingCalculator: ", sensiOpposite, hedging2, TOLERANCE);
// Tri-diagonal weights
final double[][] w3Array = { {1.0, 0.5, 0, 0}, {0.5, 1.0, 0.5, 0}, {0, 0.5, 1.0, 0.5}, {0, 0, 0.5, 1.0}};
final DoubleMatrix2D w3 = new DoubleMatrix2D(w3Array);
final double[] hedging3 = PortfolioHedgingCalculator.hedgeQuantity(ps, rs, w3, order, FX_MATRIX);
assertArrayEquals("PortfolioHedgingCalculator: ", sensiOpposite, hedging3, TOLERANCE);
}
@Test
/**
* Test the hedging portfolio. The answer is perturbed to check that it is at a minimum.
*/
public void checkMin() {
final int nbSensi = NB_SENSI_1 + NB_SENSI_2;
MultipleCurrencyParameterSensitivity ps = new MultipleCurrencyParameterSensitivity();
ps = ps.plus(NAME_1_EUR, new DoubleMatrix1D(SENSI_1));
ps = ps.plus(NAME_1_USD, new DoubleMatrix1D(SENSI_1));
ps = ps.plus(NAME_2_EUR, new DoubleMatrix1D(SENSI_2));
final int nbReference = 4;
final MultipleCurrencyParameterSensitivity[] rs = new MultipleCurrencyParameterSensitivity[nbReference];
rs[0] = new MultipleCurrencyParameterSensitivity();
rs[0] = rs[0].plus(NAME_1_EUR, new DoubleMatrix1D(new double[] {1.0, 0.0, 0.0, 0.0}));
rs[0] = rs[0].plus(NAME_2_EUR, new DoubleMatrix1D(new double[] {1.0, 0.0, 0.0, 0.0, 0.0}));
rs[1] = new MultipleCurrencyParameterSensitivity();
rs[1] = rs[1].plus(NAME_1_EUR, new DoubleMatrix1D(new double[] {0.0, 0.5, 0.0, 0.0}));
rs[1] = rs[1].plus(NAME_2_EUR, new DoubleMatrix1D(new double[] {0.0, 1.0, 0.0, 0.0, 0.0}));
rs[2] = new MultipleCurrencyParameterSensitivity();
rs[2] = rs[2].plus(NAME_1_EUR, new DoubleMatrix1D(new double[] {0.0, 0.0, 1.0, 2.0}));
rs[2] = rs[2].plus(NAME_2_EUR, new DoubleMatrix1D(new double[] {0.0, 0.0, 0.0, 0.0, 0.0}));
rs[3] = new MultipleCurrencyParameterSensitivity();
rs[3] = rs[3].plus(NAME_1_EUR, new DoubleMatrix1D(new double[] {0.0, 0.0, 0.0, 0.0}));
rs[3] = rs[3].plus(NAME_2_EUR, new DoubleMatrix1D(new double[] {0.0, 0.0, 1.0, 1.0, 1.0}));
// Weights: tridiagonal + sum by curve
final DoubleMatrix2D w = new DoubleMatrix2D(nbSensi + 2, nbSensi);
for (int loopnode = 0; loopnode < nbSensi; loopnode++) {
w.getData()[loopnode][loopnode] = 1.0;
}
for (int loopnode = 0; loopnode < nbSensi - 1; loopnode++) {
w.getData()[loopnode][loopnode + 1] = 0.5;
w.getData()[loopnode + 1][loopnode] = 0.5;
}
w.getData()[NB_SENSI_1 + NB_SENSI_2] = new double[] {1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // Order?
w.getData()[NB_SENSI_1 + NB_SENSI_2 + 1] = new double[] {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0};
final DoubleMatrix2D wtW = (DoubleMatrix2D) MATRIX.multiply(MATRIX.getTranspose(w), w);
// Hedging
final double[] hedging = PortfolioHedgingCalculator.hedgeQuantity(ps, rs, w, ORDER, FX_MATRIX);
MultipleCurrencyParameterSensitivity psMin = new MultipleCurrencyParameterSensitivity();
psMin = psMin.plus(ps);
for (int loopref = 0; loopref < nbReference; loopref++) { // To created the hedge portfolio
psMin = psMin.plus(rs[loopref].multipliedBy(hedging[loopref]));
}
final DoubleMatrix1D psMinMatrix = PortfolioHedgingCalculator.toMatrix(psMin.converted(FX_MATRIX, EUR), ORDER);
final DoubleMatrix2D psMinMatrixT = new DoubleMatrix2D(new double[][] {psMinMatrix.getData()});
final double penalty = ((DoubleMatrix2D) MATRIX.multiply(psMinMatrixT, MATRIX.multiply(wtW, psMinMatrix))).getEntry(0, 0);
final double shift = 0.01;
final double[] penaltyPlus = new double[nbReference];
final double[] penaltyMinus = new double[nbReference];
for (int loopref = 0; loopref < nbReference; loopref++) { // Shift on each quantity
MultipleCurrencyParameterSensitivity psPertPlus = new MultipleCurrencyParameterSensitivity();
psPertPlus = psPertPlus.plus(psMin);
psPertPlus = psPertPlus.plus(rs[loopref].multipliedBy(shift));
final DoubleMatrix1D psPertPlusMat = PortfolioHedgingCalculator.toMatrix(psPertPlus.converted(FX_MATRIX, EUR), ORDER);
final DoubleMatrix2D psPertPlusMatT = new DoubleMatrix2D(new double[][] {psPertPlusMat.getData()});
penaltyPlus[loopref] = ((DoubleMatrix2D) MATRIX.multiply(psPertPlusMatT, MATRIX.multiply(wtW, psPertPlusMat))).getEntry(0, 0);
assertTrue("PortfolioHedgingCalculator: minimum", penalty < penaltyPlus[loopref]);
MultipleCurrencyParameterSensitivity psPertMinus = new MultipleCurrencyParameterSensitivity();
psPertMinus = psPertMinus.plus(psMin);
psPertMinus = psPertMinus.plus(rs[loopref].multipliedBy(-shift));
final DoubleMatrix1D psPertMinusMat = PortfolioHedgingCalculator.toMatrix(psPertMinus.converted(FX_MATRIX, EUR), ORDER);
final DoubleMatrix2D psPertMinusMatT = new DoubleMatrix2D(new double[][] {psPertMinusMat.getData()});
penaltyMinus[loopref] = ((DoubleMatrix2D) MATRIX.multiply(psPertMinusMatT, MATRIX.multiply(wtW, psPertMinusMat))).getEntry(0, 0);
assertTrue("PortfolioHedgingCalculator: minimum " + loopref, penalty < penaltyMinus[loopref]);
}
}
private static final DoubleMatrix1D SENSITIVITY_1_1 = new DoubleMatrix1D(4.0, 2.0, 5.0, 1.5);
private static final DoubleMatrix1D SENSITIVITY_2_1 = new DoubleMatrix1D(5.0, 1.0, 2.0, 5.0, 1.5);
@Test
public void testToMatrix() {
final LinkedHashMap<Pair<String, Currency>, DoubleMatrix1D> map1 = Maps.newLinkedHashMap();
map1.put(NAME_1_EUR, SENSITIVITY_1_1);
map1.put(NAME_2_EUR, SENSITIVITY_2_1);
final MultipleCurrencyParameterSensitivity sensitivity1 = MultipleCurrencyParameterSensitivity.of(map1);
final LinkedHashMap<Pair<String, Currency>, DoubleMatrix1D> map2 = Maps.newLinkedHashMap();
map2.put(NAME_1_EUR, SENSITIVITY_1_1);
map2.put(NAME_2_EUR, SENSITIVITY_1_1);
final MultipleCurrencyParameterSensitivity sensitivity2 = MultipleCurrencyParameterSensitivity.of(map2);
final LinkedHashMap<Pair<String, Currency>, DoubleMatrix1D> map3 = Maps.newLinkedHashMap();
map3.put(NAME_2_EUR, SENSITIVITY_2_1);
map3.put(NAME_1_EUR, SENSITIVITY_1_1);
final MultipleCurrencyParameterSensitivity sensitivity3 = MultipleCurrencyParameterSensitivity.of(map3);
final double[] total1 = new double[SENSITIVITY_1_1.getNumberOfElements() + SENSITIVITY_2_1.getNumberOfElements()];
int j = 0;
for (int i = 0; i < SENSITIVITY_1_1.getNumberOfElements(); i++, j++) {
total1[j] = SENSITIVITY_1_1.getEntry(i);
}
for (int i = 0; i < SENSITIVITY_2_1.getNumberOfElements(); i++, j++) {
total1[j] = SENSITIVITY_2_1.getEntry(i);
}
final DoubleMatrix1D expectedMatrix1 = new DoubleMatrix1D(total1);
assertEquals("PortfolioHedgingCalculator: toMatrix", expectedMatrix1, PortfolioHedgingCalculator.toMatrix(sensitivity1, ORDER));
assertEquals("PortfolioHedgingCalculator: toMatrix", expectedMatrix1, PortfolioHedgingCalculator.toMatrix(sensitivity3, ORDER));
assertFalse("Test toMatrix, unequal sensitivities: ", expectedMatrix1.equals(PortfolioHedgingCalculator.toMatrix(sensitivity2, ORDER)));
}
}