Plotting Monte Carlo Simulations for option pricing in Python - python

I am trying to show the monte carlo barrier prices for different number of simultations in the x axis. This is what i tried so far but i'm getting the error -> ValueError: x and y must have same first dimension, but have shapes (10,) and (5,).
I am new to python and as hard as i try i cannot find the error
import numpy as np
import numpy.random as npr
import matplotlib.pyplot as plt
def mc_single_barrier_do(S0, K, T, H, r, vol, N, M):
# Constants
dt = T / N # change in time
nudt = (r - 0.5 * vol ** 2) * dt # deterministic component
volsdt = vol * np.sqrt(dt) # diffusion coefficient
erdt = np.exp(r * dt) # discount factor
# Standard Error Placeholders
sum_CT = 0
sum_CT2 = 0
# Monte Carlo Method
for i in range(M):
# Barrier Crossed Flag
BARRIER = False
St = S0
for j in range(N):
epsilon = np.random.normal()
Stn = St * np.exp(nudt + volsdt * epsilon)
St = Stn
Ptn = np.exp(-2. * (H - St) * (H - Stn) / (St ** 2. * volsdt ** 2.))
Pt = Ptn
if Pt >= npr.uniform():
BARRIER = True
if np.amin(St) > H and BARRIER == False:
CT = np.maximum(St - K, 0)
else:
CT = 0.
sum_CT = sum_CT + CT
sum_CT2 = sum_CT2 + CT * CT
C0_MC = np.exp(-r * T) * sum_CT / M
return C0_MC
def sim_iterator(max_sample, N, S0, T, r, vol, K, H, method):
assert (method in ['MC', 'AV', 'CV'])
mean_payoffs = np.zeros(int(np.ceil(max_sample / 10)))
if method == 'MC':
for n_sample in range(10, max_sample + 1, 10):
payoffs = mc_single_barrier_do(n_sample, S0, K, T, H, r, vol, N)
mean_payoffs[int(n_sample / 10 - 1)] = np.mean(payoffs)
return mean_payoffs
r = 0.1
vol = 0.2
T = 2
N = 20
dt = T / N
S0 = 50
K = 50
H = 45
max_sample = 100
MC_price_estimates = sim_iterator(S0, T, r, vol, K, H, max_sample, N, method='MC')
x_axis1 = range(10, max_sample + 1, 10)
plt.plot(x_axis1, MC_price_estimates)
plt.xlabel("No. of Simulations")
plt.ylabel("Estimated option price")
plt.title("Ordinary Monte Carlo Method")
plt.legend()
plt.show()

in your function definition you used:
def sim_iterator(max_sample, N, S0, T, r, vol, K, H, method):
while when using the function you used:
MC_price_estimates = sim_iterator(S0, T, r, vol, K, H, max_sample, N, method='MC')
python has positional arguments, which means the arguments are mapped according to their position, not their name, so in the first position is mapped to the first argument, which means S0 in the second line was mapped to max_sample in the first line, just fix the arguments arrangement, or use keyword arguments S0=S0.
MC_price_estimates = sim_iterator(S0=S0, T=T, r=r, vol=vol, K=K, H=H, max_sample=max_sample, N=N, method='MC')
this is what your code will look like when you fix all arguments to be keyword arguments.
def mc_single_barrier_do(S0, K, T, H, r, vol, N, M):
# Constants
dt = T / N # change in time
nudt = (r - 0.5 * vol ** 2) * dt # deterministic component
volsdt = vol * np.sqrt(dt) # diffusion coefficient
erdt = np.exp(r * dt) # discount factor
# Standard Error Placeholders
sum_CT = 0
sum_CT2 = 0
# Monte Carlo Method
for i in range(M):
# Barrier Crossed Flag
BARRIER = False
St = S0
for j in range(N):
epsilon = np.random.normal()
Stn = St * np.exp(nudt + volsdt * epsilon)
St = Stn
Ptn = np.exp(-2. * (H - St) * (H - Stn) / (St ** 2. * volsdt ** 2.))
Pt = Ptn
if Pt >= npr.uniform():
BARRIER = True
if np.amin(St) > H and BARRIER == False:
CT = np.maximum(St - K, 0)
else:
CT = 0.
sum_CT = sum_CT + CT
sum_CT2 = sum_CT2 + CT * CT
C0_MC = np.exp(-r * T) * sum_CT / M
return C0_MC
def sim_iterator(max_sample, N, S0, T, r, vol, K, H, method):
assert (method in ['MC', 'AV', 'CV'])
mean_payoffs = np.zeros(int(np.ceil(max_sample / 10)))
if method == 'MC':
for n_sample in range(10, max_sample + 1, 10):
payoffs = mc_single_barrier_do(M=n_sample,S0= S0, K=K, T=T, H=H, r=r, vol=vol, N=N)
mean_payoffs[int(n_sample / 10 - 1)] = np.mean(payoffs)
return mean_payoffs
r = 0.1
vol = 0.2
T = 2
N = 20
dt = T / N
S0 = 50
K = 50
H = 45
max_sample = 100
MC_price_estimates = sim_iterator(S0=S0, T=T, r=r, vol=vol, K=K, H=H, max_sample=max_sample, N=N, method='MC')
x_axis1 = range(10, max_sample + 1, 10)
plt.plot(x_axis1, MC_price_estimates)
plt.xlabel("No. of Simulations")
plt.ylabel("Estimated option price")
plt.title("Ordinary Monte Carlo Method")
plt.legend()
plt.show()

Related

Plot the multiple values returned by a function

My function returns 2 different values which I want to utilise in 2 different graphs using Matplotlib. How can I achieve it?
def option_value_european_put(T, m, r, sigma, mu, E):
cost_value_at_initial_t_put = []
portfolio_payoff_put = []
for e in E:
delta_t = T / m
u = (1 + (sigma * math.sqrt(delta_t)) * (math.sqrt(1 + ((mu ** 2) * delta_t) / math.pow(sigma, 2))))
v = 2 - u
option_stock_price_matrix_put = np.zeros((m + 1, m + 1))
sum = 0
k = m
start = m
for i in range(m + 1):
option_stock_price_matrix_put[i][start] = max(
(e - stock_price_binomial_model(
mu, sigma, T, m,
S
)[i][start], 0)
)
for j in range(m - 1, -1, -1):
for i in range(0, j + 1):
v_plus = option_stock_price_matrix_put[i][j + 1]
v_minus = option_stock_price_matrix_put[i + 1][j + 1]
v_t = ((((v_plus - v_minus) / (u - v)) * (1 + r * delta_t)) + (u * v_minus - v * v_plus) / (u - v)) / (
1 + r * delta_t)
option_stock_price_matrix_put[i][j] = v_t
cost_value_at_initial_t_put.append(option_stock_price_matrix_put[0][0])
for i in range(0, m+1):
sum = sum + option_stock_price_matrix_put[k][i]
portfolio_return_average = math.average(sum)
portfolio_payoff_put.append(portfolio_return_average-option_stock_price_matrix_put[0][0] )
return cost_value_at_initial_t_put, portfolio_payoff_put
I want to use cost_value_at_initial_t_put in 1 Matplotlib plot and the other value in another plot. How can I use it?
Supposing that cost_value_at_initial_t_put and portfolio_payoff_cut are both lists you can create subplots:
import matplotlib.pyplot as plt
fig, (ax_cost, ax_payoff) = plt.subplots(nrows=2)
ax_cost.plot(cost_value_at_initial_t_put)
ax_payoff.plot(portfolio_payoff_cut)

Not geting right solution with solve_ivp

I am trying to solve the differential equation using solve_ivp, but I am not getting the right solution. However, I obtained the right solution using ideint. Do I have some problems with the solve_ipv program?
ODEINT program :
# Arhenius Function
def Arhenius(a, T):
dadT = np.exp(lnA)/v * np.exp(- E / (8.3144 * T)) * c * np.abs(a) ** m * np.abs((1 - np.abs(a))) ** n
return dadT
# Initial data
pt = 100000
T0 = 273
Tf = 1500
a0 = 0.0000000001
T = np.linspace(T0, Tf, pt)
a_sol = np.zeros(pt)
dadt = np.zeros(pt)
# ODE solve
a_t = odeint(Arhenius, a0, T)
# For removing errored values and have maximum at 1
search1 = np.where(np.isclose(a_t, 1))
try:
ia_1 = search1[0][0]
a_sol[0,:] = a_t[:,0]
a_sol[0,ia_1+1:pt] = 1
except:
a_sol = a_t[:,0]
# Calculate the new derivative
dadt = np.exp(lnA) * np.exp(- E / (8.3144 * T)) * c * np.abs(a_sol) ** m * np.abs((1 - a_sol)) ** n
PROGRAM with solve_ivp :
# Arhenius Function
def Arhenius(a, T):
dadT = np.exp(lnA) * np.exp(- E / (8.3144 * T)) * c * np.abs(a) ** m * np.abs((1 - np.abs(a))) ** n
return dadT
# Initial data
pt = 100000
T0 = 273
Tf = 1500
a0 = 0.0000000001
T = np.linspace(T0, Tf, pt)
a_sol = np.zeros(pt)
dadt = np.zeros(pt)
# ODE solve
a_t = solve_ivp(Arhenius, t_span = (T0, Tf), y0 = (a0,), t_eval = T, method = 'RK45')
a_sol= a_t.y
# Calculate the new derivative
dadt = np.exp(lnA) * np.exp(- E / (8.3144 * T)) * c * np.abs(a_sol) ** m * np.abs((1 - a_sol)) ** n

Optimizing function parameters

I explain briefly what the attached program code should do. We give a number of passes before runs = 100. and we give I = 10.
For example we set the area_factor = 1. Then the function HH_model(I,area_factor) does the following:
run 100 times with this I and this area_factor and return the number of times the barrier 60 is broken -- this is checked in the if max(v[:]-v_Rest) > 60 query.
Now I want to do the following: Determine that area_factor so that the number of count matches observations as well as possible.
For example, I know from measurements
HH_model(2*I,area_factor) = 70
HH_model(I,area_factor)=50
HH_model(0.5*I,area_factor) = 30
...
how can I find the area_factor for a given I, so that the difference to the observations becomes minimal.
import matplotlib.pyplot as py
import numpy as np
import scipy.optimize as optimize
# HH parameters
v_Rest = -65 # in mV
gNa = 120 # in mS/cm^2
gK = 36 # in mS/cm^2
gL = 0.3 # in mS/cm^2
vNa = 115 # in mV
vK = -12 # in mV
vL = 10.6 # in mV
#Number of runs
runs = 30
c = 1 # in uF/cm^2
#performing bisection-procedure
ROOT = True
def HH_model(I,area_factor):
count = 0
t_end = 10 # in ms
delay = 1 # in ms
duration = 0.3 # in ms
dt = 0.01 # in ms
I = I
area_factor = area_factor
#geometry
d = 2 # diameter in um
r = d/2 # Radius in um
l = 10 # Length of the compartment in um
A = (2 * np.pi * r * l * 1e-8)*area_factor # surface [cm^2]
C = c * A # uF
for j in range(0,runs):
# Introduction of equations and channels
def alphaM(v): return 12 * ((2.5 - 0.1 * (v)) / (np.exp(2.5 - 0.1 * (v)) - 1))
def betaM(v): return 12 * (4 * np.exp(-(v) / 18))
def betaH(v): return 12 * (1 / (np.exp(3 - 0.1 * (v)) + 1))
def alphaH(v): return 12 * (0.07 * np.exp(-(v) / 20))
def alphaN(v): return 12 * ((1 - 0.1 * (v)) / (10 * (np.exp(1 - 0.1 * (v)) - 1)))
def betaN(v): return 12 * (0.125 * np.exp(-(v) / 80))
# compute the timesteps
t_steps= t_end/dt+1
# Compute the initial values
v0 = 0
m0 = alphaM(v0)/(alphaM(v0)+betaM(v0))
h0 = alphaH(v0)/(alphaH(v0)+betaH(v0))
n0 = alphaN(v0)/(alphaN(v0)+betaN(v0))
# Allocate memory for v, m, h, n
v = np.zeros((int(t_steps), 1))
m = np.zeros((int(t_steps), 1))
h = np.zeros((int(t_steps), 1))
n = np.zeros((int(t_steps), 1))
# Set Initial values
v[:, 0] = v0
m[:, 0] = m0
h[:, 0] = h0
n[:, 0] = n0
### Noise component
knoise= 0.003 #uA/(mS)^1/2
### --------- Step3: SOLVE
for i in range(0, int(t_steps)-1, 1):
# Get current states
vT = v[i]
mT = m[i]
hT = h[i]
nT = n[i]
# Stimulus current
IStim = 0
if delay / dt <= i <= (delay + duration) / dt:
IStim = I * A # in uA
else:
IStim = 0
# Compute change of m, h and n
m[i + 1] = (mT + dt * alphaM(vT)) / (1 + dt * (alphaM(vT) + betaM(vT)))
h[i + 1] = (hT + dt * alphaH(vT)) / (1 + dt * (alphaH(vT) + betaH(vT)))
n[i + 1] = (nT + dt * alphaN(vT)) / (1 + dt * (alphaN(vT) + betaN(vT)))
# Ionic currents
iNa = gNa * m[i + 1] ** 3. * h[i + 1] * (vT - vNa)
iK = gK * n[i + 1] ** 4. * (vT - vK)
iL = gL * (vT-vL)
Inoise = (np.random.normal(0, 1) * knoise * np.sqrt(gNa * A))
IIon = ((iNa + iK + iL) * A) + Inoise #
# Compute change of voltage
v[i + 1] = vT + ((-IIon + IStim) / C) * dt # in ((uA / cm ^ 2) / (uF / cm ^ 2)) * ms == mV
# adjust the voltage to the resting potential
v = v + v_Rest
# test if there was a spike
if max(v[:]-v_Rest) > 60:
count += 1
return count
Ich habe folgendes versucht:
I = 30
xdata = np.array([0.92*I,I,1.05*I])
ydata = np.array([28,100,110])
y0=np.array([1,1,1])
def g(y,xdata,ydata):
return ydata - HH_model(xdata,y)
fit = optimize.leastsq(g, y0, args=(xdata, ydata))
File "", line
126, in HH_model
v[i + 1] = vT + ((-IIon + IStim) / C) * dt
ValueError: could not broadcast input array from shape (3) into shape
(1)
how can I get around this and make the input in the correct format?
The result of your line 126 is a three dimensional array with three times the same value. This size-3 array does not fit into an element of v, which has size-1 elements as you initialized them this way.
Therefore, you could add a [0]:
v[i + 1] = (vT + ((-IIon + IStim) / C) * dt)[0]
Furthermore, I think you do not need to allocate memory. You could for example use numpy.append in line 126.

