Why Black–Scholes Implied Volatility Newton Solvers Return NaN
Summary
The document asks why a Newton–Raphson solver sometimes returns NaN while estimating implied volatility from option prices. Its code iterates on volatility using the difference between the market price and the Black–Scholes price, divided by vega. The reported failures occur only for some calls and puts, while other results agree with a trading platform; a separate bisection implementation also fails on the supplied data.
The accepted answer explains that some observed option prices are outside the range consistent with any Black–Scholes volatility, such as an in-the-money call priced below intrinsic value. It recommends checking price feasibility before starting the numerical search. Another answer points out that very small vega can make Newton updates unstable or undefined. The discussion offers diagnostic possibilities rather than a complete implementation: it does not establish which issue caused each failure, and it does not fully analyze the input data, boundary conditions, or solver safeguards.
Key ideas
- Some option prices cannot be represented by any implied volatility under the pricing model.
- Check option price feasibility before running an implied volatility search.
- Very small vega can make Newton–Raphson updates unstable or undefined.
- A NaN result does not by itself identify whether the input or solver caused the failure.
Tags
Full text
# C# - Using Black Scholes Newton returns NaN occasionally
# C# - Using Black Scholes Newton returns NaN occasionally
First caveat: I'm a programmer doing this for a client, and my knowledge of options probably has holes in it. So be a little forgiving here. =)
The Issue: When I run Black Scholes Newton against all options in a chain, I occasionally get NaN return values. My comparison for accuracy is against ThinkorSwim's numbers.
Here are screencaps of the issue: https://i.sstatic.net/B0vYN.jpg
spot, strike, time, etc, are all there. And most of the time the numbers are accurate. But once I hit certain ranges with Calls and Puts, I get those NaN's, and I can't seem to figure out the pattern as to why
I'm using garagebandhedgefund's code as a base, with adjustments for puts/calls.
Here's the Newton Code:
```
public static double OptionPriceImpliedVolatilityCallBlackScholesNewton(double S, double K, double r, double time, double optionPrice,bool iscall, bool isput,out double price,out double diff)
{
price = 0;
diff = 0;
int MAX_ITERATIONS = 100;
double ACCURACY = 1.0e-5;
double t_sqrt = Math.Sqrt(time);
double sigma = (optionPrice / S) / (0.398 * t_sqrt); // find initial value
price = 0;
for (int i = 0; i < MAX_ITERATIONS; i++)
{
if (iscall)
{
price = OptionPriceCallBlackScholes(S, K, r, sigma, time);
}
if (isput)
{
price = OptionPricePutBlackScholes(S, K, r, sigma, time);
}
diff = optionPrice - price;
if (Math.Abs(diff) < ACCURACY)
return sigma;
double d1 = (Math.Log(S / K) + r * time) / (sigma * t_sqrt) + 0.5 * sigma * t_sqrt;
double vega = (S * t_sqrt * NormDist(d1));
sigma = sigma + diff / vega;
}
return sigma; // something screwy happened, should throw exception // <--- original code
//throw new Exception("An error occurred"); // Comment this line if you uncomment the line above
}
```
My question: Is it my code? Is it certain ranges of values? Are there cases where Newton fails to return valid values? I'm kinda stumped here.
For the record, when I use this data against garageband's Bisection code, I get errors across the board. I don't know if that's related. I'm fairly certain of the accuracy of my input data, since I do get some valid returns with Newton.
## Answer by derenik (score 11, accepted)
https://quant.stackexchange.com/a/15332
Some option prices can't be converted to volatility. E.g. A bid for an in-the-money call which is below its intrinsic value. So sometimes NaN is a valid answer. Best way to handle it is to do precursory checks before going down to the search loop.
## Answer by Matthew (score 1)
https://quant.stackexchange.com/a/15326
.NET doubles return `double.NaN` when you do things like divide zero by zero. With doubles, anything less than `double.Epsilon` is "zero" for the purpose of this result.
I suggest that your `vega` is less than `double.Epsilon`
What happens if you run the same method using `decimal` instead?Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.