vollib/py_vollib

vectorization in py_vollib

nrathod7 opened this issue · 8 comments

i am looking to calculate implied volatility using black scholes formula for european options for around 1 million + rows. I have the data stored required data like stock price, strike, option price etc in a dataframe. I am looking for a process which i can use to get the implied volatility calculated for all the rows rather than me manually looping through each and every row of the dataframe and calling py_vollib implied volatility function. What would you recommend so that i can speed up this entire process?

This is how I would do it.

>>>from py_vollib.black_scholes import black_scholes as bs
>>>from py_vollib.black_scholes.implied_volatility import implied_volatility as iv
>>>import pandas, numpy
>>>p=[bs('c', 100, 100, .5, .01, sigma) for sigma in numpy.linspace(.05,.3,10)]
>>>df = pandas.DataFrame()
>>>df['p']=p
>>>df['flag']='c'
>>>df['S']=100
>>>df['K']=100
>>>df['r']=.01
>>>df['t']=.5
>>>df.head()
...
          p flag    S    K     r    t
0  1.670303    c  100  100  0.01  0.5
1  2.446734    c  100  100  0.01  0.5
2  3.225586    c  100  100  0.01  0.5
3  4.005140    c  100  100  0.01  0.5
4  4.784811    c  100  100  0.01  0.5
>>>df['iv'] = df.apply(lambda row: iv(row.p, row.S, row.K, row.t, row.r, row.flag), axis=1)
>>>df.head()
...
          p flag    S    K     r    t        iv
0  1.670303    c  100  100  0.01  0.5  0.050000
1  2.446734    c  100  100  0.01  0.5  0.077778
2  3.225586    c  100  100  0.01  0.5  0.105556
3  4.005140    c  100  100  0.01  0.5  0.133333
4  4.784811    c  100  100  0.01  0.5  0.161111

Implied vol is undefined for a call priced below intrinsic. (Puts can have negative time value, so the same rule doesn't strictly apply to puts and calls, but it's a good rule of thumb) Think about it: price = f(sigma_hat). Now set sigma_hat equal 10e-12, for example. You will get a certain price, say P. Remember that 10e-12 is the sigma_hat you put into f() that gives you P. That means that P is essentially the intrinsic value plus a tiny, tiny amount of time value. Now if you try to find the iv for P-0.1, for example, you're essentially requesting an iv that is considerably lower than 10e-12, i.e. negative iv. But we don't define negative standard deviations, so you must implement some kind of exception handling for such 'nonsensical' prices.

I would make a new function like this:

>>>from py_lets_be_rational.exceptions import BelowIntrinsicException
>>>def iv_with_exception_handling(price, S, K, t, r, flag):
>>>    try:
>>>       return iv(price, S, K, t, r, flag)
>>>    except BelowIntrinsicException:
>>>        return numpy.nan

>>>iv_with_exception_handling(.1, 100, 100, .5, .01, 'c')
nan

Then, just substitute this back into the previous lambda function.

Hi Neetesh,
That is a good question, and the answer has many nuances. I think it depends on your objectives. Some people will go for fixed log-moneyness boundaries, but there are other approaches. I suggest taking some hints from Jim Gatheral's writings, or Peter Jaeckel's "Let's be Rational."
Good luck,
Larry

Really great solutions. Was facing the same problem and found this very helpful