ch.algotrader.option.OptionUtil.java Source code

Java tutorial

Introduction

Here is the source code for ch.algotrader.option.OptionUtil.java

Source

/***********************************************************************************
 * AlgoTrader Enterprise Trading Framework
 *
 * Copyright (C) 2015 AlgoTrader GmbH - All rights reserved
 *
 * All information contained herein is, and remains the property of AlgoTrader GmbH.
 * The intellectual and technical concepts contained herein are proprietary to
 * AlgoTrader GmbH. Modification, translation, reverse engineering, decompilation,
 * disassembly or reproduction of this material is strictly forbidden unless prior
 * written permission is obtained from AlgoTrader GmbH
 *
 * Fur detailed terms and conditions consult the file LICENSE.txt or contact
 *
 * AlgoTrader GmbH
 * Aeschstrasse 6
 * 8834 Schindellegi
 ***********************************************************************************/
package ch.algotrader.option;

import java.util.Date;

import org.apache.commons.math.MathException;
import org.apache.commons.math.analysis.UnivariateRealFunction;
import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
import org.apache.commons.math.analysis.solvers.UnivariateRealSolver;
import org.apache.commons.math.analysis.solvers.UnivariateRealSolverFactory;

import ch.algotrader.entity.security.Option;
import ch.algotrader.entity.security.OptionFamily;
import ch.algotrader.enumeration.Duration;
import ch.algotrader.enumeration.OptionType;

/**
 * Utility class containing static methods around {@link Option Options}.
 *
 * @author <a href="mailto:aflury@algotrader.ch">Andy Flury</a>
 */
public class OptionUtil {

    private static final double beta = 0.999;

    /**
     * Gets the fair-price of a {@link Option} based on the price of the {@code underlyingSpot} and {@code volatility}.
     * {@code duration}, {@code intrest} and {@code dividend} are retrieved from the {@link OptionFamily}.
     */
    public static double getOptionPrice(Option option, double underlyingSpot, double vola, Date now) {

        OptionFamily family = (OptionFamily) option.getSecurityFamily();

        double years = (option.getExpiration().getTime() - now.getTime()) / (double) Duration.YEAR_1.getValue();

        return getOptionPrice(underlyingSpot, option.getStrike().doubleValue(), vola, years, family.getIntrest(),
                family.getDividend(), option.getOptionType());
    }

    /**
     * Gets the fair-price of a {@link Option}.
     */
    public static double getOptionPrice(double underlyingSpot, double strike, double volatility, double years,
            double intrest, double dividend, OptionType type) {

        if (years <= 0) {
            return getIntrinsicValue(underlyingSpot, strike, type);
        }

        double costOfCarry = intrest - dividend;
        double d1 = (Math.log(underlyingSpot / strike) + (costOfCarry + volatility * volatility / 2.0) * years)
                / (volatility * Math.sqrt(years));
        double d2 = d1 - volatility * Math.sqrt(years);
        double term1 = underlyingSpot * Math.exp((costOfCarry - intrest) * years);
        double term2 = strike * Math.exp(-intrest * years);

        double result = 0.0;
        if (OptionType.CALL.equals(type)) {
            result = term1 * Gaussian.Phi(d1) - term2 * Gaussian.Phi(d2);
        } else {
            result = term2 * Gaussian.Phi(-d2) - term1 * Gaussian.Phi(-d1);
        }

        return result;
    }

    /**
     * Gets the implied volatility of a {@link Option} using a {@link UnivariateRealSolverFactory}.
     * {@code duration}, {@code intrest} and {@code dividend} are retrieved from the {@link OptionFamily}.
     */
    public static double getImpliedVolatility(Option option, double underlyingSpot, final double currentValue,
            Date now) throws MathException {

        OptionFamily family = (OptionFamily) option.getSecurityFamily();

        double years = (option.getExpiration().getTime() - now.getTime()) / (double) Duration.YEAR_1.getValue();

        return getImpliedVolatility(underlyingSpot, option.getStrike().doubleValue(), currentValue, years,
                family.getIntrest(), family.getDividend(), option.getOptionType());
    }

