We have a barrier call option of European type with strike price K>0 and a barrier value
0 < b< S0 ,
where S_0 is the starting price.According to the contract, the times 0<t_1<...<t_k<T the price must be checked S(t_k)>b for every k.
Assuming the S(t) is described with the binomial option model with u=1.1 and d = 0.9,r=0.05,T=10, and t_1=2,t_2=4 and t-3=7 the times that the asset must be checked.Also consider the S_0=100,K=125 and the barrier b=60.
My attempt is the following :
# Initialise parameters
S0 = 100 # initial stock price
K = 125 # strike price
T = 10 # time to maturity in years
b = 60 # up-and-out barrier price/value
r = 0.05 # annual risk-free rate
N = 4 # number of time steps
u = 1.1 # up-factor in binomial models
d = 0.9 # ensure recombining tree
opttype = 'C' # Option Type 'C' or 'P'
def barrier_binomial(K,T,S0,b,r,N,u,d,opttype='C'):
#precompute values
dt = T/N
q = (1+r - d)/(u-d)
disc = np.exp(-r*dt)
# initialise asset prices at maturity
S = S0 * d**(np.arange(N,-1,-1)) * u**(np.arange(0,N+1,1))
# option payoff
if opttype == 'C':
C = np.maximum( S - K, 0 )
else:
C = np.maximum( K - S, 0 )
# check terminal condition payoff
C[S >= b] = 0
# backward recursion through the tree
for i in np.arange(N-1,-1,-1):
S = S0 * d**(np.arange(i,-1,-1)) * u**(np.arange(0,i+1,1))
C[:i+1] = disc * ( q * C[1:i+2] + (1-q) * C[0:i+1] )
C = C[:-1]
C[S >= H] = 0
return C[0]
barrier_binomial(K,T,S0,b,r,N,u,d,opttype='C')
I receive nothing because something is wrong and I don’t know what
But is it a simulation ?
Any help from someone ?
In your loop you are using C[S >= H] = 0, but your barrier param is defined as b. Also you are filling the array C with 0s only, so check the payoff condition. In general, I find it much easier looping through matrices when working with tree models.
Related
I'm building a Genetic Algorithm to maximize this function: x^5 - 10x^3 + 30x - y^2 + 21y.
The code must be in binary and the bounds for x and y are [-2.5, 2.5]. To generate the initial population I made a 16 bit string for both x and y where:
The first bit represents the signal [0 or 1]
The the second and third bit represents the integer part [00, 01 or 10]
The rest represents the float part
This is the function that generates the initial population:
def generate_population(n_pop):
population = list()
for _ in range(n_pop):
aux = list()
for _ in range(2):
signal = bin(randint(0, 1))[2:]
int_part = bin(randint(0, 2))[2:].zfill(2)
float_part = bin(randint(0, 5000))[2:].zfill(13)
aux.append((signal+int_part+float_part))
population.append(aux)
return population
I also made a function that returns the binary number into float:
def convert_float(individual):
float_num = list()
for i in range(2):
signal = int(individual[i][0])
int_part = int(individual[i][1:3], 2)
float_part = int(individual[i][3:], 2) * (10 ** -4)
value = round(int_part + float_part, 4)
if value > 2.5:
value = 2.5
if signal == 1:
value = value * (-1)
float_num.append(value)
return float_num
And lastly this function that calculate the fitness of each individual:
def get_fitness(individual):
x = individual[0]
y = individual[1]
return x ** 5 - 10 * x ** 3 + 30 * x - y ** 2 + 21 * y
This is my main function:
def ga(n_pop=10, n_iter=10):
population = generate_population(n_pop)
best_fitness_id, best_fitness = 0, get_fitness(convert_float(population[0]))
for i in range(n_iter):
float_population = [convert_float(x) for x in population]
fitness_population = [get_fitness(x) for x in float_population]
for j in range(n_pop):
if fitness_population[j] > best_fitness:
best_fitness_id, best_fitness = j, fitness_population[j]
print(f'--> NEW BEST FOUND AT GENERATION {i}:')
print(f'{float_population[j]} = {fitness_population[j]}')
selected_parents = rank_selection()
# childrens = list()
# childrens = childrens + population[best_fitness_id] # ELITE
After running the program I have something like this:
The population looks like: [['0000001100110111', '0000110111110101'], ['0010011111101110', '1000100101001001'], ...
The float population: [[0.0823, 0.3573], [1.203, -0.2377], ...
And the fitness values: [9.839066068044746, 16.15145434928624, ...
I need help to build the rank_selection() function, I've been stuck in this selection for 2 days. I know is something 1/N, 2/N etc and I've seen tons of examples in multiple languages but I could not apply any of them to this particular algorithm and it MUST be rank selecion.
I already know how to perform crossover and mutation.
My idea is as follows:
import random
list=[]
while True:
A = random.uniform(0,3150) / 3150
B = random.uniform(0,2200) / 2200
C = random.uniform(0,1000) / 1000
D = random.uniform(0,2600) / 2600
E = random.uniform(0,2540) / 2540
F = random.uniform(0,1200) / 1200
G = random.uniform(0,1050) / 1050
if A+B+C+D+E+F+G == 0.965:
list[0] = A
list[1] = B
list[2] = C
list[3] = D
list[4] = E
list[5] = F
list[6] = G
print(list)
break
But even if I operate this code, it takes too long to get a list.
Should I
Firstly, I dont understand why you're using random.uniform(0, upperLim)/upperLimbecause it will always be a value between 0 and 1. But if that is a necessary part of your implementation then go ahead. Otherwise change it to random.uniform(0, 1) because that avoids an unnecessary multiplication and gives you greater precision too.
To make sure the sum of the random numbers is 0.965 here's what you can do to avoid looping and checking multiple times:
Firstly change the upper limit from the one you specified to something that returns max 0.965 i.e, random.uniform(0, 3150 * 0.965) / 3150
After this, generate the numbers according to the conditions that have already been met
maxLists = 100
currLists = 0
while True:
A = random.uniform(0, 0.965) # I'll use (0, 0.965) but you can replace it
B = random.uniform(0, 0.965 - A)
C = random.uniform(0, 0.965 - (A + B))
D = random.uniform(0, 0.965 - (A + B + C))
E = random.uniform(0, 0.965 - (A + B + C + D))
F = random.uniform(0, 0.965 - (A + B + C + D + E))
G = 0.965 - (A + B + C + D + E + F)
# populate them into a list
listOfNums = [A, B, C, D, E, F, G]
# get numbers that are zero because we want to prevent only the first few numbers adding
# upto 0.965 all the time
zeros = [i for i, e in enumerate(listOfNums) if e == 0]
nonZeros = [i for i, e in enumerate(listOfNums) if e != 0]
# do something with numbers here, maybe randomly take some part of the value from
# non zero indices and assign it to indices with zero value if you need the numbers
# to have some minimum value or if you have some other constraint
currLists += 1
print(listOfNums)
if currLists == maxLists: break
EDIT :
Here are some of the results I got after running it
[0.17790933642353207, 0.203575715409321, 0.17296325968456305, 0.12888905609400236, 0.07906382215736181, 0.19622480233464165, 0.006374007896577938]
[0.049602151767929197, 0.12732071710856213, 0.6719775449266687, 0.08616832676415115, 0.002068199017310945, 0.0015719942386102515, 0.026291066176767575]
[0.4216568638854053, 0.0841285730604016, 0.12581422942385648, 0.04125099314750179, 0.013789268384205427, 0.12463265303883869, 0.1537274190597907]
[0.39352655740635734, 0.08302194874949533, 0.05585858753600888, 0.14417023258593673, 0.17742466007873198, 0.042698164836977186, 0.06829984880649254]
[0.836553479500795, 0.019661470617368986, 0.06300565338226506, 0.021033910322500717, 0.0234921077113921, 0.0002043707861913963, 0.0010490076794867909]
[0.5334487166183782, 0.07743001493044013, 0.3431304879017562, 0.001616778025312949, 0.003948535326924425, 0.001755908717321748, 0.003669558479866386]
You can see the last few results are approaching zero, which is why you will either need to take some part of the previous values and add it to them or you can shuffle the numbers around randomly
Here is one way to do it, using a generator
import random
def solution(precision):
while True:
A = random.uniform(0,3150) / 3150
B = random.uniform(0,2200) / 2200
C = random.uniform(0,1000) / 1000
D = random.uniform(0,2600) / 2600
E = random.uniform(0,2540) / 2540
F = random.uniform(0,1200) / 1200
G = random.uniform(0,1050) / 1050
if abs(A+B+C+D+E+F+G - 0.965) <= precision:
yield [A, B, C, D, E, F, G]
and you get values satisfying the condition by calling
next(solution(0.001)) as many times as you need.
If you ask for a better precision, say 0.00001, it may take much longer to compute a solution.
Here is a simple example, which numerically integrates the product of two Gaussian pdfs. One of the Gaussians is fixed, with mean always at 0. The other Gaussian varies in its mean:
import time
import jax.numpy as np
from jax import jit
from jax.scipy.stats.norm import pdf
# set up evaluation points for numerical integration
integr_resolution = 6400
lower_bound = -100
upper_bound = 100
integr_grid = np.linspace(lower_bound, upper_bound, integr_resolution)
proba = pdf(integr_grid)
integration_weight = (upper_bound - lower_bound) / integr_resolution
# integrate with new mean
def integrate(mu_new):
x_new = integr_grid - mu_new
proba_new = pdf(x_new)
total_proba = sum(proba * proba_new * integration_weight)
return total_proba
print('starting jit')
start = time.perf_counter()
integrate = jit(integrate)
integrate(1)
stop = time.perf_counter()
print('took: ', stop - start)
The function looks seemingly simple, but it doesn't scale at all. The following list contains pairs of (value for integr_resolution, time it took to run the code):
100 | 0.107s
200 | 0.23s
400 | 0.537s
800 | 1.52s
1600 | 5.2s
3200 | 19s
6400 | 134s
For reference, the unjitted function, applied to integr_resolution=6400 takes 0.02s.
I thought that this might be related to the fact that the function is accessing a global variable. But moving the code to set up the integration points inside of the function has no notable influence on the timing. The following code takes 5.36s to run. It corresponds to the table entry with 1600 which previously took 5.2s:
# integrate with new mean
def integrate(mu_new):
# set up evaluation points for numerical integration
integr_resolution = 1600
lower_bound = -100
upper_bound = 100
integr_grid = np.linspace(lower_bound, upper_bound, integr_resolution)
proba = pdf(integr_grid)
integration_weight = (upper_bound - lower_bound) / integr_resolution
x_new = integr_grid - mu_new
proba_new = pdf(x_new)
total_proba = sum(proba * proba_new * integration_weight)
return total_proba
What is happening here?
I also answered this at https://github.com/google/jax/issues/1776, but adding the answer here too.
It's because the code uses sum where it should use np.sum.
sum is a Python built-in that extracts each element of a sequence and sums them one by one using the + operator. This has the effect of building a large, unrolled chain of adds which XLA takes a long time to compile.
If you use np.sum, then JAX builds a single XLA reduction operator, which is much faster to compile.
And just to show how I figured this out: I used jax.make_jaxpr, which dumps JAX's internal trace representation of a function. Here, it shows:
In [3]: import jax
In [4]: jax.make_jaxpr(integrate)(1)
Out[4]:
{ lambda b c ; ; a.
let d = convert_element_type[ new_dtype=float32
old_dtype=int32 ] a
e = sub c d
f = sub e 0.0
g = pow f 2.0
h = div g 1.0
i = add 1.8378770351409912 h
j = neg i
k = div j 2.0
l = exp k
m = mul b l
n = mul m 2.0
o = slice[ start_indices=(0,)
limit_indices=(1,)
strides=(1,)
operand_shape=(100,) ] n
p = reshape[ new_sizes=()
dimensions=None
old_sizes=(1,) ] o
q = add p 0.0
r = slice[ start_indices=(1,)
limit_indices=(2,)
strides=(1,)
operand_shape=(100,) ] n
s = reshape[ new_sizes=()
dimensions=None
old_sizes=(1,) ] r
t = add q s
u = slice[ start_indices=(2,)
limit_indices=(3,)
strides=(1,)
operand_shape=(100,) ] n
v = reshape[ new_sizes=()
dimensions=None
old_sizes=(1,) ] u
w = add t v
x = slice[ start_indices=(3,)
limit_indices=(4,)
strides=(1,)
operand_shape=(100,) ] n
y = reshape[ new_sizes=()
dimensions=None
old_sizes=(1,) ] x
z = add w y
... similarly ...
and it's then obvious why this is slow: the program is very big.
Contrast the np.sum version:
In [5]: def integrate(mu_new):
...: x_new = integr_grid - mu_new
...:
...: proba_new = pdf(x_new)
...: total_proba = np.sum(proba * proba_new * integration_weight)
...:
...: return total_proba
...:
In [6]: jax.make_jaxpr(integrate)(1)
Out[6]:
{ lambda b c ; ; a.
let d = convert_element_type[ new_dtype=float32
old_dtype=int32 ] a
e = sub c d
f = sub e 0.0
g = pow f 2.0
h = div g 1.0
i = add 1.8378770351409912 h
j = neg i
k = div j 2.0
l = exp k
m = mul b l
n = mul m 2.0
o = reduce_sum[ axes=(0,)
input_shape=(100,) ] n
in [o] }
Hope that helps!
I have a pandas dataframe with 6 million rows. The columns are:
['x', 'y']
I need to apply a simple calculation between x an y, and append it to the dataframe.
This is what I've tried:
'''
Calculates the height of a pressure level in feet
'''
def pressure_to_elevation(P, T = None):
sea_level_pressure = 1013.25
if T is not None:
# https://www.omnicalculator.com/physics/air-pressure-at-altitude
P0 = sea_level_pressure
g = 9.80665
M = 0.0289644
R0 = 8.31447
m = (np.log(P/P0)*T) / -(g*M/R0)
f = 3.28084 * m
return f
b = 0.190284
c = 145366.45
return (1-math.pow((P/sea_level_pressure), b)) * c
test_df['result'] = test_fd.apply(lambda row: pressure_to_elevation(row['x'], row['y']),axis=1)
Unfortunately, this takes a ridiculous amount of time... in fact, I've yet to see it complete.
Is there a faster way to do this?
Try this:
def pressure_to_elevation(P, T):
sea_level_pressure = 1013.25
P0 = sea_level_pressure
g = 9.80665
M = 0.0289644
R0 = 8.31447
b = 0.190284
c = 145366.45
return np.where(T.notnull(),
3.28084 * ((np.log(P/P0)*T) / -(g*M/R0)),
(1-np.pow((P/sea_level_pressure), b)) * c)
Usage:
test_df['result'] = pressure_to_elevation(test_df['x'], test_df['y'])
I believe if you break this out into separate steps and avoid iterating through the entire dataframe, the speed will increase dramatically. Give the following a shot.
test_df['result_1'] = (test_df['x']/sea_level_pressure)
test_df['result_1'] = test_df['result']**0.190284
test_df['result_1'] = (1 - test_df['result'])*145366.45
test_df['result_2'] = 3.28084*((np.log(test_df['x']/sea_level_pressure)*test_df['y'])/(-1*(9.80665*0.0289644/8.31447)))
test_df['final_result'] = np.where(pd.isnull(test_df['y']), test_df['result_1'], test_df['result_2'])
I'm a new learner of python programming. Recently I'm trying to write a "tool" program of "dynamic programming" algorithm. However, the last part of my programe -- a while loop, failed to loop. the code is like
import numpy as np
beta, rho, B, M = 0.5, 0.9, 10, 5
S = range(B + M + 1) # State space = 0,...,B + M
Z = range(B + 1) # Shock space = 0,...,B
def U(c):
"Utility function."
return c**beta
def phi(z):
"Probability mass function, uniform distribution."
return 1.0 / len(Z) if 0 <= z <= B else 0
def Gamma(x):
"The correspondence of feasible actions."
return range(min(x, M) + 1)
def T(v):
"""An implementation of the Bellman operator.
Parameters: v is a sequence representing a function on S.
Returns: Tv, a list."""
Tv = []
for x in S:
# Compute the value of the objective function for each
# a in Gamma(x), and store the result in vals (n*m matrix)
vals = []
for a in Gamma(x):
y = U(x - a) + rho * sum(v[a + z]*phi(z) for z in Z)
# the place v comes into play, v is array for each state
vals.append(y)
# Store the maximum reward for this x in the list Tv
Tv.append(max(vals))
return Tv
# create initial value
def v_init():
v = []
for i in S:
val = []
for j in Gamma(i):
# deterministic
y = U(i-j)
val.append(y)
v.append(max(val))
return v
# Create an instance of value function
v = v_init()
# parameters
max_iter = 10000
tol = 0.0001
num_iter = 0
diff = 1.0
N = len(S)
# value iteration
value = np.empty([max_iter,N])
while (diff>=tol and num_iter<max_iter ):
v = T(v)
value[num_iter] = v
diff = np.abs(value[-1] - value[-2]).max()
num_iter = num_iter + 1
As you can see, the while loop at the bottom is used to iterate over "value function" and find the right answer. However, the while fails to loop, and just return num_iter=1. As for I know, the while loop "repeats a sequence of statements until some condition becomes false", clearly, this condition will not be satisfied until the diff converge to near 0
The major part of code works just fine, as far as I use the following for loop
value = np.empty([num_iter,N])
for x in range(num_iter):
v = T(v)
value[x] = v
diff = np.abs(value[-1] - value[-2]).max()
print(diff)
You define value as np.empty(...). That means that it is composed completely of zeros. The difference, therefore, between the last element and the second-to-last element will be zero. 0 is not >= 0.0001, so that expression will be False. Therefore, your loop breaks.