Adding multiple simulation values from a monte carlo - python

I am trying to sum the values in Callpayoffs, as they represent the payoffs based on the last price which is generated in the prior path asset price loop. If I run 10 simulations, there should be 10 Callpayoffs based on the last price of each simulation path which has 252 price points. Unfortunately I'm not able to add up the values in the Callpayoffs list in any way so I can take the average Callpayoff over the 10 simulations. Would really appreciate any help - the below is a sample of print(sum(Callpayoffs). As you can see the code only divides the last value by 10, the number of simulations
[0]
[0]
[0]
[16.651081469090343]
[14.076846993975735]
[9.483857458061152]
[5.357562042338017]
[6.09266787737144]
[0]
[27.85935401436157]
2.785935401436157 # this is the last value divided by no of
simulations, but should be the sum of all values above divided by
simulations
import numpy as np
import pandas as pd
from math import *
import matplotlib.pyplot as plt
from matplotlib import *
def Generate_asset_price(S,v,r,dt):
return (1 + r * dt + v * sqrt(dt) * np.random.normal(0,1))
# initial values
S = 100
v = 0.2
r = 0.05
T = 1
N = 252 # number of steps
dt = 0.00396825
simulations = 10
for x in range(simulations):
stream = [100]
Callpayoffs = []
t = 0
for n in range(N):
s = stream[t] * Generate_asset_price(S,v,r,dt)
stream.append(s)
t += 1
Callpayoffs.append(max(stream[-1] - S,0))
plt.plot(stream)
print(Callpayoffs)
print(sum(Callpayoffs))
(sum(Callpayoffs)) / float(simulations)

Related

