/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame.equityindexoptions;
import java.util.LinkedHashMap;
import java.util.Map;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.opengamma.analytics.financial.equity.EquityDerivativeSensitivityCalculator;
import com.opengamma.analytics.financial.equity.EquityOptionBlackPresentValueCalculator;
import com.opengamma.analytics.financial.equity.EquityOptionBlackSpotDeltaCalculator;
import com.opengamma.analytics.financial.equity.EquityOptionBlackSpotGammaCalculator;
import com.opengamma.analytics.financial.equity.EquityOptionBlackVegaCalculator;
import com.opengamma.analytics.financial.equity.StaticReplicationDataBundle;
import com.opengamma.analytics.financial.instrument.InstrumentDefinition;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitorAdapter;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.SimpleParameterSensitivity;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.financial.analytics.DoubleLabelledMatrix1D;
import com.opengamma.financial.analytics.conversion.EquityOptionsConverter;
import com.opengamma.financial.analytics.model.fixedincome.BucketedCurveSensitivities;
import com.opengamma.financial.security.option.EquityIndexOptionSecurity;
import com.opengamma.sesame.CurveMatrixLabeller;
import com.opengamma.sesame.Environment;
import com.opengamma.sesame.equity.StaticReplicationDataBundleFn;
import com.opengamma.sesame.trade.EquityIndexOptionTrade;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.CurrencyAmount;
import com.opengamma.util.result.FailureStatus;
import com.opengamma.util.result.Result;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Pairs;
/**
* Default implementation of the {@link EquityIndexOptionFn}.
*/
public class DefaultEquityIndexOptionFn implements EquityIndexOptionFn {
public static final double BP = 0.0001;
private StaticReplicationDataBundleFn _dataProviderFn;
private static final EquityOptionBlackPresentValueCalculator PV_CALC = EquityOptionBlackPresentValueCalculator.getInstance();
private static final EquityOptionBlackSpotDeltaCalculator DELTA_CALC = EquityOptionBlackSpotDeltaCalculator.getInstance();
private static final EquityOptionBlackSpotGammaCalculator GAMMA_CALC = EquityOptionBlackSpotGammaCalculator.getInstance();
private static final EquityOptionBlackVegaCalculator VEGA_CALC = EquityOptionBlackVegaCalculator.getInstance();
private static final EquityDerivativeSensitivityCalculator SENSITIVITY_CALC = new EquityDerivativeSensitivityCalculator(PV_CALC);
/**
* Constructs a function to calculate values for a equity index option.
* @param dataProviderFn data provider bundle.
*/
public DefaultEquityIndexOptionFn(StaticReplicationDataBundleFn dataProviderFn) {
_dataProviderFn = ArgumentChecker.notNull(dataProviderFn, "dataProviderFn");
}
@Override
public Result<CurrencyAmount> calculatePv(Environment env, EquityIndexOptionTrade trade) {
Result<Double> result = calculateResult(env, trade, PV_CALC);
if (result.isSuccess()) {
double scaledValue = trade.getTrade().getQuantity().doubleValue() * result.getValue();
CurrencyAmount amount = CurrencyAmount.of(trade.getSecurity().getCurrency(), scaledValue);
return Result.success(amount);
} else {
return Result.failure(result);
}
}
@Override
public Result<Double> calculateDelta(Environment env, EquityIndexOptionTrade trade) {
return calculateResult(env, trade, DELTA_CALC);
}
@Override
public Result<Double> calculateGamma(Environment env, EquityIndexOptionTrade trade) {
Result<StaticReplicationDataBundle> dataResult = _dataProviderFn.getEquityIndexDataProvider(env, trade);
if (dataResult.isSuccess()) {
// Gamma should be multiply by spot underlying
double spot = dataResult.getValue().getForwardCurve().getSpot() / 100;
Result<Double> result = calculateResult(env, trade, GAMMA_CALC);
if (result.isSuccess()) {
return Result.success(result.getValue() * spot);
} else {
return Result.failure(result);
}
} else {
return Result.failure(dataResult);
}
}
@Override
public Result<Double> calculateVega(Environment env, EquityIndexOptionTrade trade) {
Result<Double> result = calculateResult(env, trade, VEGA_CALC);
if (result.isSuccess()) {
// Vega should be divided 100
return Result.success(result.getValue() / 100);
} else {
return Result.failure(result);
}
}
@Override
public Result<Double> calculatePv01(Environment env, EquityIndexOptionTrade trade) {
Result<StaticReplicationDataBundle> dataResult = _dataProviderFn.getEquityIndexDataProvider(env, trade);
if (Result.allSuccessful(dataResult)) {
StaticReplicationDataBundle bundle = dataResult.getValue();
InstrumentDerivative derivative = createInstrumentDerivative(trade, env.getValuationTime());
double pv01 = SENSITIVITY_CALC.calcPV01(derivative, bundle);
double scaledPv01 = trade.getTrade().getQuantity().doubleValue() * pv01;
return Result.success(scaledPv01);
} else {
return Result.failure(dataResult);
}
}
@Override
public Result<BucketedCurveSensitivities> calculateBucketedPv01(Environment env, EquityIndexOptionTrade trade) {
Result<StaticReplicationDataBundle> dataResult = _dataProviderFn.getEquityIndexDataProvider(env, trade);
if (Result.allSuccessful(dataResult)) {
StaticReplicationDataBundle bundle = dataResult.getValue();
InstrumentDerivative derivative = createInstrumentDerivative(trade, env.getValuationTime());
DoubleMatrix1D deltaBucketed = SENSITIVITY_CALC.calcDeltaBucketed(derivative, bundle);
YieldAndDiscountCurve discountCurve = bundle.getDiscountCurve();
if (discountCurve instanceof YieldCurve) {
Currency currency = trade.getSecurity().getCurrency();
String name = discountCurve.getName();
Double[] tenors = ((YieldCurve) discountCurve).getCurve().getXData();
ImmutableList.Builder<String> dayBuilder = ImmutableList.builder();
for (double tenor : tenors) {
String day = Math.round(tenor * 365) + "D";
dayBuilder.add(day);
}
LinkedHashMap<String, DoubleMatrix1D> map = new LinkedHashMap<>();
map.put(name, deltaBucketed);
SimpleParameterSensitivity sensitivity = new SimpleParameterSensitivity(map);
CurveMatrixLabeller labeller = new CurveMatrixLabeller(dayBuilder.build());
double scalingFactor = BP * trade.getTrade().getQuantity().doubleValue();
DoubleMatrix1D bucketedPv01 = sensitivity.multipliedBy(scalingFactor).getSensitivity(name);
DoubleLabelledMatrix1D labelledBucketedPv01 = labeller.labelMatrix(bucketedPv01);
Map<Pair<String, Currency>, DoubleLabelledMatrix1D> sensitivitiesByCurve =
ImmutableMap.of(Pairs.of(name, currency), labelledBucketedPv01);
return Result.success(BucketedCurveSensitivities.of(sensitivitiesByCurve));
} else {
return Result.failure(FailureStatus.INVALID_INPUT, "Can only handle YieldCurve instances of YieldAndDiscountCurve");
}
} else {
return Result.failure(dataResult);
}
}
private Result<Double> calculateResult(Environment env,
EquityIndexOptionTrade trade,
InstrumentDerivativeVisitorAdapter<StaticReplicationDataBundle, Double> calculator) {
Result<StaticReplicationDataBundle> dataResult = _dataProviderFn.getEquityIndexDataProvider(env, trade);
if (Result.allSuccessful(dataResult)) {
StaticReplicationDataBundle bundle = dataResult.getValue();
InstrumentDerivative derivative = createInstrumentDerivative(trade, env.getValuationTime());
return Result.success(derivative.accept(calculator, bundle));
} else {
return Result.failure(dataResult);
}
}
private InstrumentDerivative createInstrumentDerivative(EquityIndexOptionTrade tradeWrapper, ZonedDateTime valTime) {
EquityOptionsConverter converter = new EquityOptionsConverter();
EquityIndexOptionSecurity security = tradeWrapper.getSecurity();
InstrumentDefinition<?> definition = security.accept(converter);
return definition.toDerivative(valTime);
}
}