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

net.finmath.functions.AnalyticFormulas Maven / Gradle / Ivy

Go to download

finmath lib is a Mathematical Finance Library in Java. It provides algorithms and methodologies related to mathematical finance.

There is a newer version: 6.0.19
Show newest version
/*
 * Created on 23.01.2004.
 *
 * (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: [email protected].
 */
package net.finmath.functions;

import java.util.Calendar;

import net.finmath.optimizer.GoldenSectionSearch;
import net.finmath.rootfinder.NewtonsMethod;
import net.finmath.stochastic.RandomVariableInterface;

import org.apache.commons.math3.analysis.UnivariateFunction;

/**
 * This class implements some functions as static class methods.
 * 
 * It provides functions like
 * 
    *
  • the Black-Scholes formula, *
  • the inverse of the Back-Scholes formula with respect to (implied) volatility, *
  • the corresponding functions (versions) for caplets and swaptions, *
  • some convexity adjustments. *
* * @author Christian Fries * @version 1.9 * @date 27.04.2012 */ public class AnalyticFormulas { // Suppress default constructor for non-instantiability private AnalyticFormulas() { // This constructor will never be invoked } /** * Calculates the Black-Scholes option value of a call, i.e., the payoff max(S(T)-K,0) P, where S follows a log-normal process with constant log-volatility. * * The method also handles cases where the forward and/or option strike is negative * and some limit cases where the forward and/or the option strike is zero. * * @param forward The forward of the underlying. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. If the option strike is ≤ 0.0 the method returns the value of the forward contract paying S(T)-K in T. * @param payoffUnit The payoff unit (e.g., the discount factor) * @return Returns the value of a European call option under the Black-Scholes model. */ public static double blackScholesGeneralizedOptionValue( double forward, double volatility, double optionMaturity, double optionStrike, double payoffUnit) { if(optionMaturity < 0) { return 0; } else if(forward < 0) { // We use max(X,0) = X + max(-X,0) return (forward - optionStrike) * payoffUnit + blackScholesGeneralizedOptionValue(-forward, volatility, optionMaturity, -optionStrike, payoffUnit); } else if((forward == 0) || (optionStrike <= 0.0) || (volatility <= 0.0) || (optionMaturity <= 0.0)) { // Limit case (where dPlus = +/- infty) return Math.max(forward - optionStrike,0) * payoffUnit; } else { // Calculate analytic value double dPlus = (Math.log(forward / optionStrike) + 0.5 * volatility * volatility * optionMaturity) / (volatility * Math.sqrt(optionMaturity)); double dMinus = dPlus - volatility * Math.sqrt(optionMaturity); double valueAnalytic = (forward * NormalDistribution.cumulativeDistribution(dPlus) - optionStrike * NormalDistribution.cumulativeDistribution(dMinus)) * payoffUnit; return valueAnalytic; } } /** * Calculates the Black-Scholes option value of a call, i.e., the payoff max(S(T)-K,0) P, where S follows a log-normal process with constant log-volatility. * * The model specific quantities are considered to be random variable, i.e., * the function may calculate an per-path valuation in a single call. * * @param forward The forward of the underlying. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. If the option strike is ≤ 0.0 the method returns the value of the forward contract paying S(T)-K in T. * @param payoffUnit The payoff unit (e.g., the discount factor) * @return Returns the value of a European call option under the Black-Scholes model. */ public static RandomVariableInterface blackScholesGeneralizedOptionValue( RandomVariableInterface forward, RandomVariableInterface volatility, double optionMaturity, double optionStrike, RandomVariableInterface payoffUnit) { if(optionMaturity < 0) { return forward.mult(0.0); } else { RandomVariableInterface dPlus = forward.div(optionStrike).log().add(volatility.squared().mult(0.5 * optionMaturity)).div(volatility).div(Math.sqrt(optionMaturity)); RandomVariableInterface dMinus = dPlus.sub(volatility.mult(Math.sqrt(optionMaturity))); UnivariateFunction cumulativeNormal = new UnivariateFunction() { public double value(double x) { return NormalDistribution.cumulativeDistribution(x); } }; RandomVariableInterface valueAnalytic = dPlus.apply(cumulativeNormal).mult(forward).sub(dMinus.apply(cumulativeNormal).mult(optionStrike)).mult(payoffUnit); return valueAnalytic; } } /** * Calculates the Black-Scholes option value of a call, i.e., the payoff max(S(T)-K,0), where S follows a log-normal process with constant log-volatility. * * @param initialStockValue The spot value of the underlying. * @param riskFreeRate The risk free rate r (df = exp(-r T)). * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. If the option strike is ≤ 0.0 the method returns the value of the forward contract paying S(T)-K in T. * @return Returns the value of a European call option under the Black-Scholes model. */ public static double blackScholesOptionValue( double initialStockValue, double riskFreeRate, double volatility, double optionMaturity, double optionStrike) { return blackScholesGeneralizedOptionValue( initialStockValue * Math.exp(riskFreeRate * optionMaturity), // forward volatility, optionMaturity, optionStrike, Math.exp(-riskFreeRate * optionMaturity) // payoff unit ); } /** * Calculates the Black-Scholes option value of an atm call option. * * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param forward The forward, i.e., the expectation of the index under the measure associated with payoff unit. * @param payoffUnit The payoff unit, i.e., the discount factor or the anuity associated with the payoff. * @return Returns the value of a European at-the-money call option under the Black-Scholes model */ public static double blackScholesATMOptionValue( double volatility, double optionMaturity, double forward, double payoffUnit) { if(optionMaturity < 0) return 0.0; // Calculate analytic value double dPlus = 0.5 * volatility * Math.sqrt(optionMaturity); double dMinus = -dPlus; double valueAnalytic = (NormalDistribution.cumulativeDistribution(dPlus) - NormalDistribution.cumulativeDistribution(dMinus)) * forward * payoffUnit; return valueAnalytic; } /** * Calculates the delta of a call option under a Black-Scholes model * * The method also handles cases where the forward and/or option strike is negative * and some limit cases where the forward or the option strike is zero. * In the case forward = option strike = 0 the method returns 1.0. * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The delta of the option */ public static double blackScholesOptionDelta( double initialStockValue, double riskFreeRate, double volatility, double optionMaturity, double optionStrike) { if(optionMaturity < 0) { return 0; } else if(initialStockValue < 0) { // We use Indicator(S>K) = 1 - Indicator(-S>-K) return 1 - blackScholesOptionDelta(-initialStockValue, riskFreeRate, volatility, optionMaturity, -optionStrike); } else if(initialStockValue == 0) { // Limit case (where dPlus = +/- infty) if(optionStrike < 0) return 1.0; // dPlus = +infinity else if(optionStrike > 0) return 0.0; // dPlus = -infinity else return 1.0; // Matter of definition of continuity of the payoff function } else if((optionStrike <= 0.0) || (volatility <= 0.0) || (optionMaturity <= 0.0)) // (and initialStockValue > 0) { // The Black-Scholes model does not consider it being an option return 1.0; } else { // Calculate delta double dPlus = (Math.log(initialStockValue / optionStrike) + (riskFreeRate + 0.5 * volatility * volatility) * optionMaturity) / (volatility * Math.sqrt(optionMaturity)); double delta = NormalDistribution.cumulativeDistribution(dPlus); return delta; } } /** * Calculates the delta of a call option under a Black-Scholes model * * The method also handles cases where the forward and/or option strike is negative * and some limit cases where the forward or the option strike is zero. * In the case forward = option strike = 0 the method returns 1.0. * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The delta of the option */ public static RandomVariableInterface blackScholesOptionDelta( RandomVariableInterface initialStockValue, RandomVariableInterface riskFreeRate, RandomVariableInterface volatility, double optionMaturity, double optionStrike) { if(optionMaturity < 0) { return initialStockValue.mult(0.0); } else { // Calculate delta RandomVariableInterface dPlus = initialStockValue.div(optionStrike).log().add(volatility.squared().mult(0.5).add(riskFreeRate).mult(optionMaturity)).div(volatility).div(Math.sqrt(optionMaturity)); UnivariateFunction cummulativeNormal = new UnivariateFunction() { public double value(double x) { return NormalDistribution.cumulativeDistribution(x); } }; RandomVariableInterface delta = dPlus.apply(cummulativeNormal); return delta; } } /** * Calculates the delta of a call option under a Black-Scholes model * * The method also handles cases where the forward and/or option strike is negative * and some limit cases where the forward or the option strike is zero. * In the case forward = option strike = 0 the method returns 1.0. * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The delta of the option */ public static RandomVariableInterface blackScholesOptionDelta( RandomVariableInterface initialStockValue, RandomVariableInterface riskFreeRate, RandomVariableInterface volatility, double optionMaturity, RandomVariableInterface optionStrike) { if(optionMaturity < 0) { return initialStockValue.mult(0.0); } else { // Calculate delta RandomVariableInterface dPlus = initialStockValue.div(optionStrike).log().add(volatility.squared().mult(0.5).add(riskFreeRate).mult(optionMaturity)).div(volatility).div(Math.sqrt(optionMaturity)); UnivariateFunction cummulativeNormal = new UnivariateFunction() { public double value(double x) { return NormalDistribution.cumulativeDistribution(x); } }; RandomVariableInterface delta = dPlus.apply(cummulativeNormal); return delta; } } /** * This static method calculated the gamma of a call option under a Black-Scholes model * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The gamma of the option */ public static double blackScholesOptionGamma( double initialStockValue, double riskFreeRate, double volatility, double optionMaturity, double optionStrike) { if(optionStrike <= 0.0 || optionMaturity <= 0.0) { // The Black-Scholes model does not consider it being an option return 0.0; } else { // Calculate gamma double dPlus = (Math.log(initialStockValue / optionStrike) + (riskFreeRate + 0.5 * volatility * volatility) * optionMaturity) / (volatility * Math.sqrt(optionMaturity)); double gamma = Math.exp(-0.5*dPlus*dPlus) / (Math.sqrt(2.0 * Math.PI * optionMaturity) * initialStockValue * volatility); return gamma; } } /** * This static method calculated the gamma of a call option under a Black-Scholes model * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The gamma of the option */ public static RandomVariableInterface blackScholesOptionGamma( RandomVariableInterface initialStockValue, RandomVariableInterface riskFreeRate, RandomVariableInterface volatility, double optionMaturity, double optionStrike) { if(optionStrike <= 0.0 || optionMaturity <= 0.0) { // The Black-Scholes model does not consider it being an option return initialStockValue.mult(0.0); } else { // Calculate gamma RandomVariableInterface dPlus = initialStockValue.div(optionStrike).log().add(volatility.squared().mult(0.5).add(riskFreeRate).mult(optionMaturity)).div(volatility).div(Math.sqrt(optionMaturity)); RandomVariableInterface gamma = dPlus.squared().mult(-0.5).exp().div(initialStockValue.mult(volatility).mult(Math.sqrt(2.0 * Math.PI * optionMaturity))); return gamma; } } /** * This static method calculated the vega of a call option under a Black-Scholes model * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The vega of the option */ public static double blackScholesOptionVega( double initialStockValue, double riskFreeRate, double volatility, double optionMaturity, double optionStrike) { if(optionStrike <= 0.0 || optionMaturity <= 0.0) { // The Black-Scholes model does not consider it being an option return 0.0; } else { // Calculate vega double dPlus = (Math.log(initialStockValue / optionStrike) + (riskFreeRate + 0.5 * volatility * volatility) * optionMaturity) / (volatility * Math.sqrt(optionMaturity)); double vega = Math.exp(-0.5*dPlus*dPlus) / Math.sqrt(2.0 * Math.PI) * initialStockValue * Math.sqrt(optionMaturity); return vega; } } /** * This static method calculated the rho of a call option under a Black-Scholes model * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The rho of the option */ public static double blackScholesOptionRho( double initialStockValue, double riskFreeRate, double volatility, double optionMaturity, double optionStrike) { if(optionStrike <= 0.0 || optionMaturity <= 0.0) { // The Black-Scholes model does not consider it being an option return 0.0; } else { // Calculate delta double dMinus = (Math.log(initialStockValue / optionStrike) + (riskFreeRate - 0.5 * volatility * volatility) * optionMaturity) / (volatility * Math.sqrt(optionMaturity)); double rho = optionStrike * optionMaturity * Math.exp(-riskFreeRate * optionMaturity) * NormalDistribution.cumulativeDistribution(dMinus); return rho; } } /** * Calculates the Black-Scholes option implied volatility of a call, i.e., the payoff *

max(S(T)-K,0)

, where S follows a log-normal process with constant log-volatility. * * @param forward The forward of the underlying. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. If the option strike is ≤ 0.0 the method returns the value of the forward contract paying S(T)-K in T. * @param payoffUnit The payoff unit (e.g., the discount factor) * @param optionValue The option value. * @return Returns the implied volatility of a European call option under the Black-Scholes model. */ public static double blackScholesOptionImpliedVolatility( double forward, double optionMaturity, double optionStrike, double payoffUnit, double optionValue) { // Limit the maximum number of iterations, to ensure this calculation returns fast, e.g. in cases when there is no such thing as an implied vol // TODO: An exception should be thrown, when there is no implied volatility for the given value. int maxIterations = 500; double maxAccuracy = 1E-15; if(optionStrike <= 0.0) { // Actually it is not an option return 0.0; } else { // Calculate an lower and upper bound for the volatility double p = NormalDistribution.inverseCumulativeDistribution((optionValue/payoffUnit+optionStrike)/(forward+optionStrike)) / Math.sqrt(optionMaturity); double q = 2.0 * Math.abs(Math.log(forward/optionStrike)) / optionMaturity; double volatilityLowerBound = p + Math.sqrt(Math.max(p * p - q, 0.0)); double volatilityUpperBound = p + Math.sqrt( p * p + q ); // If strike is close to forward the two bounds are close to the analytic solution if(Math.abs(volatilityLowerBound - volatilityUpperBound) < maxAccuracy) return (volatilityLowerBound+volatilityUpperBound) / 2.0; // Solve for implied volatility NewtonsMethod solver = new NewtonsMethod(0.5*(volatilityLowerBound+volatilityUpperBound) /* guess */); while(solver.getAccuracy() > maxAccuracy && !solver.isDone() && solver.getNumberOfIterations() < maxIterations) { double volatility = solver.getNextPoint(); // Calculate analytic value double dPlus = (Math.log(forward / optionStrike) + 0.5 * volatility * volatility * optionMaturity) / (volatility * Math.sqrt(optionMaturity)); double dMinus = dPlus - volatility * Math.sqrt(optionMaturity); double valueAnalytic = (forward * NormalDistribution.cumulativeDistribution(dPlus) - optionStrike * NormalDistribution.cumulativeDistribution(dMinus)) * payoffUnit; double derivativeAnalytic = forward * Math.sqrt(optionMaturity) * Math.exp(-0.5*dPlus*dPlus) / Math.sqrt(2.0*Math.PI) * payoffUnit; double error = valueAnalytic - optionValue; solver.setValueAndDerivative(error,derivativeAnalytic); } return solver.getBestPoint(); } } /** * Calculates the Black-Scholes option value of a digital call option. * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return Returns the value of a European call option under the Black-Scholes model */ public static double blackScholesDigitalOptionValue( double initialStockValue, double riskFreeRate, double volatility, double optionMaturity, double optionStrike) { if(optionStrike <= 0.0) { // The Black-Scholes model does not consider it being an option return 1.0; } else { // Calculate analytic value double dPlus = (Math.log(initialStockValue / optionStrike) + (riskFreeRate + 0.5 * volatility * volatility) * optionMaturity) / (volatility * Math.sqrt(optionMaturity)); double dMinus = dPlus - volatility * Math.sqrt(optionMaturity); double valueAnalytic = Math.exp(- riskFreeRate * optionMaturity) * NormalDistribution.cumulativeDistribution(dMinus); return valueAnalytic; } } /** * Calculates the delta of a digital option under a Black-Scholes model * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The delta of the digital option */ public static double blackScholesDigitalOptionDelta( double initialStockValue, double riskFreeRate, double volatility, double optionMaturity, double optionStrike) { if(optionStrike <= 0.0 || optionMaturity <= 0.0) { // The Black-Scholes model does not consider it being an option return 0.0; } else { // Calculate delta double dPlus = (Math.log(initialStockValue / optionStrike) + (riskFreeRate + 0.5 * volatility * volatility) * optionMaturity) / (volatility * Math.sqrt(optionMaturity)); double dMinus = dPlus - volatility * Math.sqrt(optionMaturity); double delta = Math.exp(-0.5*dMinus*dMinus) / (Math.sqrt(2.0 * Math.PI * optionMaturity) * initialStockValue * volatility); return delta; } } /** * Calculate the value of a caplet assuming the Black'76 model. * * @param forward The forward (spot). * @param volatility The Black'76 volatility. * @param optionMaturity The option maturity * @param optionStrike The option strike. * @param periodLength The period length of the underlying forward rate. * @param discountFactor The discount factor corresponding to the payment date (option maturity + period length). * @return Returns the value of a caplet under the Black'76 model */ public static double blackModelCapletValue( double forward, double volatility, double optionMaturity, double optionStrike, double periodLength, double discountFactor) { // May be interpreted as a special version of the Black-Scholes Formula return AnalyticFormulas.blackScholesGeneralizedOptionValue(forward, volatility, optionMaturity, optionStrike, periodLength * discountFactor); } /** * Calculate the value of a digital caplet assuming the Black'76 model. * * @param forward The forward (spot). * @param volatility The Black'76 volatility. * @param periodLength The period length of the underlying forward rate. * @param discountFactor The discount factor corresponding to the payment date (option maturity + period length). * @param optionMaturity The option maturity * @param optionStrike The option strike. * @return Returns the price of a digital caplet under the Black'76 model */ public static double blackModelDgitialCapletValue( double forward, double volatility, double periodLength, double discountFactor, double optionMaturity, double optionStrike) { // May be interpreted as a special version of the Black-Scholes Formula return AnalyticFormulas.blackScholesDigitalOptionValue(forward, 0.0, volatility, optionMaturity, optionStrike) * periodLength * discountFactor; } /** * Calculate the value of a swaption assuming the Black'76 model. * * @param forwardSwaprate The forward (spot) * @param volatility The Black'76 volatility. * @param optionMaturity The option maturity. * @param optionStrike The option strike. * @param swapAnnuity The swap annuity corresponding to the underlying swap. * @return Returns the value of a Swaption under the Black'76 model */ public static double blackModelSwaptionValue( double forwardSwaprate, double volatility, double optionMaturity, double optionStrike, double swapAnnuity) { // May be interpreted as a special version of the Black-Scholes Formula return AnalyticFormulas.blackScholesGeneralizedOptionValue(forwardSwaprate, volatility, optionMaturity, optionStrike, swapAnnuity); } /** * Calculates the value of an Exchange option under a generalized Black-Scholes model, i.e., the payoff \( max(S_{1}(T)-S_{2}(T),0) \), * where \( S_{1} \) and \( S_{2} \) follow a log-normal process with constant log-volatility and constant instantaneous correlation. * * The method also handles cases where the forward and/or option strike is negative * and some limit cases where the forward and/or the option strike is zero. * * @param spot1 Value of \( S_{1}(0) \) * @param spot2 Value of \( S_{2}(0) \) * @param volatility1 Volatility of \( \log(S_{1}(t)) \) * @param volatility2 Volatility of \( \log(S_{2}(t)) \) * @param correlation Instantaneous correlation of \( \log(S_{1}(t)) \) and \( \log(S_{2}(t)) \) * @param optionMaturity The option maturity \( T \). * @return Returns the value of a European exchange option under the Black-Scholes model. */ public static double margrabeExchangeOptionValue( double spot1, double spot2, double volatility1, double volatility2, double correlation, double optionMaturity) { double volatility = Math.sqrt(volatility1*volatility1 + volatility2*volatility2 - 2.0 * volatility1*volatility2*correlation); return blackScholesGeneralizedOptionValue(spot1, volatility, optionMaturity, spot2, 1.0); } /** * Calculates the option value of a call, i.e., the payoff max(S(T)-K,0) P, where S follows a * normal process with constant volatility, i.e., a Bachelier model. * * @param forward The forward of the underlying. * @param volatility The Bachelier volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @param payoffUnit The payoff unit (e.g., the discount factor) * @return Returns the value of a European call option under the Bachelier model. */ public static double bachelierOptionValue( double forward, double volatility, double optionMaturity, double optionStrike, double payoffUnit) { if(optionMaturity < 0) { return 0; } else { // Calculate analytic value double dPlus = (forward - optionStrike) / (volatility * Math.sqrt(optionMaturity)); double valueAnalytic = ((forward - optionStrike) * NormalDistribution.cumulativeDistribution(dPlus) + (volatility * Math.sqrt(optionMaturity)) * NormalDistribution.density(dPlus)) * payoffUnit; return valueAnalytic; } } /** * Calculates the option value of a call, i.e., the payoff max(S(T)-K,0) P, where S follows a * normal process with constant volatility, i.e., a Bachelier model. * * @param forward The forward of the underlying. * @param volatility The Bachelier volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @param payoffUnit The payoff unit (e.g., the discount factor) * @return Returns the value of a European call option under the Bachelier model. */ public static RandomVariableInterface bachelierOptionValue( RandomVariableInterface forward, RandomVariableInterface volatility, double optionMaturity, double optionStrike, RandomVariableInterface payoffUnit) { if(optionMaturity < 0) { return forward.mult(0.0); } else { RandomVariableInterface integratedVolatility = volatility.mult(Math.sqrt(optionMaturity)); RandomVariableInterface dPlus = forward.sub(optionStrike).div(integratedVolatility); UnivariateFunction cummulativeNormal = new UnivariateFunction() { public double value(double x) { return NormalDistribution.cumulativeDistribution(x); } }; UnivariateFunction densityNormal = new UnivariateFunction() { public double value(double x) { return NormalDistribution.density(x); } }; RandomVariableInterface valueAnalytic = dPlus.apply(cummulativeNormal).mult(forward.sub(optionStrike)) .add(dPlus.apply(densityNormal).mult(integratedVolatility)).mult(payoffUnit); return valueAnalytic; } } /** * Calculates the Bachelier option implied volatility of a call, i.e., the payoff *

max(S(T)-K,0)

, where S follows a normal process with constant volatility. * * @param forward The forward of the underlying. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. If the option strike is ≤ 0.0 the method returns the value of the forward contract paying S(T)-K in T. * @param payoffUnit The payoff unit (e.g., the discount factor) * @param optionValue The option value. * @return Returns the implied volatility of a European call option under the Bachelier model. */ public static double bachelierOptionImpliedVolatility( double forward, double optionMaturity, double optionStrike, double payoffUnit, double optionValue) { // Limit the maximum number of iterations, to ensure this calculation returns fast, e.g. in cases when there is no such thing as an implied vol // TODO: An exception should be thrown, when there is no implied volatility for the given value. int maxIterations = 100; double maxAccuracy = 0.0; if(optionStrike <= 0.0) { // Actually it is not an option return 0.0; } else { // Calculate an lower and upper bound for the volatility double volatilityLowerBound = 0.0; double volatilityUpperBound = (optionValue + Math.abs(forward-optionStrike)) / Math.sqrt(optionMaturity) / payoffUnit; volatilityUpperBound /= Math.min(1.0, NormalDistribution.density((forward - optionStrike) / (volatilityUpperBound * Math.sqrt(optionMaturity)))); // Solve for implied volatility GoldenSectionSearch solver = new GoldenSectionSearch(volatilityLowerBound, volatilityUpperBound); while(solver.getAccuracy() > maxAccuracy && !solver.isDone() && solver.getNumberOfIterations() < maxIterations) { double volatility = solver.getNextPoint(); double valueAnalytic = bachelierOptionValue(forward, volatility, optionMaturity, optionStrike, payoffUnit); double error = valueAnalytic - optionValue; solver.setValue(error*error); } return solver.getBestPoint(); } } /** * Calculate the value of a CMS option using the Black-Scholes model for the swap rate together with * the Hunt-Kennedy convexity adjustment. * * @param forwardSwaprate The forward swap rate * @param volatility Volatility of the log of the swap rate * @param swapAnnuity The swap annuity * @param optionMaturity The option maturity * @param swapMaturity The swap maturity * @param payoffUnit The payoff unit, e.g., the discount factor corresponding to the payment date * @param optionStrike The option strike * @return Value of the CMS option */ public static double huntKennedyCMSOptionValue( double forwardSwaprate, double volatility, double swapAnnuity, double optionMaturity, double swapMaturity, double payoffUnit, double optionStrike) { double a = 1.0/swapMaturity; double b = (payoffUnit / swapAnnuity - a) / forwardSwaprate; double convexityAdjustment = Math.exp(volatility*volatility*optionMaturity); double valueUnadjusted = blackModelSwaptionValue(forwardSwaprate, volatility, optionMaturity, optionStrike, swapAnnuity); double valueAdjusted = blackModelSwaptionValue(forwardSwaprate * convexityAdjustment, volatility, optionMaturity, optionStrike, swapAnnuity); return a * valueUnadjusted + b * forwardSwaprate * valueAdjusted; } /** * Calculate the value of a CMS strike using the Black-Scholes model for the swap rate together with * the Hunt-Kennedy convexity adjustment. * * @param forwardSwaprate The forward swap rate * @param volatility Volatility of the log of the swap rate * @param swapAnnuity The swap annuity * @param optionMaturity The option maturity * @param swapMaturity The swap maturity * @param payoffUnit The payoff unit, e.g., the discount factor corresponding to the payment date * @param optionStrike The option strike * @return Value of the CMS strike */ public static double huntKennedyCMSFloorValue( double forwardSwaprate, double volatility, double swapAnnuity, double optionMaturity, double swapMaturity, double payoffUnit, double optionStrike) { double huntKennedyCMSOptionValue = huntKennedyCMSOptionValue(forwardSwaprate, volatility, swapAnnuity, optionMaturity, swapMaturity, payoffUnit, optionStrike); // A floor is an option plus the strike (max(X,K) = max(X-K,0) + K) return huntKennedyCMSOptionValue + optionStrike * payoffUnit; } /** * Calculate the adjusted forward swaprate corresponding to a change of payoff unit from the given swapAnnuity to the given payoffUnit * using the Black-Scholes model for the swap rate together with the Hunt-Kennedy convexity adjustment. * * @param forwardSwaprate The forward swap rate * @param volatility Volatility of the log of the swap rate * @param swapAnnuity The swap annuity * @param optionMaturity The option maturity * @param swapMaturity The swap maturity * @param payoffUnit The payoff unit, e.g., the discount factor corresponding to the payment date * @return Convexity adjusted forward rate */ public static double huntKennedyCMSAdjustedRate( double forwardSwaprate, double volatility, double swapAnnuity, double optionMaturity, double swapMaturity, double payoffUnit) { double a = 1.0/swapMaturity; double b = (payoffUnit / swapAnnuity - a) / forwardSwaprate; double convexityAdjustment = Math.exp(volatility*volatility*optionMaturity); double rateUnadjusted = forwardSwaprate; double rateAdjusted = forwardSwaprate * convexityAdjustment; return (a * rateUnadjusted + b * forwardSwaprate * rateAdjusted) * swapAnnuity / payoffUnit; } /** * Re-implementation of the Excel PRICE function (a rather primitive bond price formula). * The re-implementation is not exact, because this function does not consider daycount conventions. * * @param settlementDate Valuation date. * @param maturityDate Maturity date of the bond. * @param coupon Coupon payment. * @param yield Yield (discount factor, using frequency: 1/(1 + yield/frequency). * @param redemption Redemption (notional repayment). * @param frequency Frequency (1,2,4). * @return price Clean price. */ public static double price( java.util.Date settlementDate, java.util.Date maturityDate, double coupon, double yield, double redemption, int frequency) { double price = 0.0; if(maturityDate.after(settlementDate)) { price += redemption; } Calendar paymentDate = Calendar.getInstance(); paymentDate.setTime(maturityDate); while(paymentDate.after(settlementDate)) { price += coupon; // Disocunt back price /= 1.0 + yield / frequency; paymentDate.add(Calendar.MONTH, -12/frequency); } Calendar periodEndDate = (Calendar)paymentDate.clone(); periodEndDate.add(Calendar.MONTH, +12/frequency); // Accrue running period double accrualPeriod = (paymentDate.getTimeInMillis() - settlementDate.getTime()) / (periodEndDate.getTimeInMillis() - paymentDate.getTimeInMillis()); price *= Math.pow(1.0 + yield / frequency, accrualPeriod); price -= coupon * accrualPeriod; return price; } /** * Re-implementation of the Excel PRICE function (a rather primitive bond price formula). * The re-implementation is not exact, because this function does not consider daycount conventions. * We assume we have (int)timeToMaturity/frequency future periods and the running period has * an accrual period of timeToMaturity - frequency * ((int)timeToMaturity/frequency). * * @param timeToMaturity The time to maturity. * @param coupon Coupon payment. * @param yield Yield (discount factor, using frequency: 1/(1 + yield/frequency). * @param redemption Redemption (notional repayment). * @param frequency Frequency (1,2,4). * @return price Clean price. */ public static double price( double timeToMaturity, double coupon, double yield, double redemption, int frequency) { double price = 0.0; if(timeToMaturity > 0) { price += redemption; } double paymentTime = timeToMaturity; while(paymentTime > 0) { price += coupon; // Discount back price = price / (1.0 + yield / frequency); paymentTime -= 1.0 / frequency; } // Accrue running period double accrualPeriod = 0.0- paymentTime; price *= Math.pow(1.0 + yield / frequency, accrualPeriod); price -= coupon * accrualPeriod; return price; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy