/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.forex.method;
import org.apache.commons.lang.ObjectUtils;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.tuple.ObjectsPair;
import com.opengamma.util.tuple.Pair;
/**
* Class describing the present value sensitivity to a Forex currency pair volatility grid.
*/
public class PresentValueForexBlackVolatilityNodeSensitivityDataBundle {
/**
* The currency pair.
*/
private final Pair<Currency, Currency> _currencyPair;
/**
* The volatility sensitivity as a matrix with same dimension as the input. The sensitivity value is in second/domestic currency.
*/
private final DoubleMatrix2D _vega;
private final DoubleMatrix1D _expiries;
private final DoubleMatrix1D _delta;
/**
* Constructor with empty sensitivities for a given currency pair.
* @param ccy1 First currency, not null
* @param ccy2 Second currency, not null
* @param numberExpiry The number of expiries, not negative
* @param numberDelta The number of deltas, not negative
*/
public PresentValueForexBlackVolatilityNodeSensitivityDataBundle(final Currency ccy1, final Currency ccy2, final int numberExpiry, final int numberDelta) {
ArgumentChecker.notNull(ccy1, "currency 1");
ArgumentChecker.notNull(ccy2, "currency 2");
ArgumentChecker.isTrue(numberExpiry >= 0, "number of expiries must be greater than or equal to zero");
ArgumentChecker.isTrue(numberDelta >= 0, "number of deltas must be greater than or equal to zero");
_currencyPair = ObjectsPair.of(ccy1, ccy2);
_expiries = new DoubleMatrix1D(new double[numberExpiry]);
_delta = new DoubleMatrix1D(new double[numberDelta]);
_vega = new DoubleMatrix2D(numberExpiry, numberDelta);
}
/**
* Constructor with initial sensitivities for a given currency pair.
* @param ccy1 First currency, not null
* @param ccy2 Second currency, not null
* @param expiries The expiries for the vega matrix, not null
* @param delta The deltas for the vega matrix, not null
* @param vega The initial sensitivity, not null
*/
public PresentValueForexBlackVolatilityNodeSensitivityDataBundle(final Currency ccy1, final Currency ccy2, final DoubleMatrix1D expiries, final DoubleMatrix1D delta, final DoubleMatrix2D vega) {
ArgumentChecker.notNull(ccy1, "currency 1");
ArgumentChecker.notNull(ccy2, "currency 2");
ArgumentChecker.notNull(expiries, "expiries");
ArgumentChecker.notNull(delta, "strikes");
ArgumentChecker.notNull(vega, "Matrix");
ArgumentChecker.isTrue(vega.getNumberOfRows() == expiries.getNumberOfElements(), "Number of rows did not match number of expiries");
ArgumentChecker.isTrue(vega.getNumberOfColumns() == delta.getNumberOfElements(), "Number of columns did not match number of delta");
_currencyPair = ObjectsPair.of(ccy1, ccy2);
_expiries = expiries;
_delta = delta;
_vega = vega;
}
/**
* Gets the currency pair.
* @return The currency pair.
*/
public Pair<Currency, Currency> getCurrencyPair() {
return _currencyPair;
}
/**
* Gets the volatility sensitivity (vega) map.
* @return The sensitivity.
*/
public DoubleMatrix2D getVega() {
return _vega;
}
public DoubleMatrix1D getExpiries() {
return _expiries;
}
public DoubleMatrix1D getDelta() {
return _delta;
}
/**
* Compare two sensitivities with a given tolerance. Return "true" if the currency pairs are the same and all the sensitivities are within the tolerance.
* @param value1 The first sensitivity.
* @param value2 The second sensitivity.
* @param tolerance The tolerance.
* @return The comparison flag.
*/
public static boolean compare(final PresentValueForexBlackVolatilityNodeSensitivityDataBundle value1, final PresentValueForexBlackVolatilityNodeSensitivityDataBundle value2,
final double tolerance) {
if (!value1._currencyPair.equals(value2._currencyPair)) {
return false;
}
if ((value1._expiries.getNumberOfElements() != value2._expiries.getNumberOfElements()) || (value1._delta.getNumberOfElements() != value2._delta.getNumberOfElements())) {
return false;
}
for (int loopexp = 0; loopexp < value1._expiries.getNumberOfElements(); loopexp++) {
for (int loopdel = 0; loopdel < value1._delta.getNumberOfElements(); loopdel++) {
if (value1._vega.getEntry(loopexp, loopdel) - value2._vega.getEntry(loopexp, loopdel) > tolerance) {
return false;
}
}
}
return true;
}
/**
* Computes the volatility sensitivities with respect to quoted data (ATM, RR, strangle) related to the (strike) node sensitivity.
* @return The volatility quote sensitivities. The sensitivity figures are in the same currency as the node sensitivity.
* The first dimension is the expiration and the second dimension the quotes: ATM, Risk Reversal, Strangle.
*/
public PresentValueForexBlackVolatilityQuoteSensitivityDataBundle quoteSensitivity() {
final double[][] vegaStrike = getVega().getData();
final int nbStrike = vegaStrike[0].length;
final double[][] result = new double[vegaStrike.length][nbStrike];
final int nbQuote = (vegaStrike[0].length - 1) / 2;
for (int loopexp = 0; loopexp < vegaStrike.length; loopexp++) {
result[loopexp][0] = vegaStrike[loopexp][nbQuote]; // ATM
for (int loopstrike = 0; loopstrike < nbQuote; loopstrike++) {
result[loopexp][loopstrike + 1] = -vegaStrike[loopexp][loopstrike] / 2.0 + vegaStrike[loopexp][nbStrike - 1 - loopstrike] / 2.0; // Risk Reversal
result[loopexp][loopstrike + nbQuote + 1] = vegaStrike[loopexp][loopstrike] + vegaStrike[loopexp][nbStrike - 1 - loopstrike]; // Strangle
result[loopexp][0] += vegaStrike[loopexp][loopstrike] + vegaStrike[loopexp][nbStrike - 1 - loopstrike];
}
}
return new PresentValueForexBlackVolatilityQuoteSensitivityDataBundle(_currencyPair.getFirst(), _currencyPair.getSecond(), _expiries.getData(), _delta.getData(), result);
}
//TODO Add possibility to add a sensitivity?
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + _currencyPair.hashCode();
result = prime * result + _expiries.hashCode();
result = prime * result + _delta.hashCode();
result = prime * result + _vega.hashCode();
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PresentValueForexBlackVolatilityNodeSensitivityDataBundle other = (PresentValueForexBlackVolatilityNodeSensitivityDataBundle) obj;
if (!ObjectUtils.equals(_currencyPair, other._currencyPair)) {
return false;
}
if (!ObjectUtils.equals(_vega, other._vega)) {
return false;
}
if (!ObjectUtils.equals(_expiries, other._expiries)) {
return false;
}
if (!ObjectUtils.equals(_delta, other._delta)) {
return false;
}
return true;
}
}