private void priceCheck(double[] strikes) { for (int i = 0; i < N; i++) { double ivNormalComputed = NormalFormulaRepository.impliedVolatilityFromBlackApproximated(FORWARD, strikes[i], T, SIGMA_BLACK[i]); double priceNormalComputed = NormalFormulaRepository.price(FORWARD, strikes[i], T, ivNormalComputed, PutCall.CALL) * DF; double priceBlack = BlackFormulaRepository.price(FORWARD, strikes[i], T, SIGMA_BLACK[i], true) * DF; assertEquals(priceNormalComputed, priceBlack, TOLERANCE_PRICE); } }
//------------------------------------------------------------------------- /// <summary> /// Computes the implied volatility. /// <para> /// If the volatility data is not zero, it is used as a starting point for the volatility search. /// </para> /// <para> /// Note that the 'numeraire' is a simple multiplier and is the responsibility of the caller. /// /// </para> /// </summary> /// <param name="optionPrice"> the price of the option </param> /// <param name="forward"> the forward value of the underlying </param> /// <param name="strike"> the strike </param> /// <param name="timeToExpiry"> the time to expiry </param> /// <param name="initialNormalVol"> the normal volatility used to start the search </param> /// <param name="numeraire"> the numeraire </param> /// <param name="putCall"> whether it is put or call </param> /// <returns> the implied volatility </returns> public static double impliedVolatility(double optionPrice, double forward, double strike, double timeToExpiry, double initialNormalVol, double numeraire, PutCall putCall) { double intrinsicPrice = numeraire * Math.Max(0, (putCall.Call ? 1 : -1) * (forward - strike)); ArgChecker.isTrue(optionPrice > intrinsicPrice || DoubleMath.fuzzyEquals(optionPrice, intrinsicPrice, 1e-6), "Option price (" + optionPrice + ") less than intrinsic value (" + intrinsicPrice + ")"); if (System.BitConverter.DoubleToInt64Bits(optionPrice) == Double.doubleToLongBits(intrinsicPrice)) { return(0d); } double sigma = (Math.Abs(initialNormalVol) < 1e-10 ? 0.3 * forward : initialNormalVol); double maxChange = 0.5 * forward; ValueDerivatives price = priceAdjoint(forward, strike, timeToExpiry, sigma, numeraire, putCall); double vega = price.getDerivative(1); double change = (price.Value - optionPrice) / vega; double sign = Math.Sign(change); change = sign * Math.Min(maxChange, Math.Abs(change)); if (change > 0 && change > sigma) { change = sigma; } int count = 0; while (Math.Abs(change) > EPS) { sigma -= change; price = priceAdjoint(forward, strike, timeToExpiry, sigma, numeraire, putCall); vega = price.getDerivative(1); change = (price.Value - optionPrice) / vega; sign = Math.Sign(change); change = sign * Math.Min(maxChange, Math.Abs(change)); if (change > 0 && change > sigma) { change = sigma; } if (count++ > MAX_ITERATIONS) { BracketRoot bracketer = new BracketRoot(); BisectionSingleRootFinder rootFinder = new BisectionSingleRootFinder(EPS); System.Func <double, double> func = (double?volatility) => { return(numeraire * NormalFormulaRepository.price(forward, strike, timeToExpiry, volatility.Value, putCall) - optionPrice); }; double[] range = bracketer.getBracketedPoints(func, 0d, 10d); return(rootFinder.getRoot(func, range[0], range[1]).Value); } } return(sigma); }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes: //ORIGINAL LINE: @SuppressWarnings("synthetic-access") @Override public System.Nullable<double> apply(NormalFunctionData data) public override double?apply(NormalFunctionData data) { ArgChecker.notNull(data, "data"); return(data.Numeraire * NormalFormulaRepository.price(data.Forward, option.Strike, option.TimeToExpiry, data.NormalVolatility, option.PutCall)); }