com.opengamma.analytics.financial.forex.method.ForexOptionSingleBarrierBlackMethod.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.forex.method.ForexOptionSingleBarrierBlackMethod.java

Source

/**
 * 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 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.ForexOptionSingleBarrier;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.financial.interestrate.InterestRateCurveSensitivity;
import com.opengamma.analytics.financial.interestrate.YieldCurveBundle;
import com.opengamma.analytics.financial.model.option.definition.SmileDeltaTermStructureDataBundle;
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.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.util.surface.SurfaceValue;
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 implements ForexPricingMethod {

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

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

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

    /**
     * The Black function used in the barrier pricing.
     */
    private static final BlackBarrierPriceFunction BLACK_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 smile The volatility and curves description.
     * @return The present value (in domestic currency).
     */
    public MultipleCurrencyAmount presentValue(final ForexOptionSingleBarrier optionForex,
            final SmileDeltaTermStructureDataBundle smile) {
        Validate.notNull(optionForex, "Forex option");
        Validate.notNull(smile, "Smile");
        final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
        final double rateDomestic = smile.getCurve(
                optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency2().getFundingCurveName())
                .getInterestRate(payTime);
        final double rateForeign = smile.getCurve(
                optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getFundingCurveName())
                .getInterestRate(payTime);
        final double spot = smile.getFxRates().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 = FXVolatilityUtils.getVolatility(smile, optionForex.getCurrency1(),
                optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(),
                optionForex.getUnderlyingOption().getStrike(), forward);
        double price = BLACK_FUNCTION.getPrice(optionForex.getUnderlyingOption(), optionForex.getBarrier(),
                rebateByForeignUnit, spot, rateForeign, rateDomestic, volatility);
        price *= Math.abs(foreignAmount) * sign;
        final CurrencyAmount priceCurrency = CurrencyAmount
                .of(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2(), price);
        return MultipleCurrencyAmount.of(priceCurrency);
    }

    @Override
    public MultipleCurrencyAmount presentValue(final InstrumentDerivative instrument,
            final YieldCurveBundle curves) {
        Validate.isTrue(instrument instanceof ForexOptionSingleBarrier, "Single barrier Forex option");
        Validate.isTrue(curves instanceof SmileDeltaTermStructureDataBundle, "Smile delta data bundle required");
        return presentValue((ForexOptionSingleBarrier) instrument, (SmileDeltaTermStructureDataBundle) curves);
    }

    /**
     * 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 smile The volatility and curves description.
     * @return The currency exposure.
     */
    public MultipleCurrencyAmount currencyExposure(final ForexOptionSingleBarrier optionForex,
            final SmileDeltaTermStructureDataBundle smile) {
        Validate.notNull(optionForex, "Forex option");
        Validate.notNull(smile, "Smile");
        final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
        final double rateDomestic = smile.getCurve(
                optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency2().getFundingCurveName())
                .getInterestRate(payTime);
        final double rateForeign = smile.getCurve(
                optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1().getFundingCurveName())
                .getInterestRate(payTime);
        final double spot = smile.getFxRates().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 = FXVolatilityUtils.getVolatility(smile, optionForex.getCurrency1(),
                optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(),
                optionForex.getUnderlyingOption().getStrike(), forward);
        final double[] priceDerivatives = new double[5];
        double price = BLACK_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(),
                rebateByForeignUnit, spot, 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);
    }

    @Override
    public MultipleCurrencyAmount currencyExposure(final InstrumentDerivative instrument,
            final YieldCurveBundle curves) {
        Validate.isTrue(instrument instanceof ForexOptionSingleBarrier, "Single barrier Forex option");
        Validate.isTrue(curves instanceof SmileDeltaTermStructureDataBundle, "Smile delta data bundle required");
        return currencyExposure((ForexOptionSingleBarrier) instrument, (SmileDeltaTermStructureDataBundle) curves);
    }

    /**
     * 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 smile The volatility and curves description.
     * @return The curve sensitivity.
     */
    public MultipleCurrencyInterestRateCurveSensitivity presentValueCurveSensitivity(
            final ForexOptionSingleBarrier optionForex, final SmileDeltaTermStructureDataBundle smile) {
        Validate.notNull(optionForex, "Forex option");
        Validate.notNull(smile, "Smile");
        final String domesticCurveName = optionForex.getUnderlyingOption().getUnderlyingForex()
                .getPaymentCurrency2().getFundingCurveName();
        final String foreignCurveName = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1()
                .getFundingCurveName();
        final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
        final double rateDomestic = smile.getCurve(domesticCurveName).getInterestRate(payTime);
        final double rateForeign = smile.getCurve(foreignCurveName).getInterestRate(payTime);
        final double spot = smile.getFxRates().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 = FXVolatilityUtils.getVolatility(smile, optionForex.getCurrency1(),
                optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(),
                optionForex.getUnderlyingOption().getStrike(), forward);
        final double[] priceDerivatives = new double[5];
        BLACK_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(),
                rebateByForeignUnit, spot, rateForeign, rateDomestic, volatility, priceDerivatives);
        // Backward sweep
        final double priceBar = 1.0;
        final double rForeignBar = priceDerivatives[3] * Math.abs(foreignAmount) * sign * priceBar;
        final double rDomesticBar = priceDerivatives[2] * Math.abs(foreignAmount) * sign * priceBar;
        // Sensitivity object
        final List<DoublesPair> listForeign = new ArrayList<DoublesPair>();
        listForeign.add(new DoublesPair(payTime, rForeignBar));
        final Map<String, List<DoublesPair>> resultForeignMap = new HashMap<String, List<DoublesPair>>();
        resultForeignMap.put(foreignCurveName, listForeign);
        InterestRateCurveSensitivity result = new InterestRateCurveSensitivity(resultForeignMap);
        final List<DoublesPair> listDomestic = new ArrayList<DoublesPair>();
        listDomestic.add(new DoublesPair(payTime, rDomesticBar));
        final Map<String, List<DoublesPair>> resultDomesticMap = new HashMap<String, List<DoublesPair>>();
        resultDomesticMap.put(domesticCurveName, listDomestic);
        result = result.plus(new InterestRateCurveSensitivity(resultDomesticMap));
        return MultipleCurrencyInterestRateCurveSensitivity
                .of(optionForex.getUnderlyingOption().getUnderlyingForex().getCurrency2(), result);
    }

    /**
     * Present value curve sensitivity with a generic instrument as argument.
     * @param instrument A single barrier Forex option.
     * @param curves The volatility and curves description (SmileDeltaTermStructureDataBundle).
     * @return The curve sensitivity.
     */
    @Override
    public MultipleCurrencyInterestRateCurveSensitivity presentValueCurveSensitivity(
            final InstrumentDerivative instrument, final YieldCurveBundle curves) {
        Validate.isTrue(instrument instanceof ForexOptionSingleBarrier, "Single barrier Forex option");
        Validate.isTrue(curves instanceof SmileDeltaTermStructureDataBundle, "Smile delta data bundle required");
        return presentValueCurveSensitivity((ForexOptionSingleBarrier) instrument,
                (SmileDeltaTermStructureDataBundle) curves);
    }

    /**
     * Computes the volatility sensitivity of the option present value.
     * @param optionForex A single barrier Forex option.
     * @param smile The volatility and curves description.
     * @return The curve sensitivity.
     */
    public PresentValueForexBlackVolatilitySensitivity presentValueBlackVolatilitySensitivity(
            final ForexOptionSingleBarrier optionForex, final SmileDeltaTermStructureDataBundle smile) {
        Validate.notNull(optionForex, "Forex option");
        Validate.notNull(smile, "Smile");
        final String domesticCurveName = optionForex.getUnderlyingOption().getUnderlyingForex()
                .getPaymentCurrency2().getFundingCurveName();
        final String foreignCurveName = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1()
                .getFundingCurveName();
        final double payTime = optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime();
        final double rateDomestic = smile.getCurve(domesticCurveName).getInterestRate(payTime);
        final double rateForeign = smile.getCurve(foreignCurveName).getInterestRate(payTime);
        final double spot = smile.getFxRates().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 = FXVolatilityUtils.getVolatility(smile, optionForex.getCurrency1(),
                optionForex.getCurrency2(), optionForex.getUnderlyingOption().getTimeToExpiry(),
                optionForex.getUnderlyingOption().getStrike(), forward);
        final double[] priceDerivatives = new double[5];
        BLACK_FUNCTION.getPriceAdjoint(optionForex.getUnderlyingOption(), optionForex.getBarrier(),
                rebateByForeignUnit, spot, 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 present value volatility sensitivity with a generic instrument as argument.
     * @param instrument A single barrier Forex option.
     * @param curves The volatility and curves description (SmileDeltaTermStructureDataBundle).
     * @return The volatility sensitivity.
     */
    public PresentValueForexBlackVolatilitySensitivity presentValueBlackVolatilitySensitivity(
            final InstrumentDerivative instrument, final YieldCurveBundle curves) {
        Validate.isTrue(instrument instanceof ForexOptionSingleBarrier, "Single barrier Forex option");
        Validate.isTrue(curves instanceof SmileDeltaTermStructureDataBundle, "Smile delta data bundle required");
        return presentValueBlackVolatilitySensitivity((ForexOptionSingleBarrier) instrument,
                (SmileDeltaTermStructureDataBundle) curves);
    }

    /**
     * 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 smile 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 SmileDeltaTermStructureDataBundle smile) {
        Validate.notNull(optionForex, "Forex option");
        Validate.notNull(smile, "Smile");
        final PresentValueForexBlackVolatilitySensitivity pointSensitivity = presentValueBlackVolatilitySensitivity(
                optionForex, smile);
        final SmileDeltaTermStructureParametersStrikeInterpolation volatilityModel = smile.getVolatilityModel();
        final double df = smile
                .getCurve(optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency2()
                        .getFundingCurveName())
                .getDiscountFactor(optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentTime());
        final double spot = smile.getFxRates().getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2());
        final double forward = spot * smile
                .getCurve(optionForex.getUnderlyingOption().getUnderlyingForex().getPaymentCurrency1()
                        .getFundingCurveName())
                .getDiscountFactor(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));
    }

}