diff --git a/gamestonk_terminal/feature_flags.py b/gamestonk_terminal/feature_flags.py index 3eaebcd2bb2..5c4910a1ab2 100644 --- a/gamestonk_terminal/feature_flags.py +++ b/gamestonk_terminal/feature_flags.py @@ -16,6 +16,9 @@ USE_PROMPT_TOOLKIT = strtobool(os.getenv("GTFF_USE_PROMPT_TOOLKIT", "False")) # Enable Prediction features ENABLE_PREDICT = strtobool(os.getenv("GTFF_ENABLE_PREDICT", "False")) +# Enable FB Prophet +ENABLE_FBPROPHET = strtobool(os.getenv("GTFF_ENABLE_FBPROPHET", "False")) + # Enable plot autoscaling USE_PLOT_AUTOSCALING = strtobool(os.getenv("GTFF_USE_PLOT_AUTOSCALING", "False")) diff --git a/gamestonk_terminal/prediction_techniques/pred_controller.py b/gamestonk_terminal/prediction_techniques/pred_controller.py new file mode 100644 index 00000000000..8d2bdb4b2be --- /dev/null +++ b/gamestonk_terminal/prediction_techniques/pred_controller.py @@ -0,0 +1,207 @@ +import argparse +from typing import List +import pandas as pd +from datetime import datetime +import matplotlib.pyplot as plt +from gamestonk_terminal import feature_flags as gtff +from gamestonk_terminal.helper_funcs import get_flair +from gamestonk_terminal.menu import session +from gamestonk_terminal.prediction_techniques import ( + arima, + ets, + knn, + neural_networks, + regression, + sma, +) +from prompt_toolkit.completion import NestedCompleter + + +class PredictionTechniquesController: + """Prediction Techniques Controller class""" + + # Command choices + CHOICES = [ + "help", + "q", + "quit", + "sma", + "ets", + "knn", + "linear", + "quadratic", + "cubic", + "regression", + "arima", + "mlp", + "rnn", + "lstm", + ] + + if gtff.ENABLE_FBPROPHET: + CHOICES.append("prophet") + + def __init__( + self, + stock: pd.DataFrame, + ticker: str, + start: datetime, + interval: str, + ): + """Constructor""" + self.stock = stock + self.ticker = ticker + self.start = start + self.interval = interval + self.pred_parser = argparse.ArgumentParser(add_help=False, prog="pred") + self.pred_parser.add_argument( + "cmd", + choices=self.CHOICES, + ) + + @staticmethod + def print_help(self): + """ Print help """ + + s_intraday = (f"Intraday {self.interval}", "Daily")[self.interval == "1440min"] + + if self.start: + print( + f"\n{s_intraday} Stock: {self.ticker} (from {self.start.strftime('%Y-%m-%d')})" + ) + else: + print(f"\n{s_intraday} Stock: {self.ticker}") + + print("\nPrediction Techniques:") + print(" help show this prediction techniques menu again") + print(" q quit this menu, and shows back to main menu") + print(" quit quit to abandon program") + print("") + print(" sma simple moving average") + print(" ets exponential smoothing (e.g. Holt-Winters)") + print(" knn k-Nearest Neighbors") + print(" linear linear regression (polynomial 1)") + print(" quadratic quadratic regression (polynomial 2)") + print(" cubic cubic regression (polynomial 3)") + print(" regression regression (other polynomial)") + print(" arima autoregressive integrated moving average") + print(" mlp MultiLayer Perceptron") + print(" rnn Recurrent Neural Network") + print(" lstm Long-Short Term Memory") + if gtff.ENABLE_FBPROPHET: + print(" prophet Facebook's prophet prediction") + print("") + + def switch(self, an_input: str): + """Process and dispatch input + + Returns + ------- + True, False or None + False - quit the menu + True - quit the program + None - continue in the menu + """ + (known_args, other_args) = self.pred_parser.parse_known_args(an_input.split()) + + return getattr( + self, "call_" + known_args.cmd, lambda: "Command not recognized!" + )(other_args) + + def call_help(self, _): + """Process Help command""" + self.print_help(self) + + def call_q(self, _): + """Process Q command - quit the menu""" + return False + + def call_quit(self, _): + """Process Quit command - quit the program""" + return True + + def call_sma(self, other_args: List[str]): + """Process sma command""" + sma.simple_moving_average(other_args, self.ticker, self.stock) + + def call_ets(self, other_args: List[str]): + """Process ets command""" + ets.exponential_smoothing(other_args, self.ticker, self.stock) + + def call_knn(self, other_args: List[str]): + """Process knn command""" + knn.k_nearest_neighbors(other_args, self.ticker, self.stock) + + def call_linear(self, other_args: List[str]): + """Process linear command""" + regression.regression(other_args, self.ticker, self.stock, regression.LINEAR) + + def call_quadratic(self, other_args: List[str]): + """Process quadratic command""" + regression.regression(other_args, self.ticker, self.stock, regression.QUADRATIC) + + def call_cubic(self, other_args: List[str]): + """Process cubic command""" + regression.regression(other_args, self.ticker, self.stock, regression.CUBIC) + + def call_regression(self, other_args: List[str]): + """Process regression command""" + regression.regression( + other_args, self.ticker, self.stock, regression.USER_INPUT + ) + + def call_arima(self, other_args: List[str]): + """Process arima command""" + arima.arima(other_args, self.ticker, self.stock) + + def call_mlp(self, other_args: List[str]): + """Process mlp command""" + neural_networks.mlp(other_args, self.ticker, self.stock) + + def call_rnn(self, other_args: List[str]): + """Process rnn command""" + neural_networks.rnn(other_args, self.ticker, self.stock) + + def call_lstm(self, other_args: List[str]): + """Process lstm command""" + neural_networks.lstm(other_args, self.ticker, self.stock) + + if gtff.ENABLE_FBPROPHET: + + def call_prophet(self, other_args: List[str]): + """Process prophet command""" + from gamestonk_terminal.prediction_techniques import fbprophet + + fbprophet.fbprophet(other_args, self.ticker, self.stock) + + +def menu(stock: pd.DataFrame, ticker: str, start: datetime, interval: str): + """Comparison Analysis Menu""" + + pred_controller = PredictionTechniquesController(stock, ticker, start, interval) + pred_controller.call_help(None) + + while True: + # Get input command from user + if session and gtff.USE_PROMPT_TOOLKIT: + completer = NestedCompleter.from_nested_dict( + {c: None for c in pred_controller.CHOICES} + ) + an_input = session.prompt( + f"{get_flair()} (pred)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (pred)> ") + + try: + plt.close("all") + + process_input = pred_controller.switch(an_input) + + if process_input is not None: + return process_input + + except SystemExit: + print("The command selected doesn't exist\n") + continue diff --git a/gamestonk_terminal/prediction_techniques/pred_menu.py b/gamestonk_terminal/prediction_techniques/pred_menu.py deleted file mode 100644 index 31460d87ae7..00000000000 --- a/gamestonk_terminal/prediction_techniques/pred_menu.py +++ /dev/null @@ -1,145 +0,0 @@ -import argparse - -import matplotlib.pyplot as plt -from gamestonk_terminal import feature_flags as gtff -from gamestonk_terminal.helper_funcs import get_flair -from gamestonk_terminal.menu import session -from gamestonk_terminal.prediction_techniques import ( - arima, - ets, - fbprophet, - knn, - neural_networks, - regression, - sma, -) -from prompt_toolkit.completion import NestedCompleter - - -def print_prediction(s_ticker, s_start, s_interval): - """ Print help """ - - s_intraday = (f"Intraday {s_interval}", "Daily")[s_interval == "1440min"] - - if s_start: - print(f"\n{s_intraday} Stock: {s_ticker} (from {s_start.strftime('%Y-%m-%d')})") - else: - print(f"\n{s_intraday} Stock: {s_ticker}") - - print("\nPrediction Techniques:") - print(" help show this prediction techniques menu again") - print(" q quit this menu, and shows back to main menu") - print(" quit quit to abandon program") - print("") - print(" sma simple moving average") - print(" ets exponential smoothing (e.g. Holt-Winters)") - print(" knn k-Nearest Neighbors") - print(" linear linear regression (polynomial 1)") - print(" quadratic quadratic regression (polynomial 2)") - print(" cubic cubic regression (polynomial 3)") - print(" regression regression (other polynomial)") - print(" arima autoregressive integrated moving average") - print(" prophet Facebook's prophet prediction") - print(" mlp MultiLayer Perceptron") - print(" rnn Recurrent Neural Network") - print(" lstm Long-Short Term Memory") - print("") - - -def pred_menu(df_stock, s_ticker, s_start, s_interval): - - # Add list of arguments that the prediction techniques parser accepts - pred_parser = argparse.ArgumentParser(prog="pred", add_help=False) - choices = [ - "help", - "q", - "quit", - "sma", - "ets", - "knn", - "linear", - "quadratic", - "cubic", - "regression", - "arima", - "prophet", - "mlp", - "rnn", - "lstm", - ] - pred_parser.add_argument("cmd", choices=choices) - completer = NestedCompleter.from_nested_dict({c: None for c in choices}) - - print_prediction(s_ticker, s_start, s_interval) - - # Loop forever and ever - while True: - # Get input command from user - if session and gtff.USE_PROMPT_TOOLKIT: - as_input = session.prompt( - f"{get_flair()} (pred)> ", - completer=completer, - ) - else: - as_input = input(f"{get_flair()} (pred)> ") - - # Images are non blocking - allows to close them if we type other command - plt.close("all") - - # Parse prediction techniques command of the list of possible commands - try: - (ns_known_args, l_args) = pred_parser.parse_known_args(as_input.split()) - - except SystemExit: - print("The command selected doesn't exist\n") - continue - - if ns_known_args.cmd == "help": - print_prediction(s_ticker, s_start, s_interval) - - elif ns_known_args.cmd == "q": - # Just leave the FA menu - return False - - elif ns_known_args.cmd == "quit": - # Abandon the program - return True - - elif ns_known_args.cmd == "sma": - sma.simple_moving_average(l_args, s_ticker, df_stock) - - elif ns_known_args.cmd == "ets": - ets.exponential_smoothing(l_args, s_ticker, df_stock) - - elif ns_known_args.cmd == "knn": - knn.k_nearest_neighbors(l_args, s_ticker, df_stock) - - elif ns_known_args.cmd == "linear": - regression.regression(l_args, s_ticker, df_stock, regression.LINEAR) - - elif ns_known_args.cmd == "quadratic": - regression.regression(l_args, s_ticker, df_stock, regression.QUADRATIC) - - elif ns_known_args.cmd == "cubic": - regression.regression(l_args, s_ticker, df_stock, regression.CUBIC) - - elif ns_known_args.cmd == "regression": - regression.regression(l_args, s_ticker, df_stock, regression.USER_INPUT) - - elif ns_known_args.cmd == "arima": - arima.arima(l_args, s_ticker, df_stock) - - elif ns_known_args.cmd == "prophet": - fbprophet.fbprophet(l_args, s_ticker, df_stock) - - elif ns_known_args.cmd == "mlp": - neural_networks.mlp(l_args, s_ticker, df_stock) - - elif ns_known_args.cmd == "rnn": - neural_networks.rnn(l_args, s_ticker, df_stock) - - elif ns_known_args.cmd == "lstm": - neural_networks.lstm(l_args, s_ticker, df_stock) - - else: - print("Command not recognized!") diff --git a/terminal.py b/terminal.py index 91fa02c2e94..bb332052050 100644 --- a/terminal.py +++ b/terminal.py @@ -252,7 +252,7 @@ def main(): try: # pylint: disable=import-outside-toplevel - from gamestonk_terminal.prediction_techniques import pred_menu as pm + from gamestonk_terminal.prediction_techniques import pred_controller except ModuleNotFoundError as e: print("One of the optional packages seems to be missing") print("Optional packages need to be installed") @@ -265,7 +265,7 @@ def main(): continue if s_interval == "1440min": - b_quit = pm.pred_menu(df_stock, s_ticker, s_start, s_interval) + b_quit = pred_controller.menu(df_stock, s_ticker, s_start, s_interval) # If stock data is intradaily, we need to get data again as prediction # techniques work on daily adjusted data. By default we load data from # Alpha Vantage because the historical data loaded gives a larger @@ -282,7 +282,7 @@ def main(): # pylint: disable=no-member df_stock_pred = df_stock_pred.sort_index(ascending=True) df_stock_pred = df_stock_pred[s_start:] - b_quit = pm.pred_menu( + b_quit = pred_controller.menu( df_stock_pred, s_ticker, s_start, s_interval="1440min" ) except Exception as e: