Implied volatility calculation in Python - python

With the comments from the answer, I rewrote the code below (math.1p(x)->math.log(x)), which now should work and give a good approximation of the volatility.
I am trying to create a short code to calculate the implied volatility of a European Call option. I wrote the code below:
from scipy.stats import norm
import math
norm.cdf(1.96)
#c_p - Call(+1) or Put(-1) option
#P - Price of option
#S - Strike price
#E - Exercise price
#T - Time to expiration
#r - Risk-free rate
#C = SN(d_1) - Ee^{-rT}N(D_2)
def implied_volatility(Price,Stock,Exercise,Time,Rf):
P = float(Price)
S = float(Stock)
E = float(Exercise)
T = float(Time)
r = float(Rf)
sigma = 0.01
print (P, S, E, T, r)
while sigma < 1:
d_1 = float(float((math.log(S/E)+(r+(sigma**2)/2)*T))/float((sigma*(math.sqrt(T)))))
d_2 = float(float((math.log(S/E)+(r-(sigma**2)/2)*T))/float((sigma*(math.sqrt(T)))))
P_implied = float(S*norm.cdf(d_1) - E*math.exp(-r*T)*norm.cdf(d_2))
if P-(P_implied) < 0.001:
return sigma
sigma +=0.001
return "could not find the right volatility"
print implied_volatility(15,100,100,1,0.05)
This yields: 0.595 volatility which should be somewhere 0.3203. That is a huge difference...
I know this is not a fast method by any means, I just want to demonstrate how the principle works, but I am not able to calculate a good approximation.
For some reason when I call the function it gives me really bad approximation of the actual implied volatility which I calculated using a Matlab Program and the following webpage: Implied Volatility. Could anyone please help me to figure out where I made the mistake?

There are two problems I see, none of which are directly python related:
You are using log1p(x), which is the natural logarithm of 1+x, while you actually want log(x), which is the natural logarithm of x (cf. Wikipedia).
An option price of 100 is way to high considering the other parameters. Try to calculate the implied volatility for a price of 10 - which should be about 0.18 both by your program and the calculator you linked.

In Python2, the result of 5 / 2 is 2. It uses floor division. To fix that, make every number a float. In your implied_volatility function, change P = Price to P = float(Price), S = Stock to S = float(Stock), etc.

Related

Review RSI function - Python