    /**
     * Gets the implied volatility of a {@link Option} using a {@link UnivariateRealSolverFactory}.
     */
    @SuppressWarnings("deprecation")
    public static double getImpliedVolatility(final double underlyingSpot, final double strike,
            final double currentValue, final double years, final double intrest, final double dividend,
            final OptionType type) throws MathException {

        if (years < 0) {
            throw new IllegalArgumentException("years cannot be negative");
        }

        double intrinsicValue = getIntrinsicValue(underlyingSpot, strike, type);
        if (currentValue <= intrinsicValue) {
            throw new IllegalArgumentException(
                    "cannot calculate volatility if optionValue is below intrinsic Value");
        }

        UnivariateRealFunction function = volatility -> getOptionPrice(underlyingSpot, strike, volatility, years,
                intrest, dividend, type) - currentValue;

        UnivariateRealSolverFactory factory = UnivariateRealSolverFactory.newInstance();
        UnivariateRealSolver solver = factory.newDefaultSolver();
        solver.setAbsoluteAccuracy(0.0001);

        return solver.solve(function, 0.01, 2.0);
    }

    /**
     * Gets the implied volatility of a {@link Option} using the Newton Rapson Method.
     * {@code duration}, {@code intrest} and {@code dividend} are retrieved from the {@link OptionFamily}.
     */
    public static double getImpliedVolatilityNR(Option option, double underlyingSpot, double currentValue, Date now)
            throws MathException {

        OptionFamily family = (OptionFamily) option.getSecurityFamily();

        double years = (option.getExpiration().getTime() - now.getTime()) / (double) Duration.YEAR_1.getValue();

        return getImpliedVolatilityNR(underlyingSpot, option.getStrike().doubleValue(), currentValue, years,
                family.getIntrest(), family.getDividend(), option.getOptionType());
    }

    /**
     * Gets the implied volatility of a {@link Option} using the Newton Rapson Method.
     */
    public static double getImpliedVolatilityNR(final double underlyingSpot, final double strike,
            final double currentValue, final double years, final double intrest, final double dividend,
            final OptionType type) throws MathException {

        double e = 0.1;

        double vi = Math.sqrt(Math.abs(Math.log(strike / strike) + intrest * years) * 2 / years);
        double ci = getOptionPrice(underlyingSpot, strike, vi, years, intrest, dividend, type);
        double vegai = getVega(underlyingSpot, strike, vi, years, intrest, dividend);
        double minDiff = Math.abs(currentValue - ci);

        while ((Math.abs(currentValue - ci) >= e) && (Math.abs(currentValue - ci) <= minDiff)) {
            vi = vi - (ci - currentValue) / vegai;
            ci = getOptionPrice(underlyingSpot, strike, vi, years, intrest, dividend, type);
            vegai = getVega(underlyingSpot, strike, vi, years, intrest, dividend);
            minDiff = Math.abs(currentValue - ci);
        }

        if (Math.abs(currentValue - ci) < e) {
            return vi;
        } else {
            throw new IllegalArgumentException("cannot calculate volatility");
        }
    }

    /**
     * Gets the implied volatility of a {@link Option} based on a {@link SABRSurfaceVO}.
     * {@code duration}, {@code intrest} and {@code dividend} are retrieved from the {@link OptionFamily}.
     */
    public static double getImpliedVolatilitySABR(final Option option, double underlyingSpot,
            final SABRSurfaceVO surface, final Date now) throws MathException {

        OptionFamily family = (OptionFamily) option.getSecurityFamily();

        double years = (option.getExpiration().getTime() - now.getTime()) / (double) Duration.YEAR_1.getValue();

        return getImpliedVolatilitySABR(underlyingSpot, option.getStrike().doubleValue(), years,
                family.getIntrest(), family.getDividend(), option.getOptionType(), surface);
    }

