From 1c653192bd195225c7af564b74432f5c3a653a33 Mon Sep 17 00:00:00 2001 From: Apurv Date: Fri, 7 Aug 2020 15:20:05 +0300 Subject: [PATCH 1/6] Prediction mechanism --- predictions_FB.py | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 predictions_FB.py diff --git a/predictions_FB.py b/predictions_FB.py new file mode 100644 index 0000000..ff54265 --- /dev/null +++ b/predictions_FB.py @@ -0,0 +1,83 @@ +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +from fbprophet import Prophet +import yfinance as yf +import math +import os +import sys +import pandas as pd +pd.options.mode.chained_assignment = None + + +class suppress_stdout_stderr(object): + ''' + Used to supress the verbose output of the inner workings of the FB prophet model + ''' + def __init__(self): + self.null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)] + self.save_fds = (os.dup(1), os.dup(2)) + + def __enter__(self): + os.dup2(self.null_fds[0], 1) + os.dup2(self.null_fds[1], 2) + + def __exit__(self, *_): + os.dup2(self.save_fds[0], 1) + os.dup2(self.save_fds[1], 2) + os.close(self.null_fds[0]) + os.close(self.null_fds[1]) + +def fb_predict(symbol, dfi, plot = False, days =1): + ''' + + ''' + df = dfi.copy() + arr = [i for i in range(len(df))] + ar = df.index + df.index= arr + df['ds'] = ar + df = df[['ds', 'Close']] + df.rename(columns = {"Close":'y'}, inplace = True) + + train_length = math.floor(0.1*len(df)) + train = df[:-train_length] + test = df[-train_length:] + m = Prophet(daily_seasonality = True) + with suppress_stdout_stderr(): + m.fit(train) + future = m.make_future_dataframe(periods=train_length) + prediction = m.predict(future) + test_predict = prediction[-train_length:] + test['predict'] = test_predict['trend'] + test['error'] =((test['y'] - test['predict'])/test['y'])*100 + mean_err = round(test['error'].mean(),3) + mn = Prophet(daily_seasonality = True) + with suppress_stdout_stderr(): + mn.fit(df) + future = mn.make_future_dataframe(periods=days) + prediction = mn.predict(future) + pred = pd.DataFrame(prediction[-days:][['ds','trend', 'yhat_upper', 'yhat_lower']]) + pred.rename(columns = {'ds' : 'Date', 'trend':'Guess', 'yhat_lower':'Lower Bound', 'yhat_upper':'Upper Bound'}, inplace = True) + pred.set_index(np.arange(1,len(pred)+1), inplace = True) + if plot: + m.plot(prediction) + plt.title("Prediction of the AZPN Stock Price using the Prophet") + plt.xlabel("Date") + plt.ylabel("Close Stock Price") + plt.show() + return mean_err, pred + +symbol = "AZPN" +days = 5 +stock = yf.Ticker(symbol) +df = stock.history(period="max") +df = df[['Open', 'High', 'Low', 'Close', 'Volume']] +me , pred= fb_predict(symbol, df, days = days) +print("The prediction after {} days is :\n".format(days)) +print(pred) +print("\n Mean Percentage Error : ", me) + + + + From 510aeefbe973c6d7a1d95b73a255d72886764e0f Mon Sep 17 00:00:00 2001 From: Apurv Date: Fri, 7 Aug 2020 15:27:25 +0300 Subject: [PATCH 2/6] Documented the functions and changed ourput style --- predictions_FB.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/predictions_FB.py b/predictions_FB.py index ff54265..4d51fef 100644 --- a/predictions_FB.py +++ b/predictions_FB.py @@ -28,8 +28,16 @@ def __exit__(self, *_): os.close(self.null_fds[0]) os.close(self.null_fds[1]) -def fb_predict(symbol, dfi, plot = False, days =1): +def fb_predict(dfi, plot = False, days =1): ''' + This function takes in: + dfi - A dataframe with just the column of close price named as Close and date as index + plot - Whether you want to plot the resulyts or not( Default is False) + days - Number of days into the future you want to predict (Dafault is 1) + + This function returns: + mean_err - The mean percentage error in calculations + pred - A pandas datafrmae with predictions, upper and lower bounds ''' df = dfi.copy() @@ -72,8 +80,8 @@ def fb_predict(symbol, dfi, plot = False, days =1): days = 5 stock = yf.Ticker(symbol) df = stock.history(period="max") -df = df[['Open', 'High', 'Low', 'Close', 'Volume']] -me , pred= fb_predict(symbol, df, days = days) +df = df[['Close']] +me , pred= fb_predict(df, days = days) print("The prediction after {} days is :\n".format(days)) print(pred) print("\n Mean Percentage Error : ", me) From c73590c8c637d0ec2b4b6eaa6517ce271df84bef Mon Sep 17 00:00:00 2001 From: Apurv Date: Fri, 7 Aug 2020 15:33:32 +0300 Subject: [PATCH 3/6] Commented the sample run --- predictions_FB.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/predictions_FB.py b/predictions_FB.py index 4d51fef..4df6153 100644 --- a/predictions_FB.py +++ b/predictions_FB.py @@ -76,15 +76,16 @@ def fb_predict(dfi, plot = False, days =1): plt.show() return mean_err, pred -symbol = "AZPN" -days = 5 -stock = yf.Ticker(symbol) -df = stock.history(period="max") -df = df[['Close']] -me , pred= fb_predict(df, days = days) -print("The prediction after {} days is :\n".format(days)) -print(pred) -print("\n Mean Percentage Error : ", me) +#Sample Run +# symbol = "AZPN" +# days = 5 +# stock = yf.Ticker(symbol) +# df = stock.history(period="max") +# df = df[['Close']] +# me , pred= fb_predict(df, days = days) +# print("\n The prediction for {} after {} days is :\n".format(symbol, days)) +# print(pred) +# print("\n Mean Percentage Error : ", me) From a963b3e5db02e00d225d4cdfcdfd9e56b18f4882 Mon Sep 17 00:00:00 2001 From: Apurv Date: Sun, 9 Aug 2020 00:23:59 +0300 Subject: [PATCH 4/6] Fixed documentation and removed extra code --- predictions_FB.py | 52 +++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/predictions_FB.py b/predictions_FB.py index 4df6153..7463be5 100644 --- a/predictions_FB.py +++ b/predictions_FB.py @@ -6,8 +6,8 @@ import math import os import sys -import pandas as pd -pd.options.mode.chained_assignment = None +from sklearn.metrics import mean_squared_error + class suppress_stdout_stderr(object): @@ -28,18 +28,27 @@ def __exit__(self, *_): os.close(self.null_fds[0]) os.close(self.null_fds[1]) -def fb_predict(dfi, plot = False, days =1): +def fb_predict(dfi, plot = False, days =1, verbose = True): ''' This function takes in: - dfi - A dataframe with just the column of close price named as Close and date as index - plot - Whether you want to plot the resulyts or not( Default is False) - days - Number of days into the future you want to predict (Dafault is 1) + :param dfi - A dataframe with just the column of close price named as Close and date as index + :param plot - Whether you want to plot the resulyts or not( Default is False) + :param days - Number of days into the future you want to predict (Dafault is 1) + :param verbose - If you want the output of the model workings - This function returns: - mean_err - The mean percentage error in calculations - pred - A pandas datafrmae with predictions, upper and lower bounds + :type dfi - pandas.dataframe + :type plot - Boolean Value + :type days - Integer + :type verbose - Boolean Value + :return mean_err - The mean percentage error in calculations + :return pred - A pandas datafrmae with predictions, upper and lower bounds + + :rtype mean_err - float + :rtype pred - pandas.dataframe ''' + + pd.options.mode.chained_assignment = None df = dfi.copy() arr = [i for i in range(len(df))] ar = df.index @@ -60,9 +69,12 @@ def fb_predict(dfi, plot = False, days =1): test['predict'] = test_predict['trend'] test['error'] =((test['y'] - test['predict'])/test['y'])*100 mean_err = round(test['error'].mean(),3) + err = round(math.sqrt(mean_squared_error(test['y'].values,test['predict'].values)), 3) + print(err) mn = Prophet(daily_seasonality = True) - with suppress_stdout_stderr(): - mn.fit(df) + if not verbose: + with suppress_stdout_stderr(): + mn.fit(df) future = mn.make_future_dataframe(periods=days) prediction = mn.predict(future) pred = pd.DataFrame(prediction[-days:][['ds','trend', 'yhat_upper', 'yhat_lower']]) @@ -77,15 +89,15 @@ def fb_predict(dfi, plot = False, days =1): return mean_err, pred #Sample Run -# symbol = "AZPN" -# days = 5 -# stock = yf.Ticker(symbol) -# df = stock.history(period="max") -# df = df[['Close']] -# me , pred= fb_predict(df, days = days) -# print("\n The prediction for {} after {} days is :\n".format(symbol, days)) -# print(pred) -# print("\n Mean Percentage Error : ", me) +symbol = "AZPN" +days = 5 +stock = yf.Ticker(symbol) +df = stock.history(period="max") +df = df[['Close']] +me , pred= fb_predict(df, days = days, verbose=False) +print("\n The prediction for {} after {} days is :\n".format(symbol, days)) +print(pred) +print("\n Mean Percentage Error : ", me) From 5b05bf1203de513198bed5be24e90ddab6625f6f Mon Sep 17 00:00:00 2001 From: Apurv Date: Sun, 9 Aug 2020 00:38:04 +0300 Subject: [PATCH 5/6] Removed dfi, Added the datatypes in function definition, added meaningful names --- predictions_FB.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/predictions_FB.py b/predictions_FB.py index 7463be5..8ed638d 100644 --- a/predictions_FB.py +++ b/predictions_FB.py @@ -9,7 +9,6 @@ from sklearn.metrics import mean_squared_error - class suppress_stdout_stderr(object): ''' Used to supress the verbose output of the inner workings of the FB prophet model @@ -28,9 +27,8 @@ def __exit__(self, *_): os.close(self.null_fds[0]) os.close(self.null_fds[1]) -def fb_predict(dfi, plot = False, days =1, verbose = True): +def fb_predict(df, plot: bool=False, days: int=1, verbose: bool=True): ''' - This function takes in: :param dfi - A dataframe with just the column of close price named as Close and date as index :param plot - Whether you want to plot the resulyts or not( Default is False) :param days - Number of days into the future you want to predict (Dafault is 1) @@ -47,9 +45,8 @@ def fb_predict(dfi, plot = False, days =1, verbose = True): :rtype mean_err - float :rtype pred - pandas.dataframe ''' - + pd.options.mode.chained_assignment = None - df = dfi.copy() arr = [i for i in range(len(df))] ar = df.index df.index= arr @@ -60,28 +57,28 @@ def fb_predict(dfi, plot = False, days =1, verbose = True): train_length = math.floor(0.1*len(df)) train = df[:-train_length] test = df[-train_length:] - m = Prophet(daily_seasonality = True) + error_check = Prophet(daily_seasonality = True) with suppress_stdout_stderr(): - m.fit(train) - future = m.make_future_dataframe(periods=train_length) - prediction = m.predict(future) + error_check.fit(train) + future = error_check.make_future_dataframe(periods=train_length) + prediction = error_check.predict(future) test_predict = prediction[-train_length:] test['predict'] = test_predict['trend'] test['error'] =((test['y'] - test['predict'])/test['y'])*100 mean_err = round(test['error'].mean(),3) err = round(math.sqrt(mean_squared_error(test['y'].values,test['predict'].values)), 3) print(err) - mn = Prophet(daily_seasonality = True) + predictions = Prophet(daily_seasonality = True) if not verbose: with suppress_stdout_stderr(): - mn.fit(df) - future = mn.make_future_dataframe(periods=days) - prediction = mn.predict(future) + predictions.fit(df) + future = predictions.make_future_dataframe(periods=days) + prediction = predictions.predict(future) pred = pd.DataFrame(prediction[-days:][['ds','trend', 'yhat_upper', 'yhat_lower']]) pred.rename(columns = {'ds' : 'Date', 'trend':'Guess', 'yhat_lower':'Lower Bound', 'yhat_upper':'Upper Bound'}, inplace = True) pred.set_index(np.arange(1,len(pred)+1), inplace = True) if plot: - m.plot(prediction) + predictions.plot(prediction) plt.title("Prediction of the AZPN Stock Price using the Prophet") plt.xlabel("Date") plt.ylabel("Close Stock Price") @@ -95,9 +92,9 @@ def fb_predict(dfi, plot = False, days =1, verbose = True): df = stock.history(period="max") df = df[['Close']] me , pred= fb_predict(df, days = days, verbose=False) -print("\n The prediction for {} after {} days is :\n".format(symbol, days)) +print("\nThe prediction for {} after {} days is :\n".format(symbol, days)) print(pred) -print("\n Mean Percentage Error : ", me) +print("\nMean Percentage Error : ", me) From e196bc8e934249a9ce77283a8ad2e8109860d4c7 Mon Sep 17 00:00:00 2001 From: Apurv Date: Sun, 9 Aug 2020 13:16:49 +0300 Subject: [PATCH 6/6] Separated the test model for error using a boolean flag to prevent running it everytime. --- predictions_FB.py | 53 ++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/predictions_FB.py b/predictions_FB.py index 8ed638d..6a0c75f 100644 --- a/predictions_FB.py +++ b/predictions_FB.py @@ -27,7 +27,23 @@ def __exit__(self, *_): os.close(self.null_fds[0]) os.close(self.null_fds[1]) -def fb_predict(df, plot: bool=False, days: int=1, verbose: bool=True): +def fb_error(df): + train_length = math.floor(0.1*len(df)) + train = df[:-train_length] + test = df[-train_length:] + error_check = Prophet(daily_seasonality = True) + with suppress_stdout_stderr(): + error_check.fit(train) + future = error_check.make_future_dataframe(periods=train_length) + prediction = error_check.predict(future) + test_predict = prediction[-train_length:] + test['predict'] = test_predict['trend'] + test['error'] =((test['y'] - test['predict'])/test['y'])*100 + mean_err = round(test['error'].mean(),3) + return mean_err + + +def fb_predict(df, plot: bool=False, days: int=1, verbose: bool=True, error= False): ''' :param dfi - A dataframe with just the column of close price named as Close and date as index :param plot - Whether you want to plot the resulyts or not( Default is False) @@ -45,7 +61,6 @@ def fb_predict(df, plot: bool=False, days: int=1, verbose: bool=True): :rtype mean_err - float :rtype pred - pandas.dataframe ''' - pd.options.mode.chained_assignment = None arr = [i for i in range(len(df))] ar = df.index @@ -54,20 +69,9 @@ def fb_predict(df, plot: bool=False, days: int=1, verbose: bool=True): df = df[['ds', 'Close']] df.rename(columns = {"Close":'y'}, inplace = True) - train_length = math.floor(0.1*len(df)) - train = df[:-train_length] - test = df[-train_length:] - error_check = Prophet(daily_seasonality = True) - with suppress_stdout_stderr(): - error_check.fit(train) - future = error_check.make_future_dataframe(periods=train_length) - prediction = error_check.predict(future) - test_predict = prediction[-train_length:] - test['predict'] = test_predict['trend'] - test['error'] =((test['y'] - test['predict'])/test['y'])*100 - mean_err = round(test['error'].mean(),3) - err = round(math.sqrt(mean_squared_error(test['y'].values,test['predict'].values)), 3) - print(err) + if error: + mean_err = fb_error(df) + predictions = Prophet(daily_seasonality = True) if not verbose: with suppress_stdout_stderr(): @@ -83,18 +87,11 @@ def fb_predict(df, plot: bool=False, days: int=1, verbose: bool=True): plt.xlabel("Date") plt.ylabel("Close Stock Price") plt.show() - return mean_err, pred - -#Sample Run -symbol = "AZPN" -days = 5 -stock = yf.Ticker(symbol) -df = stock.history(period="max") -df = df[['Close']] -me , pred= fb_predict(df, days = days, verbose=False) -print("\nThe prediction for {} after {} days is :\n".format(symbol, days)) -print(pred) -print("\nMean Percentage Error : ", me) + if error: + return mean_err, pred + else: + return pred +