Package com.opengamma.analytics.financial.forex.provider

Source Code of com.opengamma.analytics.financial.forex.provider.ForexOptionVanillaVannaVolgaMethod

/**
* Copyright (C) 2012 - 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 org.apache.commons.lang.Validate;

import com.opengamma.analytics.financial.forex.derivative.ForexOptionVanilla;
import com.opengamma.analytics.financial.forex.method.PresentValueForexBlackVolatilitySensitivity;
import com.opengamma.analytics.financial.model.option.definition.SmileDeltaParameters;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackFunctionData;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackPriceFunction;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.financial.provider.description.forex.BlackForexVannaVolgaProviderInterface;
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.util.amount.SurfaceValue;
import com.opengamma.util.money.CurrencyAmount;
import com.opengamma.util.money.MultipleCurrencyAmount;
import com.opengamma.util.tuple.DoublesPair;

/**
* Pricing method for vanilla Forex option transactions with Vanna-Volga method.
* <p>Reference: The vanna-volga method for implied volatilities (2007), A. Castagna and F. Mercurio, Risk, 106-111, January 2007.
* <p>OG implementation: Vanna-volga method for Forex options, version 1.0, June 2012.
* <p>The reference volatility used for Black computation is the second volatility (usually corresponding to the ATM strike).
*/
public final class ForexOptionVanillaVannaVolgaMethod {

  /**
   * The method unique instance.
   */
  private static final ForexOptionVanillaVannaVolgaMethod INSTANCE = new ForexOptionVanillaVannaVolgaMethod();

  /**
   * Return the unique instance of the class.
   * @return The instance.
   */
  public static ForexOptionVanillaVannaVolgaMethod getInstance() {
    return INSTANCE;
  }

  /**
   * Private constructor.
   */
  private ForexOptionVanillaVannaVolgaMethod() {
  }

  /**
   * The Black function used in the pricing.
   */
  private static final BlackPriceFunction BLACK_FUNCTION = new BlackPriceFunction();

