Skip to content
All library documents

Why Local Volatility Can Differ from Implied Volatility

Article Quant Q&A · Author: nickzhy

Summary

The document describes a numerical attempt to compute local volatility from a smoothed implied volatility surface using Dupire’s equation. The author compares the resulting local volatility with smoothed implied volatility and asks whether local volatility should appear less smooth. They also report that reducing the strike increment in finite-difference calculations makes the local volatility sharply change and become unstable.

The included method estimates derivatives of option prices with respect to time and strike using centered finite differences, then substitutes them into a Dupire formula. The example uses a fixed time increment and strike increment, but it provides no validated results or answer resolving whether the output is correct. The central issue is numerical sensitivity: finite differences on a fitted surface can amplify noise, and an arbitrarily tiny strike step is not necessarily more accurate. The document is therefore useful as a diagnostic question, but offers no recommended smoothing, grid, or stability procedure.

Key ideas

  • Dupire local volatility is calculated from derivatives of option prices across maturity and strike.
  • The example estimates these derivatives with centered finite differences.
  • Local volatility computed from a smoothed implied volatility surface may appear similarly smooth.
  • Very small strike increments can amplify numerical noise and destabilize second derivatives.
  • The document does not establish a correct increment or provide a validated local volatility result.

Tags

Full text
# Very close local volatility and implied volatility using Dupire's equation


# Very close local volatility and implied volatility using Dupire's equation












I used Dupire's equation to calculate the local volatility as in https://www.frouah.com/finance%20notes/Dupire%20Local%20Volatility.pdf and Numerical example of how to calculate local vol surface from IV surface.

But my local volatility is very close to the smoothed implied volatility. (See the attached figure.). Shouldn't the lv line be more sharp (less smooth) than the iv line?

And I find that if I decrease the delta to 1e-4, the local volatility change sharply and becomes non-smooth. As the figure below: So is it right to set a very small strike delta when calculating its partial dirivative?

The code I used is below (the excel file is can be got from https://1drv.ms/x/s!AhPTUXN0QjiSgrZeThcRtn2lJJf6dw?e=t3xqt4):

```
import pandas as pd
import numpy as np
from scipy.stats import norm

## Clean data
smooth_data = pd.read_excel('smooth.xlsx', converters={'TradingDate': str, 'ExerciseDate': str})
smooth_data.dropna(subset=['SoothIV'], inplace=True)
smooth_data = smooth_data[['Symbol', 'TradingDate', 'ExerciseDate', 'CallOrPut', 'StrikePrice', 
    'ClosePrice', 'UnderlyingScrtClose', 'RemainingTerm', 'RisklessRate', 
    'HistoricalVolatility', 'ImpliedVolatility', 'TheoreticalPrice', 'DividendYeild', 'SoothIV']]
smooth_data['RisklessRate'] = smooth_data['RisklessRate']/100

## BSM formula
def bsm_price(t, S, K, r, q, sigma, OptionType):
    r = np.log(r + 1)
    q = np.log(q + 1)
    d1 = (np.log(S/K) + t*(r - q + 0.5*sigma*sigma)) / (sigma*np.sqrt(t))
    d2 = d1 - sigma*np.sqrt(t)
    if OptionType == 'C':
        v = S*(np.e**(-q*t))*norm.cdf(d1) - K*(np.e**(-r*t))*norm.cdf(d2)
    elif OptionType == 'P':
        v = K*(np.e**(-r*t))*norm.cdf(-d2) - S*(np.e**(-q*t))*norm.cdf(-d1)
    return v

## Dupire formula
def local_vol(t, S, K, r, q, sigma, OptionType, deltat, delta):
    dc_by_dt = (bsm_price(t+deltat, S, K, r, q, sigma, OptionType) - 
                bsm_price(t-deltat, S, K, r, q, sigma, OptionType)) / (2*deltat)
    dc_by_dk = (bsm_price(t, S, K+delta, r, q, sigma, OptionType) - 
                bsm_price(t, S, K-delta, r, q, sigma, OptionType)) / (2*delta)
    dc2_by_dk2 = (bsm_price(t, S, K-delta, r, q, sigma, OptionType) - 
                2*bsm_price(t, S, K, r, q, sigma, OptionType) + 
                  bsm_price(t, S, K+delta, r, q, sigma, OptionType)) / (delta*delta)
    sigma_local = np.sqrt((dc_by_dt + (r-q)*K*dc_by_dk + (r-q)*bsm_price(t, S, K, r, q, sigma, OptionType))/(0.5*(K*K)*dc2_by_dk2))
    return sigma_local

# Calculate local volatility
smooth_data['LocalVolatility'] = smooth_data.apply(lambda x: local_vol(x['RemainingTerm'], x['UnderlyingScrtClose'], 
    x['StrikePrice'], x['RisklessRate'], x['DividendYeild'], x['SoothIV'], x['CallOrPut'], 0.01, 1.5), axis=1)
```

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.