Risk-Neutral Density
Extract the market-implied risk-neutral probability density of the underlying’s price at expiration, via the Breeden-Litzenberger (1978) theorem, applied to a volatility smile calibrated to real market option prices (see get_implied_volatility) rather than a single flat assumed volatility.
get_partial_derivative computes the same second-derivative relationship but with one flat volatility value applied at every strike – with a flat volatility input, the second derivative can only ever recover a lognormal density regardless of what the real market smile looks like, which defeats the entire purpose of the theorem. This method instead first calibrates a raw SVI (Gatheral 2004) curve to the actual market smile (see get_volatility_surface) and evaluates the density from that.
The formula is as follows:
\[f(K) = e ^{r \cdot t} \cdot d ^{2} C(K) / \text{dK} ^{2}\]Where C(K) is the Black-Scholes call price at strike K, using the SVI-smoothed implied volatility at that strike, r is the risk-free rate and t is the time to expiration. The second derivative is approximated numerically via a central finite difference on a fine, evenly-spaced strike grid, since the smile only gives implied volatility at a sparse set of traded strikes.
See the paper: Breeden, D.T., & Litzenberger, R.H. (1978), “Prices of State-Contingent Claims Implicit in Option Prices”, Journal of Business, 51(4), 621-651. https://www.jstor.org/stable/2352653
Also known as: Breeden-Litzenberger, implied risk-neutral distribution.
Notes: A warning is logged (not raised) for any ticker whose density goes negative at some strike – this indicates a butterfly-arbitrage violation (the call price is not convex in the strike) in the underlying market quotes or the SVI fit, which a well-calibrated, liquid smile should not produce.
No programming experience? With the Finance Toolkit MCP server, AI assistants such as Claude and ChatGPT can calculate the Risk-Neutral Density for you. Just ask in plain English.
Calculate the Risk-Neutral Density in Python
The Risk-Neutral Density is available in the Options module of the open-source Finance Toolkit. Install it with:
pip install financetoolkit -U
Then call get_risk_neutral_density as shown below.
from financetoolkit import Toolkit
toolkit = Toolkit(["AAPL"], api_key="FINANCIAL_MODELING_PREP_KEY")
risk_neutral_density = toolkit.options.get_risk_neutral_density()
Which returns:
| Strike Price | AAPL |
|---|---|
| 277.238 | 0.0001 |
| 278.774 | 0.0002 |
| 280.31 | 0.0004 |
| 281.846 | 0.0007 |
| 283.382 | 0.0012 |
Parameters
get_risk_neutral_density accepts the following parameters:
- expiration_date (str | None, optional): The expiration date to use. Defaults to None which means it will use the first available expiration date.
- put_option (bool, optional): Whether to use put options instead of call options. Defaults to False.
- risk_free_rate (float, optional): The risk free rate to use for the calculation. Defaults to None which means it will use the current risk free rate.
- dividend_yield (float, optional): The dividend yield to use for the calculation. Defaults to None which means it will use the dividend yield as obtained through annual historical data.
- strike_price_range (float, optional): The range of strikes to evaluate the density over, as a fraction of the forward price in each direction. Defaults to 0.5, i.e. from 50% to 150% of the forward price.
- number_of_strikes (int, optional): The number of strikes in the evaluation grid. Defaults to 200.
- outlier_threshold (float, optional): The number of median absolute deviations from the median implied volatility beyond which a quote is treated as an outlier and dropped before fitting. Defaults to 5.0.
- rounding (int | None, optional): The number of decimals to round the results to. Defaults to None.
Related Option Pricing
The Options module page introduces the module, and the sidebar lists all of its functions.