13

I am looking for a library which i can use for faster way to calculate implied volatility in python. I have options data about 1+ million rows for which i want to calculate implied volatility. what would be the fastest way i can calculate IV's. I have tried using py_vollib but it doesnt support vectorization. It takes about 5 mins approx. to calculate. Are there any other libraries which can help in faster calculation. What do people use in real time volatility calculations where there are millions of rows coming in every second?

Neetesh
  • 131
  • 1
  • 1
  • 3
  • For those of us who are not experts in financial math, can you define the implied volatility function? Some sample input data would also be helpful – Code Different Apr 18 '20 at 14:29

6 Answers6

13

You have to realize that the implied volatility calculation is computationally expensive and if you want realtime numbers maybe python is not the best solution.

Here is an example of the functions you would need:

import numpy as np
from scipy.stats import norm
N = norm.cdf

def bs_call(S, K, T, r, vol):
    d1 = (np.log(S/K) + (r + 0.5*vol**2)*T) / (vol*np.sqrt(T))
    d2 = d1 - vol * np.sqrt(T)
    return S * norm.cdf(d1) - np.exp(-r * T) * K * norm.cdf(d2)

def bs_vega(S, K, T, r, sigma):
    d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    return S * norm.pdf(d1) * np.sqrt(T)

def find_vol(target_value, S, K, T, r, *args):
    MAX_ITERATIONS = 200
    PRECISION = 1.0e-5
    sigma = 0.5
    for i in range(0, MAX_ITERATIONS):
        price = bs_call(S, K, T, r, sigma)
        vega = bs_vega(S, K, T, r, sigma)
        diff = target_value - price  # our root
        if (abs(diff) < PRECISION):
            return sigma
        sigma = sigma + diff/vega # f(x) / f'(x)
    return sigma # value wasn't found, return best guess so far

Computing a single value is quick enough

S = 100
K = 100
T = 11
r = 0.01
vol = 0.25

V_market = bs_call(S, K, T, r, vol)
implied_vol = find_vol(V_market, S, K, T, r)

print ('Implied vol: %.2f%%' % (implied_vol * 100))
print ('Market price = %.2f' % V_market)
print ('Model price = %.2f' % bs_call(S, K, T, r, implied_vol))

Implied vol: 25.00%

Market price = 35.94

Model price = 35.94

But if you try to compute many, you will realize that it takes some time...

%%time
size = 10000
S = np.random.randint(100, 200, size)
K = S * 1.25
T = np.ones(size)
R = np.random.randint(0, 3, size) / 100
vols = np.random.randint(15, 50, size) / 100
prices = bs_call(S, K, T, R, vols)

params = np.vstack((prices, S, K, T, R, vols))
vols = list(map(find_vol, *params))

Wall time: 10.5 s

David Duarte
  • 644
  • 4
  • 11
  • hi @David Duarte do you have the `PUT` side of the above equations? Right now I have a method `def bs_put(S, K, r, vol, T) - return bs_call(S, K, r, vol, T) - S + np.exp(-r*(T))*K` but I'm uncertain as to how that would impact the `def find_vol` method... can you assist? – JC23 Nov 27 '20 at 17:40
  • The `find_vol` function is basically the newton raphson method for finding roots and uses a function and its derivative. The derivative of the bs formula to price a call and a put in respect to the vol is the same (vega) so you just have to replace the function to determine the prices accordingly (change call to put). You can either price a put throught put call parity or changind the pricing formula to `K * np.exp(-r * T) * N(-d2) - S * N(-d1)` – David Duarte Nov 28 '20 at 19:23
  • Is this implementation only for EU style options? – user1424739 Nov 07 '21 at 19:30
  • `T=11/365` to get a realistic example? – midtownguru Mar 27 '22 at 06:07
  • yeah, was probably a fat finger. I was going for T=1 – David Duarte Mar 28 '22 at 20:46
9

If you change all calls to norm.cdf()-method into ndtr(), you will get a 2.4 time performance increase.

And if you change norm.pdf()-method into norm._pdf(), you will get another (huge) increase.

With both changes implemented, the example above dropped from 17.7 s down to 0.99 s on my machine.

You will lose error checking etc. but in this case you probably don't need all that.

See: https://github.com/scipy/scipy/issues/1914

ndtr() is in scipy.special