    /**
     * Gets the implied volatility of a {@link Option} based on a {@link SABRSurfaceVO}.
     */
    public static double getImpliedVolatilitySABR(final double underlyingSpot, final double strike,
            final double years, final double intrest, final double dividend, final OptionType type,
            final SABRSurfaceVO surface) throws MathException {

        double forward = getForward(underlyingSpot, years, intrest, dividend);

        // get sabrVolas for all durations at the specified strike
        int i = 0;
        double[] yearsArray = new double[surface.getSmiles().size()];
        double[] volArray = new double[surface.getSmiles().size()];
        for (SABRSmileVO smile : surface.getSmiles()) {

            double vol = SABR.volByAtmVol(forward, strike, smile.getAtmVol(), smile.getYears(), beta,
                    smile.getRho(), smile.getVolVol());

            yearsArray[i] = smile.getYears();
            volArray[i] = vol;
            i++;
        }

        // spline interpolation for years
        SplineInterpolator interpolator = new SplineInterpolator();
        PolynomialSplineFunction function = interpolator.interpolate(yearsArray, volArray);

        return function.value(years);
    }

    /**
     * Gets the intrinsic value of a {@link Option}.
     */
    public static double getIntrinsicValue(Option option, double underlyingSpot) throws RuntimeException {

        return getIntrinsicValue(underlyingSpot, option.getStrike().doubleValue(), option.getOptionType());
    }

    /**
     * Gets the intrinsic value of a {@link Option}.
     */
    public static double getIntrinsicValue(double underlyingSpot, double strike, OptionType type) {

        if (OptionType.CALL.equals(type)) {
            return Math.max(underlyingSpot - strike, 0d);
        } else {
            return Math.max(strike - underlyingSpot, 0d);
        }
    }

    /**
     * Gets the delta of a {@link Option}
     * {@code duration}, {@code intrest} and {@code dividend} are retrieved from the {@link OptionFamily}.
     */
    public static double getDelta(Option option, double currentValue, double underlyingSpot, final Date now)
            throws MathException {

        OptionFamily family = (OptionFamily) option.getSecurityFamily();

        double strike = option.getStrike().doubleValue();
        double years = (option.getExpiration().getTime() - now.getTime()) / (double) Duration.YEAR_1.getValue();
        double volatility = getImpliedVolatility(underlyingSpot, strike, currentValue, years, family.getIntrest(),
                family.getDividend(), option.getOptionType());
        return OptionUtil.getDelta(underlyingSpot, strike, volatility, years, family.getIntrest(),
                family.getDividend(), option.getOptionType());

    }

    /**
     * Gets the delta of a {@link Option}
     */
    public static double getDelta(double underlyingSpot, double strike, double volatility, double years,
            double intrest, double dividend, OptionType type) {

        if (years < 0) {
            throw new IllegalArgumentException("years cannot be negative");
        }

        double costOfCarry = intrest - dividend;
        double d1 = (Math.log(underlyingSpot / strike) + (costOfCarry + volatility * volatility / 2) * years)
                / (volatility * Math.sqrt(years));

        if (OptionType.CALL.equals(type)) {
            return Math.exp((costOfCarry - intrest) * years) * Gaussian.Phi(d1);
        } else {
            return -Math.exp((costOfCarry - intrest) * years) * Gaussian.Phi(-d1);
        }
    }

    /**
     * Gets the vega of a {@link Option}
     * {@code duration}, {@code intrest} and {@code dividend} are retrieved from the {@link OptionFamily}.
     */
    public static double getVega(Option option, double currentValue, double underlyingSpot, final Date now)
            throws MathException {

        OptionFamily family = (OptionFamily) option.getSecurityFamily();

        double strike = option.getStrike().doubleValue();
        double years = (option.getExpiration().getTime() - now.getTime()) / (double) Duration.YEAR_1.getValue();
        double volatility = getImpliedVolatility(underlyingSpot, strike, currentValue, years, family.getIntrest(),
                family.getDividend(), option.getOptionType());
        return OptionUtil.getVega(underlyingSpot, strike, volatility, years, family.getIntrest(),
                family.getDividend());
    }

