Package com.opengamma.financial.analytics.model.forex.option.black

Source Code of com.opengamma.financial.analytics.model.forex.option.black.FXOneLookBarrierOptionBlackFunction

/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.forex.option.black;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.ZonedDateTime;

import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.forex.definition.ForexDefinition;
import com.opengamma.analytics.financial.forex.definition.ForexOptionVanillaDefinition;
import com.opengamma.analytics.financial.forex.derivative.ForexOptionVanilla;
import com.opengamma.analytics.financial.instrument.payment.PaymentFixedDefinition;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.financial.model.option.definition.ForexOptionDataBundle;
import com.opengamma.core.security.Security;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValueProperties.Builder;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueRequirementNames;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.analytics.model.equity.option.EquityVanillaBarrierOptionBlackFunction;
import com.opengamma.financial.analytics.model.forex.FXUtils;
import com.opengamma.financial.currency.CurrencyPair;
import com.opengamma.financial.currency.CurrencyPairs;
import com.opengamma.financial.security.option.BarrierDirection;
import com.opengamma.financial.security.option.BarrierType;
import com.opengamma.financial.security.option.FXBarrierOptionSecurity;
import com.opengamma.financial.security.option.SamplingFrequency;
import com.opengamma.util.money.Currency;

/**
* This function splits a European ONE-LOOK Barrier Option into a sum of vanilla FXOptionSecurity's,
* and then calls down to the FXOptionBlackFunction for the particular requirement. <p>
* See FXBarrierOptionBlackFunction for Functions on TRUE Barriers. That is, options that knock in or out contingent on hitting a barrier,
* at ANY time before expiry. The one-look case here only checks the barrier at expiry. <p>
* The payoffs are thus restricted, on cannot have a Down-and-Out nor Down-and-In Calls, nor Up-and-In and Up-and-Out Puts <p>
*/
public abstract class FXOneLookBarrierOptionBlackFunction extends FXOptionBlackSingleValuedFunction {

  /**
   * @param valueRequirementName The desired output
   */
  public FXOneLookBarrierOptionBlackFunction(final String valueRequirementName) {
    super(valueRequirementName);
  }

