/** * 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. * * <p>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; } }
/** * 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 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 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; } }
public static org.nd4j.linalg.api.rng.distribution.Distribution createDistribution( Distribution dist) { if (dist == null) return null; if (dist instanceof NormalDistribution) { NormalDistribution nd = (NormalDistribution) dist; return Nd4j.getDistributions().createNormal(nd.getMean(), nd.getStd()); } if (dist instanceof GaussianDistribution) { GaussianDistribution nd = (GaussianDistribution) dist; return Nd4j.getDistributions().createNormal(nd.getMean(), nd.getStd()); } if (dist instanceof UniformDistribution) { UniformDistribution ud = (UniformDistribution) dist; return Nd4j.getDistributions().createUniform(ud.getLower(), ud.getUpper()); } if (dist instanceof BinomialDistribution) { BinomialDistribution bd = (BinomialDistribution) dist; return Nd4j.getDistributions() .createBinomial(bd.getNumberOfTrials(), bd.getProbabilityOfSuccess()); } throw new RuntimeException("unknown distribution type: " + dist.getClass()); }
/** * Calculates the Bachelier option implied volatility of a call, i.e., the payoff * * <p><i>max(S(T)-K,0)</i>, where <i>S</i> 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(); } }
/** * 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 call option under a Black-Scholes model * * <p>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 Black-Scholes option implied volatility of a call, i.e., the payoff * * <p><i>max(S(T)-K,0)</i>, where <i>S</i> 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(); } }