does this RSI code looks correct for you?
RSI I'm getting with this code are very often touching oscillator peaks (0 or 100) when comparing to RSIs on different market data apps (TradingView etc.) they hardly ever do that.
Does this formula looks correct? I found it in quite old book Technical Traders Guide to Computer Analysis of the Futures Market. Also it returns almost identical results as RSI=100-(100-RS) where RS=AVG GAIN/AVG LOSS. But still I'm a bit mad that results are different comparing to RSIs available on the web...
#
# Formula used:
#
# RSI(n) = 100 * (avg_up(n) / (avg_up(n)+avg_down(n)))
#
# Where:
#
# avg_up = average percentage gain from n-periods
# avg_down = average percentage loss from n-periods
# n = number of periods to calculate averages
#
def calculate_percentage_gains_and_losses(prices):
percentage = {"gains":[0.0],
"losses":[0.0]}
for i in range(len(prices)-1):
diff=((float(prices[i + 1]) / float(prices[i]))*100)-100
if diff>=0:
percentage["gains"].append(diff)
percentage["losses"].append(0.0)
else:
percentage["losses"].append(abs(diff))
percentage["gains"].append(0.0)
return percentage
def calculate_avg_percentage_gains_and_losses(prices, gains, losses, periods):
avg_percentage = {"gains":[ 0.0 for x in range(periods - 1)],
"losses":[ 0.0 for x in range(periods - 1)]}
for i in range(periods,len(prices)+1):
avg_percentage["gains"].append(sum(gains[i - periods:i]) / periods)
avg_percentage["losses"].append(sum(losses[i - periods:i]) / periods)
return avg_percentage
def calculate_relative_strength_index(prices, periods):
percentage = calculate_percentage_gains_and_losses(prices)
avg_percentage = calculate_avg_percentage_gains_and_losses(prices, percentage["gains"], percentage["losses"], periods)
rsi_list=[0.0 for x in range(periods - 1)]
for i in range(periods - 1, len(prices)):
rsi = 100 * round((avg_percentage["gains"][i] / (avg_percentage["gains"][i] + avg_percentage["losses"][i])), 2)
rsi_list.append(rsi)
return rsi_list
EDIT
Here is the code after adjustment
def calculate_percentage_gains_and_losses(prices):
percentage = {"gains":[0.0],
"losses":[0.0]}
for i in range(len(prices)-1):
diff=((float(prices[i + 1]) / float(prices[i]))*100)-100
if diff>=0:
percentage["gains"].append(diff)
percentage["losses"].append(0.0)
else:
percentage["losses"].append(abs(diff))
percentage["gains"].append(0.0)
return percentage
def calculate_smoothed_avg_percentage_gains_and_losses(prices, gains, losses, periods):
avg_percentage = {"gains":[ 0.0 if i<(periods-1) else sum(gains[:periods]) / periods for i in range(periods)],
"losses":[ 0.0 if i<(periods-1) else sum(losses[:periods]) / periods for i in range(periods)]}
for i in range(periods, len(prices)):
avg_percentage["gains"].append((gains[i] + (avg_percentage["gains"][i-1]* (periods-1))) / periods)
avg_percentage["losses"].append((losses[i] + (avg_percentage["losses"][i-1]* (periods-1))) / periods)
return avg_percentage
def calculate_relative_strength_index(prices, periods):
percentage = calculate_percentage_gains_and_losses(prices)
avg_percentage = calculate_smoothed_avg_percentage_gains_and_losses(prices, percentage["gains"], percentage["losses"], periods)
rsi=[ 0.0 if i < (periods-1) else round((100 * (avg_percentage["gains"][i] / (avg_percentage["gains"][i] + avg_percentage["losses"][i]))),2) for i in range(len(prices))]
return rsi
Firstly, issues with formula:
your RSI formula is calculated differently to the usual. As you acknowledge, RS is normally calculated as (Avg gains)/(Avg losses) yet you use (Avg gains)/((Avg gains) + (Avg losses)). While there may be strong correlation between the two, they are two different formulas and will therefore give you different answers. Furthermore you use percentage gains/losses when most charting platforms use raw gains/losses. And finally, you use a simple average of the gains/losses (known as Cutler's RSI). The much more widely used Wells Wilder RSI uses a smoothed average given by:
Avg gain = (gain + (prev gain) * (period -1))/period
The upshot is you will get very different numbers.
Secondly, your code:
While I have not scrutinized your code with a fine tooth comb, there are several issues rendering the code, at best, inefficient. If you intend on continuing to code in Python you MUST learn AND understand list comprehension and how to use it. It is what makes Python so useful and powerful. You have used it merely to initialize some lists. There are many websites and books that cover list comprehension quite extensively, just google it.
Additionally, if you intend to use python to calculate series information on chronologically ordered data, I strongly recommend you import the pandas module and use DataFrames rather than lists. DataFrames are like database records that keeps all data neatly in the row they belong two and are easily manipulated. List will get messy as you try to line up the data.
I will not provide code at this point because I think you already have plenty to think a bout and work on but will happily help once you have understood the above.

How to find discount rate value if total present value and future time series of cash flows is known in Python

While going through an article, I encountered a situation where I encountered below polynomial equation.
For reference, below is the equation.
15446 = 537.06/(1+r) + 612.25/(1+r)**2 + 697.86/(1+r)**3 + 795.67/(1+r)**4 + 907.07/(1+r)**5
This is discount cash flow time series values which we use in finance to get the idea of present value of future cash flows after applying the appropriate discount rate.
So from above equation, I need to calculate the variable r in python programming environment?. I do hope that there must be some library which can be used to solve such equations?.
I solve this, I thought to use the numpy.npv API.
import numpy as np
presentValue = 15446
futureValueList = [537.06, 612.25, 697.86,795.67, 907.07]
// I know it is not possible to get r from below. Just put
// it like this to describe my intention.
presentValue = np.npv(r, futureValueList)
print(r)
You can multiply your NPV formula with the highest power or (1+r) and then find the roots of the polynomial with polyroots (just take the only real root and disregard the complex ones):
import numpy as np
presentValue = 15446
futureValueList = [537.06, 612.25, 697.86,795.67, 907.07]
roots = np.polynomial.polynomial.polyroots(futureValueList[::-1]+[-presentValue])
r = roots[np.argwhere(roots.imag==0)].real[0,0] - 1
print(r)
#-0.3332398877886278
As it turns out the formula given is incomplete, see p. 14 of the linked article. The correct equation can be solved with standard optimization procedures, e.g. optimize.root providing a sensible initial guess:
from scipy import optimize
def fun(r):
r1 = 1 + r
return 537.06/r1 + 612.25/r1**2 + 697.86/r1**3 + 795.67/r1**4 + 907.07/r1**5 * (1 + 1.0676/(r-.0676)) - 15446
roots = optimize.root(fun, [.1])
print(roots.x if roots.success else roots.message)
#[0.11177762]

New to programming, same result different program

I'm taking my first course in programming, the course is meant for physics applications and the like. We have an exam coming up, my professor published a practice exam with the following question.
The Maxwell distribution in speed v for an ideal gas consisting of particles of mass m at Kelvin temperature T is given by:
Stackoverflow doesn't use MathJax for formula's, and I can't quite figure out how to write a formula on this site. So, here is a link to WolframAlpha:
where k is Boltzmann's constant, k = 1.3806503 x 10-23 J/K.
Write a Python script called maxwell.py which prints v and f(v) to standard output in two column format. For the particle mass, choose the mass of a proton, m = 1.67262158 10-27 kg. For the gas temperature, choose the temperature at the surface of the sun, T = 5778 K.
Your output should consist of 300 data points, ranging from v = 100 m/s to v = 30,000 m/s in steps of size dv = 100 m/s.
So, here is my attempt at the code.
import math as m
import sys
def f(v):
n = 1.67262158e-27 #kg
k = 1.3806503e-23 #J/K
T = 5778 #Kelvin
return (4*m.pi)*((n/(2*m.pi*k*T))**(3/2))*(v**2)*m.exp((-n*v**2)/(2*k*T))
v = 100 #m/s
for i in range(300):
a = float(f(v))
print (v, a)
v = v + 100
But, my professors solution is:
import numpy as np
def f(v):
m = 1.67262158e-27 # kg
T = 5778. # K
k = 1.3806503e-23 # J/K
return 4.*np.pi * (m/(2.*np.pi*k*T))**1.5 * v**2 * np.exp(-m*v**2/(2.*k*T))
v = np.linspace(100.,30000.,300)
fv = f(v)
vfv = zip(v,fv)
for x in vfv:
print "%5.0f %.3e"%x
# print np.sum(fv*100.)
So, these are very different codes. From what I can tell, they produce the same result. I guess my question is, simply, why is my code incorrect?
Thank you!
EDIT:
So, I asked my professor about it and this was his response.
I think your code is fine. It would run much faster using numpy, but the problem didn't specify that you needed numpy. I might have docked one point for not looping through a list of v's (your variable i doesn't do anything). Also, you should have used v += 100. Almost, these two things together would have been one point out of 10.
1st: Is there any better syntax for doing the range in my code, since my variable i doesn't do anything?
2nd: What is the purpose of v += 100?
Things to be careful about when dealing with numbers is implicit type conversion from floats to ints.
One instance I could figure in your code is that you use (3/2) which evaluates to 1, while the other code uses 1.5 directly.

Equation solver in python syntax error

I'm making a fast and simple equation solver in python for a home assaignment where I need to calculate the value of sig_a which is between 0 and 700 for each eps_komp. I've done this with 2 for loops, the first to select the eps_komp value and the second to search for the closest value of sig_a between 0 and 700 in order for the equation to be as close to zero as possible. I defined the allowable error with "delta".
It's similar logic like in bisection method. This is the code:
eps_komp = [0.00012893048999999997,
0.018839115269999998,
0.01230539995,
0.022996934109999999,
-0.0037319012899999999,
0.023293921169999999,
0.0036927752099999997,
0.020621037629999998,
0.0063656587500000002,
0.020324050569999998]
Rm=700
sigma = np.linspace(0, Rm-0.01, Rm/0.01)
delta = 0.001
sig_a = []
for j in range(len(eps_komp)):
eps_j = eps_komp[j]
for i in range(len(sig)):
eps_j - sigma[i]/Emod - (sigma[i]/RO_K)**(1/RO_n) = diff
if diff <= delta:
sig_a.append(sigma[i])
The values of eps_komp are just the first 10, there are more of them but I just gave the first 10 for an example.
Now I keep getting this error:
SyntaxError: can't assign to operator
I know it has something to do with an incorrect index but I just can't see the problem....
If anyone can help, it would mean a lot to me. Thanks.
Luka
to assign diff to the calculation it needs to be on the left side of the equal sign:
diff = eps_j - sigma[i]/Emod - (sigma[i]/RO_K)**(1/RO_n)

Float is not callable

I'm getting a float object is not callable error when trying to do a calculation for force of gravity on a mass.
def grav():
mass = float(input('Enter a mass[kg]: '))
dist = float(input('Enter a distance from the surface of earth [m]: '))
rad = 6.3781*10**6
me = 5.97219*10**24
gr = 6.67300*10**-11
f = ((gr((me)*(mass)))/((rad)+(dist)**2))
return f
grav()
It's giving the float error on the part where everything is being calculated
gr((me)*(mass))
The above tries to call gr like it's a function. It's just a constant. There's no need for all of those parentheses, anyway.
gr * me * mass / (...)
You do have a bug in the denominator though. You need to divide by (rad+dist)**2, not rad + (dist**2) (which you're doing now).
Altogether
f = gr * me * mass / (rad + dist)**2
That's all you need.
And if I can make a suggestion, make your variable names more self-documenting. I can understand your code because I recognize the formula, but not everyone is going to have that advantage.
force = G * MASS_OF_EARTH * mass / (EARTH_RADIUS + distance_from_earth)**2
Easier to read, no? You don't have to be this verbose, but too much is better than too little. It can be tempting to over-abbreviate in scientific computing but I really advise against it. Be terse when necessary but always be self-documenting.
change the line 7 like
f = ((gr*((me)*(mass)))/((rad)+(dist)**2))
or you can simplify your formula with
f = gr*(me*mass)/(rad+dist)**2
it will work.

Categories

Resources