diff --git a/gamestonk_terminal/fundamental_analysis/fa_controller.py b/gamestonk_terminal/fundamental_analysis/fa_controller.py index c077295bedc..57ec653d7fa 100644 --- a/gamestonk_terminal/fundamental_analysis/fa_controller.py +++ b/gamestonk_terminal/fundamental_analysis/fa_controller.py @@ -8,7 +8,7 @@ from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal.fundamental_analysis import alpha_vantage_controller as avc from gamestonk_terminal.fundamental_analysis import business_insider_view as biw from gamestonk_terminal.fundamental_analysis import ( - financial_modeling_prep_api as fmp_api, + financial_modeling_prep_controller as fmpc, ) from gamestonk_terminal.fundamental_analysis import finviz_api as fvz_api from gamestonk_terminal.fundamental_analysis import market_watch_api as mw_api @@ -176,7 +176,7 @@ class FundamentalAnalysisController: # pylint: disable=unused-argument def call_fmp(self, other_args: List[str]): """ Process fmp command """ - ret = fmp_api.menu(self.ticker, self.start, self.interval) + ret = fmpc.menu(self.ticker, self.start, self.interval) if ret is not True: self.print_help() diff --git a/gamestonk_terminal/fundamental_analysis/financial_modeling_prep_controller.py b/gamestonk_terminal/fundamental_analysis/financial_modeling_prep_controller.py new file mode 100644 index 00000000000..e68077aa0f9 --- /dev/null +++ b/gamestonk_terminal/fundamental_analysis/financial_modeling_prep_controller.py @@ -0,0 +1,194 @@ +""" Financial Modeling Prep Controller """ +__docformat__ = "numpy" + +import argparse +from typing import List +from prompt_toolkit.completion import NestedCompleter + +from gamestonk_terminal.fundamental_analysis import financial_modeling_prep_view as fmpv +from gamestonk_terminal import feature_flags as gtff +from gamestonk_terminal.helper_funcs import ( + get_flair, +) +from gamestonk_terminal.menu import session + + +class FinancialModelingPrepController: + """ Financial Modeling Prep Controller """ + + # Command choices + CHOICES = [ + "help", + "q", + "quit", + "profile", + "quote", + "enterprise", + "dcf", + "income", + "balance", + "cash", + "metrics", + "ratios", + "growth", + ] + + def __init__(self, ticker: str, start: str, interval: str): + """Constructor + + Parameters + ---------- + ticker : str + Fundamental analysis ticker symbol + start : str + Stat date of the stock data + interval : str + Stock data interval + """ + + self.ticker = ticker + self.start = start + self.interval = interval + self.fmp_parser = argparse.ArgumentParser(add_help=False, prog="fmp") + self.fmp_parser.add_argument( + "cmd", + choices=self.CHOICES, + ) + + def print_help(self): + """ Print help """ + + intraday = (f"Intraday {self.interval}", "Daily")[self.interval == "1440min"] + + if self.start: + print( + f"\n{intraday} Stock: {self.ticker} (from {self.start.strftime('%Y-%m-%d')})" + ) + else: + print(f"\n{intraday} Stock: {self.ticker}") + + print("\nFinancial Modeling Prep API") + print(" help show this financial modeling prep menu again") + print(" q quit this menu, and shows back to main menu") + print(" quit quit to abandon program") + print("") + print(" profile profile of the company") + print(" quote quote of the company") + print(" enterprise enterprise value of the company over time") + print(" dcf discounted cash flow of the company over time") + print(" income income statements of the company") + print(" balance balance sheet of the company") + print(" cash cash flow statement of the company") + print(" metrics key metrics of the company") + print(" ratios financial ratios of the company") + print(" growth financial statement growth of the company") + 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.fmp_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() + + 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_profile(self, other_args: List[str]): + """ Process profile command """ + fmpv.profile(other_args, self.ticker) + + def call_quote(self, other_args: List[str]): + """ Process quote command """ + fmpv.quote(other_args, self.ticker) + + def call_enterprise(self, other_args: List[str]): + """ Process income command """ + fmpv.enterprise(other_args, self.ticker) + + def call_dcf(self, other_args: List[str]): + """ Process dcf command """ + fmpv.discounted_cash_flow(other_args, self.ticker) + + def call_income(self, other_args: List[str]): + """ Process income command """ + fmpv.income_statement(other_args, self.ticker) + + def call_balance(self, other_args: List[str]): + """ Process balance command """ + fmpv.balance_sheet(other_args, self.ticker) + + def call_cash(self, other_args: List[str]): + """ Process cash command """ + fmpv.cash_flow(other_args, self.ticker) + + def call_metrics(self, other_args: List[str]): + """ Process metrics command """ + fmpv.key_metrics(other_args, self.ticker) + + def call_ratios(self, other_args: List[str]): + """ Process cash command """ + fmpv.financial_ratios(other_args, self.ticker) + + def call_growth(self, other_args: List[str]): + """ Process cash command """ + fmpv.financial_statement_growth(other_args, self.ticker) + + +def menu(ticker: str, start: str, interval: str): + """Financial Modeling Prep menu + + Parameters + ---------- + ticker : str + Fundamental analysis ticker symbol + start : str + Start date of the stock data + interval : str + Stock data interval + """ + + fmp_controller = FinancialModelingPrepController(ticker, start, interval) + fmp_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 fmp_controller.CHOICES} + ) + + an_input = session.prompt( + f"{get_flair()} (fa)>(fmp)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (fa)>(fmp)> ") + + try: + process_input = fmp_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/fundamental_analysis/financial_modeling_prep_api.py b/gamestonk_terminal/fundamental_analysis/financial_modeling_prep_view.py old mode 100644 new mode 100755 similarity index 77% rename from gamestonk_terminal/fundamental_analysis/financial_modeling_prep_api.py rename to gamestonk_terminal/fundamental_analysis/financial_modeling_prep_view.py index 7189fce3fd5..68e22a86281 --- a/gamestonk_terminal/fundamental_analysis/financial_modeling_prep_api.py +++ b/gamestonk_terminal/fundamental_analysis/financial_modeling_prep_view.py @@ -1,139 +1,31 @@ +""" Financial Modeling Prep View """ +__docformat__ = "numpy" + import argparse +from typing import List from datetime import datetime import pandas as pd import FundamentalAnalysis as fa # Financial Modeling Prep -from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal import config_terminal as cfg from gamestonk_terminal.dataframe_helpers import clean_df_index from gamestonk_terminal.helper_funcs import ( long_number_format, check_positive, parse_known_args_and_warn, - get_flair, ) -from gamestonk_terminal.menu import session -def print_menu(s_ticker, s_start, s_interval): - """ Print help """ +def profile(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker profile - s_intraday = (f"Intraday {s_interval}", "Daily")[s_interval == "1440min"] + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ - 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("\nFinancial Modeling Prep API") - print(" help show this financial modeling prep menu again") - print(" q quit this menu, and shows back to main menu") - print(" quit quit to abandon program") - print("") - print(" profile profile of the company") - print(" quote quote of the company") - print(" enterprise enterprise value of the company over time") - print(" dcf discounted cash flow of the company over time") - print(" income income statements of the company") - print(" balance balance sheet of the company") - print(" cash cash flow statement of the company") - print(" metrics key metrics of the company") - print(" ratios financial ratios of the company") - print(" growth financial statement growth of the company") - print("") - - -def menu(s_ticker, s_start, s_interval): - - # Add list of arguments that the fundamental analysis parser accepts - fmp_parser = argparse.ArgumentParser(prog="fmp", add_help=False) - choices = [ - "help", - "q", - "quit", - "profile", - "quote", - "enterprise", - "dcf", - "fmp_income", - "fmp_balance", - "fmp_cash", - "metrics", - "ratios", - "growth", - ] - fmp_parser.add_argument("cmd", choices=choices) - completer = NestedCompleter.from_nested_dict({c: None for c in choices}) - - print_menu(s_ticker, s_start, s_interval) - - # Loop forever and ever - while True: - # Get input command from user - if session: - as_input = session.prompt( - f"{get_flair()} (fa)>(fmp)> ", - completer=completer, - ) - else: - as_input = input(f"{get_flair()} (fa)>(av)> ") - - # Parse alpha vantage command of the list of possible commands - try: - (ns_known_args, l_args) = fmp_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_menu(s_ticker, s_start, s_interval) - - elif ns_known_args.cmd == "q": - # Just leave the menu - return False - - elif ns_known_args.cmd == "quit": - # Abandon the program - return True - - # Details: - elif ns_known_args.cmd == "profile": - profile(l_args, s_ticker) - - elif ns_known_args.cmd == "quote": - quote(l_args, s_ticker) - - elif ns_known_args.cmd == "enterprise": - enterprise(l_args, s_ticker) - - elif ns_known_args.cmd == "dcf": - discounted_cash_flow(l_args, s_ticker) - - # Financial statement: - elif ns_known_args.cmd == "income": - income_statement(l_args, s_ticker) - - elif ns_known_args.cmd == "balance": - balance_sheet(l_args, s_ticker) - - elif ns_known_args.cmd == "cash": - cash_flow(l_args, s_ticker) - - # Ratios: - elif ns_known_args.cmd == "metrics": - key_metrics(l_args, s_ticker) - - elif ns_known_args.cmd == "ratios": - financial_ratios(l_args, s_ticker) - - elif ns_known_args.cmd == "growth": - financial_statement_growth(l_args, s_ticker) - - else: - print("Command not recognized!") - - -def profile(l_args, s_ticker): parser = argparse.ArgumentParser( add_help=False, prog="profile", @@ -148,11 +40,11 @@ def profile(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return - df_fa = fa.profile(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) + df_fa = fa.profile(ticker, cfg.API_KEY_FINANCIALMODELINGPREP) clean_df_index(df_fa) print(df_fa.drop(index=["Description", "Image"]).to_string(header=False)) @@ -166,7 +58,17 @@ def profile(l_args, s_ticker): return -def quote(l_args, s_ticker): +def quote(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker quote + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="quote", @@ -181,11 +83,11 @@ def quote(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return - df_fa = fa.quote(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) + df_fa = fa.quote(ticker, cfg.API_KEY_FINANCIALMODELINGPREP) clean_df_index(df_fa) @@ -211,7 +113,17 @@ def quote(l_args, s_ticker): return -def enterprise(l_args, s_ticker): +def enterprise(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker enterprise + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="enterprise", @@ -242,7 +154,7 @@ def enterprise(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -253,10 +165,10 @@ def enterprise(l_args, s_ticker): if ns_parser.b_quarter: df_fa = fa.enterprise( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" + ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" ) else: - df_fa = fa.enterprise(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) + df_fa = fa.enterprise(ticker, cfg.API_KEY_FINANCIALMODELINGPREP) df_fa = clean_metrics_df(df_fa, num=ns_parser.n_num, mask=False) @@ -269,7 +181,17 @@ def enterprise(l_args, s_ticker): return -def discounted_cash_flow(l_args, s_ticker): +def discounted_cash_flow(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker discounted cash flow + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="dcf", @@ -298,7 +220,7 @@ def discounted_cash_flow(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -309,10 +231,10 @@ def discounted_cash_flow(l_args, s_ticker): if ns_parser.b_quarter: df_fa = fa.discounted_cash_flow( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" + ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" ) else: - df_fa = fa.discounted_cash_flow(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) + df_fa = fa.discounted_cash_flow(ticker, cfg.API_KEY_FINANCIALMODELINGPREP) df_fa = clean_metrics_df(df_fa, num=ns_parser.n_num, mask=False) @@ -325,7 +247,17 @@ def discounted_cash_flow(l_args, s_ticker): return -def income_statement(l_args, s_ticker): +def income_statement(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker income statement + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="inc", @@ -361,7 +293,7 @@ def income_statement(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -372,10 +304,10 @@ def income_statement(l_args, s_ticker): if ns_parser.b_quarter: df_fa = fa.income_statement( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" + ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" ) else: - df_fa = fa.income_statement(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) + df_fa = fa.income_statement(ticker, cfg.API_KEY_FINANCIALMODELINGPREP) df_fa = clean_metrics_df(df_fa, num=ns_parser.n_num) @@ -394,7 +326,17 @@ def income_statement(l_args, s_ticker): return -def balance_sheet(l_args, s_ticker): +def balance_sheet(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker balance sheet + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="bal", @@ -435,7 +377,7 @@ def balance_sheet(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -446,11 +388,11 @@ def balance_sheet(l_args, s_ticker): if ns_parser.b_quarter: df_fa = fa.balance_sheet_statement( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" + ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" ) else: df_fa = fa.balance_sheet_statement( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP + ticker, cfg.API_KEY_FINANCIALMODELINGPREP ) df_fa = clean_metrics_df(df_fa, num=ns_parser.n_num) @@ -470,7 +412,17 @@ def balance_sheet(l_args, s_ticker): return -def cash_flow(l_args, s_ticker): +def cash_flow(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker cash flow + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="cash", @@ -509,7 +461,7 @@ def cash_flow(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -520,10 +472,10 @@ def cash_flow(l_args, s_ticker): if ns_parser.b_quarter: df_fa = fa.cash_flow_statement( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" + ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" ) else: - df_fa = fa.cash_flow_statement(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) + df_fa = fa.cash_flow_statement(ticker, cfg.API_KEY_FINANCIALMODELINGPREP) df_fa = clean_metrics_df(df_fa, num=ns_parser.n_num) @@ -542,7 +494,17 @@ def cash_flow(l_args, s_ticker): return -def key_metrics(l_args, s_ticker): +def key_metrics(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker key metrics + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="metrics", @@ -560,7 +522,7 @@ def key_metrics(l_args, s_ticker): Inventory turnover, Market cap, Net current asset value, Net debt to EBITDA, Net income per share, Operating cash flow per share, Payables turnover, Payout ratio, Pb ratio, Pe ratio, Pfcf ratio, Pocfratio, Price to sales ratio, Ptb ratio, Receivables turnover, - Research and ddevelopement to revenue, Return on tangible assets, Revenue per share, + Research and development to revenue, Return on tangible assets, Revenue per share, Roe, Roic, Sales general and administrative to revenue, Shareholders equity per share, Stock based compensation to revenue, Tangible book value per share, and Working capital. [Source: Financial Modeling Prep] @@ -586,7 +548,7 @@ def key_metrics(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -597,10 +559,10 @@ def key_metrics(l_args, s_ticker): if ns_parser.b_quarter: df_fa = fa.key_metrics( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" + ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" ) else: - df_fa = fa.key_metrics(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) + df_fa = fa.key_metrics(ticker, cfg.API_KEY_FINANCIALMODELINGPREP) df_fa = clean_metrics_df(df_fa, num=ns_parser.n_num) @@ -613,7 +575,17 @@ def key_metrics(l_args, s_ticker): return -def financial_ratios(l_args, s_ticker): +def financial_ratios(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker ratios + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="ratios", @@ -658,7 +630,7 @@ def financial_ratios(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -669,10 +641,10 @@ def financial_ratios(l_args, s_ticker): if ns_parser.b_quarter: df_fa = fa.financial_ratios( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" + ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" ) else: - df_fa = fa.financial_ratios(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) + df_fa = fa.financial_ratios(ticker, cfg.API_KEY_FINANCIALMODELINGPREP) df_fa = clean_metrics_df(df_fa, num=ns_parser.n_num) @@ -685,7 +657,17 @@ def financial_ratios(l_args, s_ticker): return -def financial_statement_growth(l_args, s_ticker): +def financial_statement_growth(other_args: List[str], ticker: str): + """Financial Modeling Prep ticker growth + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Fundamental analysis ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="growth", @@ -726,7 +708,7 @@ def financial_statement_growth(l_args, s_ticker): ) try: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -737,11 +719,11 @@ def financial_statement_growth(l_args, s_ticker): if ns_parser.b_quarter: df_fa = fa.financial_statement_growth( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" + ticker, cfg.API_KEY_FINANCIALMODELINGPREP, period="quarter" ) else: df_fa = fa.financial_statement_growth( - s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP + ticker, cfg.API_KEY_FINANCIALMODELINGPREP ) df_fa = clean_metrics_df(df_fa, num=ns_parser.n_num) @@ -756,6 +738,23 @@ def financial_statement_growth(l_args, s_ticker): def clean_metrics_df(df_fa: pd.DataFrame, num: int, mask: bool = True) -> pd.DataFrame: + """Clean metrics data frame + + Parameters + ---------- + df_fa : pd.DataFrame + Metrics data frame + num : int + Number of columns to clean + mask : bool, optional + Apply mask, by default True + + Returns + ------- + pd.DataFrame + Cleaned metrics data frame + """ + df_fa = df_fa.iloc[:, 0:num] if mask: df_fa = df_fa.mask(df_fa.astype(object).eq(num * ["None"])).dropna() diff --git a/terminal.py b/terminal.py index 2a54ea0287f..6f637eba85e 100644 --- a/terminal.py +++ b/terminal.py @@ -329,7 +329,7 @@ def main(): df_stock_pred, s_ticker.split(".")[0] if "." in s_ticker else s_ticker, s_start, - s_interval="1440min", + interval="1440min", ) except Exception as e: print(e)