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