/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.volatility.smile.function; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption; import com.opengamma.analytics.financial.model.volatility.BlackFormulaRepository; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.util.ArgumentChecker; /** * */ public final class MixedLogNormalVolatilityFunction extends VolatilityFunctionProvider<MixedLogNormalModelData> { private static final MixedLogNormalVolatilityFunction INSTANCE = new MixedLogNormalVolatilityFunction(); public static MixedLogNormalVolatilityFunction getInstance() { return INSTANCE; } private MixedLogNormalVolatilityFunction() { } @Override public Function1D<MixedLogNormalModelData, Double> getVolatilityFunction(final EuropeanVanillaOption option, final double forward) { ArgumentChecker.notNull(option, "option"); ArgumentChecker.isTrue(forward >= 0.0, "forward must be greater than zero"); return new Function1D<MixedLogNormalModelData, Double>() { @Override public Double evaluate(final MixedLogNormalModelData data) { ArgumentChecker.notNull(data, "data"); return getVolatility(option, forward, data); } }; } public double getVolatility(final EuropeanVanillaOption option, final double forward, final MixedLogNormalModelData data) { ArgumentChecker.notNull(option, "option"); ArgumentChecker.notNull(data, "data"); final double price = getPrice(option, forward, data); final double t = option.getTimeToExpiry(); final double k = option.getStrike(); final boolean isCall = option.isCall(); return BlackFormulaRepository.impliedVolatility(price, forward, k, t, isCall); } public double getPrice(final EuropeanVanillaOption option, final double forward, final MixedLogNormalModelData data) { final double[] w = data.getWeights(); final double[] sigma = data.getVolatilities(); final double[] rf = data.getRelativeForwards(); final int n = w.length; final double t = option.getTimeToExpiry(); final double k = option.getStrike(); final boolean isCall = option.isCall(); final double kStar = k / forward; double sum = 0; for (int i = 0; i < n; i++) { sum += w[i] * BlackFormulaRepository.price(rf[i], kStar, t, sigma[i], isCall); } return forward * sum; } @Override public Function1D<MixedLogNormalModelData, double[]> getVolatilityAdjointFunction(final EuropeanVanillaOption option, final double forward) { ArgumentChecker.notNull(option, "option"); final double strike = option.getStrike(); final double expiry = option.getTimeToExpiry(); return new Function1D<MixedLogNormalModelData, double[]>() { @SuppressWarnings("synthetic-access") @Override public double[] evaluate(final MixedLogNormalModelData data) { return getVolatilityAdjoint(forward, strike, expiry, data); } }; } @Override public Function1D<MixedLogNormalModelData, double[][]> getVolatilityAdjointFunction(final double forward, final double[] strikes, final double timeToExpiry) { return getVolatilityAdjointFunctionByCallingSingleStrikes(forward, strikes, timeToExpiry); } @Override public Function1D<MixedLogNormalModelData, double[]> getModelAdjointFunction(final EuropeanVanillaOption option, final double forward) { ArgumentChecker.notNull(option, "option"); final double strike = option.getStrike(); final double expiry = option.getTimeToExpiry(); return new Function1D<MixedLogNormalModelData, double[]>() { @Override public double[] evaluate(final MixedLogNormalModelData data) { return getModelAjoint(forward, strike, expiry, data); } }; } @Override public Function1D<MixedLogNormalModelData, double[][]> getModelAdjointFunction(final double forward, final double[] strikes, final double timeToExpiry) { return getModelAdjointFunctionByCallingSingleStrikes(forward, strikes, timeToExpiry); } private double[] getVolatilityAdjoint(final double forward, final double strike, final double expiry, final MixedLogNormalModelData data) { final int nParms = data.getNumberOfParameters(); final boolean isCall = strike >= forward; final double[] sigmas = data.getVolatilities(); final double[] rFwds = data.getRelativeForwards(); final double[] w = data.getWeights(); final int n = sigmas.length; final double[] deltas = new double[n]; final double[] dualDeltas = new double[n]; for (int i = 0; i < n; i++) { final double f = forward * rFwds[i]; deltas[i] = BlackFormulaRepository.delta(f, strike, expiry, sigmas[i], isCall); dualDeltas[i] = BlackFormulaRepository.dualDelta(f, strike, expiry, sigmas[i], isCall); } final double impVol = getVolatility(new EuropeanVanillaOption(strike, expiry, isCall), forward, data); final double vega = BlackFormulaRepository.vega(forward, strike, expiry, impVol); final double delta = BlackFormulaRepository.delta(forward, strike, expiry, impVol, isCall); final double dualDelta = BlackFormulaRepository.dualDelta(forward, strike, expiry, impVol, isCall); final double[] res = new double[nParms + 3]; res[0] = impVol; double sum = 0; for (int i = 0; i < n; i++) { sum += w[i] * rFwds[i] * deltas[i]; } res[1] = (sum - delta) / vega; //fBar sum = 0.0; for (int i = 0; i < n; i++) { sum += w[i] * dualDeltas[i]; } res[2] = (sum - dualDelta) / vega; //strikeBar //calculate the sensitivity to model parameters final double[] modelAjoint = getModelAjoint(forward, strike, expiry, data, deltas, vega); System.arraycopy(modelAjoint, 0, res, 3, nParms); return res; } public double[] getModelAjoint(final double forward, final double strike, final double expiry, final MixedLogNormalModelData data) { final boolean isCall = strike >= forward; final double[] sigmas = data.getVolatilities(); final double[] rFwds = data.getRelativeForwards(); final int n = sigmas.length; final double[] deltas = new double[n]; for (int i = 0; i < n; i++) { deltas[i] = BlackFormulaRepository.delta(forward * rFwds[i], strike, expiry, sigmas[i], isCall); } final double impVol = getVolatility(new EuropeanVanillaOption(strike, expiry, isCall), forward, data); final double vega = BlackFormulaRepository.vega(forward, strike, expiry, impVol); return getModelAjoint(forward, strike, expiry, data, deltas, vega); } private double[] getModelAjoint(final double forward, final double strike, final double expiry, final MixedLogNormalModelData data, final double[] deltas, final double vega) { final boolean isCall = strike >= forward; final int nParms = data.getNumberOfParameters(); final double[] sigmas = data.getVolatilities(); final double[] rFwds = data.getRelativeForwards(); final double[] w = data.getWeights(); final int n = sigmas.length; final double[] prices = new double[n]; final double[] vegas = new double[n]; for (int i = 0; i < n; i++) { final double f = forward * rFwds[i]; prices[i] = BlackFormulaRepository.price(f, strike, expiry, sigmas[i], isCall); vegas[i] = BlackFormulaRepository.vega(f, strike, expiry, sigmas[i]); } final double[] res = new double[nParms]; double sum = 0.0; for (int i = 0; i < n; i++) { sum += w[i] * vegas[i]; } res[0] = sum / vega; for (int i = 1; i < n; i++) { sum = 0.0; for (int j = i; j < n; j++) { sum += w[j] * vegas[j]; } res[i] = sum / vega; } final double[][] wJac = data.getWeightsJacobian(); for (int i = 0; i < n - 1; i++) { sum = 0.0; for (int j = 0; j < n; j++) { sum += prices[j] * wJac[j][i]; } res[n + i] = sum / vega; } if (nParms > 2 * n - 1) { final double[][] fJac = data.getRelativeForwardsJacobian(); for (int i = 0; i < n - 1; i++) { sum = 0.0; for (int j = 0; j < n; j++) { sum -= rFwds[j] * deltas[j] * wJac[j][i]; } res[n + i] += sum * forward / vega; sum = 0.0; for (int j = 0; j < n; j++) { sum += deltas[j] * fJac[j][i]; } res[2 * n - 1 + i] = sum * forward / vega; } } return res; } @Override public int hashCode() { return toString().hashCode(); } @Override public boolean equals(final Object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (getClass() != obj.getClass()) { return false; } return true; } @Override public String toString() { return "Mixed log normal"; } }