com.opengamma.financial.analytics.model.equity.option.EquityVanillaBarrierOptionBlackFunction.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.financial.analytics.model.equity.option.EquityVanillaBarrierOptionBlackFunction.java

Source

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

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

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

import com.google.common.collect.Sets;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.ExerciseDecisionType;
import com.opengamma.analytics.financial.commodity.definition.SettlementType;
import com.opengamma.analytics.financial.equity.StaticReplicationDataBundle;
import com.opengamma.analytics.financial.equity.option.EquityIndexOption;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.core.security.Security;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.security.ExerciseTypeAnalyticsVisitorAdapter;
import com.opengamma.financial.security.FinancialSecurityTypes;
import com.opengamma.financial.security.option.BarrierDirection;
import com.opengamma.financial.security.option.BarrierType;
import com.opengamma.financial.security.option.EquityBarrierOptionSecurity;
import com.opengamma.financial.security.option.EuropeanExerciseType;
import com.opengamma.financial.security.option.ExerciseType;
import com.opengamma.financial.security.option.OptionType;
import com.opengamma.id.ExternalId;
import com.opengamma.util.async.AsynchronousExecution;
import com.opengamma.util.money.Currency;

/**
 * This function splits a barrier option into a sum of vanilla calls or puts,
 * and then calls down to the EquityIndexOptionFunction as its requirements
 */
public abstract class EquityVanillaBarrierOptionBlackFunction extends EquityOptionBlackFunction {
    /** The logger */
    private static final Logger s_logger = LoggerFactory.getLogger(EquityVanillaBarrierOptionBlackFunction.class);

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

    /**
     * This method is defined by extending Functions
     * @param vanillaOptions Set of EquityIndexOptions that European Barrier is composed of. Binaries are modelled as spreads
     * @param market EquityOptionDataBundle
     * @param inputs The market data inputs
     * @param desiredValues The desired values
     * @param targetSpec The target specification of the result
     * @param resultProperties The result properties
     * @return the result
     */
    protected abstract Set<ComputedValue> computeValues(Set<EquityIndexOption> vanillaOptions,
            StaticReplicationDataBundle market, final FunctionInputs inputs,
            final Set<ValueRequirement> desiredValues, final ComputationTargetSpecification targetSpec,
            final ValueProperties resultProperties);

    @Override
    protected Set<ComputedValue> computeValues(final InstrumentDerivative derivative,
            final StaticReplicationDataBundle market, final FunctionInputs inputs,
            final Set<ValueRequirement> desiredValues, final ComputationTargetSpecification targetSpec,
            final ValueProperties resultProperties) {
        throw new OpenGammaRuntimeException("Execution wasn't intended to go here. Please review.");
    }

