/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.option.pricing.analytic; import org.apache.commons.lang.Validate; import org.threeten.bp.ZonedDateTime; import com.opengamma.analytics.financial.greeks.GreekVisitor; import com.opengamma.analytics.financial.model.option.definition.OptionDefinition; import com.opengamma.analytics.financial.model.option.definition.StandardOptionDataBundle; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.analytics.math.statistics.distribution.NormalDistribution; import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution; /** * Generalized Black-Scholes-Merton option pricing. * <p> * The price of an option is given by * $$ * \begin{align*} * c &= Se^{(b-r)T}N(d_1) - Ke^{-rT}N(d_2)\\ * p &= Ke^{-rT}N(-d_2) - Se^{(b-r)T}N(-d_1) * \end{align*} * $$ * where * $$ * \begin{align*} * d_1 &= \frac{\ln(\frac{S}{K}) + (b + \frac{\sigma^2}{2})T}{\sigma\sqrt{T}}\\ * d_2 &= d_1 - \sigma\sqrt{T} * \end{align*} * $$ * Depending on the data supplied, the model is: * <ul> * <li>$b=r$ Black-Scholes stock option pricing model * <li>$b=r-q$ Merton stock option model with continuous dividend yield $q$ * <li>$b=0$ Black future option model * <li>$b=0, r=0$ Asay margined future option model * <li>$b=r-r_f$ Garman-Kohlhagen FX option model, with foreign risk-free rate $r_f$. * </ul> * */ public class BlackScholesMertonModel extends AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> { private static final ProbabilityDistribution<Double> NORMAL = new NormalDistribution(0, 1); /** * Returns a visitor that calculates the greeks analytically. * @param pricingFunction The pricing function, not null * @param data The data, not null * @param definition The option definition, not null * @return A visitor that calculates BSM greeks analytically. */ @Override public GreekVisitor<Double> getGreekVisitor(final Function1D<StandardOptionDataBundle, Double> pricingFunction, final StandardOptionDataBundle data, final OptionDefinition definition) { Validate.notNull(pricingFunction); Validate.notNull(data); Validate.notNull(definition); return new BlackScholesMertonGreekVisitor(data, pricingFunction, definition); } /** * {@inheritDoc} */ @Override public Function1D<StandardOptionDataBundle, Double> getPricingFunction(final OptionDefinition definition) { Validate.notNull(definition); final Function1D<StandardOptionDataBundle, Double> pricingFunction = new Function1D<StandardOptionDataBundle, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final StandardOptionDataBundle data) { Validate.notNull(data); final ZonedDateTime date = data.getDate(); final double s = data.getSpot(); final double k = definition.getStrike(); final double t = definition.getTimeToExpiry(date); final double r = data.getInterestRate(t); final double b = data.getCostOfCarry(); if (s == 0) { return definition.isCall() ? 0 : Math.exp(-r * t) * k; } final double sigma = data.getVolatility(t, k); final double d1 = getD1(s, k, t, sigma, b); final double d2 = getD2(d1, sigma, t); final int sign = definition.isCall() ? 1 : -1; return sign * Math.exp(-r * t) * (s * Math.exp(b * t) * NORMAL.getCDF(sign * d1) - k * NORMAL.getCDF(sign * d2)); } }; return pricingFunction; } /** * Greek visitor for this class. Analytic solutions for the greeks are used. */ @SuppressWarnings("synthetic-access") protected class BlackScholesMertonGreekVisitor extends AnalyticOptionModelFiniteDifferenceGreekVisitor<StandardOptionDataBundle, OptionDefinition> { private final double _s; private final double _k; private final double _sigma; private final double _t; private final double _b; private final double _r; private final boolean _isCall; private final double _df; private final double _d1; private final double _d2; private final double _price; /** * @param data The data, not null * @param pricingFunction The pricing function, not null * @param definition The option definition, not null */ public BlackScholesMertonGreekVisitor(final StandardOptionDataBundle data, final Function1D<StandardOptionDataBundle, Double> pricingFunction, final OptionDefinition definition) { super(pricingFunction, data, definition); _s = data.getSpot(); _k = definition.getStrike(); _t = definition.getTimeToExpiry(data.getDate()); _r = data.getInterestRate(_t); _sigma = data.getVolatility(_t, _k); _b = data.getCostOfCarry(); _isCall = definition.isCall(); _df = getDF(_r, _b, _t); _d1 = getD1(_s, _k, _t, _sigma, _b); _d2 = getD2(_d1, _sigma, _t); _price = pricingFunction.evaluate(data); } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Carry rho}_{call} &= TSe^{(b-r)T}N(d_1)\\ * \text{Carry rho}_{put} &= -TSe^{(b-r)T}N(-d_1) * \end{align*} * $$ */ @Override public Double visitCarryRho() { final int sign = _isCall ? 1 : -1; final double value = sign * _t * _s * _df * NORMAL.getCDF(sign * _d1); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \Delta_{call} &= e^{(b-r)T)}N(d_1)\\ * \Delta_{put} &= e^{(b-r)T}(N(d_1) - 1) * \end{align*} * $$ */ @Override public Double visitDelta() { final double value = _df * (_isCall ? NORMAL.getCDF(_d1) : NORMAL.getCDF(_d1) - 1); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Delta bleed}_{call} &= -e^{(b-r)T}\left[n(d_1)\left(\frac{b}{\sigma\sqrt{T}} + \frac{d_2}{2T}\right) + (b - r)N(d_1)\right]\\ * \text{Delta bleed}_{put} &= -e^{(b-r)T}\left[n(d_1)\left(\frac{b}{\sigma\sqrt{T}} - \frac{d_2}{2T}\right) + (b - r)N(d_1)\right] * \end{align*} * $$ */ @Override public Double visitDeltaBleed() { final int sign = _isCall ? 1 : -1; final double value = -_df * (NORMAL.getPDF(_d1) * (_b / (_sigma * Math.sqrt(_t)) - _d2 / (2 * _t)) + sign * (_b - _r) * NORMAL.getCDF(sign * _d1)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Driftless theta} = \frac{Sn(d_1)\sigma}{2\sqrt{T}} * \end{align*} * $$ */ @Override public Double visitDriftlessTheta() { final double value = -_s * NORMAL.getPDF(_d1) * _sigma / (2 * Math.sqrt(_t)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{dVanna dVol} = \frac{\text{Vanna}}{\sigma}\left(d_1 d_2 - \frac{d_1}{d_2} - 1\right) * \end{align*} * $$ */ @Override public Double visitDVannaDVol() { final double value = visitVanna() * (_d1 * _d2 - _d1 / _d2 - 1) / _sigma; return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{dZeta dVol}_{call} &= -\frac{n(d_2)d_1}{\sigma}\\ * \text{dZeta dVol}_{put} &= \frac{n(d_2)d_1}{\sigma} * \end{align*} * $$ */ @Override public Double visitDZetaDVol() { final double value = (_isCall ? -1 : 1) * NORMAL.getPDF(_d2) * _d1 / _sigma; return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \Lambda_{call} &= \frac{e^{(b-r)T}N(d_1)S}{P_{call}}\\ * \Lambda_{put} &= \frac{e^{(b-r)T}(N(d_1) - 1)S}{P_{put}}\\ * \end{align*} * $$ */ @Override public Double visitElasticity() { final double value = _df * (_isCall ? NORMAL.getCDF(_d1) : NORMAL.getCDF(_d1) - 1) * _s / _price; return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \Gamma = \frac{n(d_1)e^{(b-r)T}}{S\sigma\sqrt{T}} * \end{align*} * $$ */ @Override public Double visitGamma() { if (_s == 0) { return 0.0; } final double value = _df * NORMAL.getPDF(_d1) / (_s * _sigma * Math.sqrt(_t)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Gamma bleed} = \Gamma\left[r - b + \frac{b d_1}{\sigma\sqrt{T}} + \frac{1 - d_1 d_2}{2T}\right] * \end{align*} * $$ */ @Override public Double visitGammaBleed() { final double value = visitGamma() * (_r - _b + _b * _d1 / (_sigma * Math.sqrt(_t)) + (1 - _d1 * _d2) / (2 * _t)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \Gamma_P = \frac{S\Gamma}{100} * \end{align*} * $$ */ @Override public Double visitGammaP() { return visitGamma() * _s / 100; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Gamma bleed}_P = \Gamma_P\left[r - b + \frac{b d_1}{\sigma\sqrt{T}} + \frac{1 - d_1 d_2}{2T}\right] * \end{align*} * $$ */ @Override public Double visitGammaPBleed() { final double value = visitGammaP() * (_r - _b + _b * _d1 / (_sigma * Math.sqrt(_t)) + (1 - _d1 * _d2) / (2 * _t)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \Phi_{call} &= -TSe^{(b-r)T}N(d_1)\\ * \Phi_{put} &= TSe^{(b-r)T}N(-d_1) * \end{align*} * $$ */ @Override public Double visitPhi() { final int sign = _isCall ? 1 : -1; final double value = -sign * _t * _s * _df * NORMAL.getCDF(_d1 * sign); return value; } /** * {@inheritDoc} */ @Override public Double visitPrice() { return _price; } /** * {@inheritDoc} * $$ * \begin{align*} * \rho_{call} &= TKe^{-rT}N(d_2)\\ * \rho_{put} &= -TKe^{-rT}N(-d_2) * \end{align*} * $$ */ @Override public Double visitRho() { final int sign = _isCall ? 1 : -1; final double value = sign * _t * _k * Math.exp(-_r * _t) * NORMAL.getCDF(sign * _d2); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Speed} = \frac{\Gamma\left(1 + \frac{d_1}{\sigma\sqrt{T}}\right)}{S} * \end{align*} * $$ */ @Override public Double visitSpeed() { final double value = -visitGamma() * (1 + _d1 / (_sigma * Math.sqrt(_t))) / _s; return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Speed}_P = -\frac{\Gamma d_1}{100\sigma\sqrt{T}} * \end{align*} * } */ @Override public Double visitSpeedP() { final double value = -visitGamma() * _d1 / (100 * _sigma * Math.sqrt(_t)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Strike delta}_{call} &= -e^{-rT}N(d_2)\\ * \text{Strike delta}_{put} &= e^{-rT}N(-d_2) * \end{align*} * $$ */ @Override public Double visitStrikeDelta() { final int sign = _isCall ? 1 : -1; final double value = -sign * Math.exp(-_r * _t) * NORMAL.getCDF(sign * _d2); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Strike gamma} = \frac{n(d_2)e^{-rT}}{K\sigma\sqrt{T}} * \end{align*} * $$ */ @Override public Double visitStrikeGamma() { final double value = NORMAL.getPDF(_d2) * Math.exp(-_r * _t) / (_k * _sigma * Math.sqrt(_t)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \Theta_{call} &= -\frac{Se^{(b-r)T}\sqrt{T}}{2\sqrt{T}} - (b - r)Se^{(b-r)T}N(d_1) - rKe^{-rT}N(d_2)\\ * \Theta_{put} &= -\frac{Se^{(b-r)T}\sqrt{T}}{2\sqrt{T}} + (b - r)Se^{(b-r)T}N(-d_1) + rKe^{-rT}N(-d_2) * \end{align*} * $$ */ @Override public Double visitTheta() { final int sign = _isCall ? 1 : -1; final double value = -_s * _df * NORMAL.getPDF(_d1) * _sigma / (2 * Math.sqrt(_t)) - sign * (_b - _r) * _s * _df * NORMAL.getCDF(sign * _d1) - sign * _r * _k * Math.exp(-_r * _t) * NORMAL.getCDF(sign * _d2); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Vanna} = \frac{-e^{(b-r)T}d_2 n(d_1)}{\sigma} * \end{align*} * $$ */ @Override public Double visitVanna() { final double value = -_df * _d2 * NORMAL.getPDF(_d1) / _sigma; return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Variance vomma} = \frac{Se^{(b-r)T}\sqrt{T}n(d_1)[(d_1 d_2 - 1)(d_1 d_2 - 3) - (d_1^2 + d_2^2)]}{8\sigma^5} * \end{align*} * $$ */ @Override public Double visitVarianceUltima() { final double value = _s * _df * Math.sqrt(_t) / (8 * Math.pow(_sigma, 5)) * NORMAL.getPDF(_d1) * ((_d1 * _d2 - 1) * (_d1 * _d2 - 3) - (_d1 * _d1 + _d2 * _d2)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \end{align*} * $$ */ @Override public Double visitVarianceVanna() { final double value = -_s * _df * NORMAL.getPDF(_d1) * _d2 / (2 * _sigma * _sigma); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Variance vega} = \frac{Se^{(b-r)T}n(d_1)\sqrt{T}}{2\sigma} * \end{align*} * $$ */ @Override public Double visitVarianceVega() { final double value = _s * _df * NORMAL.getPDF(_d1) * Math.sqrt(_t) / (2 * _sigma); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Variance vomma} = \frac{Se^{(b-r)T}\sqrt{T}n(d_1)(d_1 d_2 - 1)}{4\sigma^3} * \end{align*} * $$ */ @Override public Double visitVarianceVomma() { final double value = _s * _df * Math.sqrt(_t) / (4 * Math.pow(_sigma, 3)) * NORMAL.getPDF(_d1) * (_d1 * _d2 - 1); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Vega} = Se^{(b-r)T}n(d_1)\sqrt{T} * \end{align*} * $$ */ @Override public Double visitVega() { final double value = _s * _df * NORMAL.getPDF(_d1) * Math.sqrt(_t); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Vega bleed} = \text{Vega}\left(r - b + \frac{b d_1}{\sigma\sqrt{T}} - \frac{1 + d_1 d_2}{2T}\right) * \end{align*} * $$ */ @Override public Double visitVegaBleed() { final double value = visitVega() * (_r - _b + _b * _d1 / (_sigma * Math.sqrt(_t)) - (1 + _d1 * _d2) / (2 * _t)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Vega}_p = \frac{S\sigma e^{(b-r)T}n(d_1)\sqrt{T}}{10} * \end{align*} * $$ */ @Override public Double visitVegaP() { final double value = visitVega() * _sigma / 10; return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Ultima} = \frac{\text{Vomma}}{\sigma}\left(d_1 d_2 - \frac{d_1}{d_2} - \frac{d_2}{d_1} - 1\right) * \end{align*} * $$ */ @Override public Double visitUltima() { final double value = visitVomma() * (_d1 * _d2 - _d1 / _d2 - _d2 / _d1 - 1) / _sigma; return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Vomma} = \frac{\text{Vega } d_1 d_2}{\sigma} * \end{align*} * $$ */ @Override public Double visitVomma() { final double value = visitVega() * _d1 * _d2 / _sigma; return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Vomma}_P= \frac{\text{Vega}_P d_1 d_2}{\sigma} * \end{align*} * $$ */ @Override public Double visitVommaP() { final double value = visitVegaP() * _d1 * _d2 / _sigma; return value; } /** * {@inheritDoc} */ @Override public Double visitZeta() { final double value = NORMAL.getCDF(_isCall ? _d2 : -_d2); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \end{align*} * $$ */ @Override public Double visitZetaBleed() { final double value = (_isCall ? 1 : -1) * NORMAL.getPDF(_d2) * (_b / (_sigma * Math.sqrt(_t)) - _d1 / (2 * _t)); return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Zomma} = \frac{\Gamma(d_1 d_2 - 1)}{\sigma} * \end{align*} * $$ */ @Override public Double visitZomma() { final double value = visitGamma() * (_d1 * _d2 - 1) / _sigma; return value; } /** * {@inheritDoc} * $$ * \begin{align*} * \text{Zomma}_P = \frac{\Gamma_P(d_1 d_2 - 1)}{\sigma} * \end{align*} * $$ */ @Override public Double visitZommaP() { final double value = visitGammaP() * (_d1 * _d2 - 1) / _sigma; return value; } } }