0
votes

I have the following code which makes the following error. I think the error comes from the loop section while epsilon > tol:. Ive added a small df with the desired results in the column "IV".

line 1478, in nonzero raise ValueError( ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

def d(sigma, S, K, r, q, t):
    d1 = 1 / (sigma * np.sqrt(t)) * ( np.log(S/K) + (r - q + sigma**2/2) * t)
    d2 = d1 - sigma * np.sqrt(t)

    return d1, d2
def call_price(sigma, S, K, r, q, t, d1, d2):
    C = norm.cdf(d1) * S * np.exp(-q * t)- norm.cdf(d2) * K * np.exp(-r * t)

    return C
# From Put call Prity
def put_price(sigma, S, K, r, q, t, d1, d2):

    P = - S * np.exp(-q * t) + K * np.exp(-r * t) + call_price(sigma, S, K, r, q, t, d1, d2)

    return P
def calc_put_iv(S,K,t,r,q,P0,tol,epsilon,count,max_iter,vol):
    while epsilon > tol:
        #  Count how many iterations and make sure while loop doesn't run away
        count += 1
        print(count)
        if count >= max_iter:
            print('Breaking on count')
            break;
        #  Log the value previously calculated to computer percent change
        #  between iterations
        orig_vol = vol
        #  Calculate the vale of the call price
        d1, d2 = d(vol, S, K, r,q, t)
        function_value = put_price(vol, S, K, r, q, t, d1, d2) - P0
        #  Calculate vega, the derivative of the price with respect to
        #  volatility
        vega = S * norm.pdf(d1) * np.sqrt(t)* np.exp(-q * t)
        #  Update for value of the volatility
        vol = -function_value / vega + vol
        #  Check the percent change between current and last iteration
        epsilon = abs( (vol - orig_vol) / orig_vol )
    
        print(vol)
    return vol


#  Print out the results
df["IV"] = calc_put_iv(df["Stock Price"], df["Strike"], df["Length / 365"],0.001,df["Div Yield"],df["Premium"],1e-8,1,0,1000,.5)


Strike  Stock Price Premium Length  Div Yield   Length / 365    IV
470 407.339996  65.525  17  0   0.008219178 1.3080322786580916
400 407.339996  14.375  3   0   0.008219178 1.2202688594244515
490 490.649994  17.35   17  0   0.046575342 0.4190594565249461
2

2 Answers

0
votes

I managed to find a solution which is:

list_of_iv = []
#  Print out the results
for index, row in df.iterrows():
        iv = calc_put_iv(df["Stock Price"].iloc[index], df["Strike"].iloc[index], df["Length/365"].iloc[index],0.001,df["Div Yield"].iloc[index],df["Premium"].iloc[index],1e-8,1,0,1000,.5)
    list_of_iv.append(iv)
df['Put IV'] = pd.Series(list_of_iv)

Its quite ugly and probably not that efficient especially with larger datasets so i would really appreciate if anyone can improve on this.

0
votes

You are right.

Did you want to do the calculation on each row of the df as df.iterrows()?

Otherwise, as it currently is, you're passing a series for each df['x'] call in your calc function therefore your epsilon and tol are actually a series and not an absolute value. Alternatively, the map function may be useful here to broadcast your functions.

#  Print out the results
df["IV"] = list([calc_put_iv(row[1], row[0], row[5],0.001,row[4],row[2],1e-8,1,0,1000,.5) for row in df.iterrows()])

Hard to tell what number the columns are from the dset, but it should look something like that if you want to pass 1 row at a time into your calculations.

itertuple can be used to sidestep renaming everything:

#  Print out the results
df["IV"] = list([calc_put_iv(df["Stock Price"], df["Strike"], df["Length / 365"],0.001,df["Div Yield"],df["Premium"],1e-8,1,0,1000,.5) for df in df.itertuples()])