    @Override
    public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs,
            final ComputationTarget target, final Set<ValueRequirement> desiredValues)
            throws AsynchronousExecution {

        final ZonedDateTime now = ZonedDateTime.now(executionContext.getValuationClock());
        final EquityBarrierOptionSecurity barrierSec = (EquityBarrierOptionSecurity) target.getSecurity();
        final ExternalId underlyingId = barrierSec.getUnderlyingId();
        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 derivatives
        final Set<EquityIndexOption> vanillas = vanillaDecomposition(now, barrierSec, smoothing, overhedge);
        if (vanillas.iterator().next().getTimeToSettlement() < 0.0) {
            throw new OpenGammaRuntimeException("EquityBarrierOptionSecurity with expiry, "
                    + barrierSec.getExpiry().getExpiry().toString() + ", has already settled.");
        }
        // 3. Build up the market data bundle
        final StaticReplicationDataBundle market = buildMarketBundle(underlyingId, executionContext, inputs, target,
                desiredValues);

        // 4. Properties of what's required of this function
        final ValueProperties resultProperties = desiredValue.getConstraints().copy().get();
        // 5. Compute Values and return
        return computeValues(vanillas, market, inputs, desiredValues, target.toSpecification(), resultProperties);
    }

    @Override
    public ComputationTargetType getTargetType() {
        return FinancialSecurityTypes.EQUITY_BARRIER_OPTION_SECURITY;
    }

    @Override
    public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
        final Security security = target.getSecurity();
        final ExerciseType exerciseType = ((EquityBarrierOptionSecurity) security).getExerciseType();
        if (!(exerciseType instanceof EuropeanExerciseType)) {
            return false;
        }
        return true;
    }

    @Override
    public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context,
            final ComputationTarget target, final ValueRequirement desiredValue) {
        // Get requirements common to all EquityIndexOptionss
        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 EquityIndexVanillaBarrierOptionDefaultPropertiesFunction 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;
    }

    @Override
    public Set<ValueSpecification> getResults(final FunctionCompilationContext context,
            final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) {
        final Set<ValueSpecification> results = super.getResults(context, target, inputs);
        final Set<ValueSpecification> resultsWithExtraProperties = Sets.newHashSetWithExpectedSize(results.size());
        for (final ValueSpecification spec : results) {
            final String name = spec.getValueName();
            final ComputationTargetSpecification targetSpec = spec.getTargetSpecification();
            final ValueProperties properties = spec.getProperties().copy()
                    .withAny(ValuePropertyNames.BINARY_OVERHEDGE)
                    .withAny(ValuePropertyNames.BINARY_SMOOTHING_FULLWIDTH).get();
            resultsWithExtraProperties.add(new ValueSpecification(name, targetSpec, properties));
        }
        return results;
    }

    //TODO does all of this work need to be done in financial?
    private Set<EquityIndexOption> vanillaDecomposition(final ZonedDateTime valuation,
            final EquityBarrierOptionSecurity barrierOption, final double smoothingFullWidth,
            final double overhedge) {

        final Set<EquityIndexOption> vanillas = new HashSet<>();
        // Unpack the barrier security
        final BarrierDirection bInOut = barrierOption.getBarrierDirection(); //   KNOCK_IN, KNOCK_OUT,
        final BarrierType bUpDown = barrierOption.getBarrierType(); //   UP, DOWN, DOUBLE
        final double strike = barrierOption.getStrike();
        final double barrier = barrierOption.getBarrierLevel();
        final ZonedDateTime expiry = barrierOption.getExpiry().getExpiry();
        final double ttm = TimeCalculator.getTimeBetween(valuation, expiry);
        final Currency ccy = barrierOption.getCurrency();
        final double ptVal = barrierOption.getPointValue();

        // parameters to model binary as call/put spread
        final double oh = overhedge;
        final double width = barrier * smoothingFullWidth; // we specify smoothing as relative value
        final double size; // = (barrier - strike ) / smoothingFullWidth;

        // 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 isCall;
        double nearStrike;
        double farStrike;
        switch (bUpDown) {
        case UP:
            isCall = true;
            if (barrierOption.getOptionType().equals(OptionType.PUT)) {
                throw new OpenGammaRuntimeException(
                        "ONE_LOOK / Vanilla Barriers do not apply to an UP type of Barrier with OptionType.CALL. Confirm that the intended samplingFrequency is ONE_LOOK");
            }
            if (barrier < strike) {
                throw new OpenGammaRuntimeException("Encountered an UP / CALL 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;
            break;
        case DOWN:
            isCall = false;
            if (barrierOption.getOptionType().equals(OptionType.CALL)) {
                throw new OpenGammaRuntimeException(
                        "ONE_LOOK / Vanilla Barriers do not apply to a DOWN type of Barrier with OptionType.PUT. Confirm that the intended samplingFrequency is ONE_LOOK");
            }
            if (barrier > strike) {
                throw new OpenGammaRuntimeException("Encountered a DOWN / PUT 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);
        }

        // Switch  on type
        final ExerciseDecisionType exerciseType = barrierOption.getExerciseType()
                .accept(ExerciseTypeAnalyticsVisitorAdapter.getInstance());
        switch (bInOut) {
        case KNOCK_OUT:
            // Long a linear at strike, short a linear at barrier
            final EquityIndexOption longlinearK = new EquityIndexOption(ttm, ttm, strike, isCall, ccy, ptVal,
                    exerciseType, SettlementType.PHYSICAL);
            final EquityIndexOption shortLinearB = new EquityIndexOption(ttm, ttm, barrier, isCall, ccy, -ptVal,
                    exerciseType, SettlementType.PHYSICAL);
            vanillas.add(longlinearK);
            vanillas.add(shortLinearB);
            // Short a binary of size, barrier - strike. Modelled as call spread struck around strike + oh, with spread of 2*eps
            final EquityIndexOption shortNear = new EquityIndexOption(ttm, ttm, nearStrike, isCall, ccy,
                    -1 * ptVal * size, exerciseType, SettlementType.PHYSICAL);
            final EquityIndexOption longFar = new EquityIndexOption(ttm, ttm, farStrike, isCall, ccy, ptVal * size,
                    exerciseType, SettlementType.PHYSICAL);
            vanillas.add(shortNear);
            vanillas.add(longFar);
            break;
        case KNOCK_IN:
            // Long a linear at barrier
            final EquityIndexOption longLinearB = new EquityIndexOption(ttm, ttm, barrier, isCall, ccy, ptVal,
                    exerciseType, SettlementType.PHYSICAL);
            vanillas.add(longLinearB);
            // Long a binary of size, barrier - strike. Modelled as call spread struck around strike + oh, with spread of 2*eps
            final EquityIndexOption longNear = new EquityIndexOption(ttm, ttm, nearStrike, isCall, ccy,
                    ptVal * size, exerciseType, SettlementType.PHYSICAL);
            final EquityIndexOption shortFar = new EquityIndexOption(ttm, ttm, farStrike, isCall, ccy,
                    -1 * ptVal * size, exerciseType, SettlementType.PHYSICAL);
            vanillas.add(longNear);
            vanillas.add(shortFar);
            break;
        default:
            throw new OpenGammaRuntimeException(
                    "Encountered an EquityBarrierOption with unexpected BarrierDirection of: " + bUpDown);
        }
        return vanillas;
    }

}