diff --git a/gamestonk_terminal/dataframe_helpers.py b/gamestonk_terminal/dataframe_helpers.py new file mode 100644 index 00000000000..ac54d59c25f --- /dev/null +++ b/gamestonk_terminal/dataframe_helpers.py @@ -0,0 +1,9 @@ +import pandas as pd + + +def clean_df_index(df: pd.DataFrame): + df.index = [ + "".join(" " + char if char.isupper() else char.strip() for char in idx).strip() + for idx in df.index.tolist() + ] + df.index = [s_val.capitalize() for s_val in df.index] diff --git a/gamestonk_terminal/discovery/alpha_vantage_api.py b/gamestonk_terminal/discovery/alpha_vantage_api.py index 45366585994..2ee79e0d5f7 100644 --- a/gamestonk_terminal/discovery/alpha_vantage_api.py +++ b/gamestonk_terminal/discovery/alpha_vantage_api.py @@ -2,28 +2,22 @@ import argparse import matplotlib.pyplot as plt from alpha_vantage.sectorperformance import SectorPerformances from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn + -# ---------------------------------------------------- SECTORS ---------------------------------------------------- def sectors(l_args): parser = argparse.ArgumentParser( prog="sectors", - description="""Real-time and historical sector performances calculated from S&P500 incumbents. - Pops plot in terminal. [Source: Alpha Vantage]""", + description="""Real-time and historical sector performances calculated from + S&P500 incumbents. Pops plot in terminal. [Source: Alpha Vantage]""", ) - try: - (_, l_unknown_args) = parser.parse_known_args(l_args) - except SystemExit: - print("") - return - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") + parse_known_args_and_warn(parser, l_args) sector_perf = SectorPerformances( key=cfg.API_KEY_ALPHAVANTAGE, output_format="pandas" ) - # pylint: disable unbalanced-tuple-unpacking + # pylint: disable=unbalanced-tuple-unpacking df_sectors, _ = sector_perf.get_sector() # pylint: disable=invalid-sequence-index df_sectors["Rank A: Real-Time Performance"].plot(kind="bar") diff --git a/gamestonk_terminal/discovery/disc_menu.py b/gamestonk_terminal/discovery/disc_menu.py index 986920a2e64..932fb74e143 100644 --- a/gamestonk_terminal/discovery/disc_menu.py +++ b/gamestonk_terminal/discovery/disc_menu.py @@ -10,7 +10,6 @@ from gamestonk_terminal.discovery import spachero_api from gamestonk_terminal.discovery import unusual_whales_api -# ----------------------------------------------------------------------------------------------------------------------- def print_discovery(): """ Print help """ @@ -37,7 +36,6 @@ def print_discovery(): return -# ---------------------------------------------------- MENU ---------------------------------------------------- def disc_menu(): # Add list of arguments that the discovery parser accepts @@ -89,45 +87,35 @@ def disc_menu(): # Abandon the program return True - # --------------------------------------------------- FINVIZ --------------------------------------------------- elif ns_known_args.cmd == "map": finviz_api.map_sp500(l_args) - # ------------------------------------------------ ALPHA VANTAGE ----------------------------------------------- elif ns_known_args.cmd == "sectors": alpha_vantage_api.sectors(l_args) - # ------------------------------------------------ YAHOO FINANCE ------------------------------------------------ elif ns_known_args.cmd == "gainers": yahoo_finance_api.gainers(l_args) - # -------------------------------------------------- SPACHERO -------------------------------------------------- elif ns_known_args.cmd == "spachero": spachero_api.spachero(l_args) - # -------------------------------------------------- UNUSUAL WHALES -------------------------------------------------- elif ns_known_args.cmd == "uwhales": unusual_whales_api.unusual_whales(l_args) - # --------------------------------------------------- FIDELITY --------------------------------------------------- elif ns_known_args.cmd == "orders": fidelity_api.orders(l_args) - # ------------------------------------------------ SIMPLY WALL ST ------------------------------------------------ elif ns_known_args.cmd == "simply_wallst": simply_wallst_api.simply_wallst(l_args) - # ------------------------------------------------ SEEKING ALPHA ------------------------------------------------ elif ns_known_args.cmd == "up_earnings": seeking_alpha_api.earnings_release_dates(l_args) - # ------------------------------------------------ SHORT_INTEREST ------------------------------------------------ elif ns_known_args.cmd == "high_short": short_interest_api.high_short_interest(l_args) elif ns_known_args.cmd == "low_float": short_interest_api.low_float(l_args) - # ------------------------------------------------------------------------------------------------------------ else: print("Command not recognized!") diff --git a/gamestonk_terminal/discovery/fidelity_api.py b/gamestonk_terminal/discovery/fidelity_api.py index c55db33429b..2a96ef56572 100644 --- a/gamestonk_terminal/discovery/fidelity_api.py +++ b/gamestonk_terminal/discovery/fidelity_api.py @@ -9,6 +9,7 @@ from gamestonk_terminal.helper_funcs import ( check_positive, get_user_agent, patch_pandas_text_adjustment, + parse_known_args_and_warn, ) from gamestonk_terminal.config_terminal import USE_COLOR @@ -39,7 +40,6 @@ def price_change_color_red_green(val: str) -> str: return color + val + Style.RESET_ALL -# ---------------------------------------------------- ORDERS ---------------------------------------------------- def orders(l_args): parser = argparse.ArgumentParser( prog="orders", @@ -61,109 +61,88 @@ def orders(l_args): help="Number of top ordered stocks to be printed.", ) - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + ns_parser = parse_known_args_and_warn(parser, l_args) - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") + url_orders = ( + "https://eresearch.fidelity.com/eresearch/gotoBL/fidelityTopOrders.jhtml" + ) - url_orders = ( - "https://eresearch.fidelity.com/eresearch/gotoBL/fidelityTopOrders.jhtml" - ) + text_soup_url_orders = BeautifulSoup( + requests.get(url_orders, headers={"User-Agent": get_user_agent()}).text, "lxml" + ) - text_soup_url_orders = BeautifulSoup( - requests.get(url_orders, headers={"User-Agent": get_user_agent()}).text, - "lxml", - ) - - l_orders = list() - l_orders_vals = list() - idx = 0 - order_list = text_soup_url_orders.findAll( - "td", - { - "class": [ - "second", - "third", - "fourth", - "fifth", - "sixth", - "seventh", - "eight", - ] - }, - ) - for an_order in order_list: - if ((idx + 1) % 3 == 0) or ((idx + 1) % 4 == 0) or ((idx + 1) % 6 == 0): - if not an_order: - l_orders_vals.append("") - else: - l_orders_vals.append(an_order.contents[1]) - elif (idx + 1) % 5 == 0: - s_orders = str(an_order) - l_orders_vals.append( - s_orders[ - s_orders.find('title="') + len('title="') : s_orders.find('"/>') - ] - ) + l_orders = list() + l_orders_vals = list() + idx = 0 + order_list = text_soup_url_orders.findAll( + "td", + {"class": ["second", "third", "fourth", "fifth", "sixth", "seventh", "eight"]}, + ) + for an_order in order_list: + if ((idx + 1) % 3 == 0) or ((idx + 1) % 4 == 0) or ((idx + 1) % 6 == 0): + if not an_order: + l_orders_vals.append("") else: - l_orders_vals.append(an_order.text.strip()) + l_orders_vals.append(an_order.contents[1]) + elif (idx + 1) % 5 == 0: + s_orders = str(an_order) + l_orders_vals.append( + s_orders[ + s_orders.find('title="') + len('title="') : s_orders.find('"/>') + ] + ) + else: + l_orders_vals.append(an_order.text.strip()) - idx += 1 + idx += 1 - # Add value to dictionary - if (idx + 1) % 8 == 0: - l_orders.append(l_orders_vals) - l_orders_vals = list() - idx = 0 + # Add value to dictionary + if (idx + 1) % 8 == 0: + l_orders.append(l_orders_vals) + l_orders_vals = list() + idx = 0 - df_orders = pd.DataFrame( - l_orders, - columns=[ - "Symbol", - "Company", - "Price Change", - "# Buy Orders", - "Buy / Sell Ratio", - "# Sell Orders", - "Latest News", - ], - ) + df_orders = pd.DataFrame( + l_orders, + columns=[ + "Symbol", + "Company", + "Price Change", + "# Buy Orders", + "Buy / Sell Ratio", + "# Sell Orders", + "Latest News", + ], + ) - df_orders = df_orders[ - [ - "Symbol", - "Buy / Sell Ratio", - "Price Change", - "Company", - "# Buy Orders", - "# Sell Orders", - "Latest News", - ] + df_orders = df_orders[ + [ + "Symbol", + "Buy / Sell Ratio", + "Price Change", + "Company", + "# Buy Orders", + "# Sell Orders", + "Latest News", ] + ] - print( - text_soup_url_orders.findAll("span", {"class": "source"})[ - 0 - ].text.capitalize() - + ":" + print( + text_soup_url_orders.findAll("span", {"class": "source"})[0].text.capitalize() + + ":" + ) + + pd.set_option("display.max_colwidth", -1) + + if USE_COLOR: + df_orders["Buy / Sell Ratio"] = df_orders["Buy / Sell Ratio"].apply( + buy_sell_ratio_color_red_green + ) + df_orders["Price Change"] = df_orders["Price Change"].apply( + price_change_color_red_green ) - pd.set_option("display.max_colwidth", -1) + patch_pandas_text_adjustment() - if USE_COLOR: - df_orders["Buy / Sell Ratio"] = df_orders["Buy / Sell Ratio"].apply( - buy_sell_ratio_color_red_green - ) - df_orders["Price Change"] = df_orders["Price Change"].apply( - price_change_color_red_green - ) - - patch_pandas_text_adjustment() - - print(df_orders.head(n=ns_parser.n_num).iloc[:, :-1].to_string(index=False)) - print("") - - except SystemExit: - print("") - return + print(df_orders.head(n=ns_parser.n_num).iloc[:, :-1].to_string(index=False)) + print("") diff --git a/gamestonk_terminal/discovery/finviz_api.py b/gamestonk_terminal/discovery/finviz_api.py index 2c8053906b4..fcc719c18e4 100644 --- a/gamestonk_terminal/discovery/finviz_api.py +++ b/gamestonk_terminal/discovery/finviz_api.py @@ -1,7 +1,8 @@ import argparse import webbrowser +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn + -# ---------------------------------------------------- MAP_SP500 ---------------------------------------------------- def map_sp500(l_args): parser = argparse.ArgumentParser( prog="map", @@ -36,17 +37,9 @@ def map_sp500(l_args): d_period = {"1d": "", "1w": "w1", "1m": "w4", "3m": "w13", "6m": "w26", "1y": "w52"} d_type = {"sp500": "sec", "world": "geo", "full": "sec_all", "etf": "etf"} - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + ns_parser = parse_known_args_and_warn(parser, l_args) - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - webbrowser.open( - f"https://finviz.com/map.ashx?t={d_type[ns_parser.s_type]}&st={d_period[ns_parser.s_period]}" - ) - print("") - - except SystemExit: - print("") - return + webbrowser.open( + f"https://finviz.com/map.ashx?t={d_type[ns_parser.s_type]}&st={d_period[ns_parser.s_period]}" + ) + print("") diff --git a/gamestonk_terminal/discovery/seeking_alpha_api.py b/gamestonk_terminal/discovery/seeking_alpha_api.py index 36582963b23..673249c3cd0 100644 --- a/gamestonk_terminal/discovery/seeking_alpha_api.py +++ b/gamestonk_terminal/discovery/seeking_alpha_api.py @@ -2,10 +2,13 @@ import argparse import requests from bs4 import BeautifulSoup import pandas as pd -from gamestonk_terminal.helper_funcs import check_positive, get_user_agent +from gamestonk_terminal.helper_funcs import ( + check_positive, + get_user_agent, + parse_known_args_and_warn, +) -# ------------------------------------------------ EARNINGS_RELEASE_DATES ------------------------------------------------- def earnings_release_dates(l_args): parser = argparse.ArgumentParser( prog="up_earnings", @@ -31,54 +34,43 @@ def earnings_release_dates(l_args): help="Number of upcoming earnings release dates to print", ) - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + ns_parser = parse_known_args_and_warn(parser, l_args) - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - l_earnings = list() - for idx in range(0, ns_parser.n_pages): - if idx == 0: - url_next_earnings = ( - "https://seekingalpha.com/earnings/earnings-calendar" - ) - else: - url_next_earnings = ( - f"https://seekingalpha.com/earnings/earnings-calendar/{idx+1}" - ) - text_soup_earnings = BeautifulSoup( - requests.get( - url_next_earnings, headers={"User-Agent": get_user_agent()} - ).text, - "lxml", + l_earnings = list() + for idx in range(0, ns_parser.n_pages): + if idx == 0: + url_next_earnings = "https://seekingalpha.com/earnings/earnings-calendar" + else: + url_next_earnings = ( + f"https://seekingalpha.com/earnings/earnings-calendar/{idx+1}" ) + text_soup_earnings = BeautifulSoup( + requests.get( + url_next_earnings, headers={"User-Agent": get_user_agent()} + ).text, + "lxml", + ) - for bs_stock in text_soup_earnings.findAll( - "tr", {"data-exchange": "NASDAQ"} - ): - l_stock = list() - for stock in bs_stock.contents[:3]: - l_stock.append(stock.text) - l_earnings.append(l_stock) + for bs_stock in text_soup_earnings.findAll("tr", {"data-exchange": "NASDAQ"}): + l_stock = list() + for stock in bs_stock.contents[:3]: + l_stock.append(stock.text) + l_earnings.append(l_stock) - df_earnings = pd.DataFrame(l_earnings, columns=["Ticker", "Name", "Date"]) - df_earnings["Date"] = pd.to_datetime(df_earnings["Date"]) - df_earnings = df_earnings.set_index("Date") + df_earnings = pd.DataFrame(l_earnings, columns=["Ticker", "Name", "Date"]) + df_earnings["Date"] = pd.to_datetime(df_earnings["Date"]) + df_earnings = df_earnings.set_index("Date") - pd.set_option("display.max_colwidth", -1) - for n_days, earning_date in enumerate(df_earnings.index.unique()): - if n_days > (ns_parser.n_num - 1): - break + pd.set_option("display.max_colwidth", -1) + for n_days, earning_date in enumerate(df_earnings.index.unique()): + if n_days > (ns_parser.n_num - 1): + break - print(f"Earning Release on {earning_date.date()}") - print("----------------------------------------------") - print( - df_earnings[earning_date == df_earnings.index][ - ["Ticker", "Name"] - ].to_string(index=False, header=False) - ) - print("") - except SystemExit: + print(f"Earning Release on {earning_date.date()}") + print("----------------------------------------------") + print( + df_earnings[earning_date == df_earnings.index][ + ["Ticker", "Name"] + ].to_string(index=False, header=False) + ) print("") - return diff --git a/gamestonk_terminal/discovery/short_interest_api.py b/gamestonk_terminal/discovery/short_interest_api.py index f41ae09f2c5..936ae8003ac 100644 --- a/gamestonk_terminal/discovery/short_interest_api.py +++ b/gamestonk_terminal/discovery/short_interest_api.py @@ -2,10 +2,13 @@ import argparse import requests from bs4 import BeautifulSoup import pandas as pd -from gamestonk_terminal.helper_funcs import check_positive, get_user_agent +from gamestonk_terminal.helper_funcs import ( + check_positive, + get_user_agent, + parse_known_args_and_warn, +) -# ------------------------------------------------ HIGH_SHORT_INTEREST ------------------------------------------------- def high_short_interest(l_args): parser = argparse.ArgumentParser( prog="high_short", @@ -28,15 +31,7 @@ def high_short_interest(l_args): help="Number of top stocks to print.", ) - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - except SystemExit: - print("") - return - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - return + ns_parser = parse_known_args_and_warn(parser, l_args) url_high_short_interested_stocks = "https://www.highshortinterest.com" text_soup_high_short_interested_stocks = BeautifulSoup( @@ -79,7 +74,6 @@ def high_short_interest(l_args): print("") -# ---------------------------------------------------- LOW_FLOAT ----------------------------------------------------- def low_float(l_args): parser = argparse.ArgumentParser( prog="low_float", @@ -102,14 +96,7 @@ def low_float(l_args): help="Number of top stocks to print.", ) - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - except SystemExit: - print("") - return - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") + ns_parser = parse_known_args_and_warn(parser, l_args) url_high_short_interested_stocks = "https://www.lowfloat.com" text_soup_low_float_stocks = BeautifulSoup( diff --git a/gamestonk_terminal/discovery/simply_wallst_api.py b/gamestonk_terminal/discovery/simply_wallst_api.py index 24f1e7f7a7b..b2e9640a980 100644 --- a/gamestonk_terminal/discovery/simply_wallst_api.py +++ b/gamestonk_terminal/discovery/simply_wallst_api.py @@ -1,7 +1,8 @@ import argparse import webbrowser +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn + -# ---------------------------------------------------- Simply WallSt ---------------------------------------------------- def simply_wallst(l_args): parser = argparse.ArgumentParser( prog="simply_wallst", @@ -49,17 +50,7 @@ def simply_wallst(l_args): ], ) - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + ns_parser = parse_known_args_and_warn(parser, l_args) - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - webbrowser.open( - f"https://simplywall.st/stocks/us/{ns_parser.s_industry}?page=1" - ) - print("") - - except SystemExit: - print("") - return + webbrowser.open(f"https://simplywall.st/stocks/us/{ns_parser.s_industry}?page=1") + print("") diff --git a/gamestonk_terminal/discovery/spachero_api.py b/gamestonk_terminal/discovery/spachero_api.py index ec07035fd36..4665fedb8d2 100644 --- a/gamestonk_terminal/discovery/spachero_api.py +++ b/gamestonk_terminal/discovery/spachero_api.py @@ -1,22 +1,15 @@ import argparse import webbrowser +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn + -# ---------------------------------------------------- SPACHERO ---------------------------------------------------- def spachero(l_args): parser = argparse.ArgumentParser( prog="spachero", description="""Great website for SPACs research. [Source: www.spachero.com]""", ) - try: - (_, l_unknown_args) = parser.parse_known_args(l_args) + parse_known_args_and_warn(parser, l_args) - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - webbrowser.open("https://www.spachero.com") - print("") - - except SystemExit: - print("") - return + webbrowser.open("https://www.spachero.com") + print("") diff --git a/gamestonk_terminal/discovery/unusual_whales_api.py b/gamestonk_terminal/discovery/unusual_whales_api.py index 8540dcca95b..31b5b4509d9 100644 --- a/gamestonk_terminal/discovery/unusual_whales_api.py +++ b/gamestonk_terminal/discovery/unusual_whales_api.py @@ -1,22 +1,15 @@ import argparse import webbrowser +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn + -# ---------------------------------------------------- UNUSUAL WHALES ---------------------------------------------------- def unusual_whales(l_args): parser = argparse.ArgumentParser( prog="uwhales", description="""Good website for SPACs research. [Source: www.unusualwhales.com]""", ) - try: - (_, l_unknown_args) = parser.parse_known_args(l_args) + parse_known_args_and_warn(parser, l_args) - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - - webbrowser.open("https://unusualwhales.com/spacs") - print("") - - except SystemExit: - print("") - return + webbrowser.open("https://unusualwhales.com/spacs") + print("") diff --git a/gamestonk_terminal/discovery/yahoo_finance_api.py b/gamestonk_terminal/discovery/yahoo_finance_api.py index 67ca97ea869..825ebef2827 100644 --- a/gamestonk_terminal/discovery/yahoo_finance_api.py +++ b/gamestonk_terminal/discovery/yahoo_finance_api.py @@ -1,7 +1,8 @@ import argparse import pandas as pd +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn + -# ---------------------------------------------------- GAINERS ---------------------------------------------------- def gainers(l_args): parser = argparse.ArgumentParser( prog="gainers", @@ -19,14 +20,7 @@ def gainers(l_args): help="Number of the top gainers stocks to retrieve.", ) - try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - except SystemExit: - print("") - return - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") + ns_parser = parse_known_args_and_warn(parser, l_args) df_gainers = pd.read_html( "https://finance.yahoo.com/screener/predefined/day_gainers" diff --git a/gamestonk_terminal/due_diligence/business_insider_api.py b/gamestonk_terminal/due_diligence/business_insider_api.py index 6a0cb9e4c5d..4dad7fbc5bb 100644 --- a/gamestonk_terminal/due_diligence/business_insider_api.py +++ b/gamestonk_terminal/due_diligence/business_insider_api.py @@ -10,12 +10,12 @@ from gamestonk_terminal.helper_funcs import ( check_positive, get_next_stock_market_days, get_user_agent, + parse_known_args_and_warn, ) register_matplotlib_converters() -# ------------------------------------------- PRICE_TARGET_FROM_ANALYSTS ------------------------------------------- def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): parser = argparse.ArgumentParser( prog="pt", @@ -33,11 +33,7 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) url_market_business_insider = ( f"https://markets.businessinsider.com/stocks/{s_ticker.lower()}-stock" @@ -49,12 +45,13 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): "lxml", ) + d_analyst_data = None for script in text_soup_market_business_insider.find_all("script"): # Get Analyst data - if "window.analyseChartConfigs.push" in script.get_text(): + if "window.analyseChartConfigs.push" in str(script): # Extract config data: s_analyst_data = ( - script.get_text().split("config: ", 1)[1].split(",\r\n", 1)[0] + str(script).split("config: ", 1)[1].split(",\r\n", 1)[0] ) d_analyst_data = json.loads(s_analyst_data) break @@ -115,7 +112,6 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval): return -# ----------------------------------------------- ESTIMATES ----------------------------------------------- def estimates(l_args, s_ticker): parser = argparse.ArgumentParser( prog="est", @@ -123,11 +119,7 @@ def estimates(l_args, s_ticker): ) try: - (_, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + parse_known_args_and_warn(parser, l_args) url_market_business_insider = ( f"https://markets.businessinsider.com/stocks/{s_ticker.lower()}-stock" @@ -276,7 +268,6 @@ def estimates(l_args, s_ticker): return -# ----------------------------------------------- INSIDER_ACTIVITY ----------------------------------------------- def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval): parser = argparse.ArgumentParser( prog="ins", @@ -293,11 +284,7 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) url_market_business_insider = ( f"https://markets.businessinsider.com/stocks/{s_ticker.lower()}-stock" diff --git a/gamestonk_terminal/due_diligence/dd_menu.py b/gamestonk_terminal/due_diligence/dd_menu.py index 7cce9eec7f0..5ed2acfc858 100644 --- a/gamestonk_terminal/due_diligence/dd_menu.py +++ b/gamestonk_terminal/due_diligence/dd_menu.py @@ -8,7 +8,6 @@ from gamestonk_terminal.due_diligence import financial_modeling_prep_api as fmp_ from gamestonk_terminal.due_diligence import business_insider_api as bi_api -# ----------------------------------------------------------------------------------------------------------------------- def print_due_diligence(s_ticker, s_start, s_interval): """ Print help """ @@ -43,7 +42,6 @@ def print_due_diligence(s_ticker, s_start, s_interval): return -# ---------------------------------------------------- MENU ---------------------------------------------------- def dd_menu(df_stock, s_ticker, s_start, s_interval): # Add list of arguments that the due diligence parser accepts @@ -55,19 +53,19 @@ def dd_menu(df_stock, s_ticker, s_start, s_interval): "help", "q", "quit", - "red", # Reddit - "short", # Quandl - "rating", # FMP + "red", + "short", + "rating", "pt", "est", - "ins", # BI + "ins", "insider", "news", - "analyst", # Finviz + "analyst", "warnings", "sec", ], - ) # MW + ) print_due_diligence(s_ticker, s_start, s_interval) diff --git a/gamestonk_terminal/due_diligence/financial_modeling_prep_api.py b/gamestonk_terminal/due_diligence/financial_modeling_prep_api.py index 4d43024ab5d..8bf80caf12f 100644 --- a/gamestonk_terminal/due_diligence/financial_modeling_prep_api.py +++ b/gamestonk_terminal/due_diligence/financial_modeling_prep_api.py @@ -1,8 +1,9 @@ import argparse 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 + -# ---------------------------------------------------- RATING ---------------------------------------------------- def rating(l_args, s_ticker): parser = argparse.ArgumentParser( prog="rating", @@ -14,11 +15,7 @@ def rating(l_args, s_ticker): ) try: - (_, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + parse_known_args_and_warn(parser, l_args) df_fa = fa.rating(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP) print(df_fa) diff --git a/gamestonk_terminal/due_diligence/finviz_api.py b/gamestonk_terminal/due_diligence/finviz_api.py index b7f854321f5..eb8192369aa 100644 --- a/gamestonk_terminal/due_diligence/finviz_api.py +++ b/gamestonk_terminal/due_diligence/finviz_api.py @@ -3,7 +3,11 @@ from colorama import Fore, Style import finviz import pandas as pd from pandas.core.frame import DataFrame -from gamestonk_terminal.helper_funcs import check_positive, patch_pandas_text_adjustment +from gamestonk_terminal.helper_funcs import ( + check_positive, + patch_pandas_text_adjustment, + parse_known_args_and_warn, +) def category_color_red_green(val: str) -> str: @@ -14,7 +18,6 @@ def category_color_red_green(val: str) -> str: return val -# ---------------------------------------------------- INSIDER ---------------------------------------------------- def insider(l_args, s_ticker): parser = argparse.ArgumentParser( prog="insider", @@ -35,11 +38,7 @@ def insider(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) d_finviz_insider = finviz.get_insider(s_ticker) df_fa = pd.DataFrame.from_dict(d_finviz_insider) @@ -66,7 +65,6 @@ def insider(l_args, s_ticker): return -# ---------------------------------------------------- NEWS ---------------------------------------------------- def news(l_args, s_ticker): parser = argparse.ArgumentParser( prog="news", @@ -86,11 +84,7 @@ def news(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) d_finviz_news = finviz.get_news(s_ticker) i = 0 @@ -110,7 +104,6 @@ def news(l_args, s_ticker): return -# ---------------------------------------------------- ANALYST ---------------------------------------------------- def analyst_df(s_ticker: str) -> DataFrame: d_finviz_analyst_price = finviz.get_analyst_price_targets(s_ticker) df_fa = pd.DataFrame.from_dict(d_finviz_analyst_price) @@ -140,15 +133,7 @@ def analyst(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return - - # d_finviz_analyst_price = finviz.get_analyst_price_targets(s_ticker) - # df_fa = pd.DataFrame.from_dict(d_finviz_analyst_price) - # df_fa.set_index("date", inplace=True) + ns_parser = parse_known_args_and_warn(parser, l_args) df_fa = analyst_df(s_ticker) diff --git a/gamestonk_terminal/due_diligence/market_watch_api.py b/gamestonk_terminal/due_diligence/market_watch_api.py index cf0224c3d6e..5a466c56be8 100644 --- a/gamestonk_terminal/due_diligence/market_watch_api.py +++ b/gamestonk_terminal/due_diligence/market_watch_api.py @@ -1,4 +1,5 @@ import argparse +import re import requests import pandas as pd from bs4 import BeautifulSoup @@ -8,10 +9,10 @@ from gamestonk_terminal.helper_funcs import ( get_user_agent, clean_data_values_to_float, int_or_round_float, + parse_known_args_and_warn, ) -# ---------------------------------------------------- SEC FILLINGS ---------------------------------------------------- def sec_fillings(l_args, s_ticker): parser = argparse.ArgumentParser( prog="sec", @@ -32,11 +33,7 @@ def sec_fillings(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) pd.set_option("display.max_colwidth", -1) @@ -83,7 +80,6 @@ def sec_fillings(l_args, s_ticker): return -# ---------------------------------------------------- Sean Seah Warnings ---------------------------------------------------- def sean_seah_warnings(l_args, s_ticker): parser = argparse.ArgumentParser( prog="warnings", @@ -114,11 +110,7 @@ def sean_seah_warnings(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) if ns_parser.b_info: filepath = "fundamental_analysis/info_sean_seah.txt" @@ -149,16 +141,23 @@ def sean_seah_warnings(l_args, s_ticker): df_financials = pd.DataFrame(columns=a_financials_header[0:-1]) # Add financials values - soup_financials = text_soup_financials.findAll("tr", {"class": "table__row "}) + soup_financials = text_soup_financials.findAll( + lambda tag: tag.name == "tr" and tag.get("class") == ["table__row"] + ) soup_financials += text_soup_financials.findAll( "tr", {"class": "table__row is-highlighted"} ) for financials_info in soup_financials: - a_financials_info = financials_info.text.split("\n") - l_financials = [a_financials_info[2]] - l_financials.extend(a_financials_info[5:-2]) - # Append data values to financials - df_financials.loc[len(df_financials.index)] = l_financials + financials_row = financials_info.text.split("\n") + if len(financials_row) > 5: + for item in financials_row: + if bool(re.search(r"\d", item)): + a_financials_info = financials_info.text.split("\n") + l_financials = [a_financials_info[2]] + l_financials.extend(a_financials_info[5:-2]) + # Append data values to financials + df_financials.loc[len(df_financials.index)] = l_financials + break # Set item name as index df_financials = df_financials.set_index("Item") @@ -191,16 +190,23 @@ def sean_seah_warnings(l_args, s_ticker): ) # Add financials values - soup_financials = text_soup_financials.findAll("tr", {"class": "table__row "}) + soup_financials = text_soup_financials.findAll( + lambda tag: tag.name == "tr" and tag.get("class") == ["table__row"] + ) soup_financials += text_soup_financials.findAll( "tr", {"class": "table__row is-highlighted"} ) for financials_info in soup_financials: - a_financials_info = financials_info.text.split("\n") - l_financials = [a_financials_info[2]] - l_financials.extend(a_financials_info[5:-2]) - # Append data values to financials - df_financials.loc[len(df_financials.index)] = l_financials + financials_row = financials_info.text.split("\n") + if len(financials_row) > 5: + for item in financials_row: + if bool(re.search(r"\d", item)): + a_financials_info = financials_info.text.split("\n") + l_financials = [a_financials_info[2]] + l_financials.extend(a_financials_info[5:-2]) + # Append data values to financials + df_financials.loc[len(df_financials.index)] = l_financials + break # Set item name as index df_financials = df_financials.set_index("Item") diff --git a/gamestonk_terminal/due_diligence/quandl_api.py b/gamestonk_terminal/due_diligence/quandl_api.py index 22463024dbf..a0e4e55d78e 100644 --- a/gamestonk_terminal/due_diligence/quandl_api.py +++ b/gamestonk_terminal/due_diligence/quandl_api.py @@ -3,10 +3,14 @@ import quandl from matplotlib import pyplot as plt import matplotlib.ticker as ticker import pandas as pd -from gamestonk_terminal.helper_funcs import check_positive, long_number_format +from gamestonk_terminal.helper_funcs import ( + check_positive, + long_number_format, + parse_known_args_and_warn, +) from gamestonk_terminal import config_terminal as cfg -# -------------------------------------------------------- SHORT_INTEREST -------------------------------------------------------- + def short_interest(l_args, s_ticker, s_start): parser = argparse.ArgumentParser( prog="short", @@ -35,11 +39,7 @@ def short_interest(l_args, s_ticker, s_start): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) quandl.ApiConfig.api_key = cfg.API_KEY_QUANDL diff --git a/gamestonk_terminal/due_diligence/reddit_api.py b/gamestonk_terminal/due_diligence/reddit_api.py index 02bc8c20489..3cb12778faf 100644 --- a/gamestonk_terminal/due_diligence/reddit_api.py +++ b/gamestonk_terminal/due_diligence/reddit_api.py @@ -1,12 +1,12 @@ import argparse from datetime import datetime, timedelta -from prettytable import PrettyTable from psaw import PushshiftAPI import praw -from gamestonk_terminal.helper_funcs import check_positive +from gamestonk_terminal.helper_funcs import check_positive, parse_known_args_and_warn 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): parser = argparse.ArgumentParser( prog="red", @@ -46,11 +46,7 @@ def due_diligence(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) praw_api = praw.Reddit( client_id=cfg.API_REDDIT_CLIENT_ID, @@ -103,54 +99,7 @@ def due_diligence(l_args, s_ticker): submission.link_flair_text not in ["Yolo", "Meme"], )[ns_parser.b_all]: - # Refactor data - s_datetime = datetime.utcfromtimestamp( - submission.created_utc - ).strftime("%d/%m/%Y %H:%M:%S") - s_link = f"https://old.reddit.com{submission.permalink}" - s_all_awards = "" - for award in submission.all_awardings: - s_all_awards += f"{award['count']} {award['name']}\n" - s_all_awards = s_all_awards[:-2] - - # Create dictionary with data to construct dataframe allows to save data - d_submission[submission.id] = { - "created_utc": s_datetime, - "subreddit": submission.subreddit, - "link_flair_text": submission.link_flair_text, - "title": submission.title, - "score": submission.score, - "link": s_link, - "num_comments": submission.num_comments, - "upvote_ratio": submission.upvote_ratio, - "awards": s_all_awards, - } - - # Print post data collected so far - print(f"{s_datetime} - {submission.title}") - print(f"{s_link}") - t_post = PrettyTable( - [ - "Subreddit", - "Flair", - "Score", - "# Comments", - "Upvote %", - "Awards", - ] - ) - t_post.add_row( - [ - submission.subreddit, - submission.link_flair_text, - submission.score, - submission.num_comments, - f"{round(100*submission.upvote_ratio)}%", - s_all_awards, - ] - ) - print(t_post) - print("\n") + print_and_record_reddit_post(d_submission, submission) # If needed, submission.comments could give us the top comments diff --git a/gamestonk_terminal/helper_funcs.py b/gamestonk_terminal/helper_funcs.py index 803cbc836d5..7f9f21b0759 100644 --- a/gamestonk_terminal/helper_funcs.py +++ b/gamestonk_terminal/helper_funcs.py @@ -10,7 +10,7 @@ import pandas.io.formats.format from pandas._config.config import get_option from holidays import US as holidaysUS -# ----------------------------------------------------------------------------------------------------------------------- + def check_non_negative(value) -> int: ivalue = int(value) if ivalue < 0: @@ -18,7 +18,6 @@ def check_non_negative(value) -> int: return ivalue -# ----------------------------------------------------------------------------------------------------------------------- def check_positive(value) -> int: ivalue = int(value) if ivalue <= 0: @@ -26,7 +25,6 @@ def check_positive(value) -> int: return ivalue -# ----------------------------------------------------------------------------------------------------------------------- def valid_date(s: str) -> datetime: try: return datetime.strptime(s, "%Y-%m-%d") @@ -34,7 +32,6 @@ def valid_date(s: str) -> datetime: raise argparse.ArgumentTypeError("Not a valid date: {s}") from value_error -# ----------------------------------------------------------------------------------------------------------------------- def plot_view_stock(df, symbol): df.sort_index(ascending=True, inplace=True) _, axVolume = plt.subplots() @@ -54,7 +51,6 @@ def plot_view_stock(df, symbol): print("") -# ----------------------------------------------------------------------------------------------------------------------- def plot_stock_ta(df_stock, s_ticker, df_ta, s_ta): plt.plot(df_stock.index, df_stock.values, color="k") plt.plot(df_ta.index, df_ta.values) @@ -77,7 +73,6 @@ def plot_stock_ta(df_stock, s_ticker, df_ta, s_ta): print("") -# ----------------------------------------------------------------------------------------------------------------------- def plot_stock_and_ta(df_stock, s_ticker, df_ta, s_ta): _, axPrice = plt.subplots() plt.title(f"{s_ta} on {s_ticker}") @@ -104,7 +99,6 @@ def plot_stock_and_ta(df_stock, s_ticker, df_ta, s_ta): print("") -# ----------------------------------------------------------------------------------------------------------------------- def plot_ta(s_ticker, df_ta, s_ta): plt.plot(df_ta.index, df_ta.values) plt.title(f"{s_ta} on {s_ticker}") @@ -120,7 +114,6 @@ def plot_ta(s_ticker, df_ta, s_ta): print("") -# ----------------------------------------------------------------------------------------------------------------------- def b_is_stock_market_open() -> bool: """ checks if the stock market is open """ # Get current US time @@ -141,7 +134,6 @@ def b_is_stock_market_open() -> bool: return True -# ----------------------------------------------------------------------------------------------------------------------- def long_number_format(num) -> str: if isinstance(num, float): magnitude = 0 @@ -168,7 +160,6 @@ def long_number_format(num) -> str: return num -# ----------------------------------------------------------------------------------------------------------------------- def clean_data_values_to_float(val: str) -> float: # Remove parenthesis if they exist if val.startswith("("): @@ -190,14 +181,13 @@ def clean_data_values_to_float(val: str) -> float: val = float(val[:-1]) * 1_000_000 # Convert from thousands elif val.endswith("K"): - val = float(val[:-1]) * 1_000 + val = float(val[:-1]) * 1000 else: val = float(val) return val -# ----------------------------------------------------------------------------------------------------------------------- def int_or_round_float(x): if (x - int(x) < -sys.float_info.epsilon) or (x - int(x) > sys.float_info.epsilon): return " " + str(round(x, 2)) @@ -205,14 +195,12 @@ def int_or_round_float(x): return " " + str(int(x)) -# ----------------------------------------------------------------------------------------------------------------------- -def divide_chunks(l, n): - # looping till length l - for i in range(0, len(l), n): - yield l[i : i + n] +def divide_chunks(data, n): + # looping till length of data + for i in range(0, len(data), n): + yield data[i : i + n] -# ----------------------------------------------------------------------------------------------------------------------- def get_next_stock_market_days(last_stock_day, n_next_days) -> list: n_days = 0 l_pred_days = list() @@ -233,7 +221,6 @@ def get_next_stock_market_days(last_stock_day, n_next_days) -> list: return l_pred_days -# ----------------------------------------------------------------------------------------------------------------------- def get_data(tweet) -> hash: if "+" in tweet["created_at"]: s_datetime = tweet["created_at"].split(" +")[0] @@ -251,7 +238,6 @@ def get_data(tweet) -> hash: return data -# ----------------------------------------------------------------------------------------------------------------------- def clean_tweet(tweet: str, s_ticker: str) -> str: whitespace = re.compile(r"\s+") web_address = re.compile(r"(?i)http(s):\/\/[a-z0-9.~_\-\/]+") @@ -347,3 +333,10 @@ def patch_pandas_text_adjustment(): pandas.io.formats.format.TextAdjustment.justify = text_adjustment_justify pandas.io.formats.format.TextAdjustment.join_unicode = text_adjustment_join_unicode pandas.io.formats.format.TextAdjustment.adjoin = text_adjustment_adjoin + + +def parse_known_args_and_warn(parser, l_args): + (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + if l_unknown_args: + print(f"The following args couldn't be interpreted: {l_unknown_args}") + return ns_parser diff --git a/gamestonk_terminal/reddit_helpers.py b/gamestonk_terminal/reddit_helpers.py new file mode 100644 index 00000000000..2e5b3b06f12 --- /dev/null +++ b/gamestonk_terminal/reddit_helpers.py @@ -0,0 +1,45 @@ +from datetime import datetime + +from prettytable import PrettyTable + + +def print_and_record_reddit_post(submissions_dict, submission): + # Refactor data + s_datetime = datetime.utcfromtimestamp(submission.created_utc).strftime( + "%d/%m/%Y %H:%M:%S" + ) + s_link = f"https://old.reddit.com{submission.permalink}" + s_all_awards = "" + for award in submission.all_awardings: + s_all_awards += f"{award['count']} {award['name']}\n" + s_all_awards = s_all_awards[:-2] + # Create dictionary with data to construct dataframe allows to save data + submissions_dict[submission.id] = { + "created_utc": s_datetime, + "subreddit": submission.subreddit, + "link_flair_text": submission.link_flair_text, + "title": submission.title, + "score": submission.score, + "link": s_link, + "num_comments": submission.num_comments, + "upvote_ratio": submission.upvote_ratio, + "awards": s_all_awards, + } + # Print post data collected so far + print(f"{s_datetime} - {submission.title}") + print(f"{s_link}") + t_post = PrettyTable( + ["Subreddit", "Flair", "Score", "# Comments", "Upvote %", "Awards"] + ) + t_post.add_row( + [ + submission.subreddit, + submission.link_flair_text, + submission.score, + submission.num_comments, + f"{round(100 * submission.upvote_ratio)}%", + s_all_awards, + ] + ) + print(t_post) + print("\n")