Refactor prediction menu to controller, and fbprophet optional

This commit is contained in:
didier
2021-04-12 23:13:03 +01:00
parent e6156d448d
commit b5b7832020
4 changed files with 213 additions and 148 deletions

View File

@@ -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"))

View File

@@ -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

View File

@@ -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!")

View File

@@ -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: