All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.pricer.impl.volatility.smile.SsviVolatilityFunction Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.pricer.impl.volatility.smile;

import java.io.Serializable;
import java.lang.invoke.MethodHandles;

import org.joda.beans.ImmutableBean;
import org.joda.beans.MetaBean;
import org.joda.beans.TypedMetaBean;
import org.joda.beans.gen.BeanDefinition;
import org.joda.beans.impl.light.LightMetaBean;

import com.opengamma.strata.basics.value.ValueDerivatives;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.array.DoubleArray;

/**
 * Surface Stochastic Volatility Inspired (SSVI) formula.
 * 

* Reference: Gatheral, Jim and Jacquier, Antoine. Arbitrage-free SVI volatility surfaces. arXiv:1204.0646v4, 2013. Section 4. */ @BeanDefinition(style = "light") public final class SsviVolatilityFunction extends VolatilityFunctionProvider implements ImmutableBean, Serializable { /** * Default implementation. */ public static final SsviVolatilityFunction DEFAULT = new SsviVolatilityFunction(); /** SSVI volatility description diverge for theta -> 0. Lower bound for which time to expiry is accepted. */ public static final double MIN_TIME_TO_EXPIRY = 1.0E-3; //------------------------------------------------------------------------- @Override public double volatility(double forward, double strike, double timeToExpiry, SsviFormulaData data) { ArgChecker.isTrue(timeToExpiry > MIN_TIME_TO_EXPIRY, "time to expiry must not be zero to be able to compute volatility"); double volatilityAtm = data.getSigma(); double rho = data.getRho(); double eta = data.getEta(); double theta = volatilityAtm * volatilityAtm * timeToExpiry; double phi = eta / Math.sqrt(theta); double k = Math.log(strike / forward); double w = 0.5 * theta * (1.0d + rho * phi * k + Math.sqrt(1.0d + 2 * rho * phi * k + phi * k * phi * k)); return Math.sqrt(w / timeToExpiry); } /** * Computes the implied volatility in the SSVI formula and its derivatives. *

* The derivatives are stored in an array with: *

    *
  • [0] derivative with respect to the forward *
  • [1] derivative with respect to the strike *
  • [2] derivative with respect to the time to expiry *
  • [3] derivative with respect to the sigma (ATM volatility) *
  • [4] derivative with respect to the rho *
  • [5] derivative with respect to the eta *
* * @param forward the forward value of the underlying * @param strike the strike value of the option * @param timeToExpiry the time to expiry of the option * @param data the SSVI data * @return the volatility and associated derivatives */ @Override public ValueDerivatives volatilityAdjoint(double forward, double strike, double timeToExpiry, SsviFormulaData data) { ArgChecker.isTrue(timeToExpiry > MIN_TIME_TO_EXPIRY, "time to expiry must not be zero to be able to compute volatility"); double volatilityAtm = data.getSigma(); double rho = data.getRho(); double eta = data.getEta(); double theta = volatilityAtm * volatilityAtm * timeToExpiry; double stheta = Math.sqrt(theta); double phi = eta / stheta; double k = Math.log(strike / forward); double s = Math.sqrt(1.0d + 2 * rho * phi * k + phi * k * phi * k); double w = 0.5 * theta * (1.0d + rho * phi * k + s); double volatility = Math.sqrt(w / timeToExpiry); // Backward sweep. double[] derivatives = new double[6]; // 6 inputs double volatilityBar = 1.0; double wBar = 0.5 * volatility / w * volatilityBar; derivatives[2] += -0.5 * volatility / timeToExpiry * volatilityBar; double thetaBar = w / theta * wBar; derivatives[4] += 0.5 * theta * phi * k * wBar; double phiBar = 0.5 * theta * rho * k * wBar; double kBar = 0.5 * theta * rho * phi * wBar; double sBar = 0.5 * theta * wBar; derivatives[4] += phi * k / s * sBar; phiBar += (rho * k + phi * k * k) / s * sBar; kBar += (rho * phi + phi * phi * k) / s * sBar; derivatives[1] += 1.0d / strike * kBar; derivatives[0] += -1.0d / forward * kBar; derivatives[5] += phiBar / stheta; double sthetaBar = -eta / (stheta * stheta) * phiBar; thetaBar += 0.5 / stheta * sthetaBar; derivatives[3] += 2 * volatilityAtm * timeToExpiry * thetaBar; derivatives[2] += volatilityAtm * volatilityAtm * thetaBar; return ValueDerivatives.of(volatility, DoubleArray.ofUnsafe(derivatives)); } @Override public double volatilityAdjoint2(double forward, double strike, double timeToExpiry, SsviFormulaData data, double[] volatilityD, double[][] volatilityD2) { throw new UnsupportedOperationException("Not implemented"); } //------------------------- AUTOGENERATED START ------------------------- /** * The meta-bean for {@code SsviVolatilityFunction}. */ private static final TypedMetaBean META_BEAN = LightMetaBean.of(SsviVolatilityFunction.class, MethodHandles.lookup()); /** * The meta-bean for {@code SsviVolatilityFunction}. * @return the meta-bean, not null */ public static TypedMetaBean meta() { return META_BEAN; } static { MetaBean.register(META_BEAN); } /** * The serialization version id. */ private static final long serialVersionUID = 1L; private SsviVolatilityFunction() { } @Override public TypedMetaBean metaBean() { return META_BEAN; } //----------------------------------------------------------------------- @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj != null && obj.getClass() == this.getClass()) { return true; } return false; } @Override public int hashCode() { int hash = getClass().hashCode(); return hash; } @Override public String toString() { StringBuilder buf = new StringBuilder(32); buf.append("SsviVolatilityFunction{"); buf.append('}'); return buf.toString(); } //-------------------------- AUTOGENERATED END -------------------------- }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy