diff --git a/pdesolvers/optionspricing/market_data.py b/pdesolvers/optionspricing/market_data.py index b6b0b3a..e99e15c 100644 --- a/pdesolvers/optionspricing/market_data.py +++ b/pdesolvers/optionspricing/market_data.py @@ -21,8 +21,8 @@ def estimate_metrics(self): self.__stock_data["Time Diff"] = self.__stock_data.index.to_series().diff().dt.days self.__stock_data["Z"] = self.__stock_data["Log Returns"] / self.__stock_data["Time Diff"] - sigma = self.__stock_data["Log Returns"].std() - mu = self.__stock_data["Log Returns"].mean() + sigma = self.__stock_data["Log Returns"].std() * np.sqrt(252) + mu = self.__stock_data["Log Returns"].mean() * 252 return sigma, mu @@ -30,4 +30,16 @@ def get_latest_stock_price(self): if self.__stock_data is None: raise ValueError("No data available. Call fetch_data first.") - return self.__stock_data["Close"].iloc[-1].item() \ No newline at end of file + return self.__stock_data["Close"].iloc[-1].item() + + def get_initial_stock_price(self): + if self.__stock_data is None: + raise ValueError("No data available. Call fetch_data first.") + + return self.__stock_data["Close"].iloc[0].item() + + def get_closing_prices(self): + if self.__stock_data is None: + raise ValueError("No data available. Call fetch_data first.") + + return self.__stock_data["Close"].values \ No newline at end of file diff --git a/pdesolvers/optionspricing/monte_carlo.py b/pdesolvers/optionspricing/monte_carlo.py index bb1b0b7..749b2d8 100644 --- a/pdesolvers/optionspricing/monte_carlo.py +++ b/pdesolvers/optionspricing/monte_carlo.py @@ -14,7 +14,7 @@ class MonteCarloPricing: - def __init__(self, option_type: OptionType, S0, strike_price, r, sigma, T, time_steps, sim, seed=None): + def __init__(self, option_type: OptionType, S0, strike_price, mu, sigma, T, time_steps, sim, seed=None): """ Initialize the Geometric Brownian Motion model with the given parameters. @@ -30,7 +30,7 @@ def __init__(self, option_type: OptionType, S0, strike_price, r, sigma, T, time_ self.__option_type = option_type self.__S0 = S0 self.__strike_price = strike_price - self.__r = r + self.__mu = mu self.__sigma = sigma self.__T = T self.__time_steps = time_steps @@ -60,7 +60,7 @@ def get_monte_carlo_option_price(self): else: raise ValueError(f'Unsupported Option Type: {self.__option_type}') - option_price = np.exp(-self.__r * self.__T) * np.mean(self.__payoff) + option_price = np.exp(-self.__mu * self.__T) * np.mean(self.__payoff) logging.info(f"Option price calculated successfully with {self.__class__.__name__}.") @@ -95,7 +95,7 @@ def simulate_gbm(self): # updates brownian motion B[i,j] = B[i,j-1] + np.sqrt(dt) * Z[i,j-1] # calculates stock price based on the incremental difference - S[i,j] = S[i, j-1] * np.exp((self.__r - 0.5*self.__sigma**2)*dt + self.__sigma * (B[i, j] - B[i, j - 1])) + S[i,j] = S[i, j-1] * np.exp((self.__mu - 0.5*self.__sigma**2)*dt + self.__sigma * (B[i, j] - B[i, j - 1])) end = time.perf_counter() self.__duration = end - start @@ -150,7 +150,7 @@ def __generate_grid(self): return np.linspace(0, self.__T, self.__time_steps) - def plot_price_paths(self, export=False): + def plot_price_paths(self, closing_prices=None, export=False): """ Plot the simulated stock prices for all simulations. """ @@ -171,6 +171,12 @@ def plot_price_paths(self, export=False): plt.xlabel("Time (Years)") plt.ylabel("Stock Price") + if closing_prices is not None: + if len(closing_prices) != len(t): + raise ValueError("Length of closing prices does not match the number of time steps in the simulation.") + + plt.plot(t , closing_prices, color='red') + if export: plt.savefig("monte_carlo_prices.pdf", format="pdf", bbox_inches="tight") @@ -199,7 +205,6 @@ def plot_distribution_of_final_prices(self, export=False): if export: plt.savefig("monte_carlo_prices.pdf", format="pdf", bbox_inches="tight", pad_inches=0.2) - plt.show() def plot_distribution_of_payoff(self, export=False): diff --git a/pdesolvers/tests/test_options_pricing.py b/pdesolvers/tests/test_options_pricing.py index 09ad74e..99f2f8d 100644 --- a/pdesolvers/tests/test_options_pricing.py +++ b/pdesolvers/tests/test_options_pricing.py @@ -10,7 +10,7 @@ def mc_pricing_params(): return { 'S0': 100.0, 'strike_price': 100.0, - 'r': 0.05, + 'mu': 0.05, 'sigma': 0.2, 'T': 1.0, 'time_steps': 252, @@ -31,7 +31,7 @@ def test_get_european_call_option_price(self, mock_simulate_gbm, mc_pricing_para ) mock_payoffs = np.maximum(np.array([110, 120, 90, 105, 115]) - mc_pricing_params['strike_price'], 0) - expected_price = np.exp(-mc_pricing_params['r'] * mc_pricing_params['T']) * np.mean(mock_payoffs) + expected_price = np.exp(-mc_pricing_params['mu'] * mc_pricing_params['T']) * np.mean(mock_payoffs) actual_price = test_mc.get_monte_carlo_option_price() @@ -49,7 +49,7 @@ def test_get_european_put_option_price(self, mock_simulate_gbm, mc_pricing_param ) mock_payoffs = np.maximum(mc_pricing_params['strike_price'] - np.array([110, 120, 90, 105, 115]), 0) - expected_price = np.exp(-mc_pricing_params['r'] * mc_pricing_params['T']) * np.mean(mock_payoffs) + expected_price = np.exp(-mc_pricing_params['mu'] * mc_pricing_params['T']) * np.mean(mock_payoffs) actual_price = test_mc.get_monte_carlo_option_price()