diff --git a/gamestonk_terminal/due_diligence/business_insider_api.py b/gamestonk_terminal/due_diligence/business_insider_view.py similarity index 80% rename from gamestonk_terminal/due_diligence/business_insider_api.py rename to gamestonk_terminal/due_diligence/business_insider_view.py index 63ca11b3dc9..32a1995198c 100644 --- a/gamestonk_terminal/due_diligence/business_insider_api.py +++ b/gamestonk_terminal/due_diligence/business_insider_view.py @@ -1,6 +1,11 @@ +""" Business Insider View """ +__docformat__ = "numpy" + import argparse +from typing import List import json import re +from pandas.core.frame import DataFrame import requests import matplotlib.pyplot as plt import pandas as pd @@ -12,11 +17,30 @@ from gamestonk_terminal.helper_funcs import ( get_user_agent, parse_known_args_and_warn, ) +from gamestonk_terminal import feature_flags as gtff register_matplotlib_converters() -def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): +def price_target_from_analysts( + other_args: List[str], stock: DataFrame, ticker: str, start: str, interval: str +): + """Display analysts' price targets for a given stock + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "10"] + stock : DataFrame + Due diligence stock dataframe + ticker : str + Due diligence ticker symbol + start : str + Start date of the stock data + interval : str + Stock data interval + """ + parser = argparse.ArgumentParser( add_help=False, prog="pt", @@ -34,12 +58,12 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): ) 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 url_market_business_insider = ( - f"https://markets.businessinsider.com/stocks/{s_ticker.lower()}-stock" + f"https://markets.businessinsider.com/stocks/{ticker.lower()}-stock" ) text_soup_market_business_insider = BeautifulSoup( requests.get( @@ -59,8 +83,7 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): d_analyst_data = json.loads(s_analyst_data) break - # pprint.pprint(d_analyst_data) - df_analyst_data = pd.DataFrame.from_dict(d_analyst_data["Markers"]) + df_analyst_data = pd.DataFrame.from_dict(d_analyst_data["Markers"]) # type: ignore df_analyst_data = df_analyst_data[ ["DateLabel", "Company", "InternalRating", "PriceTarget"] ] @@ -73,17 +96,17 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): df_analyst_data = df_analyst_data.set_index("Date") # Slice start of ratings - if s_start: - df_analyst_data = df_analyst_data[s_start:] + if start: + df_analyst_data = df_analyst_data[start:] # type: ignore - if s_interval == "1440min": - plt.plot(df_stock.index, df_stock["5. adjusted close"].values, lw=3) + if interval == "1440min": + plt.plot(stock.index, stock["5. adjusted close"].values, lw=3) # Intraday else: - plt.plot(df_stock.index, df_stock["4. close"].values, lw=3) + plt.plot(stock.index, stock["4. close"].values, lw=3) - if s_start: - plt.plot(df_analyst_data.groupby(by=["Date"]).mean()[s_start:]) + if start: + plt.plot(df_analyst_data.groupby(by=["Date"]).mean()[start:]) # type: ignore else: plt.plot(df_analyst_data.groupby(by=["Date"]).mean()) @@ -91,13 +114,17 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): plt.legend(["Closing Price", "Average Price Target", "Price Target"]) - plt.title(f"{s_ticker} (Time Series) and Price Target") - plt.xlim(df_stock.index[0], df_stock.index[-1]) + plt.title(f"{ticker} (Time Series) and Price Target") + plt.xlim(stock.index[0], stock.index[-1]) plt.xlabel("Time") plt.ylabel("Share Price ($)") plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) + + if gtff.USE_ION: + plt.ion() + plt.show() print("") @@ -115,7 +142,17 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): return -def estimates(l_args, s_ticker): +def estimates(other_args: List[str], ticker: str): + """Display analysts' estimates for a given ticker + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Due diligence ticker symbol + """ + parser = argparse.ArgumentParser( add_help=False, prog="est", @@ -123,12 +160,12 @@ def estimates(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 url_market_business_insider = ( - f"https://markets.businessinsider.com/stocks/{s_ticker.lower()}-stock" + f"https://markets.businessinsider.com/stocks/{ticker.lower()}-stock" ) text_soup_market_business_insider = BeautifulSoup( requests.get( @@ -274,7 +311,24 @@ def estimates(l_args, s_ticker): return -def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval): +def insider_activity( + other_args: List[str], stock: DataFrame, ticker: str, start: str, interval: str +): + """Display insider activity + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "10"] + stock : DataFrame + Due diligence stock dataframe + ticker : str + Due diligence ticker symbol + start : str + Start date of the stock data + interval : str + Stock data interval + """ parser = argparse.ArgumentParser( add_help=False, prog="ins", @@ -291,12 +345,12 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval): ) 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 url_market_business_insider = ( - f"https://markets.businessinsider.com/stocks/{s_ticker.lower()}-stock" + f"https://markets.businessinsider.com/stocks/{ticker.lower()}-stock" ) text_soup_market_business_insider = BeautifulSoup( requests.get( @@ -334,17 +388,17 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval): df_insider = df_insider.set_index("Date") df_insider = df_insider.sort_index(ascending=True) - if s_start: - df_insider = df_insider[s_start:] + if start: + df_insider = df_insider[start:] # type: ignore _, ax = plt.subplots() - if s_interval == "1440min": - plt.plot(df_stock.index, df_stock["5. adjusted close"].values, lw=3) + if interval == "1440min": + plt.plot(stock.index, stock["5. adjusted close"].values, lw=3) else: # Intraday - plt.plot(df_stock.index, df_stock["4. close"].values, lw=3) + plt.plot(stock.index, stock["4. close"].values, lw=3) - plt.title(f"{s_ticker.upper()} (Time Series) and Price Target") + plt.title(f"{ticker.upper()} (Time Series) and Price Target") plt.xlabel("Time") plt.ylabel("Share Price ($)") @@ -354,7 +408,7 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval): * float(row["Shares Traded"].replace(",", "")), axis=1, ) - plt.xlim(df_insider.index[0], df_stock.index[-1]) + plt.xlim(df_insider.index[0], stock.index[-1]) min_price, max_price = ax.get_ylim() price_range = max_price - min_price @@ -373,16 +427,16 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval): for ind in ( df_insider[df_insider["Type"] == "Sell"].groupby(by=["Date"]).sum().index ): - if ind in df_stock.index: + if ind in stock.index: ind_dt = ind else: ind_dt = get_next_stock_market_days(ind, 1)[0] n_stock_price = 0 - if s_interval == "1440min": - n_stock_price = df_stock["5. adjusted close"][ind_dt] + if interval == "1440min": + n_stock_price = stock["5. adjusted close"][ind_dt] else: - n_stock_price = df_stock["4. close"][ind_dt] + n_stock_price = stock["4. close"][ind_dt] plt.vlines( x=ind_dt, @@ -402,16 +456,16 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval): for ind in ( df_insider[df_insider["Type"] == "Buy"].groupby(by=["Date"]).sum().index ): - if ind in df_stock.index: + if ind in stock.index: ind_dt = ind else: ind_dt = get_next_stock_market_days(ind, 1)[0] n_stock_price = 0 - if s_interval == "1440min": - n_stock_price = df_stock["5. adjusted close"][ind_dt] + if interval == "1440min": + n_stock_price = stock["5. adjusted close"][ind_dt] else: - n_stock_price = df_stock["4. close"][ind_dt] + n_stock_price = stock["4. close"][ind_dt] plt.vlines( x=ind_dt, @@ -431,6 +485,10 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval): plt.grid(b=True, which="major", color="#666666", linestyle="-") plt.minorticks_on() plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) + + if gtff.USE_ION: + plt.ion() + plt.show() l_names = list() diff --git a/gamestonk_terminal/due_diligence/dd_controller.py b/gamestonk_terminal/due_diligence/dd_controller.py new file mode 100644 index 00000000000..14ecab8fc40 --- /dev/null +++ b/gamestonk_terminal/due_diligence/dd_controller.py @@ -0,0 +1,219 @@ +""" Due Diligence Controller """ +__docformat__ = "numpy" + +import argparse +from typing import List +from pandas.core.frame import DataFrame +from prompt_toolkit.completion import NestedCompleter + +from gamestonk_terminal.due_diligence import business_insider_view as bi_view +from gamestonk_terminal.due_diligence import financial_modeling_prep_view as fmp_view +from gamestonk_terminal.due_diligence import finviz_view as fvz_view +from gamestonk_terminal.due_diligence import market_watch_view as mw_view +from gamestonk_terminal.due_diligence import quandl_view as q_view +from gamestonk_terminal.due_diligence import reddit_view as r_view +from gamestonk_terminal.due_diligence import news_view +from gamestonk_terminal import feature_flags as gtff +from gamestonk_terminal.helper_funcs import get_flair +from gamestonk_terminal.menu import session + + +class DueDiligenceController: + """ Due Diligence Controller """ + + # Command choices + CHOICES = [ + "help", + "q", + "quit", + "red", + "short", + "rating", + "pt", + "est", + "ins", + "insider", + "news", + "analyst", + "warnings", + "sec", + ] + + def __init__(self, stock: DataFrame, ticker: str, start: str, interval: str): + """Constructor + + Parameters + ---------- + stock : DataFrame + Due diligence stock dataframe + ticker : str + Due diligence ticker symbol + start : str + Start date of the stock data + interval : str + Stock data interval + """ + self.stock = stock + self.ticker = ticker + self.start = start + self.interval = interval + self.dd_parser = argparse.ArgumentParser(add_help=False, prog="dd") + self.dd_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("\nDue Diligence:") + print(" help show this fundamental analysis menu again") + print(" q quit this menu, and shows back to main menu") + print(" quit quit to abandon program") + print("") + print(" news latest news of the company [News API]") + print(" red gets due diligence from another user's post [Reddit]") + print(" analyst analyst prices and ratings of the company [Finviz]") + print( + " rating rating of the company from strong sell to strong buy [FMP]" + ) + print(" pt price targets over time [Business Insider]") + print( + " est quarter and year analysts earnings estimates [Business Insider]" + ) + print(" ins insider activity over time [Business Insider]") + print(" insider insider trading of the company [Finviz]") + print(" sec SEC filings [Market Watch]") + print(" short short interest [Quandl]") + print( + " warnings company warnings according to Sean Seah book [Market Watch]" + ) + 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.dd_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_red(self, other_args: List[str]): + """ Process red command """ + r_view.due_diligence(other_args, self.ticker) + + def call_insider(self, other_args: List[str]): + """ Process insider command """ + fvz_view.insider(other_args, self.ticker) + + def call_news(self, other_args: List[str]): + """ Process news command """ + news_view.news(other_args, self.ticker) + + def call_analyst(self, other_args: List[str]): + """ Process analyst command """ + fvz_view.analyst(other_args, self.ticker) + + def call_pt(self, other_args: List[str]): + """ Process pt command """ + bi_view.price_target_from_analysts( + other_args, self.stock, self.ticker, self.start, self.interval + ) + + def call_est(self, other_args: List[str]): + """ Process est command """ + bi_view.estimates(other_args, self.ticker) + + def call_ins(self, other_args: List[str]): + """ Process ins command """ + bi_view.insider_activity( + other_args, self.stock, self.ticker, self.start, self.interval + ) + + def call_rating(self, other_args: List[str]): + """ Process rating command """ + fmp_view.rating(other_args, self.ticker) + + def call_warnings(self, other_args: List[str]): + """ Process rating command """ + mw_view.sean_seah_warnings(other_args, self.ticker) + + def call_sec(self, other_args: List[str]): + """ Process sec command """ + mw_view.sec_fillings(other_args, self.ticker) + + def call_short(self, other_args: List[str]): + """ Process short command """ + q_view.short_interest(other_args, self.ticker, self.start) + + +def menu(stock: DataFrame, ticker: str, start: str, interval: str): + """Due Diligence Menu + + Parameters + ---------- + stock : DataFrame + Due diligence stock dataframe + ticker : str + Due diligence ticker symbol + start : str + Start date of the stock data + interval : str + Stock data interval + """ + + dd_controller = DueDiligenceController(stock, ticker, start, interval) + dd_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 dd_controller.CHOICES} + ) + + an_input = session.prompt( + f"{get_flair()} (dd)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (dd)> ") + + try: + process_input = dd_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/due_diligence/dd_menu.py b/gamestonk_terminal/due_diligence/dd_menu.py deleted file mode 100644 index 94635e6d8c8..00000000000 --- a/gamestonk_terminal/due_diligence/dd_menu.py +++ /dev/null @@ -1,148 +0,0 @@ -import argparse - -from gamestonk_terminal.due_diligence import business_insider_api as bi_api -from gamestonk_terminal.due_diligence import financial_modeling_prep_api as fmp_api -from gamestonk_terminal.due_diligence import finviz_api as fvz_api -from gamestonk_terminal.due_diligence import market_watch_api as mw_api -from gamestonk_terminal.due_diligence import quandl_api as q_api -from gamestonk_terminal.due_diligence import reddit_api as r_api -from gamestonk_terminal.due_diligence import news_api -from gamestonk_terminal import feature_flags as gtff -from gamestonk_terminal.helper_funcs import get_flair -from gamestonk_terminal.menu import session -from prompt_toolkit.completion import NestedCompleter - - -def print_due_diligence(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("\nDue Diligence:") - print(" help show this fundamental analysis menu again") - print(" q quit this menu, and shows back to main menu") - print(" quit quit to abandon program") - print("") - print(" news latest news of the company [News API]") - print(" red gets due diligence from another user's post [Reddit]") - print(" analyst analyst prices and ratings of the company [Finviz]") - print(" rating rating of the company from strong sell to strong buy [FMP]") - print(" pt price targets over time [Business Insider]") - print( - " est quarter and year analysts earnings estimates [Business Insider]" - ) - print(" ins insider activity over time [Business Insider]") - print(" insider insider trading of the company [Finviz]") - print(" sec SEC filings [Market Watch]") - print(" short short interest [Quandl]") - print( - " warnings company warnings according to Sean Seah book [Market Watch]" - ) - print("") - return - - -def dd_menu(df_stock, s_ticker, s_start, s_interval): - - # Add list of arguments that the due diligence parser accepts - dd_parser = argparse.ArgumentParser(prog="dd", add_help=False) - choices = [ - "info", - "help", - "q", - "quit", - "red", - "short", - "rating", - "pt", - "est", - "ins", - "insider", - "news", - "analyst", - "warnings", - "sec", - ] - dd_parser.add_argument("cmd", choices=choices) - completer = NestedCompleter.from_nested_dict({c: None for c in choices}) - - print_due_diligence(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()} (dd)> ", - completer=completer, - ) - else: - as_input = input(f"{get_flair()} (dd)> ") - - # Parse due diligence command of the list of possible commands - try: - (ns_known_args, l_args) = dd_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_due_diligence(s_ticker, s_start, s_interval) - - elif ns_known_args.cmd == "q": - # Just leave the DD menu - return False - - elif ns_known_args.cmd == "quit": - # Abandon the program - return True - - # REDDIT API - elif ns_known_args.cmd == "red": - r_api.due_diligence(l_args, s_ticker) - - # FINVIZ API - elif ns_known_args.cmd == "insider": - fvz_api.insider(l_args, s_ticker) - - elif ns_known_args.cmd == "news": - news_api.news(l_args, s_ticker) - - elif ns_known_args.cmd == "analyst": - fvz_api.analyst(l_args, s_ticker) - - # BUSINESS INSIDER API - elif ns_known_args.cmd == "pt": - bi_api.price_target_from_analysts( - l_args, df_stock, s_ticker, s_start, s_interval - ) - - elif ns_known_args.cmd == "est": - bi_api.estimates(l_args, s_ticker) - - elif ns_known_args.cmd == "ins": - bi_api.insider_activity(l_args, df_stock, s_ticker, s_start, s_interval) - - # FINANCIAL MODELING PREP API - elif ns_known_args.cmd == "rating": - fmp_api.rating(l_args, s_ticker) - - # MARKET WATCH API - elif ns_known_args.cmd == "sec": - mw_api.sec_fillings(l_args, s_ticker) - - elif ns_known_args.cmd == "warnings": - mw_api.sean_seah_warnings(l_args, s_ticker) - - # QUANDL API - elif ns_known_args.cmd == "short": - q_api.short_interest(l_args, s_ticker, s_start) - - else: - print("Command not recognized!") diff --git a/gamestonk_terminal/due_diligence/financial_modeling_prep_api.py b/gamestonk_terminal/due_diligence/financial_modeling_prep_view.py similarity index 65% rename from gamestonk_terminal/due_diligence/financial_modeling_prep_api.py rename to gamestonk_terminal/due_diligence/financial_modeling_prep_view.py index 9163bf02149..4026649e2fe 100644 --- a/gamestonk_terminal/due_diligence/financial_modeling_prep_api.py +++ b/gamestonk_terminal/due_diligence/financial_modeling_prep_view.py @@ -1,10 +1,24 @@ +""" Financial Modeling Prep View """ +__docformat__ = "numpy" + import argparse +from typing import List import FundamentalAnalysis as fa # Financial Modeling Prep from gamestonk_terminal import config_terminal as cfg from gamestonk_terminal.helper_funcs import parse_known_args_and_warn -def rating(l_args, s_ticker): +def rating(other_args: List[str], ticker: str): + """Display ratings for a given ticker + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Stock ticker + """ + parser = argparse.ArgumentParser( add_help=False, prog="rating", @@ -16,18 +30,18 @@ def rating(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.rating(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) + df_fa = fa.rating(ticker, cfg.API_KEY_FINANCIALMODELINGPREP) print(df_fa) print("") except KeyError: print( - f"Financialmodelingprep.com is returning empty response the ticker {s_ticker}." + f"Financialmodelingprep.com is returning empty response the ticker {ticker}." ) print("") return diff --git a/gamestonk_terminal/due_diligence/finviz_api.py b/gamestonk_terminal/due_diligence/finviz_view.py similarity index 70% rename from gamestonk_terminal/due_diligence/finviz_api.py rename to gamestonk_terminal/due_diligence/finviz_view.py index 50b47b433ee..56a3eaa8a21 100644 --- a/gamestonk_terminal/due_diligence/finviz_api.py +++ b/gamestonk_terminal/due_diligence/finviz_view.py @@ -1,4 +1,8 @@ +""" FinViz View """ +__docformat__ = "numpy" + import argparse +from typing import List from colorama import Fore, Style import finviz import pandas as pd @@ -11,6 +15,19 @@ from gamestonk_terminal.helper_funcs import ( def category_color_red_green(val: str) -> str: + """Add color to analyst rating + + Parameters + ---------- + val : str + Analyst rating - Upgrade/Downgrade + + Returns + ------- + str + Analyst rating with color + """ + if val == "Upgrade": return Fore.GREEN + val + Style.RESET_ALL if val == "Downgrade": @@ -18,7 +35,16 @@ def category_color_red_green(val: str) -> str: return val -def insider(l_args, s_ticker): +def insider(other_args: List[str], ticker: str): + """Display insider activity for a given stock ticker + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "10"] + ticker : str + Stock ticker + """ parser = argparse.ArgumentParser( add_help=False, prog="insider", @@ -39,11 +65,11 @@ def insider(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 - d_finviz_insider = finviz.get_insider(s_ticker) + d_finviz_insider = finviz.get_insider(ticker) df_fa = pd.DataFrame.from_dict(d_finviz_insider) df_fa.set_index("Date", inplace=True) df_fa = df_fa[ @@ -68,7 +94,17 @@ def insider(l_args, s_ticker): return -def news(l_args, s_ticker): +def news(other_args: List[str], ticker: str): + """Display news for a given stock ticker + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "10"] + ticker : str + Stock ticker + """ + parser = argparse.ArgumentParser( add_help=False, prog="news", @@ -88,11 +124,11 @@ def news(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 - d_finviz_news = finviz.get_news(s_ticker) + d_finviz_news = finviz.get_news(ticker) i = 0 for s_news_title, s_news_link in {*d_finviz_news}: print(f"-> {s_news_title}") @@ -110,9 +146,22 @@ def news(l_args, s_ticker): return -def analyst_df(s_ticker: str) -> DataFrame: +def analyst_df(ticker: str) -> DataFrame: + """[summary] + + Parameters + ---------- + ticker : str + Stock ticker + + Returns + ------- + DataFrame + [description] + """ + try: - d_finviz_analyst_price = finviz.get_analyst_price_targets(s_ticker) + d_finviz_analyst_price = finviz.get_analyst_price_targets(ticker) df_fa = pd.DataFrame.from_dict(d_finviz_analyst_price) df_fa.set_index("date", inplace=True) except Exception as e: @@ -122,7 +171,17 @@ def analyst_df(s_ticker: str) -> DataFrame: return df_fa -def analyst(l_args, s_ticker): +def analyst(other_args, ticker): + """Display analyst ratings + + Parameters + ---------- + other_args : [type] + argparse other args + ticker : [type] + Stock ticker + """ + parser = argparse.ArgumentParser( add_help=False, prog="analyst", @@ -144,11 +203,11 @@ def analyst(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 = analyst_df(s_ticker) + df_fa = analyst_df(ticker) if ns_parser.n_color == 1: df_fa["category"] = df_fa["category"].apply(category_color_red_green) diff --git a/gamestonk_terminal/due_diligence/market_watch_api.py b/gamestonk_terminal/due_diligence/market_watch_view.py similarity index 92% rename from gamestonk_terminal/due_diligence/market_watch_api.py rename to gamestonk_terminal/due_diligence/market_watch_view.py index f9cfd054983..7a080347cb1 100644 --- a/gamestonk_terminal/due_diligence/market_watch_api.py +++ b/gamestonk_terminal/due_diligence/market_watch_view.py @@ -1,4 +1,8 @@ +""" Market Watch View """ +__docformat__ = "numpy" + import argparse +from typing import List import re import requests import pandas as pd @@ -13,7 +17,17 @@ from gamestonk_terminal.helper_funcs import ( ) -def sec_fillings(l_args, s_ticker): +def sec_fillings(other_args: List[str], ticker: str): + """Display SEC filings for a given stock ticker + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "10"] + ticker : str + Stock ticker + """ + parser = argparse.ArgumentParser( add_help=False, prog="sec", @@ -34,13 +48,13 @@ def sec_fillings(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 pd.set_option("display.max_colwidth", None) - url_financials = f"https://www.marketwatch.com/investing/stock/{s_ticker}/financials/secfilings" + url_financials = f"https://www.marketwatch.com/investing/stock/{ticker}/financials/secfilings" text_soup_financials = BeautifulSoup( requests.get(url_financials, headers={"User-Agent": get_user_agent()}).text, @@ -60,7 +74,7 @@ def sec_fillings(l_args, s_ticker): l_financials_info.extend(a_financials[5:-1]) l_financials_info.append(financials_info.a["href"]) # Append data values to financials - df_financials.loc[len(df_financials.index)] = l_financials_info + df_financials.loc[len(df_financials.index)] = l_financials_info # type: ignore if "Filing Date" in a_financials: l_financials_header = [a_financials[2]] @@ -72,7 +86,7 @@ def sec_fillings(l_args, s_ticker): b_ready_to_process_info = True # Set Filing Date as index - df_financials = df_financials.set_index("Filing Date") + df_financials = df_financials.set_index("Filing Date") # type: ignore print(df_financials.head(n=ns_parser.n_num).to_string()) print("") @@ -83,7 +97,17 @@ def sec_fillings(l_args, s_ticker): return -def sean_seah_warnings(l_args, s_ticker): +def sean_seah_warnings(other_args: List[str], ticker: str): + """Display Sean Seah warnings + + Parameters + ---------- + other_args : List[str] + argparse other args + ticker : str + Stock ticker + """ + parser = argparse.ArgumentParser( add_help=False, prog="warnings", @@ -114,7 +138,7 @@ def sean_seah_warnings(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 @@ -129,7 +153,7 @@ def sean_seah_warnings(l_args, s_ticker): # From INCOME STATEMENT, get: 'EPS (Basic)', 'Net Income', 'Interest Expense', 'EBITDA' url_financials = ( - f"https://www.marketwatch.com/investing/stock/{s_ticker}/financials/income" + f"https://www.marketwatch.com/investing/stock/{ticker}/financials/income" ) text_soup_financials = BeautifulSoup( requests.get(url_financials, headers={"User-Agent": get_user_agent()}).text, @@ -173,7 +197,7 @@ def sean_seah_warnings(l_args, s_ticker): ] # From BALANCE SHEET, get: 'Liabilities & Shareholders\' Equity', 'Long-Term Debt' - url_financials = f"https://www.marketwatch.com/investing/stock/{s_ticker}/financials/balance-sheet" + url_financials = f"https://www.marketwatch.com/investing/stock/{ticker}/financials/balance-sheet" text_soup_financials = BeautifulSoup( requests.get(url_financials, headers={"User-Agent": get_user_agent()}).text, "lxml", diff --git a/gamestonk_terminal/due_diligence/news_api.py b/gamestonk_terminal/due_diligence/news_view.py similarity index 78% rename from gamestonk_terminal/due_diligence/news_api.py rename to gamestonk_terminal/due_diligence/news_view.py index 7d61da02925..9b82d67caae 100644 --- a/gamestonk_terminal/due_diligence/news_api.py +++ b/gamestonk_terminal/due_diligence/news_view.py @@ -1,4 +1,8 @@ +""" News View """ +__docformat__ = "numpy" + import argparse +from typing import List from datetime import datetime, timedelta import requests from gamestonk_terminal import config_terminal as cfg @@ -8,7 +12,17 @@ from gamestonk_terminal.helper_funcs import ( ) -def news(l_args, s_ticker): +def news(other_args: List[str], ticker: str): + """Display news for a given ticker + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "10"] + ticker : str + Stock ticker + """ + parser = argparse.ArgumentParser( add_help=False, prog="news", @@ -28,14 +42,14 @@ def news(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 s_from = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d") response = requests.get( - f"https://newsapi.org/v2/everything?q={s_ticker}&from={s_from}" + f"https://newsapi.org/v2/everything?q={ticker}&from={s_from}" f"&sortBy=publishedAt&language=en&apiKey={cfg.API_NEWS_TOKEN}", ) @@ -45,7 +59,7 @@ def news(l_args, s_ticker): else: print( - f"{response.json()['totalResults']} news articles from {s_ticker} were found since {s_from}\n" + f"{response.json()['totalResults']} news articles from {ticker} were found since {s_from}\n" ) for idx, article in enumerate(response.json()["articles"]): diff --git a/gamestonk_terminal/due_diligence/quandl_api.py b/gamestonk_terminal/due_diligence/quandl_view.py similarity index 77% rename from gamestonk_terminal/due_diligence/quandl_api.py rename to gamestonk_terminal/due_diligence/quandl_view.py index 7d4254a9f6d..b0ca0f09f67 100644 --- a/gamestonk_terminal/due_diligence/quandl_api.py +++ b/gamestonk_terminal/due_diligence/quandl_view.py @@ -1,7 +1,11 @@ +""" Quandl View """ +__docformat__ = "numpy" + import argparse +from typing import List import quandl from matplotlib import pyplot as plt -import matplotlib.ticker as ticker +import matplotlib.ticker import pandas as pd from gamestonk_terminal.helper_funcs import ( check_positive, @@ -11,7 +15,19 @@ from gamestonk_terminal.helper_funcs import ( from gamestonk_terminal import config_terminal as cfg -def short_interest(l_args, s_ticker, s_start): +def short_interest(other_args: List[str], ticker: str, start: str): + """Display short interest for a given ticker and a given start date + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-d", "10"] + ticker : str + Stock ticker + start : str + Start date of the stock data + """ + parser = argparse.ArgumentParser( add_help=False, prog="short", @@ -40,18 +56,18 @@ def short_interest(l_args, s_ticker, s_start): ) 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 quandl.ApiConfig.api_key = cfg.API_KEY_QUANDL if ns_parser.b_nyse: - df_short_interest = quandl.get(f"FINRA/FNYX_{s_ticker}") + df_short_interest = quandl.get(f"FINRA/FNYX_{ticker}") else: - df_short_interest = quandl.get(f"FINRA/FNSQ_{s_ticker}") + df_short_interest = quandl.get(f"FINRA/FNSQ_{ticker}") - df_short_interest = df_short_interest[s_start:] + df_short_interest = df_short_interest[start:] # type: ignore df_short_interest.columns = [ "".join( " " + char if char.isupper() else char.strip() for char in idx @@ -77,18 +93,18 @@ def short_interest(l_args, s_ticker, s_start): ax.set_ylabel("Shares") ax.set_xlabel("Date") - if s_start: + if start: ax.set_title( - f"{('NASDAQ', 'NYSE')[ns_parser.b_nyse]} Short Interest on {s_ticker} from {s_start.date()}" + f"{('NASDAQ', 'NYSE')[ns_parser.b_nyse]} Short Interest on {ticker} from {start.date()}" # type: ignore ) else: ax.set_title( - f"{('NASDAQ', 'NYSE')[ns_parser.b_nyse]} Short Interest on {s_ticker}" + f"{('NASDAQ', 'NYSE')[ns_parser.b_nyse]} Short Interest on {ticker}" ) ax.legend(labels=["Short Volume", "Total Volume"]) ax.tick_params(axis="both", which="major") - ax.yaxis.set_major_formatter(ticker.EngFormatter()) + ax.yaxis.set_major_formatter(matplotlib.ticker.EngFormatter()) ax_twin = ax.twinx() ax_twin.tick_params(axis="y", colors="green") ax_twin.set_ylabel("Percentage of Volume Shorted", color="green") @@ -98,7 +114,9 @@ def short_interest(l_args, s_ticker, s_start): color="green", ) ax_twin.tick_params(axis="y", which="major", color="green") - ax_twin.yaxis.set_major_formatter(ticker.FormatStrFormatter("%.0f%%")) + ax_twin.yaxis.set_major_formatter( + matplotlib.ticker.FormatStrFormatter("%.0f%%") + ) plt.xlim([df_short_interest.index[0], df_short_interest.index[-1]]) df_short_interest["% of Volume Shorted"] = df_short_interest[ diff --git a/gamestonk_terminal/due_diligence/reddit_api.py b/gamestonk_terminal/due_diligence/reddit_view.py similarity index 88% rename from gamestonk_terminal/due_diligence/reddit_api.py rename to gamestonk_terminal/due_diligence/reddit_view.py index f305abacef7..5c536add200 100644 --- a/gamestonk_terminal/due_diligence/reddit_api.py +++ b/gamestonk_terminal/due_diligence/reddit_view.py @@ -1,4 +1,8 @@ +""" Reddit View """ +__docformat__ = "numpy" + import argparse +from typing import List from datetime import datetime, timedelta from psaw import PushshiftAPI import praw @@ -7,7 +11,17 @@ from gamestonk_terminal import config_terminal as cfg from gamestonk_terminal.reddit_helpers import print_and_record_reddit_post -def due_diligence(l_args, s_ticker): +def due_diligence(other_args: List[str], ticker: str): + """Display Reddit due diligence data for a given ticker + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-l", "5"] + ticker : str + Stock ticker + """ + parser = argparse.ArgumentParser( add_help=False, prog="red", @@ -47,7 +61,7 @@ def due_diligence(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 @@ -83,9 +97,9 @@ def due_diligence(l_args, s_ticker): ] submissions = psaw_api.search_submissions( - after=int(n_ts_after), subreddit=l_sub_reddits, q=s_ticker, filter=["id"] + after=int(n_ts_after), subreddit=l_sub_reddits, q=ticker, filter=["id"] ) - d_submission = {} + d_submission = {} # type: ignore n_flair_posts_found = 0 while True: submission = next(submissions, None) diff --git a/terminal.py b/terminal.py index 91fa02c2e94..1408473bebc 100644 --- a/terminal.py +++ b/terminal.py @@ -15,7 +15,7 @@ from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal import thought_of_the_day as thought from gamestonk_terminal import res_menu as rm from gamestonk_terminal.discovery import disc_controller -from gamestonk_terminal.due_diligence import dd_menu as ddm +from gamestonk_terminal.due_diligence import dd_controller from gamestonk_terminal.fundamental_analysis import fa_menu as fam from gamestonk_terminal.helper_funcs import b_is_stock_market_open, get_flair from gamestonk_terminal.main_helper import clear, export, load, print_help, view, candle @@ -208,7 +208,7 @@ def main(): b_quit = ta_controller.menu(df_stock, s_ticker, s_start, s_interval) elif ns_known_args.opt == "dd": - b_quit = ddm.dd_menu(df_stock, s_ticker, s_start, s_interval) + b_quit = dd_controller.menu(df_stock, s_ticker, s_start, s_interval) elif ns_known_args.opt == "eda": if s_interval == "1440min": diff --git a/tests/test_dd_financial_modeling_prep_api.py b/tests/test_dd_financial_modeling_prep_api.py deleted file mode 100644 index 952fecdfb73..00000000000 --- a/tests/test_dd_financial_modeling_prep_api.py +++ /dev/null @@ -1,9 +0,0 @@ -""" fundamental_analysis/business_insider_api.py tests """ -import unittest - -from gamestonk_terminal.due_diligence.financial_modeling_prep_api import rating - - -class TestDdFinancialModelingPrepApi(unittest.TestCase): - def test_rating(self): - rating([], "PLTR") diff --git a/tests/test_dd_financial_modeling_prep_view.py b/tests/test_dd_financial_modeling_prep_view.py new file mode 100644 index 00000000000..0db0c299699 --- /dev/null +++ b/tests/test_dd_financial_modeling_prep_view.py @@ -0,0 +1,9 @@ +""" fundamental_analysis/business_insider_api.py tests """ +import unittest + +from gamestonk_terminal.due_diligence.financial_modeling_prep_view import rating + + +class TestDdFinancialModelingPrepView(unittest.TestCase): + def test_rating(self): + rating([], "PLTR") diff --git a/tests/test_dd_finviz_api.py b/tests/test_dd_finviz_view.py similarity index 70% rename from tests/test_dd_finviz_api.py rename to tests/test_dd_finviz_view.py index b0e1e680b2e..8c062510029 100644 --- a/tests/test_dd_finviz_api.py +++ b/tests/test_dd_finviz_view.py @@ -1,7 +1,7 @@ """ due_diligence/finviz_api.py tests """ import unittest -from gamestonk_terminal.due_diligence.finviz_api import analyst +from gamestonk_terminal.due_diligence.finviz_view import analyst class TestDdFinvizApi(unittest.TestCase):