Skip to content

Commit 06a0c4a

Browse files
committed
FEATURE: fixing greek plots + adding execution times
1 parent 568a057 commit 06a0c4a

5 files changed

Lines changed: 102 additions & 14 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
4+
class BrownianMotion:
5+
6+
def __init__(self, T, time_steps, sim, mu=0, sigma=1):
7+
self.__mu = mu
8+
self.__sigma = sigma
9+
self.__T = T
10+
self.__time_steps = time_steps
11+
self.__sim = sim
12+
self.__B = None
13+
14+
def simulate_bm(self):
15+
t = self.__generate_grid()
16+
dt = t[1] - t[0]
17+
18+
Z = np.random.normal(0,1, (self.__sim, self.__time_steps))
19+
B = np.zeros((self.__sim, self.__time_steps))
20+
21+
for i in range(self.__sim):
22+
for j in range(1, self.__time_steps):
23+
B[i,j] = B[i,j-1] + self.__mu * dt + self.__sigma * np.sqrt(dt) * Z[i,j]
24+
25+
self.__B = B
26+
return self
27+
28+
29+
def __generate_grid(self):
30+
"""
31+
Generate a time grid from 0 to T with `time_steps` intervals.
32+
33+
Returns:
34+
- A numpy array representing the time grid.
35+
"""
36+
37+
return np.linspace(0, self.__T, self.__time_steps)
38+
39+
def __get_paths(self):
40+
return self.__B
41+
42+
def plot(self):
43+
t = self.__generate_grid()
44+
B = self.__get_paths()
45+
46+
plt.figure(figsize=(10,6))
47+
for i in range(self.__sim):
48+
plt.plot(t, B[i], color='grey', alpha=0.3)
49+
50+
plt.title("Simulated Brownian Motion Paths")
51+
plt.xlabel("Time (Years)")
52+
plt.ylabel("B(t)")
53+
# plt.show()
54+
plt.savefig("brownian_plot.pdf", bbox_inches='tight')
55+
56+
57+
def main():
58+
BrownianMotion(T=1, time_steps=365, sim=200).simulate_bm().plot()
59+
60+
if __name__ == "__main__":
61+
main()

pdesolvers/solution/solution.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66

77
class Solution:
88

9-
def __init__(self, result, x_grid, y_grid, dx, dy):
9+
def __init__(self, result, x_grid, y_grid, dx, dy, duration):
1010
self.result = result
1111
self.x_grid = x_grid
1212
self.y_grid = y_grid
1313
self.dx = dx
1414
self.dy = dy
15+
self.duration = duration
1516

1617
def plot(self):
1718
"""
@@ -46,6 +47,9 @@ def _set_plot_labels(self, ax):
4647
ax.set_zlabel('Value')
4748
ax.set_title('3D Surface Plot')
4849

50+
def get_execution_time(self):
51+
return self.duration
52+
4953
def __sub__(self, other):
5054
"""
5155
Compares two solutions by interpolating the sparse grid to the dense grid and computing the difference
@@ -83,8 +87,8 @@ def __sub__(self, other):
8387

8488
class Solution1D(Solution):
8589

86-
def __init__(self, result, x_grid, y_grid, dx, dy):
87-
super().__init__(result, x_grid, y_grid, dx, dy)
90+
def __init__(self, result, x_grid, y_grid, dx, dy, duration):
91+
super().__init__(result, x_grid, y_grid, dx, dy, duration)
8892

8993
def _set_plot_labels(self, ax):
9094
ax.set_xlabel('Space')
@@ -94,8 +98,8 @@ def _set_plot_labels(self, ax):
9498

9599

96100
class SolutionBlackScholes(Solution):
97-
def __init__(self, result, x_grid, y_grid, dx, dy, delta, gamma, theta, option_type):
98-
super().__init__(result, x_grid, y_grid, dx, dy)
101+
def __init__(self, result, x_grid, y_grid, dx, dy, duration, delta, gamma, theta, option_type):
102+
super().__init__(result, x_grid, y_grid, dx, dy, duration)
99103
self.option_type = option_type
100104
self.delta = delta
101105
self.gamma = gamma
@@ -105,8 +109,8 @@ def plot_greek(self, greek_type=enum.Greeks.DELTA, time_step=0):
105109

106110
greek_types = {
107111
enum.Greeks.DELTA : {'data': self.delta, 'title': enum.Greeks.DELTA.value},
108-
enum.Greeks.GAMMA : {'data': self.gamma, 'title': enum.Greeks.DELTA.value},
109-
enum.Greeks.THETA : {'data': self.theta, 'title': enum.Greeks.DELTA.value}
112+
enum.Greeks.GAMMA : {'data': self.gamma, 'title': enum.Greeks.GAMMA.value},
113+
enum.Greeks.THETA : {'data': self.theta, 'title': enum.Greeks.THETA.value}
110114
}
111115

112116
# if greek_type.lower() not in greek_types:
@@ -115,14 +119,15 @@ def plot_greek(self, greek_type=enum.Greeks.DELTA, time_step=0):
115119
chosen_greek = greek_types[greek_type]
116120
greek_data = chosen_greek['data'][:, time_step]
117121
plt.figure(figsize=(8, 6))
118-
plt.plot(self.y_grid, greek_data, label=f"Delta at t={self.x_grid[time_step]:.4f}", color="blue")
122+
plt.plot(self.y_grid, greek_data, label=f"{chosen_greek['title']} at t={self.x_grid[time_step]:.4f}", color="grey", alpha=0.8)
119123