In the below code I am getting an error of only size-1 arrays can be converted to Python scalars in the last line of Pd[i[]

# Input of signal bandwidth
# Here we are taking M = 14 snapshots at a time
#Import the random library
# Here we need input if random values, in reality we have taken values from horn antena, but for reproducing research paper I have considered
import numpy as np
sigma_s = np.random.randint(0,4,(1,14))
sigma_w = np.random.randint(0,3,(1,14))
sigma_x = np.random.randint(0,2,(1,14))
sigma_g = np.random.randint(0,5,(1,14))
sigma_h = np.random.randint(0,9,(1,14))
Pd = np.zeros((1,14))
# The random array generate is in db so converting it into linear values
snr_db = np.random.randint(-30,0,(1,14))
snr_linear = (10\*\*(np.random.randint(-30,0,(1,14)) \* (-1/10)))
# y = threshold value
y = 1.1
u =np.random.rand(1,14)# u here is a small step size
h = np.random.rand(1,14)
M = 14
N = 4
a = 3
import numpy as np
import sympy as sy
import math
import numpy as np
np.seterr(divide='ignore', invalid='ignore')
for i in range(14):
rho_one_sqaure = h[i]* ((snr_linear[i] / sigma_h[i])** 2 ) * ((snr_linear[i]+1) ** 1/2)
rho = h[i]*(((((sigma_s[i])**2)/(sigma_w[i])**2))**(1/2))
z = (sigma_x[i])**2/sigma_g[i]**2
sigma_zero_sqaure = (1-(rho)**2)*(rho**2)*(u**2)*(2-2*N*M*u/(1+(2*z)**(N*M)))/(1+z)**(N*M)/2
u_one = M * ((rho_one_sqaure)**2) *u[i]*((1+2*M*N*z)**(N*M/2))
sigma_one_square = (sigma_x **2) * (1-rho_one_sqaure)*rho_one_sqaure*(u**2)*(2-2*N*M*u/(1+(2*N*M*z))**(N*M/2))/(1+(2*N*M*z))**(N*M/2)
# T(wave function) is the sufficient statistics and it is determined as given below
def sufficient_statistics(wave_function):
return ((wave_function - u_one * (sigma_zero_sqaure)/sigma_one_square - sigma_zero_sqaure)**2)
t_wave_function = sufficient_statistics(4)
Pd[i]= np.int(((math.sqrt(a*a*a*snr_linear[i]))**((N/2)-1)) * sy.integrate(x**(N/2-1))*math.exp(-(x**2+a*a*a*snr_linear[i]))*1/3.14 * (sy.integrate(math.exp(a*x)*math.cos(a*theta), (theta,0,math.pi()))) ,(x,math.sqrt(y),2))
I was trying to calculate the value of Pd[i] at the end of every loop for 14 times.
And the wanted to have graph of Pd.
BUt the error comes

Not understanding how movering average implementation works

I have a simple time series and I have a code implementing the moving average:
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
keras = tf.keras
def plot_series(time, series, format="-", start=0, end=None, label=None):
plt.plot(time[start:end], series[start:end], format, label=label)
plt.xlabel("Time")
plt.ylabel("Value")
if label:
plt.legend(fontsize=14)
plt.grid(True)
def trend(time, slope=0):
return slope * time
def seasonal_pattern(season_time):
"""Just an arbitrary pattern, you can change it if you wish"""
return np.where(season_time < 0.4,
np.cos(season_time * 2 * np.pi),
1 / np.exp(3 * season_time))
def seasonality(time, period, amplitude=1, phase=0):
"""Repeats the same pattern at each period"""
season_time = ((time + phase) % period) / period
return amplitude * seasonal_pattern(season_time)
def white_noise(time, noise_level=1, seed=None):
rnd = np.random.RandomState(seed)
return rnd.randn(len(time)) * noise_level
time = np.arange(4 * 365 + 1)
slope = 0.05
baseline = 10
amplitude = 40
series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)
noise_level = 5
noise = white_noise(time, noise_level, seed=42)
series += noise
plt.figure(figsize=(10, 6))
plot_series(time, series)
plt.show()
def moving_average_forecast(series, window_size):
"""Forecasts the mean of the last few values.
If window_size=1, then this is equivalent to naive forecast"""
forecast = []
for time in range(len(series) - window_size):
forecast.append(series[time:time + window_size].mean())
return np.array(forecast)
split_time = 1000
time_train = time[:split_time]
x_train = series[:split_time]
time_valid = time[split_time:]
x_valid = series[split_time:]
moving_avg = moving_average_forecast(series, 30)[split_time - 30:]
plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid, label="Series")
plot_series(time_valid, moving_avg, label="Moving average (30 days)")
I am not getting this part:
for time in range(len(series) - window_size):
forecast.append(series[time:time + window_size].mean())
return np.array(forecast)
What I do not understand is how series[time:time + window_size] works? Window_size is given into the function and can be a value specifying how many days are considered to calculate the mean, like 5 or 30 days.
When I try something similiar to illustrate this to myself, like
plot(series[time:time + 30]) this does not work.
Furthermore I do not get how len(series) - window_size) works.
debug your code and add some print statements to see how it is responding
Write them down and try to analyze the results
Step back and write a similar code that reproduce the same output
Compare
if it is the same congrats
if it is no then try to run again with timers on and see which one is faster.
if your code is faster the congrats.
Seems like the function moving_average_forecast simply calculates the x day rolling average? If that is the intention then:
The line for time in range(len(series) - window_size): gives you the index time that goes from 0 to some number n where n + 1 is the number of rolling averages you can get out of a time series of size N (i.e. if you have 11 data points and want to calculate 10 day rolling averages, you can get at most 2, here N = 11 = len(series), window_size = 10, so n = 1 and time = [0, 1]
The line series[time:time + window_size] I think should actually be series[time:time + window_size - 1] simply index into your data contained in series and calculate each of the rolling averages (i.e. using our example earlier, in the first iteration time = 0, time + window_size - 1 = 9 so series[time:time + window_size - 1] returns an array with the first 10 data points and so on
Hope that helps.

Calculate probability 2 random people are in the same group?

In my dataset, there are N people who are each split into one 3 groups (groups = {A, B, C}). I want to find the probability that two random people, n_1 and n_2, belong to the same group.
I have data on each of these groups and how many people belong to them. Importantly, each group is of a different size.
import pandas as pd
import numpy as np
import math
data = {
"Group": ['A', 'B', 'C'],
"Count": [20, 10, 5],
}
df = pd.DataFrame(data)
Group Count
0 A 20
1 B 10
2 C 5
I think I know how to get the sample space, S but I am unsure how to get the numerator.
def nCk(n,k):
f = math.factorial
return f(n) / f(k) / f(n-k)
n = sum(df['Count'])
k = 2
s = nCk(n, k)
My discrete mathematics skills are a bit rusty so feel free to correct me. You have N people split into groups of sizes s_1, ..., s_n so that N = s_1 + ... + s_n.
The chance of one random person belonging to group i is s_i / N
The chance of a second person being in group i is (s_i - 1) / (N - 1)
The chance of both being in group i is s_i / N * (s_i - 1) / (N - 1)
The probability of them being together in any group is the sum of the probabilities in #3 across all groups.
Code:
import numpy as np
s = df['Count'].values
n = s.sum()
prob = np.sum(s/n * (s-1)/(n-1)) # 0.4117647058823529
We can generalize this solution to "the probability of k people all being in the same group":
k = 2
i = np.arange(k)[:, None]
tmp = (s-i) / (n-i)
prob = np.prod(tmp, axis=0).sum()
When k > s.max() (20 in this case), the answer is 0 because you cannot fit all of them in one group. When k > s.sum() (35 in this case), the result is nan.
I will answer your problem by using hypergeometric distribution, hypergeometric distribution is a discrete probability distribution that describes the probability of k successes (random draws for which the object drawn has a specified feature) in n draws, without replacement, from a finite population of size N that contains exactly K objects with that feature, wherein each draw is either a success or a failure. In contrast, the binomial distribution describes the probability of k successes in n draws with replacement.
So the total probability should be the probability of both belonging to A + probability of both belonging to B + probability of both belonging to C.
This means
P(A) = (nCk(20,2) * nCk(15,0))/nCk(35,2)
P(B) = (nCk(10,2) * nCk(25,0))/nCk(35,2)
P(C) = (nCk(5,2) * nCk(5,0)) / nCk(35,2)
In code terms:
import pandas as pd
import numpy as np
import math
data = {
"Group": ['A', 'B', 'C'],
"Count": [20, 10, 5],
}
df = pd.DataFrame(data)
def nCk(n,k):
f = math.factorial
return f(n) / f(k) / f(n-k)
samples = 2
succeses = 2
observations = df['Count'].sum()
p_a = ((nCk(df[df['Group'] == 'A'].set_index('Group').max(),samples)) * (nCk((observations - df[df['Group'] == 'A'].set_index('Group').max()),(samples-succeses)))) / nCk(observations,samples)
p_b = ((nCk(df[df['Group'] == 'B'].set_index('Group').max(),samples)) * (nCk((observations - df[df['Group'] == 'B'].set_index('Group').max()),(samples-succeses)))) / nCk(observations,samples)
p_c =((nCk(df[df['Group'] == 'C'].set_index('Group').max(),samples)) * (nCk((observations - df[df['Group'] == 'C'].set_index('Group').max()),(samples-succeses)))) / nCk(observations,samples)
proba = p_a + p_b + p_c
print(proba)
Output:
0.41176470588235287

Python: While loop - how do I avoid division by zero?

I am writing code for summing the Fourier Series that ranges from [-n,n]. However, I'm having trouble with it iterating when it gets to n = 0. I wrote an 'if' statement inside my while loop so it can ignore it, but it seems like it isn't. Here's my code:
from __future__ import division
import numpy as np
import math
import matplotlib.pyplot as plt
#initial values
ni = -10
nf = 10
ti = -3
tf = 3
dt = 0.01
yi = 0 #initial f(t) value
j = complex(0,1)
#initialization
tarray = [ti]
yarray = [yi]
t = ti
n = ni
y = yi
cn = 1/(8*(np.pi)**3*n**3*j**3)*(j*4*np.pi*n) #part (b)
#iterating loop
while t<tf:
n = ni
y = yi
while n<nf:
if n == 0:
cn = 1/6
y += cn
n += 1
else:
y += cn*np.exp(j*np.pi*n*t)
n += 1
yarray.append(y)
t+=dt
tarray.append(t)
#converting list-array
tarray = np.array(tarray)
yarray = np.array(yarray)
#plotting
plt.plot(tarray,yarray, linewidth = 1)
plt.axis("tight")
plt.xlabel('t')
plt.ylabel('f(t) upto n partial sums')
plt.title('Fourier Series for n terms')
plt.legend()
plt.show()
I want it to iterate and create an array of y-values for n ranging from some negative number to some positive number (say for n from [-10,10]), but as soon as it hits n = 0 it seems to be plugging that in into the 'else' clause even though I want it to use what's in the 'if' clause, giving me a "ZeroDivisionError: complex division by zero". How do I fix this?
Edit: Put the entire code block here so you can see the context.
This is not the most elegant way at all but try this:
while t<tf:
n = ni
y = yi
while n<nf:
try:
1/n
cn = 1/6
y += cn
n += 1
except ZeroDivisionError:
y += cn*np.exp(j*np.pi*n*t) #1/n*np.sin(n*t)
n += 1
yarray.append(y)
t+=dt
tarray.append(t)
The coefficient cn is a function of n and should be updated in every loop. You made it constant (and even equal to 1/6 for positive n).
The inner loop could look like
y = 1/6 # starting with n = 0
for n in range(1,nf):
y -= 1/(2*np.pi*n)**2 * np.sin(np.pi*n*t) # see below
Corresponding coefficients for positive and negative n's are equal and exp(ix) - exp(-ix) = 2i sin(x), so it nicely reduces. (Double check the calculation.)

Previous value in while loop in Python

I tried to make simple monte carlo simulation for stock investments where you start with some investment value, investment period (in years) and mean and std of stock mutual fund. I also wanted to implement an easy way for stock market crash - I did it so that whenever new calculated value was for 40 % higher than previous one, the new value should fall for 90 % - like some kind of crash. I managed to make it working and here is the code, but I think that it is not working right. The problem is probably hidden where I call previous value. Could you try to make it working?
import matplotlib
import matplotlib.pyplot as plt
import random
import numpy as np
mean = 7.0 #mean for stock mutual fund
std = 19.0 #std for stock mutual fund
def investment_return(): #random normal distribution of returns
investment_return = (np.random.normal(mean,std))/100
return investment_return
def investor(A, B):
investment_value = A
investment_period = B
wX = []
vY = []
x = 1
while x <= investment_period:
value = A + A*investment_return()
if value > value * 1.4: #if new value is 1.4x bigger than previous
A = value * 0.1 #than make -90 percent adjustment
else:
A = value #else use new value
wX.append(x)
vY.append(value)
x += 1
#print(value)
plt.plot(wX,vY)
i = 0
while i < 10: #number of investors
investor(100,20) #starting value and investment period
i += 1
plt.ylabel('Investment_value')
plt.xlabel('Investment_period')
plt.show()
Well, I tried best I could to interpret what you were after. It helped that you provided a solid basis to work with :).
Ok so, here we go: obviously, Kevin's remark that value > value * 1.4 will never evaluate to True is a solid one. I did rename some variables (for example, normally we compare stocks as indices, so I renamed A to index). Time is generally referred to as t, not x. The while loops were a little quirky, so I got rid of those.
import matplotlib.pyplot as plt
import numpy as np
mean = 7.0
std = 19.0
def investment_return():
return (np.random.normal(mean, std)) / 100
def investor(index, period):
wT = []
vY = []
for t in range(1, period + 1):
new_index = index + index * investment_return()
if new_index > index * 1.4:
index = new_index * 0.1
else:
index = new_index
wT.append(t)
vY.append(index)
return wT, vY
for i in range(0, 10):
wT, vY = investor(100, 20)
# do something with your data
plt.plot(wT, vY)
plt.ylabel('Investment_value')
plt.xlabel('Investment_period')
plt.show()
This occasionally does have a stock crash, as can clearly be seen (do keep in mind that this requires you to sample >40 from an N(7,19) distribution: that should not happen in a little over 95% of all cases).

Categories

Resources