  @Override
  public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
    final Security security = target.getSecurity();
    if (security instanceof FXBarrierOptionSecurity) {
      return ((FXBarrierOptionSecurity) security).getSamplingFrequency().equals(SamplingFrequency.ONE_LOOK);
    }
    return false;
  }

  @Override
  public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
    final ZonedDateTime now = ZonedDateTime.now(executionContext.getValuationClock());
    final FXBarrierOptionSecurity barrierSec = (FXBarrierOptionSecurity) target.getSecurity();

    final ValueRequirement desiredValue = desiredValues.iterator().next();

    // 1. Get parameters for the smoothing of binary payoffs into put spreads
    final String strOH = desiredValue.getConstraint(ValuePropertyNames.BINARY_OVERHEDGE);
    if (strOH == null) {
      throw new OpenGammaRuntimeException("Could not find: " + ValuePropertyNames.BINARY_OVERHEDGE);
    }
    final Double overhedge = Double.parseDouble(strOH);

    final String strSmooth = desiredValue.getConstraint(ValuePropertyNames.BINARY_SMOOTHING_FULLWIDTH);
    if (strSmooth == null) {
      throw new OpenGammaRuntimeException("Could not find: " + ValuePropertyNames.BINARY_SMOOTHING_FULLWIDTH);
    }
    final Double smoothing = Double.parseDouble(strSmooth);

    // 2. Break the barrier security into it's vanilla analytic DEFINITIONS
    final Set<ForexOptionVanilla> vanillas = vanillaDecomposition(barrierSec, smoothing, overhedge, now, desiredValues);


    // 3. Build up the market data bundle
    final ForexOptionDataBundle<?> market = FXOptionFunctionUtils.buildMarketBundle(now, inputs, target, desiredValues);

    // TODO Confirm whether we need to support both types of dataBundles: SmileDeltaTermStructureParametersStrikeInterpolation AND BlackForexTermStructureParameters

    // 4. Compute Values - in base class
    final Object results = computeValues(vanillas, market);

    // 5. Properties of what's required of this function
    final Object baseQuotePairsObject = inputs.getValue(ValueRequirementNames.CURRENCY_PAIRS);
    if (baseQuotePairsObject == null) {
      throw new OpenGammaRuntimeException("Could not get base/quote pair data");
    }
    final Currency putCurrency = barrierSec.getPutCurrency();
    final Currency callCurrency = barrierSec.getCallCurrency();
    final CurrencyPairs baseQuotePairs = (CurrencyPairs) baseQuotePairsObject;
    final CurrencyPair baseQuotePair = baseQuotePairs.getCurrencyPair(putCurrency, callCurrency);
    if (baseQuotePair == null) {
      throw new OpenGammaRuntimeException("Could not get base/quote pair for currency pair (" + putCurrency + ", " + callCurrency + ")");
    }
    final ValueSpecification spec = new ValueSpecification(getValueRequirementName(), target.toSpecification(), getResultProperties(target, desiredValue, baseQuotePair).get());
    // 6. Return result
    return Collections.singleton(new ComputedValue(spec, results));

  }

  @Override
  protected Builder getResultProperties(final ComputationTarget target) {
    final Builder properties = super.getResultProperties(target);
    return properties.withAny(ValuePropertyNames.BINARY_OVERHEDGE)
        .withAny(ValuePropertyNames.BINARY_SMOOTHING_FULLWIDTH);
  }

  @Override
  protected ValueProperties.Builder getResultProperties(final ComputationTarget target, final String putCurve, final String putCurveCalculationConfig, final String callCurve,
      final String callCurveCalculationConfig, final CurrencyPair baseQuotePair, final ValueProperties optionalProperties) {
    final Builder properties = super.getResultProperties(target, putCurve, putCurveCalculationConfig, callCurve, callCurveCalculationConfig, baseQuotePair, optionalProperties);
    return properties.withAny(ValuePropertyNames.BINARY_OVERHEDGE)
        .withAny(ValuePropertyNames.BINARY_SMOOTHING_FULLWIDTH);
  }

  @Override
  protected ValueProperties.Builder getResultProperties(final ComputationTarget target, final ValueRequirement desiredValue, final CurrencyPair baseQuotePair) {
    final Builder properties = super.getResultProperties(target, desiredValue, baseQuotePair);
    final String binaryOverhead = desiredValue.getConstraint(ValuePropertyNames.BINARY_OVERHEDGE);
    final String binarySmoothing = desiredValue.getConstraint(ValuePropertyNames.BINARY_SMOOTHING_FULLWIDTH);
    return properties.with(ValuePropertyNames.BINARY_OVERHEDGE, binaryOverhead)
        .with(ValuePropertyNames.BINARY_SMOOTHING_FULLWIDTH, binarySmoothing);
  }

  /**
   * This method is defined by extending Functions
   * @param vanillas Set of ForexOptionVanilla that European Barrier is composed of. Binaries are modelled as spreads
   * @param market ForexOptionDataBundle, typically SmileDeltaTermStructureParametersStrikeInterpolation
   * @return  ComputedValue what the function promises to deliver
   */
  protected abstract Object computeValues(Set<ForexOptionVanilla> vanillas, ForexOptionDataBundle<?> market);

  @Override
  public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
    // Get requirements common to all FXOptionBlackFunctions
    final Set<ValueRequirement> commonReqs = super.getRequirements(context, target, desiredValue);
    if (commonReqs == null) {
      return null;
    }
    // Barriers additionally have parameters for the smoothing of binary payoffs into put spreads
    // Return null if they haven't been set so that FXOneLookBarrierOptionDefaultPropertiesFunction can set them
    final Set<String> overhedgeSet = desiredValue.getConstraints().getValues(ValuePropertyNames.BINARY_OVERHEDGE);
    if (overhedgeSet == null || overhedgeSet.size() != 1) {
      s_logger.info("Could not find {} requirement. Looking for a default..", ValuePropertyNames.BINARY_OVERHEDGE);
      return null;
    }
    final Set<String> smoothingSet = desiredValue.getConstraints().getValues(ValuePropertyNames.BINARY_SMOOTHING_FULLWIDTH);
    if (smoothingSet == null || smoothingSet.size() != 1) {
      s_logger.info("Could not find {} requirement. Looking for a default..", ValuePropertyNames.BINARY_SMOOTHING_FULLWIDTH);
      return null;
    }
    return commonReqs;
  }

  // TODO: Consider whether to interpret overhedge so that a positive value always reduces the price, i.e. incorporate isLong
  private static Set<ForexOptionVanilla> vanillaDecomposition(final FXBarrierOptionSecurity barrierSec,
      final double smoothingFullWidth, final double overhedge, final ZonedDateTime valTime, final Set<ValueRequirement> desiredValues) {

    final HashSet<ForexOptionVanilla> vanillas = new HashSet<>();
    // Unpack the barrier security
    final boolean isLong = barrierSec.getLongShort().isLong();
    final ZonedDateTime expiry = barrierSec.getExpiry().getExpiry();
    final ZonedDateTime settlement = barrierSec.getSettlementDate();

    // The barrier has four types
    final BarrierDirection bInOut = barrierSec.getBarrierDirection(); //   KNOCK_IN, KNOCK_OUT,
    final BarrierType bUpDown = barrierSec.getBarrierType(); //   UP, DOWN, DOUBLE
    final double barrier = barrierSec.getBarrierLevel();

    // Put and Call Amounts, along with market convention for quote/base ccy define the strike, notional, and call/put interpretation
    final ValueRequirement desiredValue = desiredValues.iterator().next();
    final String putCurveName = desiredValue.getConstraint(PUT_CURVE);
    final String callCurveName = desiredValue.getConstraint(CALL_CURVE);
    final double callAmt = barrierSec.getCallAmount();
    final Currency callCcy = barrierSec.getCallCurrency();
    final double putAmt = barrierSec.getPutAmount();
    final Currency putCcy = barrierSec.getPutCurrency();

    final boolean inOrder = FXUtils.isInBaseQuoteOrder(putCcy, callCcy);
    double baseAmt; // This is the Notional of the option if interpreted as N*max(w(X-K),0)
    double quoteAmt;
    Currency baseCcy;
    Currency quoteCcy; // This is the valuation currency in the (X,K) interpretation
    String baseCurveName;
    String quoteCurveName;
    boolean linearIsCall; //
    if (inOrder) {
      linearIsCall = false; // putCcy == baseCcy => Put
      baseAmt = putAmt;
      baseCcy = putCcy;
      baseCurveName = putCurveName + "_" + putCcy.getCode();
      quoteAmt = callAmt;
      quoteCcy = callCcy;
      quoteCurveName = callCurveName + "_" + callCcy.getCode();
    } else {
      linearIsCall = true; // callCcy == baseCcy => Call
      baseAmt = callAmt;
      baseCcy = callCcy;
      baseCurveName = callCurveName + "_" + callCcy.getCode();
      quoteAmt = putAmt;
      quoteCcy = putCcy;
      quoteCurveName = putCurveName + "_" + putCcy.getCode();
    }
    final double strike = quoteAmt / baseAmt;
    final String[] baseQuoteCurveNames = new String[] {baseCurveName, quoteCurveName};

    // parameters to model binary as call/put spread
    final double oh = overhedge;
    final double width = barrier * smoothingFullWidth;
    final double size;

    // There are four cases: UP and IN, UP and OUT, DOWN and IN, DOWN and OUT
    // Switch on direction: If UP, use Call Spreads. If DOWN, use Put spreads.
    boolean useCallSpread;
    double nearStrike;
    double farStrike;
    switch (bUpDown) {
      case UP:
        useCallSpread = true;
        if (!linearIsCall) {
          throw new OpenGammaRuntimeException("ONE_LOOK Barriers do not apply to an UP type of Barrier unless the option itself is a Call. Check Call/Put currencies.");
        }
        if (barrier < strike) {
          throw new OpenGammaRuntimeException("Encountered an UP type of BarrierOption where barrier, " + barrier + ", is below strike, " + strike);
        }
        size = (barrier - strike) / width;
        nearStrike = barrier + oh - 0.5 * width;
        farStrike = barrier + oh + 0.5 * width;
        if (nearStrike < 0.0 || farStrike < 0.0) {
          throw new OpenGammaRuntimeException("A strike in the put binary approximation is negative. Look at the BinaryOverhedge and BinarySmoothingFullWidth properties.");
        }
        break;
      case DOWN:
        useCallSpread = false;
        if (linearIsCall) {
          throw new OpenGammaRuntimeException("ONE_LOOK Barriers do not apply to a DOWN type of Barrier unless the option itself is a Put. Check Call/Put currencies.");
        }
        if (barrier > strike) {
          throw new OpenGammaRuntimeException("Encountered a DOWN type of BarrierOption where barrier, " + barrier + ", is above strike, " + strike);
        }
        size = (strike - barrier) / width;
        nearStrike = barrier + oh + 0.5 * width;
        farStrike = barrier + oh - 0.5 * width;
        break;
      case DOUBLE:
        throw new OpenGammaRuntimeException("Encountered an EquityBarrierOption where barrierType is DOUBLE. This isn't yet handled.");
      default:
        throw new OpenGammaRuntimeException("Encountered an EquityBarrierOption with unexpected BarrierType of: " + bUpDown);
    }

    // ForexVanillaOption's are defined in terms of the underlying forward FX transaction, the exchange of two fixed amounts in different currencies.
    // The relative size of the payments implicitly defines the option's strike. So we will build a number of ForexDefinition's below
    final PaymentFixedDefinition quoteCcyPayment = new PaymentFixedDefinition(quoteCcy, settlement, -1 * quoteAmt);
    final PaymentFixedDefinition baseCcyPayment = new PaymentFixedDefinition(baseCcy, settlement, baseAmt);
    final ForexDefinition fxFwd = new ForexDefinition(baseCcyPayment, quoteCcyPayment); // This is what defines the strike, K = quoteAmt / baseAmt
    // We restrike an option by changing the underlying Forex, adjusting the Payments to match the formulae: k = A2/A1, N = A1.
    final ForexDefinition fxFwdForBarrier = new ForexDefinition(baseCcyPayment, new PaymentFixedDefinition(quoteCcy, settlement, -1 * barrier * baseAmt));

    // For the binaries, we do this by adjusting A1' = size * A1; A2' = A1' * newStrike as A1 is the Notional in this interpretation
    final double baseAmtForSpread = size * baseAmt;
    final PaymentFixedDefinition baseCcyPmtForSpread = new PaymentFixedDefinition(baseCcy, settlement, baseAmtForSpread);
    final ForexDefinition fxFwdForNearStrike = new ForexDefinition(baseCcyPmtForSpread, new PaymentFixedDefinition(quoteCcy, settlement, -1 * nearStrike * baseAmtForSpread));
    final ForexDefinition fxFwdForFarStrike = new ForexDefinition(baseCcyPmtForSpread, new PaymentFixedDefinition(quoteCcy, settlement, -1 * farStrike * baseAmtForSpread));


    // Switch  on type
    switch (bInOut) {
      case KNOCK_OUT: // Long a linear at strike, short a linear at barrier, short a binary at barrier of size (barrier-strike)

        final ForexOptionVanillaDefinition longLinearK = new ForexOptionVanillaDefinition(fxFwd, expiry, useCallSpread, isLong);
        final ForexOptionVanillaDefinition shortLinearB = new ForexOptionVanillaDefinition(fxFwdForBarrier, expiry, useCallSpread, !isLong);
        vanillas.add(longLinearK.toDerivative(valTime, baseQuoteCurveNames));
        vanillas.add(shortLinearB.toDerivative(valTime, baseQuoteCurveNames));
        // Short a binary of size, barrier - strike. Modelled as call spread struck around strike + oh, with spread of 2*eps
        final ForexOptionVanillaDefinition shortNear = new ForexOptionVanillaDefinition(fxFwdForNearStrike, expiry, useCallSpread, !isLong);
        final ForexOptionVanillaDefinition longFar = new ForexOptionVanillaDefinition(fxFwdForFarStrike, expiry, useCallSpread, isLong);
        vanillas.add(shortNear.toDerivative(valTime, baseQuoteCurveNames));
        vanillas.add(longFar.toDerivative(valTime, baseQuoteCurveNames));
        break;

      case KNOCK_IN:  // Long a linear at barrier, long a binary at barrier of size (barrier - strike)

        final ForexOptionVanillaDefinition longLinearB = new ForexOptionVanillaDefinition(fxFwdForBarrier, expiry, useCallSpread, isLong);
        vanillas.add(longLinearB.toDerivative(valTime, baseQuoteCurveNames));
        // Long a binary of size, barrier - strike. Modelled as call spread struck around strike + oh, with spread of 2*eps
        final ForexOptionVanillaDefinition longNear = new ForexOptionVanillaDefinition(fxFwdForNearStrike, expiry, useCallSpread, isLong);
        final ForexOptionVanillaDefinition shortFar = new ForexOptionVanillaDefinition(fxFwdForFarStrike, expiry, useCallSpread, !isLong);
        vanillas.add(longNear.toDerivative(valTime, baseQuoteCurveNames));
        vanillas.add(shortFar.toDerivative(valTime, baseQuoteCurveNames));
        break;
      default:
        throw new OpenGammaRuntimeException("Encountered an EquityBarrierOption with unexpected BarrierDirection of: " + bUpDown);
    }
    return vanillas;
  }


  private static final Logger s_logger = LoggerFactory.getLogger(EquityVanillaBarrierOptionBlackFunction.class);

  @Override
  protected Set<ComputedValue> getResult(final InstrumentDerivative forex, final ForexOptionDataBundle<?> data, final ComputationTarget target, final Set<ValueRequirement> desiredValues,
      final FunctionInputs inputs, final ValueSpecification spec, final FunctionExecutionContext executionContext) {
    throw new OpenGammaRuntimeException("Unexpectedly called FXVanillaBarrierOptionBlackFunction.getResult");
  }
}
TOP

Related Classes of com.opengamma.financial.analytics.model.forex.option.black.FXOneLookBarrierOptionBlackFunction

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.