-
Notifications
You must be signed in to change notification settings - Fork 5
/
LeastSquaresMonteCarlo.py
130 lines (102 loc) · 3.58 KB
/
LeastSquaresMonteCarlo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# import necessary libraries
import math
import random
import numpy as np
import matplotlib.pyplot as plt
# Class for up-and-out American-style Asian puts option
class LSMC:
def __init__(
self,
type,
spot,
strike,
time,
interest,
dividend,
volatility,
period,
simulations,
degree=2,
):
self.type = type
self.spot_price = spot
self.strike_price = strike
self.time = time
self.interest_rate = interest
self.dividend_rate = dividend
self.volatility_rate = volatility
self.period = int(period)
self.path = int(simulations)
self.deltaT = time / period
self.discount = -self.interest_rate * self.deltaT
self.degree = degree
# Generate random path
def random_path(self):
# Randomized the path of Brownian Motions
self.randomWalk = np.zeros((self.path, self.period + 1))
# Initialize price
self.randomWalk[:, 0] = self.spot_price
# Random Walk each period. The price follow brownian motion
for i in range(1, self.period + 1):
self.randomWalk[:, i] = self.randomWalk[:, i - 1] * np.exp(
(
self.interest_rate
- self.dividend_rate
- (self.volatility_rate ** 2) / 2
)
* self.deltaT
+ self.volatility_rate
* np.sqrt(self.deltaT)
* np.random.normal(size=self.path)
)
# Apply least square method on all path
def pricing(self):
# Payoff for each period of each paths
if type == "p":
self.payoff = np.maximum(0, self.strike_price - self.randomWalk)
else:
self.payoff = np.maximum(0, self.randomWalk - self.strike_price)
Y = self.randomWalk[:, -1]
for i in range(self.period - 2, -1, -1):
# Calculate discount
Y *= np.exp(self.discount)
# Find valid path
hold = np.where(self.payoff[:, i] > 0)
if len(hold) > self.degree:
# Apply Least square method
regression = np.polyfit(self.randomWalk[hold, i], Y[hold], self.degree)
CV = np.polyval(regression, self.randomWalk[hold, i])
# Whether to exercise now
Y[hold] = np.where(
self.payoff[hold, i] > CV, self.payoff[hold, i], Y[hold]
)
# Calculate mean and variance of the stimulation
self.Price = np.mean(Y)
self.std = np.std(Y) / np.sqrt(self.path)
self.rmsre = np.sqrt(np.mean(((Y - self.Price) / self.Price) ** 2))
# Plot the distibution
def plot(self, bin=50, sampling=50):
# draw histograms
f1 = plt.figure(1)
n, x, _ = plt.hist(self.randomWalk[:, -1], bins=bin)
x_bar = 0.5 * (x[1:] + x[:-1])
plt.title("Distribution")
plt.xlabel("Stock Price")
plt.ylabel("Occurance")
plt.figtext(
0.01,
0.01,
f"estimated price: {round(self.Price, 4)}\nstandard deviation: {round(self.std, 4)}",
fontsize=8,
)
# draw the fitting curve
plt.plot(x_bar, n)
# draw sampled path
f2 = plt.figure(2)
x = np.arange(0, self.period)
for path in random.sample(range(self.path), sampling):
plt.plot(x, self.randomWalk[path, :-1])
plt.title("Sampled Path")
plt.xlabel("Period")
plt.ylabel("Price")
plt.show()