    /**
     * Gets the vega of a {@link Option}
     */
    public static double getVega(double underlyingSpot, double strike, double volatility, double years,
            double intrest, double dividend) {

        double costOfCarry = intrest - dividend;
        double d1 = (Math.log(underlyingSpot / strike) + (costOfCarry + volatility * volatility / 2) * years)
                / (volatility * Math.sqrt(years));
        double n = standardNormalDensity(d1);

        return underlyingSpot * Math.exp((costOfCarry - intrest) * years) * n * Math.sqrt(years);

    }

    /**
     * Gets the theta of a {@link Option}
     * {@code duration}, {@code intrest} and {@code dividend} are retrieved from the {@link OptionFamily}.
     */
    public static double getTheta(Option option, double currentValue, double underlyingSpot, Date now)
            throws MathException {

        OptionFamily family = (OptionFamily) option.getSecurityFamily();

        double strike = option.getStrike().doubleValue();
        double years = (option.getExpiration().getTime() - now.getTime()) / (double) Duration.YEAR_1.getValue();
        double volatility = getImpliedVolatility(underlyingSpot, strike, currentValue, years, family.getIntrest(),
                family.getDividend(), option.getOptionType());
        return OptionUtil.getTheta(underlyingSpot, strike, volatility, years, family.getIntrest(),
                family.getDividend(), option.getOptionType());

    }

    /**
     * Gets the theta of a {@link Option}
     */
    public static double getTheta(double underlyingSpot, double strike, double volatility, double years,
            double intrest, double dividend, OptionType type) {

        double costOfCarry = intrest - dividend;
        double d1 = (Math.log(underlyingSpot / strike) + (costOfCarry + volatility * volatility / 2) * years)
                / (volatility * Math.sqrt(years));
        double d2 = d1 - volatility * Math.sqrt(years);

        double term1 = -underlyingSpot * Math.exp((costOfCarry - intrest) * years) * standardNormalDensity(d1)
                * volatility / (2.0 * Math.sqrt(years));
        double term2 = (costOfCarry - intrest) * underlyingSpot * Math.exp((costOfCarry - intrest) * years);
        double term3 = intrest * strike * Math.exp(-intrest * years);

        if (OptionType.CALL.equals(type)) {
            double N1 = Gaussian.Phi(d1);
            double N2 = Gaussian.Phi(d2);
            return term1 - term2 * N1 - term3 * N2;
        } else {
            double N1 = Gaussian.Phi(-d1);
            double N2 = Gaussian.Phi(-d2);
            return term1 + term2 * N1 + term3 * N2;
        }
    }

    /**
     * Gets the forward price of a {@link Option}
     */
    public static double getForward(double spot, double years, double intrest, double dividend) {

        return spot * Math.exp(years * (intrest - dividend));
    }

    /**
     * Gets the moneyness of a {@link Option}
     */
    public static double getMoneyness(Option option, double underlyingSpot) {

        if (OptionType.CALL.equals(option.getOptionType())) {
            return (underlyingSpot - option.getStrike().doubleValue()) / underlyingSpot;
        } else {
            return (option.getStrike().doubleValue() - underlyingSpot) / underlyingSpot;
        }
    }

    /**
     * Gets the strike of a {@link Option} based on its delta.
     * Based on FX conventions as reported in "FX Volatility Smile Construction" by Dimitri Reiswich and Uwe Wystrup
     */
    public static double getStrikeByDelta(double delta, double impliedVol, double years, double forward,
            double intrest, OptionType type) {

        double midTerm = 1.0;
        if (years <= 1) {
            midTerm = Math.exp(intrest * years);
        }

        return forward * Math.exp((OptionType.CALL.equals(type) ? -1.0 : 1.0) * Gaussian.PhiInverse(midTerm * delta)
                * impliedVol * Math.sqrt(years) + 0.5 * Math.pow(impliedVol, 2) * years);
    }

    private static double standardNormalDensity(double input) {

        return 1.0 / Math.sqrt(2.0 * Math.PI) * Math.exp(-(input * input) / 2.0);
    }
}