Eric Hulsinga
  • 136
  • 1
  • 4
3

As of recent, there is a vectorized version of py_vollib available at py_vollib_vectorized, which is built on top of the py_vollib and makes pricing thousands of options contracts and calculating greeks much faster.

Kenly
  • 24,317
  • 7
  • 44
  • 60
xan
  • 101
  • 6
  • Just to mention that this is based on py_vollib, which is limited to European-style options, as this is a bit misleading - I've spent time exploring this path before finding it out. – Oleg Medvedyev Jan 21 '21 at 04:08
  • the vectorized package mentioned here works really well. And @oleg while true it technically is only for euro-options, there is a near null incentive to exercise an option early, so it is a fine proxy for american options – Jhirschibar Aug 19 '23 at 11:09
2
!pip install py_vollib

This will return greeks along with black_scholes price and iv

import py_vollib 
from py_vollib.black_scholes  import black_scholes as bs
from py_vollib.black_scholes.implied_volatility import implied_volatility as iv
from py_vollib.black_scholes.greeks.analytical import delta 
from py_vollib.black_scholes.greeks.analytical import gamma
from py_vollib.black_scholes.greeks.analytical import rho
from py_vollib.black_scholes.greeks.analytical import theta
from py_vollib.black_scholes.greeks.analytical import vega
import numpy as np

#py_vollib.black_scholes.implied_volatility(price, S, K, t, r, flag)

"""
price (float) – the Black-Scholes option price
S (float) – underlying asset price
sigma (float) – annualized standard deviation, or volatility
K (float) – strike price
t (float) – time to expiration in years
r (float) – risk-free interest rate
flag (str) – ‘c’ or ‘p’ for call or put.
"""
def greek_val(flag, S, K, t, r, sigma):
    price = bs(flag, S, K, t, r, sigma)
    imp_v = iv(price, S, K, t, r, flag)
    delta_calc = delta(flag, S, K, t, r, sigma)
    gamma_calc = gamma(flag, S, K, t, r, sigma)
    rho_calc = rho(flag, S, K, t, r, sigma)
    theta_calc = theta(flag, S, K, t, r, sigma)
    vega_calc = vega(flag, S, K, t, r, sigma)
    return np.array([ price, imp_v ,theta_calc, delta_calc ,rho_calc ,vega_calc ,gamma_calc])

S = 8400
K = 8600
sigma = 16
r = 0.07
t = 1

call=greek_val('c', S, K, t, r, sigma)

put=greek_val('p', S, K, t, r, sigma)
  • 2
    Same comment as above. Note that `py_vollib` and underlying `lets_be_rational` libraries are [limited to European style options](https://github.com/vollib/py_vollib/issues/7). The comment in the github is a bit misleading as well, as I found that py_vollib basically fails to work deep ITM American-style options. Keep this in mind before you venture too deep into it. – Oleg Medvedyev Jan 21 '21 at 04:12
  • @omdv Do you find a package that can compute option price for American style options? – user1424739 Nov 07 '21 at 19:29
0

You could use a binary search to find the implied vol quickly

def goalseek(spot_price: float,
             strike_price: float,
             time_to_maturity: float,
             option_type: str,
             option_price: float):
    volatility = 2.5
    upper_range = 5.0
    lower_range = 0
    MOE = 0.0001 # Minimum margin of error
    max_iters = 100
    iter = 0

    while iter < max_iters: # Don't iterate too much
        price = proposedPrice(spot_price=spot_price,
                               strike_price=strike_price,
                               time_to_maturity=time_to_maturity,
                               volatility=volatility,
                               option_type=option_type) # BS Model Pricing
        if abs((price - option_price)/option_price) < MOE:
            return volatility

        if price > option_price:
            tmp = volatility
            volatility = (volatility + lower_range)/2
            upper_range = tmp
        elif price < option_price:
            tmp = volatility
            volatility = (volatility + upper_range)/2
            lower_range = tmp
        iter += 1
    return volatility
0

please use py_vollib.black_scholes.greeks.numerical instead of analytical for back testing purpose. Analytical throwing errors when option strike prices are deep out or in the money as well as illiquid contract, for this case use historical volatility instead of implied volatility to calculate option greeks. try: with iv and except: with hv

Hirak Dey
  • 3
  • 3
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 25 '22 at 09:10