Plotting a NACA 4-series airfoil

I'm trying to plot an airfoil from the formula as described on this wikipedia page.
This Jupyter notebook can be viewed on this github page.
%matplotlib inline
import math
import matplotlib.pyplot as pyplot
def frange( start, stop, step ):
yield start
while start <= stop:
start += step
yield start
#https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4-digit_NACA_airfoil
def camber_line( x, m, p, c ):
if 0 <= x <= c * p:
yc = m * (x / math.pow(p,2)) * (2 * p - (x / c))
#elif p * c <= x <= c:
else:
yc = m * ((c - x) / math.pow(1-p,2)) * (1 + (x / c) - 2 * p )
return yc
def dyc_over_dx( x, m, p, c ):
if 0 <= x <= c * p:
dyc_dx = ((2 * m) / math.pow(p,2)) * (p - x / c)
#elif p * c <= x <= c:
else:
dyc_dx = ((2 * m ) / math.pow(1-p,2)) * (p - x / c )
return dyc_dx
def thickness( x, t, c ):
term1 = 0.2969 * (math.sqrt(x/c))
term2 = -0.1260 * (x/c)
term3 = -0.3516 * math.pow(x/c,2)
term4 = 0.2843 * math.pow(x/c,3)
term5 = -0.1015 * math.pow(x/c,4)
return 5 * t * c * (term1 + term2 + term3 + term4 + term5)
def naca4( m, p, t, c=1 ):
for x in frange( 0, 1.0, 0.01 ):
dyc_dx = dyc_over_dx( x, m, p, c )
th = math.atan( dyc_dx )
yt = thickness( x, t, c )
yc = camber_line( x, m, p, c )
xu = x - yt * math.sin(th)
xl = x + yt * math.sin(th)
yu = yc + yt * math.cos(th)
yl = yc - yt * math.cos(th)
yield (xu, yu), (xl, yl)
#naca2412
m = 0.02
p = 0.4
t = 12
naca4points = naca4( m, p, t )
for (xu,yu),(xl,yl) in naca4points:
pyplot.plot( xu, yu, 'r,')
pyplot.plot( xl, yl, 'r,')
pyplot.ylabel('y')
pyplot.xlabel('x')
pyplot.axis('equal')
figure = pyplot.gcf()
figure.set_size_inches(16,16,forward=True)
The result looks like .
I expected it to look more like .
Questions: Why is the line not completely smooth? There seems to be a discontinuity where the beginning and end meet. Why does it not look like the diagram on wikipedia? How do I remove the extra loop at the trailing edge? How do I fix the chord so that it runs from 0.0 to 1.0?
First, t should be 0.12 not 12. Second, to make a smoother plot, increase the sample points.
It is also a good idea to use vectorize method in numpy:
%matplotlib inline
import math
import matplotlib.pyplot as plt
import numpy as np
#https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4-digit_NACA_airfoil
def camber_line( x, m, p, c ):
return np.where((x>=0)&(x<=(c*p)),
m * (x / np.power(p,2)) * (2.0 * p - (x / c)),
m * ((c - x) / np.power(1-p,2)) * (1.0 + (x / c) - 2.0 * p ))
def dyc_over_dx( x, m, p, c ):
return np.where((x>=0)&(x<=(c*p)),
((2.0 * m) / np.power(p,2)) * (p - x / c),
((2.0 * m ) / np.power(1-p,2)) * (p - x / c ))
def thickness( x, t, c ):
term1 = 0.2969 * (np.sqrt(x/c))
term2 = -0.1260 * (x/c)
term3 = -0.3516 * np.power(x/c,2)
term4 = 0.2843 * np.power(x/c,3)
term5 = -0.1015 * np.power(x/c,4)
return 5 * t * c * (term1 + term2 + term3 + term4 + term5)
def naca4(x, m, p, t, c=1):
dyc_dx = dyc_over_dx(x, m, p, c)
th = np.arctan(dyc_dx)
yt = thickness(x, t, c)
yc = camber_line(x, m, p, c)
return ((x - yt*np.sin(th), yc + yt*np.cos(th)),
(x + yt*np.sin(th), yc - yt*np.cos(th)))
#naca2412
m = 0.02
p = 0.4
t = 0.12
c = 1.0
x = np.linspace(0,1,200)
for item in naca4(x, m, p, t, c):
plt.plot(item[0], item[1], 'b')
plt.plot(x, camber_line(x, m, p, c), 'r')
plt.axis('equal')
plt.xlim((-0.05, 1.05))
# figure.set_size_inches(16,16,forward=True)
Thanks for the code.
I have modified the code for symmetrical airfoils:
def naca4s(x, t, c=1):
yt = thickness(x, t, c)
return ((x, yt),
(x, -yt))

