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 the three-point central difference (C(K - h) - 2 * C(K) + C(K + h)) / h^2 on a fine, evenly-spaced strike grid with spacing h, since the smile only gives implied volatility at a sparse set of traded strikes. The first and last grid points have no neighbor on one side and are left out.

The option chains and the days to expiration are live, while the stock price is the close of the last historical date; a warning is logged when that date lies more than five days before today.

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.

Risk-Neutral Density in Python or via the MCP

Install the Finance Toolkit with:

pip install financetoolkit -U

Then call get_risk_neutral_density as shown below.

from financetoolkit import Toolkit

toolkit = Toolkit(["SPY"], api_key="FINANCIAL_MODELING_PREP_KEY", benchmark_ticker=None)

toolkit.options.get_risk_neutral_density(strike_price_range=0.1, number_of_strikes=81)

Or, after setting up the Finance Toolkit MCP, ask:

What probability distribution for the price of SPY at its nearest option expiration do current call prices imply, on 81 strikes within 10% of the price?

Which returns:

Strike Price SPY
686.021 0
687.921 0
689.821 0
691.722 0
693.622 0.0001
695.522 0.0001
697.423 0.0001
699.323 0.0001
701.223 0.0001
703.124 0.0001
705.024 0.0001
706.924 0.0001
708.825 0.0001
710.725 0.0001
712.625 0.0002
714.526 0.0002
716.426 0.0002
718.326 0.0002
720.227 0.0003
722.127 0.0003
724.028 0.0004
725.928 0.0005
727.828 0.0006
729.729 0.0007
731.629 0.0009
733.529 0.0011
735.43 0.0015
737.33 0.0019
739.23 0.0024
741.131 0.0032
743.031 0.0043
744.931 0.0058
746.832 0.008
748.732 0.011
750.632 0.0151
752.533 0.0209
754.433 0.0284
756.333 0.0377
758.234 0.048
760.134 0.0573
762.034 0.0626
763.935 0.0612
765.835 0.0525
767.735 0.0392
769.636 0.0256
771.536 0.015
773.436 0.0081
775.337 0.0042
777.237 0.0022
779.137 0.0011
781.038 0.0006
782.938 0.0003
784.838 0.0002
786.739 0.0001
788.639 0.0001
790.539 0.0001
792.44 0
794.34 0
796.24 0
798.141 0
800.041 0
801.941 0
803.842 0
805.742 0
807.642 0
809.543 0
811.443 0
813.343 0
815.244 0
817.144 0
819.044 0
820.945 0
822.845 0
824.745 0
826.646 0
828.546 0
830.446 0
832.347 0
834.247 0

Parameters

get_risk_neutral_density accepts the following parameters.

ParameterTypeDescriptionExample
expiration_datestr | NoneThe expiration date to use. Defaults to None which means it will use the first available expiration date."2028-01-21"
put_optionboolWhether to use put options instead of call options. Defaults to False.True
risk_free_ratefloatThe risk free rate to use for the calculation. Defaults to None, which uses the Toolkit’s risk-free rate on the pricing date: the 10-year Treasury yield unless the Toolkit’s risk_free_rate parameter selects ‘13w’, ‘5y’ or ‘30y’, converted from its semi-annual bond-equivalent quote to continuous compounding. A rate passed here is used as given, as a continuously compounded rate.0.04
dividend_yieldfloatThe dividend yield to use for the calculation. Defaults to None, which uses the dividend yield on the pricing date (the dividends of the 365 days up to it relative to its close, as the continuously compounded ln(1 + q)). A given value is used as a continuous yield.0.02
strike_price_rangefloatThe 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.0.3
number_of_strikesintThe number of strikes in the evaluation grid. Defaults to 200.100
outlier_thresholdfloatThe 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.3.0
roundingint | NoneThe number of decimals to round the results to. Defaults to None.2
Share