120124
plt.title(f"{chosen_greek['title']} vs. Stock Price at t={self.x_grid[time_step]:.4f}")
121125
plt.xlabel("Stock Price (S)")
122126
plt.ylabel(chosen_greek['title'])
123127
plt.grid()
124128
plt.legend()
125129

130+
np.savetxt(f"option_{chosen_greek['title']}.csv", np.column_stack((self.y_grid, greek_data)), delimiter=',', header=f"StockPrice,{chosen_greek['title']}", comments='')
126131
plt.show()
127132

128133
def _set_plot_labels(self, ax):

pdesolvers/solvers/black_scholes_solvers.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pdesolvers.pdes.black_scholes as bse
44
import pdesolvers.enums.enums as enum
55
import pdesolvers.utils.utility as utility
6+
import time
67

78
from scipy import sparse
89
from scipy.sparse.linalg import spsolve
@@ -19,6 +20,8 @@ def solve(self):
1920
:return: the solver instance with the computed option values
2021
"""
2122

23+
start = time.perf_counter()
24+
2225
S = self.equation.generate_grid(self.equation.S_max, self.equation.s_nodes)
2326
T = self.equation.generate_grid(self.equation.expiry, self.equation.t_nodes)
2427

@@ -64,7 +67,10 @@ def solve(self):
6467

6568
delta, gamma, theta = utility.BlackScholesHelper.calculate_greeks_at_boundary(self.equation, delta, gamma, theta, tau, V, S, ds)
6669

67-
return sol.SolutionBlackScholes(V, T, S, dt, ds, delta, gamma, theta, self.equation.option_type)
70+
end = time.perf_counter()
71+
duration = end - start
72+
73+
return sol.SolutionBlackScholes(V, T, S, dt, ds, duration, delta, gamma, theta, self.equation.option_type)
6874

6975

7076
class BlackScholesCNSolver:
@@ -79,6 +85,7 @@ def solve(self):
7985
:return: the solver instance with the computed option values
8086
"""
8187

88+
start = time.perf_counter()
8289
S = self.equation.generate_grid(self.equation.S_max, self.equation.s_nodes)
8390
T = self.equation.generate_grid(self.equation.expiry, self.equation.t_nodes)
8491

@@ -130,5 +137,8 @@ def solve(self):
130137

131138
delta, gamma, theta = utility.BlackScholesHelper.calculate_greeks_at_boundary(self.equation, delta, gamma, theta, tau, V, S, ds)
132139

133-
return sol.SolutionBlackScholes(V, T, S, dt, ds, delta, gamma, theta, self.equation.option_type)
140+
end = time.perf_counter()
141+
duration = end - start
142+
143+
return sol.SolutionBlackScholes(V, T, S, dt, ds, duration, delta, gamma, theta, self.equation.option_type)
134144

pdesolvers/solvers/heat_solvers.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import time
2+
13
import numpy as np
24
import pdesolvers.solution as sol
35
import pdesolvers.pdes.heat_1d as heat
@@ -16,6 +18,8 @@ def solve(self):
1618
:return: the solver instance with the computed temperature values
1719
"""
1820

21+
start = time.perf_counter()
22+
1923
x = self.equation.generate_grid(self.equation.length, self.equation.x_nodes)
2024
t = self.equation.generate_grid(self.equation.time, self.equation.t_nodes)
2125

@@ -42,7 +46,10 @@ def solve(self):
4246
for i in range(1, self.equation.x_nodes - 1):
4347
u[tau+1,i] = u[tau, i] + (dt * self.equation.k * (u[tau, i-1] - 2 * u[tau, i] + u[tau, i+1]) / dx**2)
4448

45-
return sol.Solution1D(u, x, t, dx, dt)
49+
end = time.perf_counter()
50+
duration = end - start
51+
52+
return sol.Solution1D(u, x, t, dx, dt, duration)
4653

4754
class Heat1DCNSolver:
4855
def __init__(self, equation: heat.HeatEquation):
@@ -55,6 +62,8 @@ def solve(self):
5562
:return: the solver instance with the computed temperature values
5663
"""
5764

65+
start = time.perf_counter()
66+
5867
x = self.equation.generate_grid(self.equation.length, self.equation.x_nodes)
5968
t = self.equation.generate_grid(self.equation.time, self.equation.t_nodes)
6069

@@ -85,4 +94,7 @@ def solve(self):
8594

8695
u[tau+1, 1:-1] = spsolve(lhs, rhs)
8796

88-
return sol.Solution1D(u, x, t, dx, dt)
97+
end = time.perf_counter()
98+
duration = end - start
99+
100+
return sol.Solution1D(u, x, t, dx, dt, duration)

playground/geometric_brownian_motion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def plot(self):
8282

8383
fig = plt.figure(figsize=(10,6))
8484
for i in range(self.__sim):
85-
plt.plot(t, S[i])
85+
plt.plot(t, S[i], color='grey', alpha=0.3)
8686

8787
plt.title("Simulated Geometric Brownian Motion")
8888
plt.xlabel("Time (Years)")
@@ -91,7 +91,7 @@ def plot(self):
9191

9292

9393
def main():
94-
GeometricBrownianMotion(100, 0.05, 0.03, 1, 365, 100).simulate_gbm().plot()
94+
GeometricBrownianMotion(100, 2, 0.3, 1, 365, 100).simulate_gbm().plot()
9595

9696
if __name__ == "__main__":
9797
main()

0 commit comments

Comments
 (0)