Simple Dynamical Model in PyMC3

I'm trying to put together a model of a dynamical system in PyMC3, to infer two parameters. The model is the basic SIR, commonly used in epidemiology :
dS/dt = - r0 * g * S * I
dI/dt = g * I ( r * S - 1 )
where r0 and g are parameters to be inferred. So far, I'm unable to get very far at all. The only examples I've seen of putting together a Markov chain like this yields errors about recursion being too deep. Here's my example code.
# Time
t = np.linspace(0, 8, 200)
# Simulated observation
def SIR(y, t, r0, gamma) :
S = - r0 * gamma * y[0] * y[1]
I = r0 * gamma * y[0] * y[1] - gamma * y[1]
return [S, I]
# Currently no noise, we just want to infer params r0 = 16 and g = 0.5
solution = odeint(SIR, [0.99, 0.01, 0], t, args=(16., 0.5))
with pymc.Model() as model :
r0 = pymc.Normal("r0", 15, sd=10)
gamma = pymc.Uniform("gamma", 0.3, 1.)
# Use forward Euler to solve
dt = t[1] - t[0]
# Initial conditions
S = [0.99]
I = [0.01]
for i in range(1, len(t)) :
S.append(pymc.Normal("S%i" % i, \
mu = S[-1] + dt * (-r0 * gamma * S[-1] * I[-1]), \
sd = solution[:, 0].std()))
I.append(pymc.Normal("I%i" % i, \
mu = I[-1] + dt * ( r0 * gamma * S[-1] * I[-1] - gamma * I[-1]), \
sd = solution[:, 1].std()))
Imcmc = pymc.Normal("Imcmc", mu = I, sd = solution[:, 1].std(), observed = solution[:, 1])
#start = pymc.find_MAP()
trace = pymc.sample(2000, pymc.NUTS())
Any help would be much appreciated. Thanks !
I would try defining a new distribution. Something like the following. However, this is not quite working, and I'm not quite sure what I did wrong.
class SIR(Distribution):
def __init__(self, gamma, r0,dt, std):
self.gamma = gamma
self.r0 = r0
self.std = std
self.dt = dt
def logp(self, SI):
r0 = self.r0
std = self.std
gamma = self.gamma
dt = self.dt
S=SI[:,0]
I=SI[:,1]
Si = S[1:]
Si_m1 = S[:-1]
Ii = I[1:]
Ii_m1 = I[:-1]
Sdelta = (Si - Si_m1)
Idelta = (Ii - Ii_m1)
Sexpected_delta = dt* (-r0 * gamma * Si_m1 * Ii_m1)
Iexpected_delta = dt * gamma * Ii_m1 *( r0 * Si_m1 - 1 )
return (Normal.dist(Sexpected_delta, sd=std).logp(Sdelta) +
Normal.dist(Iexpected_delta, sd=std).logp(Idelta))
with Model() as model:
r0 = pymc.Normal("r0", 15, sd=10)
gamma = pymc.Normal("gamma", 0.3, 1.)
std = .5
dt = t[1]-t[0]
SI = SIR('SI', gamma, r0, std,dt, observed=solution[:,:2])
#start = pymc.find_MAP(start={'gamma' : .45, 'r0' : 17})
trace = pymc.sample(2000, pymc.NUTS())

Categories

Resources