/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.option.pricing.analytic.formula; import com.opengamma.analytics.math.statistics.distribution.NormalDistribution; import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution; import com.opengamma.util.ArgumentChecker; /** * Compute price and Greeks of digital (cash-or-nothing) option * Cash-or-nothing call option pays 0 if S <= K and K if S > K, whereas cash-or-nothing put option pays 1. if S < K and 0 if S >= K, * where S is asset price at expiry. */ public class DigitalOptionFunction { private static final ProbabilityDistribution<Double> NORMAL = new NormalDistribution(0, 1); /** * Compute <b>spot</b> price of cash-or-nothing option * @param spot The spot * @param strike The strike * @param timeToExpiry The time to expiry * @param lognormalVol The log-normal volatility * @param interestRate The interest rate * @param costOfCarry The cost-of-carry * @param isCall True for calls, false for puts * @return The option price */ public static double price(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry, final boolean isCall) { ArgumentChecker.isTrue(spot > 0.0, "negative/NaN spot; have {}", spot); ArgumentChecker.isTrue(strike > 0.0, "negative/NaN strike; have {}", strike); ArgumentChecker.isTrue(timeToExpiry > 0.0, "negative/NaN timeToExpiry; have {}", timeToExpiry); ArgumentChecker.isTrue(lognormalVol > 0.0, "negative/NaN lognormalVol; have {}", lognormalVol); ArgumentChecker.isFalse(Double.isNaN(interestRate), "interestRate is NaN"); ArgumentChecker.isFalse(Double.isNaN(costOfCarry), "costOfCarry is NaN"); final double d = (Math.log(spot / strike) + (costOfCarry - 0.5 * lognormalVol * lognormalVol) * timeToExpiry) / lognormalVol / Math.sqrt(timeToExpiry); final double sign = isCall ? 1. : -1.; return Math.exp(-interestRate * timeToExpiry) * NORMAL.getCDF(sign * d); } /** * Compute delta of cash-or-nothing option * @param spot The spot * @param strike The strike * @param timeToExpiry The time to expiry * @param lognormalVol The log-normal volatility * @param interestRate The interest rate * @param costOfCarry The cost-of-carry * @param isCall True for calls, false for puts * @return The option price */ public static double delta(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry, final boolean isCall) { ArgumentChecker.isTrue(spot > 0.0, "negative/NaN spot; have {}", spot); ArgumentChecker.isTrue(strike > 0.0, "negative/NaN strike; have {}", strike); ArgumentChecker.isTrue(timeToExpiry > 0.0, "negative/NaN timeToExpiry; have {}", timeToExpiry); ArgumentChecker.isTrue(lognormalVol > 0.0, "negative/NaN lognormalVol; have {}", lognormalVol); ArgumentChecker.isFalse(Double.isNaN(interestRate), "interestRate is NaN"); ArgumentChecker.isFalse(Double.isNaN(costOfCarry), "costOfCarry is NaN"); final double sigmaRootT = lognormalVol * Math.sqrt(timeToExpiry); final double d = (Math.log(spot / strike) + (costOfCarry - 0.5 * lognormalVol * lognormalVol) * timeToExpiry) / sigmaRootT; final double sign = isCall ? 1. : -1.; return Math.exp(-interestRate * timeToExpiry) * (sign * NORMAL.getPDF(d) / spot / sigmaRootT); } /** * Compute gamma of cash-or-nothing option * @param spot The spot * @param strike The strike * @param timeToExpiry The time to expiry * @param lognormalVol The log-normal volatility * @param interestRate The interest rate * @param costOfCarry The cost-of-carry * @param isCall True for calls, false for puts * @return The option price */ public static double gamma(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry, final boolean isCall) { ArgumentChecker.isTrue(spot > 0.0, "negative/NaN spot; have {}", spot); ArgumentChecker.isTrue(strike > 0.0, "negative/NaN strike; have {}", strike); ArgumentChecker.isTrue(timeToExpiry > 0.0, "negative/NaN timeToExpiry; have {}", timeToExpiry); ArgumentChecker.isTrue(lognormalVol > 0.0, "negative/NaN lognormalVol; have {}", lognormalVol); ArgumentChecker.isFalse(Double.isNaN(interestRate), "interestRate is NaN"); ArgumentChecker.isFalse(Double.isNaN(costOfCarry), "costOfCarry is NaN"); final double sigmaRootT = lognormalVol * Math.sqrt(timeToExpiry); final double d = (Math.log(spot / strike) + (costOfCarry - 0.5 * lognormalVol * lognormalVol) * timeToExpiry) / sigmaRootT; final double sign = isCall ? 1. : -1.; return -sign * (Math.exp(-interestRate * timeToExpiry) * NORMAL.getPDF(d) * (1. + d / sigmaRootT) / spot / spot / sigmaRootT); } /** * Compute theta price of cash-or-nothing option * @param spot The spot * @param strike The strike * @param timeToExpiry The time to expiry * @param lognormalVol The log-normal volatility * @param interestRate The interest rate * @param costOfCarry The cost-of-carry * @param isCall True for calls, false for puts * @return The option price */ public static double theta(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry, final boolean isCall) { ArgumentChecker.isTrue(spot > 0.0, "negative/NaN spot; have {}", spot); ArgumentChecker.isTrue(strike > 0.0, "negative/NaN strike; have {}", strike); ArgumentChecker.isTrue(timeToExpiry > 0.0, "negative/NaN timeToExpiry; have {}", timeToExpiry); ArgumentChecker.isTrue(lognormalVol > 0.0, "negative/NaN lognormalVol; have {}", lognormalVol); ArgumentChecker.isFalse(Double.isNaN(interestRate), "interestRate is NaN"); ArgumentChecker.isFalse(Double.isNaN(costOfCarry), "costOfCarry is NaN"); final double d = (Math.log(spot / strike) + (costOfCarry - 0.5 * lognormalVol * lognormalVol) * timeToExpiry) / lognormalVol / Math.sqrt(timeToExpiry); final double sign = isCall ? 1. : -1.; final double div = 0.5 * (-Math.log(spot / strike) / Math.pow(timeToExpiry, 1.5) + (costOfCarry - 0.5 * lognormalVol * lognormalVol) / Math.pow(timeToExpiry, 0.5)) / lognormalVol; return interestRate * Math.exp(-interestRate * timeToExpiry) * NORMAL.getCDF(sign * d) - sign * Math.exp(-interestRate * timeToExpiry) * NORMAL.getPDF(d) * div; } /** * Compute driftless (forward) theta price of cash-or-nothing option * @param forward The forward * @param strike The strike * @param timeToExpiry The time to expiry * @param lognormalVol The log-normal volatility * @param isCall True for calls, false for puts * @return The option price */ public static double driftlessTheta(final double forward, final double strike, final double timeToExpiry, final double lognormalVol, final boolean isCall) { ArgumentChecker.isTrue(forward > 0.0, "negative/NaN forward; have {}", forward); ArgumentChecker.isTrue(strike > 0.0, "negative/NaN strike; have {}", strike); ArgumentChecker.isTrue(timeToExpiry > 0.0, "negative/NaN timeToExpiry; have {}", timeToExpiry); ArgumentChecker.isTrue(lognormalVol > 0.0, "negative/NaN lognormalVol; have {}", lognormalVol); final double sigmaRootT = lognormalVol * Math.sqrt(timeToExpiry); final double d = Math.log(forward / strike) / sigmaRootT - 0.5 * sigmaRootT; final double sign = isCall ? 1. : -1.; final double div = 0.5 * (-Math.log(forward / strike) / Math.pow(timeToExpiry, 1.5) / lognormalVol - 0.5 * lognormalVol / Math.pow(timeToExpiry, 0.5)); return -sign * NORMAL.getPDF(d) * div; } /** * Compute vega price of cash-or-nothing option * @param spot The spot * @param strike The strike * @param timeToExpiry The time to expiry * @param lognormalVol The log-normal volatility * @param interestRate The interest rate * @param costOfCarry The cost-of-carry * @param isCall True for calls, false for puts * @return The option price */ public static double vega(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry, final boolean isCall) { ArgumentChecker.isTrue(spot > 0.0, "negative/NaN spot; have {}", spot); ArgumentChecker.isTrue(strike > 0.0, "negative/NaN strike; have {}", strike); ArgumentChecker.isTrue(timeToExpiry > 0.0, "negative/NaN timeToExpiry; have {}", timeToExpiry); ArgumentChecker.isTrue(lognormalVol > 0.0, "negative/NaN lognormalVol; have {}", lognormalVol); ArgumentChecker.isFalse(Double.isNaN(interestRate), "interestRate is NaN"); ArgumentChecker.isFalse(Double.isNaN(costOfCarry), "costOfCarry is NaN"); final double rootT = Math.sqrt(timeToExpiry); final double d = (Math.log(spot / strike) + (costOfCarry - 0.5 * lognormalVol * lognormalVol) * timeToExpiry) / lognormalVol / rootT; final double sign = isCall ? 1. : -1.; final double div = -(Math.log(spot / strike) + costOfCarry * timeToExpiry) / lognormalVol / lognormalVol / rootT - 0.5 * rootT; return sign * Math.exp(-interestRate * timeToExpiry) * NORMAL.getPDF(d) * div; } }