com.opengamma.analytics.financial.model.option.pricing.analytic.BlackScholesMertonModel.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.model.option.pricing.analytic.BlackScholesMertonModel.java

Source

/**
 * 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;
        }
    }
}