/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.forex.provider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.opengamma.analytics.financial.forex.derivative.ForexOptionSingleBarrier;
import com.opengamma.analytics.financial.forex.method.PresentValueForexBlackVolatilityNodeSensitivityDataBundle;
import com.opengamma.analytics.financial.forex.method.PresentValueForexBlackVolatilitySensitivity;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackBarrierPriceFunction;
import com.opengamma.analytics.financial.model.volatility.VolatilityAndBucketedSensitivities;
import com.opengamma.analytics.financial.model.volatility.surface.SmileDeltaTermStructureParametersStrikeInterpolation;
import com.opengamma.analytics.financial.provider.description.forex.BlackForexSmileProviderInterface;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderInterface;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MulticurveSensitivity;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.util.amount.SurfaceValue;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.CurrencyAmount;
import com.opengamma.util.money.MultipleCurrencyAmount;
import com.opengamma.util.tuple.DoublesPair;
/**
* Pricing method for single barrier Forex option transactions in the Black world.
*/
public final class ForexOptionSingleBarrierBlackMethod {
/**
* The method unique instance.
*/
private static final ForexOptionSingleBarrierBlackMethod INSTANCE = new ForexOptionSingleBarrierBlackMethod();
/**
* Private constructor.
*/
private ForexOptionSingleBarrierBlackMethod() {
}
/**
* Return the unique instance of the class.
* @return The instance.
*/
public static ForexOptionSingleBarrierBlackMethod getInstance() {
return INSTANCE;
}
private static final double DEFAULT_GAMMA_SHIFT = 0.00001; // 0.1 basis point
private static final double DEFAULT_VOMMA_SHIFT = 0.00001; // 0.1 basis point
private static final double DEFAULT_VANNA_SHIFT = 0.00001; // 0.1 basis point
/**
* The Black function used in the barrier pricing.
*/
private static final BlackBarrierPriceFunction BARRIER_FUNCTION = BlackBarrierPriceFunction.getInstance();
/**
* Computes the present value for single barrier Forex option in Black model (log-normal spot rate).
* @param optionForex The Forex option.
* @param smileMulticurves The curve and smile data.
* @return The present value (in domestic currency).
*/
public MultipleCurrencyAmount presentValue(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot * dfForeign / dfDomestic;
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double volatility = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex
.getUnderlyingOption().getStrike(), forward);
double price = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, rateDomestic - rateForeign, rateDomestic, volatility);
price *= Math.abs(foreignAmount) * sign;
final CurrencyAmount priceCurrency = CurrencyAmount.of(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2(), price);
return MultipleCurrencyAmount.of(priceCurrency);
}
/**
* Computes the currency exposure for single barrier Forex option in Black model (log-normal spot rate). The sensitivity of the volatility on the spot
* is not taken into account. It is the currency exposure in the Black model where the volatility is suppose to be constant for curve and forward changes.
* @param optionForex The Forex option.
* @param smileMulticurves The curve and smile data.
* @return The currency exposure.
*/
public MultipleCurrencyAmount currencyExposure(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot * Math.exp(-rateForeign * payTime) / Math.exp(-rateDomestic * payTime);
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double volatility = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex
.getUnderlyingOption().getStrike(), forward);
final double[] priceDerivatives = new double[5];
double price = BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, rateDomestic - rateForeign, rateDomestic, volatility,
priceDerivatives);
price *= Math.abs(foreignAmount) * sign;
final double deltaSpot = priceDerivatives[0];
final CurrencyAmount[] currencyExposure = new CurrencyAmount[2];
// Implementation note: foreign currency (currency 1) exposure = Delta_spot * amount1.
currencyExposure[0] = CurrencyAmount.of(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency1(), deltaSpot * Math.abs(foreignAmount) * sign);
// Implementation note: domestic currency (currency 2) exposure = -Delta_spot * amount1 * spot+PV
currencyExposure[1] = CurrencyAmount.of(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2(),
-deltaSpot * Math.abs(optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount()) * spot * sign + price);
return MultipleCurrencyAmount.of(currencyExposure);
}
/**
* Computes the curve sensitivity of the option present value. The sensitivity of the volatility on the forward (and on the curves) is not taken into account. It is the curve
* sensitivity in the Black model where the volatility is suppose to be constant for curve and forward changes.
* @param optionForex The Forex option.
* @param smileMulticurves The curve and smile data.
* @return The curve sensitivity.
*/
public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
// Forward sweep
final double forward = spot * Math.exp(-rateForeign * payTime) / Math.exp(-rateDomestic * payTime);
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double volatility = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex
.getUnderlyingOption().getStrike(), forward);
// The Barrier pricing method parameterizes as a function of rate (r=rateDomestic), and costOfCarry (b=rateDomestic-rateForeign)
// We wish to compute derivatives wrt rateDomestic and rateForeign, not the costOfCarry parameter.
final double[] priceDerivatives = new double[5];
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, rateDomestic - rateForeign, rateDomestic, volatility, priceDerivatives);
// Backward sweep
final double priceBar = 1.0;
final double rCostOfCarryBar = priceDerivatives[3] * Math.abs(foreignAmount) * sign * priceBar;
final double rDomesticBar = (priceDerivatives[2] + priceDerivatives[3]) * Math.abs(foreignAmount) * sign * priceBar;
final double rForeignBar = -1 * rCostOfCarryBar;
// Sensitivity object
final Map<String, List<DoublesPair>> resultMap = new HashMap<>();
final List<DoublesPair> listForeign = new ArrayList<>();
listForeign.add(new DoublesPair(payTime, rForeignBar));
resultMap.put(multicurves.getName(optionForex.getCurrency1()), listForeign);
final List<DoublesPair> listDomestic = new ArrayList<>();
listDomestic.add(new DoublesPair(payTime, rDomesticBar));
resultMap.put(multicurves.getName(optionForex.getCurrency2()), listDomestic);
final MulticurveSensitivity result = MulticurveSensitivity.ofYieldDiscounting(resultMap);
return MultipleCurrencyMulticurveSensitivity.of(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2(), result);
}
/**
* Computes the volatility sensitivity of the option present value.
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @return The curve sensitivity.
*/
public PresentValueForexBlackVolatilitySensitivity presentValueBlackVolatilitySensitivity(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot * Math.exp(-rateForeign * payTime) / Math.exp(-rateDomestic * payTime);
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double volatility = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex
.getUnderlyingOption().getStrike(), forward);
final double[] priceDerivatives = new double[5];
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, rateDomestic - rateForeign, rateDomestic, volatility, priceDerivatives);
final double volatilitySensitivityValue = priceDerivatives[4] * Math.abs(foreignAmount) * sign;
final DoublesPair point = DoublesPair.of(optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex.getUnderlyingOption().getStrike());
// Map<DoublesPair, Double> result = new HashMap<DoublesPair, Double>();
// result.put(point, volatilitySensitivityValue);
final SurfaceValue result = SurfaceValue.from(point, volatilitySensitivityValue);
final PresentValueForexBlackVolatilitySensitivity sensi = new PresentValueForexBlackVolatilitySensitivity(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency1(), optionForex
.getUnderlyingOption().getUnderlyingForex().getCurrency2(), result);
return sensi;
}
/**
* Computes the volatility sensitivity with respect to input data for a vanilla option with the Black function and a volatility from a volatility surface. The sensitivity
* is computed with respect to each node in the volatility surface.
* @param optionForex The Forex option.
* @param smileMulticurves The curve and smile data.
* @return The volatility node sensitivity. The sensitivity figures are, like the present value, in the domestic currency (currency 2).
*/
public PresentValueForexBlackVolatilityNodeSensitivityDataBundle presentValueBlackVolatilityNodeSensitivity(final ForexOptionSingleBarrier optionForex,
final BlackForexSmileProviderInterface smileMulticurves) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
final PresentValueForexBlackVolatilitySensitivity pointSensitivity = presentValueBlackVolatilitySensitivity(optionForex, smileMulticurves);
final SmileDeltaTermStructureParametersStrikeInterpolation volatilityModel = smileMulticurves.getVolatility();
final double df = multicurves.getDiscountFactor(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2(), optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime());
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot
* multicurves.getDiscountFactor(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency1(), optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime()) / df;
final VolatilityAndBucketedSensitivities volAndSensitivities = volatilityModel.getVolatilityAndSensitivities(optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex.getUnderlyingOption()
.getStrike(), forward);
final double[][] nodeWeight = volAndSensitivities.getBucketedSensitivities();
final DoublesPair point = DoublesPair.of(optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex.getUnderlyingOption().getStrike());
final double[][] vega = new double[volatilityModel.getNumberExpiration()][volatilityModel.getNumberStrike()];
for (int loopexp = 0; loopexp < volatilityModel.getNumberExpiration(); loopexp++) {
for (int loopstrike = 0; loopstrike < volatilityModel.getNumberStrike(); loopstrike++) {
vega[loopexp][loopstrike] = nodeWeight[loopexp][loopstrike] * pointSensitivity.getVega().getMap().get(point);
}
}
return new PresentValueForexBlackVolatilityNodeSensitivityDataBundle(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency1(), optionForex.getUnderlyingOption().getUnderlyingForex()
.getCurrency2(), new DoubleMatrix1D(volatilityModel.getTimeToExpiration()), new DoubleMatrix1D(volatilityModel.getDeltaFull()), new DoubleMatrix2D(vega));
}
/**
* Computes the 2nd order spot fx sensitivity of the option present value by centered finite difference <p>
* This gamma is be computed with respect to the direct quote (1 foreign = x domestic)
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @param relShift The shift to the black volatility expressed relative to the input vol level
* @return Gamma
*/
public CurrencyAmount gammaFd(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves, final double relShift) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
// repackage for calls to BARRIER_FUNCTION
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot * Math.exp(-rateForeign * payTime) / Math.exp(-rateDomestic * payTime);
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double vol = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex.getUnderlyingOption()
.getStrike(), forward);
// Bump and compute vega
final double spotUp = (1.0 + relShift) * spot;
final double spotDown = (1.0 - relShift) * spot;
final double[] adjointUp = new double[5];
final double[] adjointDown = new double[5];
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotUp, rateDomestic - rateForeign, rateDomestic, vol, adjointUp);
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotDown, rateDomestic - rateForeign, rateDomestic, vol, adjointDown);
final double deltaUp = adjointUp[0] * Math.abs(foreignAmount) * sign;
final double deltaDown = adjointDown[0] * Math.abs(foreignAmount) * sign;
final double gamma = (deltaUp - deltaDown) / (2 * relShift * spot);
final Currency ccy = optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2();
return CurrencyAmount.of(ccy, gamma);
}
/**
* Computes the 2nd order spot fx sensitivity of the option present value by centered finite difference and a relative shift of 10 basis points
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @return Gamma
*/
public CurrencyAmount gammaFd(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves) {
return gammaFd(optionForex, smileMulticurves, DEFAULT_GAMMA_SHIFT);
}
/**
* Computes the 2nd order volatility sensitivity of the option present value by centered finite difference
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @param relShift The shift to the black volatility expressed relative to the input vol level
* @return Vomma as a SurfaceValue (point, value)
*/
public CurrencyAmount vommaFd(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves, final double relShift) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
// repackage for calls to BARRIER_FUNCTION
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot * Math.exp(-rateForeign * payTime) / Math.exp(-rateDomestic * payTime);
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double vol = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex.getUnderlyingOption()
.getStrike(), forward);
// Bump and compute vega
final double volUp = (1.0 + relShift) * vol;
final double volDown = (1.0 - relShift) * vol;
final double[] adjointUp = new double[5];
final double[] adjointDown = new double[5];
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, rateDomestic - rateForeign, rateDomestic, volUp, adjointUp);
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, rateDomestic - rateForeign, rateDomestic, volDown, adjointDown);
final double vegaUp = adjointUp[4] * Math.abs(foreignAmount) * sign;
final double vegaDown = adjointDown[4] * Math.abs(foreignAmount) * sign;
final double vomma = (vegaUp - vegaDown) / (2 * relShift * vol);
final Currency ccy = optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2();
return CurrencyAmount.of(ccy, vomma);
}
/**
* Computes the 2nd order volatility sensitivity of the option present value by centered finite difference and a default relative shift of 1 basis point
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @return Vomma as a SurfaceValue
*/
public CurrencyAmount vommaFd(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves) {
return vommaFd(optionForex, smileMulticurves, DEFAULT_VOMMA_SHIFT);
}
/**
* Computes the 2nd order cross sensitivity (to spot and vol) by centered finite difference of the price
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @param relShift The shift to the black volatility expressed relative to the input vol level
* @return Vomma as a SurfaceValue
*/
public CurrencyAmount vannaFd(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves, final double relShift) {
return d2PriceDSpotDVolFD(optionForex, smileMulticurves, relShift);
}
/**
* Computes the 2nd order volatility sensitivity of the option present value by centered finite difference and a relative shift of 10 basis points
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @return Vomma as a SurfaceValue
*/
public CurrencyAmount vannaFd(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves) {
return vannaFd(optionForex, smileMulticurves, DEFAULT_VANNA_SHIFT);
}
/**
* Computes the 2nd order cross sensitivity (to spot and vol) by centered finite difference of the vega
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @param relShift The shift to the black volatility expressed relative to the input vol level
* @return Vomma as a SurfaceValue
*/
public CurrencyAmount dVegaDSpotFD(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves, final double relShift) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
// repackage for calls to BARRIER_FUNCTION
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot * Math.exp(-rateForeign * payTime) / Math.exp(-rateDomestic * payTime);
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double vol = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex.getUnderlyingOption()
.getStrike(), forward);
// Bump *spot* and compute vega
final double spotUp = (1.0 + relShift) * spot;
final double spotDown = (1.0 - relShift) * spot;
final double[] adjointUp = new double[5];
final double[] adjointDown = new double[5];
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotUp, rateDomestic - rateForeign, rateDomestic, vol, adjointUp);
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotDown, rateDomestic - rateForeign, rateDomestic, vol, adjointDown);
final double vegaUp = adjointUp[4] * Math.abs(foreignAmount) * sign;
final double vegaDown = adjointDown[4] * Math.abs(foreignAmount) * sign;
final double vanna = (vegaUp - vegaDown) / (2 * relShift * vol);
final Currency ccy = optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2();
return CurrencyAmount.of(ccy, vanna);
}
/**
* Computes the 2nd order cross sensitivity (to spot and vol) by centered finite difference of the delta
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @param relShift The shift to the black volatility expressed relative to the input vol level
* @return Vomma as a SurfaceValue
*/
public CurrencyAmount dDeltaDVolFD(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves, final double relShift) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
// repackage for calls to BARRIER_FUNCTION
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot * Math.exp(-rateForeign * payTime) / Math.exp(-rateDomestic * payTime);
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double vol = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex.getUnderlyingOption()
.getStrike(), forward);
// Bump *vol* and compute delta
final double volUp = (1.0 + relShift) * vol;
final double volDown = (1.0 - relShift) * vol;
final double[] adjointUp = new double[5];
final double[] adjointDown = new double[5];
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, rateDomestic - rateForeign, rateDomestic, volUp, adjointUp);
BARRIER_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, rateDomestic - rateForeign, rateDomestic, volDown, adjointDown);
final double deltaUp = adjointUp[0] * Math.abs(foreignAmount) * sign;
final double deltaDown = adjointDown[0] * Math.abs(foreignAmount) * sign;
final double vanna = (deltaUp - deltaDown) / (2 * relShift * spot);
final Currency ccy = optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2();
return CurrencyAmount.of(ccy, vanna);
}
/**
* Computes the 2nd order cross sensitivity (to spot and vol) by centered finite difference of the price
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @param relShift The shift to the black volatility expressed relative to the input vol level
* @return Vomma as a SurfaceValue
*/
public CurrencyAmount d2PriceDSpotDVolFD(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves, final double relShift) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
// repackage for calls to BARRIER_FUNCTION
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double costOfCarry = rateDomestic - rateForeign;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot * Math.exp(-rateForeign * payTime) / Math.exp(-rateDomestic * payTime);
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double vol = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex.getUnderlyingOption()
.getStrike(), forward);
// Bump spot *and* vol and compute *price*
final double volUp = (1.0 + relShift) * vol;
final double volDown = (1.0 - relShift) * vol;
final double spotUp = (1.0 + relShift) * spot;
final double spotDown = (1.0 - relShift) * spot;
final double pxUpUp = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotUp, costOfCarry, rateDomestic, volUp);
final double pxDownDown = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotDown, costOfCarry, rateDomestic, volDown);
final double pxUpDown = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotUp, costOfCarry, rateDomestic, volDown);
final double pxDownUp = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotDown, costOfCarry, rateDomestic, volUp);
final double vanna = (pxUpUp - pxUpDown - pxDownUp + pxDownDown) / (2 * relShift * spot) / (2 * relShift * vol) * Math.abs(foreignAmount) * sign;
final Currency ccy = optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2();
return CurrencyAmount.of(ccy, vanna);
}
/**
* Computes the 2nd order cross sensitivity (to spot and vol) by centered finite difference of the price
* @param optionForex A single barrier Forex option.
* @param smileMulticurves The curve and smile data.
* @param relShift The shift to the black volatility expressed relative to the input vol level
* @return Vomma as a SurfaceValue
*/
public CurrencyAmount d2PriceDSpotDVolFdAlt(final ForexOptionSingleBarrier optionForex, final BlackForexSmileProviderInterface smileMulticurves, final double relShift) {
ArgumentChecker.notNull(optionForex, "Forex option");
ArgumentChecker.notNull(smileMulticurves, "Smile");
ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
// repackage for calls to BARRIER_FUNCTION
final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
final double rateDomestic = -Math.log(dfDomestic) / payTime;
final double rateForeign = -Math.log(dfForeign) / payTime;
final double costOfCarry = rateDomestic - rateForeign;
final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
final double forward = spot * Math.exp(-rateForeign * payTime) / Math.exp(-rateDomestic * payTime);
final double foreignAmount = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getAmount();
final double rebateByForeignUnit = optionForex.getRebate() / Math.abs(foreignAmount);
final double sign = (optionForex.getUnderlyingOption().isLong() ? 1.0 : -1.0);
final double vol = smileMulticurves.getVolatility(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(), optionForex.getUnderlyingOption()
.getStrike(), forward);
// Bump spot *and* vol and compute *price*
final double volUp = (1.0 + relShift) * vol;
final double volDown = (1.0 - relShift) * vol;
final double spotUp = (1.0 + relShift) * spot;
final double spotDown = (1.0 - relShift) * spot;
final double pxBase = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, costOfCarry, rateDomestic, vol);
final double pxUpUp = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotUp, costOfCarry, rateDomestic, volUp);
final double pxDownDown = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotDown, costOfCarry, rateDomestic, volDown);
final double pxVolUp = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, costOfCarry, rateDomestic, volUp);
final double pxVolDown = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spot, costOfCarry, rateDomestic, volDown);
final double pxSpotUp = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotUp, costOfCarry, rateDomestic, vol);
final double pxSpotDown = BARRIER_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(), rebateByForeignUnit, spotDown, costOfCarry, rateDomestic, vol);
final double vanna = (pxUpUp - pxVolUp - pxSpotUp + 2 * pxBase + pxDownDown - pxVolDown - pxSpotDown) / (2 * relShift * spot * relShift * vol) * Math.abs(foreignAmount) * sign;
final Currency ccy = optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2();
return CurrencyAmount.of(ccy, vanna);
}
}