  /**
   * Computes the present value of the vanilla option with the Black function and a volatility from a volatility surface.
   * @param optionForex The Forex option.
   * @param smileMulticurves The curve and smile data.
   * @return The present value. The value is in the domestic currency (currency 2).
   */
  public MultipleCurrencyAmount presentValue(final ForexOptionVanilla optionForex, final BlackForexVannaVolgaProviderInterface smileMulticurves) {
    Validate.notNull(optionForex, "Forex option");
    Validate.notNull(smileMulticurves, "Smile");
    Validate.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
    final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
    final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), optionForex.getUnderlyingForex().getPaymentTime());
    final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), optionForex.getUnderlyingForex().getPaymentTime());
    final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
    final double forward = spot * dfForeign / dfDomestic;
    final SmileDeltaParameters smileAtTime = smileMulticurves.getSmile(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getTimeToExpiry());
    final double[] strikesVV = smileAtTime.getStrike(forward);
    final double[] volVV = smileAtTime.getVolatility();
    final double volATM = volVV[1];
    final double[] priceVVATM = new double[3];
    final double[] priceVVsmile = new double[3];
    final BlackFunctionData dataBlackATM = new BlackFunctionData(forward, dfDomestic, volATM);
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) { // Implementation note: The adjustment for K2 is 0
      final BlackFunctionData dataBlackSmile = new BlackFunctionData(forward, dfDomestic, volVV[loopvv]);
      final EuropeanVanillaOption optionVV = new EuropeanVanillaOption(strikesVV[loopvv], optionForex.getTimeToExpiry(), true);
      priceVVATM[loopvv] = BLACK_FUNCTION.getPriceFunction(optionVV).evaluate(dataBlackATM);
      priceVVsmile[loopvv] = BLACK_FUNCTION.getPriceFunction(optionVV).evaluate(dataBlackSmile);
    }
    final double priceFlat = BLACK_FUNCTION.getPriceFunction(optionForex).evaluate(dataBlackATM);
    final double[] x = vannaVolgaWeights(optionForex, forward, dfDomestic, strikesVV, volVV);
    double price = priceFlat;
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) {
      price += x[loopvv] * (priceVVsmile[loopvv] - priceVVATM[loopvv]);
    }
    price *= Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()) * (optionForex.isLong() ? 1.0 : -1.0);
    final CurrencyAmount pvCurrency = CurrencyAmount.of(optionForex.getUnderlyingForex().getCurrency2(), price);
    return MultipleCurrencyAmount.of(pvCurrency);
  }

  /**
   * Computes the currency exposure of the vanilla option with the Black function and a volatility from a volatility surface. The exposure is computed in both option currencies.
   * @param optionForex The Forex option.
   * @param smileMulticurves The curve and smile data.
   * @return The currency exposure
   */
  public MultipleCurrencyAmount currencyExposure(final ForexOptionVanilla optionForex, final BlackForexVannaVolgaProviderInterface smileMulticurves) {
    Validate.notNull(optionForex, "Forex option");
    Validate.notNull(smileMulticurves, "Smile");
    Validate.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
    final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
    final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), optionForex.getUnderlyingForex().getPaymentTime());
    final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), optionForex.getUnderlyingForex().getPaymentTime());
    final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
    final double forward = spot * dfForeign / dfDomestic;
    final SmileDeltaParameters smileAtTime = smileMulticurves.getSmile(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getTimeToExpiry());
    final double[] strikesVV = smileAtTime.getStrike(forward);
    final double[] volVV = smileAtTime.getVolatility();
    final double volATM = volVV[1];
    final double[][] priceVVATM = new double[3][];
    final double[][] priceVVsmile = new double[3][];
    final BlackFunctionData dataBlackATM = new BlackFunctionData(forward, dfDomestic, volATM);
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) { // Implementation note: The adjustment for K2 is 0
      final BlackFunctionData dataBlackSmile = new BlackFunctionData(forward, dfDomestic, volVV[loopvv]);
      final EuropeanVanillaOption optionVV = new EuropeanVanillaOption(strikesVV[loopvv], optionForex.getTimeToExpiry(), true);
      priceVVATM[loopvv] = BLACK_FUNCTION.getPriceAdjoint(optionVV, dataBlackATM);
      priceVVsmile[loopvv] = BLACK_FUNCTION.getPriceAdjoint(optionVV, dataBlackSmile);
    }
    final double[] priceFlat = BLACK_FUNCTION.getPriceAdjoint(optionForex, dataBlackATM);
    final double[] x = vannaVolgaWeights(optionForex, forward, dfDomestic, strikesVV, volVV);
    double price = priceFlat[0];
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) {
      price += x[loopvv] * (priceVVsmile[loopvv][0] - priceVVATM[loopvv][0]);
    }
    price *= Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()) * (optionForex.isLong() ? 1.0 : -1.0);
    double deltaSpot = priceFlat[1];
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) {
      deltaSpot += x[loopvv] * (priceVVsmile[loopvv][1] - priceVVATM[loopvv][1]);
    }
    deltaSpot *= dfForeign / dfDomestic;
    final double sign = (optionForex.isLong() ? 1.0 : -1.0);
    final CurrencyAmount[] currencyExposure = new CurrencyAmount[2];
    // Implementation note: foreign currency (currency 1) exposure = Delta_spot * amount1.
    currencyExposure[0] = CurrencyAmount.of(optionForex.getUnderlyingForex().getCurrency1(), deltaSpot * Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()) * sign);
    // Implementation note: domestic currency (currency 2) exposure = -Delta_spot * amount1 * spot+PV
    currencyExposure[1] = CurrencyAmount.of(optionForex.getUnderlyingForex().getCurrency2(), -deltaSpot * Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()) * spot * sign
        + price);
    return MultipleCurrencyAmount.of(currencyExposure);
  }

  /**
   * Computes the volatility sensitivity of the vanilla option to the reference volatilities.
   * @param optionForex The Forex option.
   * @param smileMulticurves The curve and smile data.
   * @return The volatility sensitivity. The sensitivity figures are, like the present value, in the domestic currency (currency 2).
   */
  public PresentValueForexBlackVolatilitySensitivity presentValueBlackVolatilitySensitivity(final ForexOptionVanilla optionForex, final BlackForexVannaVolgaProviderInterface smileMulticurves) {
    Validate.notNull(optionForex, "Forex option");
    Validate.notNull(smileMulticurves, "Smile");
    Validate.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
    final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
    final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), optionForex.getUnderlyingForex().getPaymentTime());
    final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), optionForex.getUnderlyingForex().getPaymentTime());
    final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
    final double forward = spot * dfForeign / dfDomestic;
    final SmileDeltaParameters smileAtTime = smileMulticurves.getSmile(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getTimeToExpiry());
    final double[] strikesVV = smileAtTime.getStrike(forward);
    final double[] volVV = smileAtTime.getVolatility();
    final double volATM = volVV[1];
    final double[] priceVVATM = new double[3];
    final double[] priceVVsmile = new double[3];
    final double[] vegaSmile = new double[3];
    final BlackFunctionData dataBlackATM = new BlackFunctionData(forward, dfDomestic, volATM);
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) { // Implementation note: The adjustment for K2 is 0
      final BlackFunctionData dataBlackSmile = new BlackFunctionData(forward, dfDomestic, volVV[loopvv]);
      final EuropeanVanillaOption optionVV = new EuropeanVanillaOption(strikesVV[loopvv], optionForex.getTimeToExpiry(), true);
      priceVVATM[loopvv] = BLACK_FUNCTION.getPriceFunction(optionVV).evaluate(dataBlackATM);
      priceVVsmile[loopvv] = BLACK_FUNCTION.getPriceFunction(optionVV).evaluate(dataBlackSmile);
      vegaSmile[loopvv] = BLACK_FUNCTION.getVegaFunction(optionVV).evaluate(dataBlackSmile);
    }
    //    final double priceFlat = BLACK_FUNCTION.getPriceFunction(optionForex).evaluate(dataBlackATM);
    final double[] vega = new double[3];
    final double[] x = vannaVolgaWeights(optionForex, forward, dfDomestic, strikesVV, volVV, vega);
    //    double price = priceFlat;
    //    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) {
    //      price += x[loopvv] * (priceVVsmile[loopvv] - priceVVATM[loopvv]);
    //    }
    //    price *= Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()) * (optionForex.isLong() ? 1.0 : -1.0);
    final double[] vegaReference = new double[3];
    vegaReference[0] = x[0] * vegaSmile[0];
    vegaReference[2] = x[2] * vegaSmile[2];
    vegaReference[1] = vega[1] - x[0] * vega[0] - x[2] * vega[2];
    final SurfaceValue result = new SurfaceValue();
    for (int loopvv = 0; loopvv < 3; loopvv++) {
      final DoublesPair point = DoublesPair.of(optionForex.getTimeToExpiry(), strikesVV[loopvv]);
      result.add(point, vegaReference[loopvv] * Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()) * (optionForex.isLong() ? 1.0 : -1.0));
    }
    final PresentValueForexBlackVolatilitySensitivity sensi = new PresentValueForexBlackVolatilitySensitivity(optionForex.getUnderlyingForex().getCurrency1(), optionForex.getUnderlyingForex()
        .getCurrency2(), result);
    // TODO: Review when the currency order is not in the standard order.
    return sensi;
  }

  /**
   * Computes the curve sensitivity of the option present value. The sensitivity of the volatility and the weights on the forward (and on the curves) is not taken into account.
   * @param optionForex The Forex option.
   * @param smileMulticurves The curve and smile data.
   * @return The curve sensitivity.
   */
  public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final ForexOptionVanilla optionForex, final BlackForexVannaVolgaProviderInterface smileMulticurves) {
    Validate.notNull(optionForex, "Forex option");
    Validate.notNull(smileMulticurves, "Smile");
    Validate.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
    final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
    final SmileDeltaParameters smileAtTime = smileMulticurves.getSmile(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getTimeToExpiry());
    final double payTime = optionForex.getUnderlyingForex().getPaymentTime();
    // Forward sweep
    final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), payTime);
    final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), payTime);
    final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
    final double forward = spot * dfForeign / dfDomestic;
    final double[] strikesVV = smileAtTime.getStrike(forward);
    final double[] volVV = smileAtTime.getVolatility();
    final double volATM = volVV[1];
    final double[][] priceVVAdjATM = new double[3][];
    final double[][] priceVVAdjsmile = new double[3][];
    final BlackFunctionData dataBlackATM = new BlackFunctionData(forward, dfDomestic, volATM);
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) { // Implementation note: The adjustment for K2 is 0
      final BlackFunctionData dataBlackSmile = new BlackFunctionData(forward, dfDomestic, volVV[loopvv]);
      final EuropeanVanillaOption optionVV = new EuropeanVanillaOption(strikesVV[loopvv], optionForex.getTimeToExpiry(), true);
      priceVVAdjATM[loopvv] = BLACK_FUNCTION.getPriceAdjoint(optionVV, dataBlackATM);
      priceVVAdjsmile[loopvv] = BLACK_FUNCTION.getPriceAdjoint(optionVV, dataBlackSmile);
    }
    final double[] priceFlat = BLACK_FUNCTION.getPriceAdjoint(optionForex, dataBlackATM);
    final double[] x = vannaVolgaWeights(optionForex, forward, dfDomestic, strikesVV, volVV);
    final double factor = Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()) * (optionForex.isLong() ? 1.0 : -1.0);
    //    double pv = priceFlat[0];
    //    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) {
    //      pv += x[loopvv] * (priceVVAdjsmile[loopvv][0] - priceVVAdjATM[loopvv][0]);
    //    }
    //    pv *= factor;
    // Backward sweep
    final double pvBar = 1.0;
    final double[] priceVVATMBar = new double[3];
    final double[] priceVVsmileBar = new double[3];
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) {
      priceVVATMBar[loopvv] = -x[loopvv] * factor * pvBar;
      priceVVsmileBar[loopvv] = x[loopvv] * factor * pvBar;
    }
    final double priceFlatBar = factor * pvBar;
    double forwardBar = priceFlat[1] * priceFlatBar;
    double dfDomesticBar = priceFlat[0] / dfDomestic * priceFlatBar;
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) {
      forwardBar += priceVVAdjATM[loopvv][1] * priceVVATMBar[loopvv];
      forwardBar += priceVVAdjsmile[loopvv][1] * priceVVsmileBar[loopvv];
      dfDomesticBar += priceVVAdjATM[loopvv][0] / dfDomestic * priceVVATMBar[loopvv];
      dfDomesticBar += priceVVAdjsmile[loopvv][0] / dfDomestic * priceVVsmileBar[loopvv];
    }
    dfDomesticBar += -spot * dfForeign / (dfDomestic * dfDomestic) * forwardBar;
    final double dfForeignBar = spot / dfDomestic * forwardBar;
    final double rForeignBar = -payTime * dfForeign * dfForeignBar;
    final double rDomesticBar = -payTime * dfDomestic * dfDomesticBar;
    // 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.getUnderlyingForex().getCurrency2(), result);
  }

  /**
   * Computes the weights used for adjustment in the vanna-volga method.
   * The weight for the second adjustment (corresponding to ATM strike) is not computed as the adjustment itself is 0 for that strike in our implementation.
   * @param optionForex The option.
   * @param forward The forward FX rate.
   * @param dfDomestic The discounting factor to the payment date in the domestic currency.
   * @param strikesReference The reference strikes used for the vanna-volga method.
   * @param volatilitiesReference The volatilities at the reference strikes.
   * @return The weights.
   */
  public double[] vannaVolgaWeights(final ForexOptionVanilla optionForex, final double forward, final double dfDomestic, final double[] strikesReference, final double[] volatilitiesReference) {
    return vannaVolgaWeights(optionForex, forward, dfDomestic, strikesReference, volatilitiesReference, new double[3]);
  }

  /**
   * Computes the weights used for adjustment in the vanna-volga method.
   * The weight for the second adjustment (corresponding to ATM strike) is not computed as the adjustment itself is 0 for that strike in our implementation.
   * @param optionForex The option.
   * @param forward The forward FX rate.
   * @param dfDomestic The discounting factor to the payment date in the domestic currency.
   * @param strikesReference The reference strikes used for the vanna-volga method.
   * @param volatilitiesReference The volatilities at the reference strikes.
   * @param vega The vega using the base volatility at the reference points (index 0 and 2) and at the strike (index 1). The array is changed with the method call.
   * @return The weights.
   */
  public double[] vannaVolgaWeights(final ForexOptionVanilla optionForex, final double forward, final double dfDomestic, final double[] strikesReference, final double[] volatilitiesReference,
      final double[] vega) {
    final double strike = optionForex.getStrike();
    final double volATM = volatilitiesReference[1]; // The reference volatility is the "middle" one, which is often ATM.
    final BlackFunctionData dataBlackATM = new BlackFunctionData(forward, dfDomestic, volATM);
    for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) { // Implementation note: The adjustment for K2 is 0
      final EuropeanVanillaOption optionVV = new EuropeanVanillaOption(strikesReference[loopvv], optionForex.getTimeToExpiry(), true);
      vega[loopvv] = BLACK_FUNCTION.getVegaFunction(optionVV).evaluate(dataBlackATM);
    }
    final double vegaFlat = BLACK_FUNCTION.getVegaFunction(optionForex).evaluate(dataBlackATM);
    vega[1] = vegaFlat;
    final double lnk21 = Math.log(strikesReference[1] / strikesReference[0]);
    final double lnk31 = Math.log(strikesReference[2] / strikesReference[0]);
    final double lnk32 = Math.log(strikesReference[2] / strikesReference[1]);
    final double[] lnk = new double[3];
    for (int loopvv = 0; loopvv < 3; loopvv++) {
      lnk[loopvv] = Math.log(strikesReference[loopvv] / strike);
    }
    final double[] x = new double[3];
    x[0] = vegaFlat * lnk[1] * lnk[2] / (vega[0] * lnk21 * lnk31);
    x[2] = vegaFlat * lnk[0] * lnk[1] / (vega[2] * lnk31 * lnk32);
    return x;
  }

}
TOP

Related Classes of com.opengamma.analytics.financial.forex.provider.ForexOptionVanillaVannaVolgaMethod

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.