/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.volatilityswap;
import com.google.common.primitives.Doubles;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitorAdapter;
import com.opengamma.analytics.financial.model.volatility.surface.SmileDeltaTermStructureParameters;
import com.opengamma.analytics.financial.provider.description.volatilityswap.CarrLeeFXData;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.tuple.Triple;
/**
* Compute vega of forward volatility by parallel shift to volatility surface
*/
public class CarrLeeFXVolatilitySwapVegaCalculator extends InstrumentDerivativeVisitorAdapter<CarrLeeFXData, Double> {
private static final double DEFAULT_BUMP = 1.0e-7;
private static final CarrLeeNewlyIssuedSyntheticVolatilitySwapCalculator NEW_CALCULATOR = new CarrLeeNewlyIssuedSyntheticVolatilitySwapCalculator();
private static final CarrLeeSeasonedSyntheticVolatilitySwapCalculator SEASONED_CALCULATOR = new CarrLeeSeasonedSyntheticVolatilitySwapCalculator();
private final CarrLeeFXVolatilitySwapCalculator _cal;
private final double _bumpVol;
/**
* Constructor using default bump amount.
* Note that fractional volatility is bumped
*/
public CarrLeeFXVolatilitySwapVegaCalculator() {
this(DEFAULT_BUMP);
}
/**
* Constructor specifying bump amount
* @param bump The bump amount
*/
public CarrLeeFXVolatilitySwapVegaCalculator(final double bump) {
_bumpVol = bump;
_cal = new CarrLeeFXVolatilitySwapCalculator();
}
/**
* Constructor specifying bump amount
* @param bump The bump amount
* @param cal Base calculator
*/
public CarrLeeFXVolatilitySwapVegaCalculator(final double bump, final CarrLeeFXVolatilitySwapCalculator cal) {
ArgumentChecker.notNull(cal, "cal");
_bumpVol = bump;
_cal = cal;
}
/**
* Vega calculator for FX volatility swap based on "bump and reprice" using {@link VolatilitySwapCalculatorResultWithStrikes},
* i.e., assuming the fair value has been already calculated.
* @param result {@link VolatilitySwapCalculatorResultWithStrikes}
* @param swap The FX volatility swap
* @param data The FX data for Carr-Lee
* @return vega
*/
public Double getFXVolatilitySwapVega(final VolatilitySwapCalculatorResultWithStrikes result, final FXVolatilitySwap swap, final CarrLeeFXData data) {
ArgumentChecker.notNull(result, "result");
ArgumentChecker.notNull(swap, "swap");
ArgumentChecker.notNull(data, "data");
final double spot = data.getSpot();
final double timeToExpiry = swap.getTimeToMaturity();
ArgumentChecker.isTrue(Doubles.isFinite(timeToExpiry), "timeToExpiry should be finite");
ArgumentChecker.isTrue(timeToExpiry > 0., "timeToExpiry should be positive");
ArgumentChecker.isTrue(Doubles.isFinite(spot), "spot should be finite");
ArgumentChecker.isTrue(spot > 0., "spot should be positive");
final double domesticDF = data.getMulticurveProvider().getDiscountFactor(swap.getBaseCurrency(), timeToExpiry);
final double foreignDF = data.getMulticurveProvider().getDiscountFactor(swap.getCounterCurrency(), timeToExpiry);
final double domesticRate = -Math.log(domesticDF) / timeToExpiry;
final double foreignRate = -Math.log(foreignDF) / timeToExpiry;
ArgumentChecker.isTrue(Doubles.isFinite(domesticRate), "domestic rate should be finite");
ArgumentChecker.isTrue(Doubles.isFinite(foreignRate), "foreign rate should be finite");
final double[] putStrikes = result.getPutStrikes();
final double forward = spot * Math.exp((domesticRate - foreignRate) * timeToExpiry);
final double[] callStrikes = result.getCallStrikes();
final int nPuts = putStrikes.length;
final int nCalls = callStrikes.length;
final SmileDeltaTermStructureParameters smile = data.getVolatilityData();
final double[] putVols = new double[nPuts];
final double[] callVols = new double[nCalls];
final double[] bumpedPutVols = new double[nPuts];
final double[] bumpedCallVols = new double[nCalls];
for (int i = 0; i < nPuts; ++i) {
putVols[i] = smile.getVolatility(Triple.of(timeToExpiry, putStrikes[i], forward));
bumpedPutVols[i] = putVols[i] + _bumpVol;
}
for (int i = 0; i < nCalls; ++i) {
callVols[i] = smile.getVolatility(Triple.of(timeToExpiry, callStrikes[i], forward));
bumpedCallVols[i] = callVols[i] + _bumpVol;
}
final double baseFV = result.getFairValue();
final double volBumpedFV;
final Double rv = data.getRealizedVariance();
if (rv == null) {
final double stdVol = smile.getVolatility(Triple.of(timeToExpiry, forward, forward));
final double bumpedStdVol = stdVol + _bumpVol;
final VolatilitySwapCalculatorResult volBumpedRes = NEW_CALCULATOR.evaluate(spot, putStrikes, callStrikes, timeToExpiry, domesticRate, foreignRate, bumpedPutVols, bumpedStdVol, bumpedCallVols);
volBumpedFV = volBumpedRes.getFairValue();
} else {
final double timeFromInception = swap.getTimeToObservationStart() < 0 ? Math.abs(swap.getTimeToObservationStart()) : 0;
final VolatilitySwapCalculatorResult volBumpedRes = SEASONED_CALCULATOR.evaluate(spot, putStrikes, callStrikes, timeToExpiry, timeFromInception, domesticRate, foreignRate, bumpedPutVols,
bumpedCallVols, rv);
volBumpedFV = volBumpedRes.getFairValue();
}
return (volBumpedFV - baseFV) / _bumpVol * 1.0e-2;
}
@Override
public Double visitFXVolatilitySwap(final FXVolatilitySwap swap, final CarrLeeFXData data) {
ArgumentChecker.notNull(swap, "swap");
ArgumentChecker.notNull(data, "data");
final VolatilitySwapCalculatorResultWithStrikes result = _cal.visitFXVolatilitySwap(swap, data);
return getFXVolatilitySwapVega(result, swap, data);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(_bumpVol);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((_cal == null) ? 0 : _cal.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof CarrLeeFXVolatilitySwapVegaCalculator)) {
return false;
}
CarrLeeFXVolatilitySwapVegaCalculator other = (CarrLeeFXVolatilitySwapVegaCalculator) obj;
if (Double.doubleToLongBits(_bumpVol) != Double.doubleToLongBits(other._bumpVol)) {
return false;
}
if (_cal == null) {
if (other._cal != null) {
return false;
}
} else if (!_cal.equals(other._cal)) {
return false;
}
return true;
}
}