From 6c6c2d9ce6b055b4d59416db3a495a0f2facef4b Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Sun, 28 Mar 2021 12:17:55 -0700 Subject: [PATCH 01/11] Discovery menu refactor part 1 --- gamestonk_terminal/discovery/__init__.py | 2 +- ...a_vantage_api.py => alpha_vantage_view.py} | 0 .../discovery/{ark_api.py => ark_view.py} | 0 .../discovery/disc_controller.py | 185 ++++++++++++++++++ gamestonk_terminal/discovery/disc_menu.py | 144 -------------- .../{fidelity_api.py => fidelity_view.py} | 0 .../{finviz_api.py => finviz_view.py} | 0 ...ing_alpha_api.py => seeking_alpha_view.py} | 0 ...interest_api.py => short_interest_view.py} | 0 ...ly_wallst_api.py => simply_wallst_view.py} | 0 .../{spachero_api.py => spachero_view.py} | 0 ...l_whales_api.py => unusual_whales_view.py} | 0 ...o_finance_api.py => yahoo_finance_view.py} | 0 terminal.py | 4 +- tests/test_discovery_fidelity_api.py | 2 +- tests/test_discovery_short_interest_api.py | 6 +- 16 files changed, 192 insertions(+), 151 deletions(-) rename gamestonk_terminal/discovery/{alpha_vantage_api.py => alpha_vantage_view.py} (100%) rename gamestonk_terminal/discovery/{ark_api.py => ark_view.py} (100%) create mode 100644 gamestonk_terminal/discovery/disc_controller.py delete mode 100644 gamestonk_terminal/discovery/disc_menu.py rename gamestonk_terminal/discovery/{fidelity_api.py => fidelity_view.py} (100%) rename gamestonk_terminal/discovery/{finviz_api.py => finviz_view.py} (100%) rename gamestonk_terminal/discovery/{seeking_alpha_api.py => seeking_alpha_view.py} (100%) rename gamestonk_terminal/discovery/{short_interest_api.py => short_interest_view.py} (100%) rename gamestonk_terminal/discovery/{simply_wallst_api.py => simply_wallst_view.py} (100%) rename gamestonk_terminal/discovery/{spachero_api.py => spachero_view.py} (100%) rename gamestonk_terminal/discovery/{unusual_whales_api.py => unusual_whales_view.py} (100%) rename gamestonk_terminal/discovery/{yahoo_finance_api.py => yahoo_finance_view.py} (100%) diff --git a/gamestonk_terminal/discovery/__init__.py b/gamestonk_terminal/discovery/__init__.py index a1605dd4a10..0c8c0d157a7 100644 --- a/gamestonk_terminal/discovery/__init__.py +++ b/gamestonk_terminal/discovery/__init__.py @@ -1 +1 @@ -from . import disc_menu # noqa: F401 +from . import disc_controller # noqa: F401 diff --git a/gamestonk_terminal/discovery/alpha_vantage_api.py b/gamestonk_terminal/discovery/alpha_vantage_view.py similarity index 100% rename from gamestonk_terminal/discovery/alpha_vantage_api.py rename to gamestonk_terminal/discovery/alpha_vantage_view.py diff --git a/gamestonk_terminal/discovery/ark_api.py b/gamestonk_terminal/discovery/ark_view.py similarity index 100% rename from gamestonk_terminal/discovery/ark_api.py rename to gamestonk_terminal/discovery/ark_view.py diff --git a/gamestonk_terminal/discovery/disc_controller.py b/gamestonk_terminal/discovery/disc_controller.py new file mode 100644 index 00000000000..fb754300b51 --- /dev/null +++ b/gamestonk_terminal/discovery/disc_controller.py @@ -0,0 +1,185 @@ +import argparse +from typing import List + +from gamestonk_terminal import feature_flags as gtff +from gamestonk_terminal.helper_funcs import get_flair +from gamestonk_terminal.menu import session +from gamestonk_terminal.discovery import ( + alpha_vantage_view, + ark_view, + fidelity_view, + finviz_view, + seeking_alpha_view, + short_interest_view, + simply_wallst_view, + spachero_view, + unusual_whales_view, + yahoo_finance_view, +) +from prompt_toolkit.completion import NestedCompleter + + +class DiscoveryController: + """ Discovery Controller """ + + # Command choices + CHOICES = [ + "help", + "q", + "quit", + "map", + "sectors", + "gainers", + "losers", + "orders", + "ark_orders", + "up_earnings", + "high_short", + "low_float", + "simply_wallst", + "spachero", + "uwhales", + ] + + def __init__(self): + """Constructor""" + self.disc_parser = argparse.ArgumentParser(add_help=False, prog="disc") + self.disc_parser.add_argument( + "cmd", + choices=self.CHOICES, + ) + + @staticmethod + def print_help(): + """ Print help """ + + print("\nDiscovery Mode:") + print(" help show this discovery menu again") + print(" q quit this menu, and shows back to main menu") + print(" quit quit to abandon program") + print("") + print(" map S&P500 index stocks map [Finviz]") + print(" sectors show sectors performance [Alpha Vantage]") + print(" gainers show latest top gainers [Yahoo Finance]") + print(" losers show latest top losers [Yahoo Finance]") + print(" orders orders by Fidelity Customers [Fidelity]") + print(" ark_orders orders by ARK Investment Management LLC") + print(" up_earnings upcoming earnings release dates [Seeking Alpha]") + print( + " high_short show top high short interest stocks of over 20% ratio [www.highshortinterest.com]" + ) + print( + " low_float show low float stocks under 10M shares float [www.lowfloat.com]" + ) + print(" simply_wallst Simply Wall St. research data [Simply Wall St.]") + print(" spachero great website for SPACs research [SpacHero]") + print(" uwhales good website for SPACs research [UnusualWhales]") + 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.disc_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_map(self, other_args: List[str]): + """Process map command""" + finviz_view.map_sp500(other_args) + + def call_sectors(self, other_args: List[str]): + """Process sectors command""" + alpha_vantage_view.sectors(other_args) + + def call_gainers(self, other_args: List[str]): + """Process gainers command""" + yahoo_finance_view.gainers(other_args) + + def call_losers(self, other_args: List[str]): + """Process losers command""" + yahoo_finance_view.losers(other_args) + + def call_orders(self, other_args: List[str]): + """Process orders command""" + fidelity_view.orders(other_args) + + def call_ark_orders(self, other_args: List[str]): + """Process ark_orders command""" + ark_view.ark_orders(other_args) + + def call_up_earnings(self, other_args: List[str]): + """Process up_earnings command""" + seeking_alpha_view.earnings_release_dates(other_args) + + def call_high_short(self, other_args: List[str]): + """Process high_short command""" + short_interest_view.high_short_interest(other_args) + + def call_low_float(self, other_args: List[str]): + """Process low_float command""" + short_interest_view.low_float(other_args) + + def call_simply_wallst(self, other_args: List[str]): + """Process simply_wallst command""" + simply_wallst_view.simply_wallst(other_args) + + def call_spachero(self, other_args: List[str]): + """Process spachero command""" + spachero_view.spachero(other_args) + + def call_uwhales(self, other_args: List[str]): + """Process uwhales command""" + unusual_whales_view.unusual_whales(other_args) + + +def menu(): + """Discovery Menu""" + + disc_controller = DiscoveryController() + disc_controller.call_help(None) + + # Loop forever and ever + while True: + # Get input command from user + if session and gtff.USE_PROMPT_TOOLKIT: + completer = NestedCompleter.from_nested_dict( + {c: None for c in disc_controller.CHOICES} + ) + + an_input = session.prompt( + f"{get_flair()} (disc)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (disc)> ") + + try: + process_input = disc_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/discovery/disc_menu.py b/gamestonk_terminal/discovery/disc_menu.py deleted file mode 100644 index 130a32b3bc2..00000000000 --- a/gamestonk_terminal/discovery/disc_menu.py +++ /dev/null @@ -1,144 +0,0 @@ -import argparse - -from gamestonk_terminal.discovery import ( - alpha_vantage_api, - ark_api, - fidelity_api, - finviz_api, - seeking_alpha_api, - short_interest_api, - simply_wallst_api, - spachero_api, - unusual_whales_api, - yahoo_finance_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_discovery(): - """ Print help """ - - print("\nDiscovery Mode:") - print(" help show this discovery menu again") - print(" q quit this menu, and shows back to main menu") - print(" quit quit to abandon program") - print("") - print(" map S&P500 index stocks map [Finviz]") - print(" sectors show sectors performance [Alpha Vantage]") - print(" gainers show latest top gainers [Yahoo Finance]") - print(" losers show latest top losers [Yahoo Finance]") - print(" orders orders by Fidelity Customers [Fidelity]") - print(" ark_orders orders by ARK Investment Management LLC") - print(" up_earnings upcoming earnings release dates [Seeking Alpha]") - print( - " high_short show top high short interest stocks of over 20% ratio [www.highshortinterest.com]" - ) - print( - " low_float show low float stocks under 10M shares float [www.lowfloat.com]" - ) - print(" simply_wallst Simply Wall St. research data [Simply Wall St.]") - print(" spachero great website for SPACs research [SpacHero]") - print(" uwhales good website for SPACs research [UnusualWhales]") - print("") - return - - -def disc_menu(): - - # Add list of arguments that the discovery parser accepts - disc_parser = argparse.ArgumentParser(add_help=False, prog="discovery") - choices = [ - "help", - "q", - "quit", - "map", - "sectors", - "gainers", - "losers", - "spacs", - "orders", - "ark_orders", - "spachero", - "high_short", - "low_float", - "up_earnings", - "simply_wallst", - "uwhales", - "mill", - ] - disc_parser.add_argument("cmd", choices=choices) - completer = NestedCompleter.from_nested_dict({c: None for c in choices}) - - print_discovery() - - # 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()} (disc)> ", - completer=completer, - ) - else: - as_input = input(f"{get_flair()} (disc)> ") - - # Parse fundamental analysis command of the list of possible commands - try: - (ns_known_args, l_args) = disc_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_discovery() - - elif ns_known_args.cmd == "q": - # Just leave the DISC menu - return False - - elif ns_known_args.cmd == "quit": - # Abandon the program - return True - - elif ns_known_args.cmd == "map": - finviz_api.map_sp500(l_args) - - elif ns_known_args.cmd == "sectors": - alpha_vantage_api.sectors(l_args) - - elif ns_known_args.cmd == "gainers": - yahoo_finance_api.gainers(l_args) - - elif ns_known_args.cmd == "losers": - yahoo_finance_api.losers(l_args) - - elif ns_known_args.cmd == "spachero": - spachero_api.spachero(l_args) - - elif ns_known_args.cmd == "uwhales": - unusual_whales_api.unusual_whales(l_args) - - elif ns_known_args.cmd == "orders": - fidelity_api.orders(l_args) - - elif ns_known_args.cmd == "ark_orders": - ark_api.ark_orders(l_args) - - elif ns_known_args.cmd == "simply_wallst": - simply_wallst_api.simply_wallst(l_args) - - elif ns_known_args.cmd == "up_earnings": - seeking_alpha_api.earnings_release_dates(l_args) - - 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_view.py similarity index 100% rename from gamestonk_terminal/discovery/fidelity_api.py rename to gamestonk_terminal/discovery/fidelity_view.py diff --git a/gamestonk_terminal/discovery/finviz_api.py b/gamestonk_terminal/discovery/finviz_view.py similarity index 100% rename from gamestonk_terminal/discovery/finviz_api.py rename to gamestonk_terminal/discovery/finviz_view.py diff --git a/gamestonk_terminal/discovery/seeking_alpha_api.py b/gamestonk_terminal/discovery/seeking_alpha_view.py similarity index 100% rename from gamestonk_terminal/discovery/seeking_alpha_api.py rename to gamestonk_terminal/discovery/seeking_alpha_view.py diff --git a/gamestonk_terminal/discovery/short_interest_api.py b/gamestonk_terminal/discovery/short_interest_view.py similarity index 100% rename from gamestonk_terminal/discovery/short_interest_api.py rename to gamestonk_terminal/discovery/short_interest_view.py diff --git a/gamestonk_terminal/discovery/simply_wallst_api.py b/gamestonk_terminal/discovery/simply_wallst_view.py similarity index 100% rename from gamestonk_terminal/discovery/simply_wallst_api.py rename to gamestonk_terminal/discovery/simply_wallst_view.py diff --git a/gamestonk_terminal/discovery/spachero_api.py b/gamestonk_terminal/discovery/spachero_view.py similarity index 100% rename from gamestonk_terminal/discovery/spachero_api.py rename to gamestonk_terminal/discovery/spachero_view.py diff --git a/gamestonk_terminal/discovery/unusual_whales_api.py b/gamestonk_terminal/discovery/unusual_whales_view.py similarity index 100% rename from gamestonk_terminal/discovery/unusual_whales_api.py rename to gamestonk_terminal/discovery/unusual_whales_view.py diff --git a/gamestonk_terminal/discovery/yahoo_finance_api.py b/gamestonk_terminal/discovery/yahoo_finance_view.py similarity index 100% rename from gamestonk_terminal/discovery/yahoo_finance_api.py rename to gamestonk_terminal/discovery/yahoo_finance_view.py diff --git a/terminal.py b/terminal.py index 125be2ffc1a..040e1914398 100644 --- a/terminal.py +++ b/terminal.py @@ -14,7 +14,7 @@ from gamestonk_terminal import config_terminal as cfg 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_menu as dm +from gamestonk_terminal.discovery import disc_controller from gamestonk_terminal.due_diligence import dd_menu as ddm from gamestonk_terminal.fundamental_analysis import fa_menu as fam from gamestonk_terminal.helper_funcs import b_is_stock_market_open, get_flair @@ -184,7 +184,7 @@ def main(): main_cmd = True elif ns_known_args.opt == "disc": - b_quit = dm.disc_menu() + b_quit = disc_controller.menu() elif ns_known_args.opt == "mill": b_quit = mill.papermill_menu() diff --git a/tests/test_discovery_fidelity_api.py b/tests/test_discovery_fidelity_api.py index ec06fe607db..ac6850610a8 100644 --- a/tests/test_discovery_fidelity_api.py +++ b/tests/test_discovery_fidelity_api.py @@ -1,7 +1,7 @@ """ discovery/fidelity_api.py tests """ import unittest -from gamestonk_terminal.discovery.fidelity_api import ( +from gamestonk_terminal.discovery.fidelity_view import ( buy_sell_ratio_color_red_green, price_change_color_red_green, orders, diff --git a/tests/test_discovery_short_interest_api.py b/tests/test_discovery_short_interest_api.py index ced87574ca0..20a80788a42 100644 --- a/tests/test_discovery_short_interest_api.py +++ b/tests/test_discovery_short_interest_api.py @@ -8,7 +8,7 @@ from gamestonk_terminal.test_helper import ( # noqa: F401 pytest_generate_tests, ) -from gamestonk_terminal.discovery.short_interest_api import ( +from gamestonk_terminal.discovery.short_interest_view import ( high_short_interest, low_float, ) @@ -17,7 +17,7 @@ assertions = unittest.TestCase("__init__") class TestDiscoveryShortInterestApi: - @mock.patch("gamestonk_terminal.discovery.short_interest_api.requests") + @mock.patch("gamestonk_terminal.discovery.short_interest_view.requests") @parameterize_from_file( "test_high_short_interest", "../tests/data/discovery_short_interest_api.yaml", @@ -29,7 +29,7 @@ class TestDiscoveryShortInterestApi: mock_request_get.get().text = mock_high_short high_short_interest(["-n", "10"]) - @mock.patch("gamestonk_terminal.discovery.short_interest_api.requests") + @mock.patch("gamestonk_terminal.discovery.short_interest_view.requests") @parameterize_from_file( "test_low_float", "../tests/data/discovery_short_interest_api.yaml", From 8856fb7826b8376a8519b68f492f19bdc728007f Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Sun, 28 Mar 2021 13:09:22 -0700 Subject: [PATCH 02/11] Refactoring ARK --- gamestonk_terminal/discovery/ark_model.py | 76 +++++++++++++++++++++ gamestonk_terminal/discovery/ark_view.py | 80 ++--------------------- 2 files changed, 80 insertions(+), 76 deletions(-) create mode 100755 gamestonk_terminal/discovery/ark_model.py diff --git a/gamestonk_terminal/discovery/ark_model.py b/gamestonk_terminal/discovery/ark_model.py new file mode 100755 index 00000000000..a94b467b0ab --- /dev/null +++ b/gamestonk_terminal/discovery/ark_model.py @@ -0,0 +1,76 @@ +from datetime import timedelta +import json +import requests +from bs4 import BeautifulSoup +import numpy as np +import pandas as pd +from pandas.core.frame import DataFrame +import yfinance as yf + +from gamestonk_terminal.helper_funcs import get_user_agent + + +def get_ark_orders() -> DataFrame: + url_orders = "https://cathiesark.com/ark-funds-combined/trades" + + raw_page = requests.get(url_orders, headers={"User-Agent": get_user_agent()}).text + + parsed_script = BeautifulSoup(raw_page, "lxml").find( + "script", {"id": "__NEXT_DATA__"} + ) + + parsed_json = json.loads(parsed_script.string) + + df_orders = pd.json_normalize(parsed_json["props"]["pageProps"]["arkTrades"]) + + if df_orders.empty: + return pd.DataFrame() + + df_orders.drop( + [ + "everything", + "everything.profile.customThumbnail", + "hidden", + "images.thumbnail", + ], + axis=1, + inplace=True, + ) + + df_orders["date"] = pd.to_datetime(df_orders["date"], format="%Y-%m-%dZ").dt.date + + return df_orders + + +def add_order_total(df_orders: DataFrame) -> DataFrame: + start_date = df_orders["date"].iloc[-1] - timedelta(days=1) + + tickers = " ".join(df_orders["ticker"].unique()) + + print("") + + prices = yf.download(tickers, start=start_date, progress=False) + + for i, candle in enumerate(["Volume", "Open", "Close", "High", "Low", "Total"]): + df_orders.insert(i + 3, candle.lower(), 0) + + for i, _ in df_orders.iterrows(): + if np.isnan( + prices["Open"][df_orders.loc[i, "ticker"]][ + df_orders.loc[i, "date"].strftime("%Y-%m-%d") + ] + ): + for candle in ["Volume", "Open", "Close", "High", "Low", "Total"]: + df_orders.loc[i, candle.lower()] = 0 + continue + + for candle in ["Volume", "Open", "Close", "High", "Low"]: + df_orders.loc[i, candle.lower()] = prices[candle][ + df_orders.loc[i, "ticker"] + ][df_orders.loc[i, "date"].strftime("%Y-%m-%d")] + + df_orders.loc[i, "total"] = ( + df_orders.loc[i, "close"] * df_orders.loc[i, "shares"] + ) + + return df_orders diff --git a/gamestonk_terminal/discovery/ark_view.py b/gamestonk_terminal/discovery/ark_view.py index 34a84c27601..ed6f77cb3a0 100644 --- a/gamestonk_terminal/discovery/ark_view.py +++ b/gamestonk_terminal/discovery/ark_view.py @@ -1,22 +1,16 @@ import argparse -from datetime import timedelta -import json -import requests from colorama import Fore, Style -from bs4 import BeautifulSoup -import numpy as np import pandas as pd -from pandas.core.frame import DataFrame -import yfinance as yf from gamestonk_terminal.helper_funcs import ( check_positive, - get_user_agent, patch_pandas_text_adjustment, parse_known_args_and_warn, ) from gamestonk_terminal import feature_flags as gtff +from gamestonk_terminal.discovery import ark_model + def direction_color_red_green(val: str) -> str: if val == "Buy": @@ -29,72 +23,6 @@ def direction_color_red_green(val: str) -> str: return ret -def get_ark_orders() -> DataFrame: - url_orders = "https://cathiesark.com/ark-funds-combined/trades" - - raw_page = requests.get(url_orders, headers={"User-Agent": get_user_agent()}).text - - parsed_script = BeautifulSoup(raw_page, "lxml").find( - "script", {"id": "__NEXT_DATA__"} - ) - - parsed_json = json.loads(parsed_script.string) - - df_orders = pd.json_normalize(parsed_json["props"]["pageProps"]["arkTrades"]) - - if df_orders.empty: - return pd.DataFrame() - - df_orders.drop( - [ - "everything", - "everything.profile.customThumbnail", - "hidden", - "images.thumbnail", - ], - axis=1, - inplace=True, - ) - - df_orders["date"] = pd.to_datetime(df_orders["date"], format="%Y-%m-%dZ").dt.date - - return df_orders - - -def add_order_total(df_orders: DataFrame) -> DataFrame: - start_date = df_orders["date"].iloc[-1] - timedelta(days=1) - - tickers = " ".join(df_orders["ticker"].unique()) - - print("") - - prices = yf.download(tickers, start=start_date, progress=False) - - for i, candle in enumerate(["Volume", "Open", "Close", "High", "Low", "Total"]): - df_orders.insert(i + 3, candle.lower(), 0) - - for i, _ in df_orders.iterrows(): - if np.isnan( - prices["Open"][df_orders.loc[i, "ticker"]][ - df_orders.loc[i, "date"].strftime("%Y-%m-%d") - ] - ): - for candle in ["Volume", "Open", "Close", "High", "Low", "Total"]: - df_orders.loc[i, candle.lower()] = 0 - continue - - for candle in ["Volume", "Open", "Close", "High", "Low"]: - df_orders.loc[i, candle.lower()] = prices[candle][ - df_orders.loc[i, "ticker"] - ][df_orders.loc[i, "date"].strftime("%Y-%m-%d")] - - df_orders.loc[i, "total"] = ( - df_orders.loc[i, "close"] * df_orders.loc[i, "shares"] - ) - - return df_orders - - def ark_orders(l_args): parser = argparse.ArgumentParser( add_help=False, @@ -118,14 +46,14 @@ def ark_orders(l_args): if not ns_parser: return - df_orders = get_ark_orders() + df_orders = ark_model.get_ark_orders() if df_orders.empty: print("The ARK orders aren't anavilable at the moment.\n") return pd.set_option("mode.chained_assignment", None) - df_orders = add_order_total(df_orders.head(ns_parser.n_num)) + df_orders = ark_model.add_order_total(df_orders.head(ns_parser.n_num)) if gtff.USE_COLOR: df_orders["direction"] = df_orders["direction"].apply(direction_color_red_green) From c5b4f74bf4cd9730bddc05a5ba04345a249d5bff Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Sun, 28 Mar 2021 13:12:45 -0700 Subject: [PATCH 03/11] Add ion to Discovery/Sectors view --- gamestonk_terminal/discovery/alpha_vantage_view.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gamestonk_terminal/discovery/alpha_vantage_view.py b/gamestonk_terminal/discovery/alpha_vantage_view.py index 01fd3723edc..57f07cbecae 100644 --- a/gamestonk_terminal/discovery/alpha_vantage_view.py +++ b/gamestonk_terminal/discovery/alpha_vantage_view.py @@ -3,6 +3,7 @@ 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 +from gamestonk_terminal import feature_flags as gtff def sectors(l_args): @@ -27,5 +28,9 @@ def sectors(l_args): plt.title("Real Time Performance (%) per Sector") plt.tight_layout() plt.grid() + + if gtff.USE_ION: + plt.ion() + plt.show() print("") From 326fcc43c7ad1b6370f28edc9ac88431f59a7071 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Sun, 28 Mar 2021 13:30:22 -0700 Subject: [PATCH 04/11] Add documentation to ARK model --- gamestonk_terminal/discovery/ark_model.py | 27 +++++++++++++++++++++++ gamestonk_terminal/discovery/ark_view.py | 3 +++ 2 files changed, 30 insertions(+) diff --git a/gamestonk_terminal/discovery/ark_model.py b/gamestonk_terminal/discovery/ark_model.py index a94b467b0ab..e48988dee60 100755 --- a/gamestonk_terminal/discovery/ark_model.py +++ b/gamestonk_terminal/discovery/ark_model.py @@ -1,3 +1,6 @@ +""" ARK Model """ +__docformat__ = "numpy" + from datetime import timedelta import json import requests @@ -11,6 +14,15 @@ from gamestonk_terminal.helper_funcs import get_user_agent def get_ark_orders() -> DataFrame: + """Returns a ARK orders as a Dataframe + + Returns + ------- + DataFrame + ARK orders data frame with the following columns: + ticker, date, shares, weight, fund, direction + """ + url_orders = "https://cathiesark.com/ark-funds-combined/trades" raw_page = requests.get(url_orders, headers={"User-Agent": get_user_agent()}).text @@ -43,6 +55,21 @@ def get_ark_orders() -> DataFrame: def add_order_total(df_orders: DataFrame) -> DataFrame: + """Takes an ARK orders dataframe and pulls data from Yahoo Finance to add + volume, open, close, high, low, and total columns + + Parameters + ---------- + df_orders : DataFrame + ARK orders data frame with the following columns: + ticker, date, shares, weight, fund, direction + + Returns + ------- + DataFrame + ARK orders data frame with the following columns: + ticker, date, shares, volume, open, close, high, low, total, weight, fund, direction + """ start_date = df_orders["date"].iloc[-1] - timedelta(days=1) tickers = " ".join(df_orders["ticker"].unique()) diff --git a/gamestonk_terminal/discovery/ark_view.py b/gamestonk_terminal/discovery/ark_view.py index ed6f77cb3a0..2244258c5a8 100644 --- a/gamestonk_terminal/discovery/ark_view.py +++ b/gamestonk_terminal/discovery/ark_view.py @@ -1,3 +1,6 @@ +""" ARK View """ +__docformat__ = "numpy" + import argparse from colorama import Fore, Style import pandas as pd From 4b873be3192a44bd6eda5c4885b34825d9644c8c Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Sun, 28 Mar 2021 13:40:35 -0700 Subject: [PATCH 05/11] Updating ARK view --- gamestonk_terminal/discovery/ark_view.py | 25 +++++++++++++++++-- .../discovery/disc_controller.py | 4 +-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/gamestonk_terminal/discovery/ark_view.py b/gamestonk_terminal/discovery/ark_view.py index 2244258c5a8..afa17263e36 100644 --- a/gamestonk_terminal/discovery/ark_view.py +++ b/gamestonk_terminal/discovery/ark_view.py @@ -2,6 +2,7 @@ __docformat__ = "numpy" import argparse +from typing import List from colorama import Fore, Style import pandas as pd @@ -16,6 +17,19 @@ from gamestonk_terminal.discovery import ark_model def direction_color_red_green(val: str) -> str: + """Adds color tags to the Direction information: Buy -> Green, Sell -> Red + + Parameters + ---------- + val : str + Direction string - either Buy or Sell + + Returns + ------- + str + Direction string with color tags added + """ + if val == "Buy": ret = Fore.GREEN + val + Style.RESET_ALL elif val == "Sell": @@ -26,7 +40,14 @@ def direction_color_red_green(val: str) -> str: return ret -def ark_orders(l_args): +def ark_orders_view(other_args: List[str]): + """Prints a table of the last N ARK Orders + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "10"] + """ parser = argparse.ArgumentParser( add_help=False, prog="ARK Orders", @@ -45,7 +66,7 @@ def ark_orders(l_args): help="Last N orders.", ) - 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 diff --git a/gamestonk_terminal/discovery/disc_controller.py b/gamestonk_terminal/discovery/disc_controller.py index fb754300b51..4cafd4bff4a 100644 --- a/gamestonk_terminal/discovery/disc_controller.py +++ b/gamestonk_terminal/discovery/disc_controller.py @@ -1,5 +1,6 @@ import argparse from typing import List +from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair @@ -16,7 +17,6 @@ from gamestonk_terminal.discovery import ( unusual_whales_view, yahoo_finance_view, ) -from prompt_toolkit.completion import NestedCompleter class DiscoveryController: @@ -126,7 +126,7 @@ class DiscoveryController: def call_ark_orders(self, other_args: List[str]): """Process ark_orders command""" - ark_view.ark_orders(other_args) + ark_view.ark_orders_view(other_args) def call_up_earnings(self, other_args: List[str]): """Process up_earnings command""" From 366f42a515d5090479b13c7e50d11da2a221d62b Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Sun, 28 Mar 2021 20:46:22 -0700 Subject: [PATCH 06/11] Refactoring Fidelity view --- gamestonk_terminal/discovery/ark_model.py | 2 +- .../discovery/disc_controller.py | 2 +- .../discovery/fidelity_model.py | 93 ++++++++++++++ gamestonk_terminal/discovery/fidelity_view.py | 116 +++++++----------- tests/test_discovery_fidelity_api.py | 4 +- 5 files changed, 141 insertions(+), 76 deletions(-) mode change 100755 => 100644 gamestonk_terminal/discovery/ark_model.py create mode 100644 gamestonk_terminal/discovery/fidelity_model.py diff --git a/gamestonk_terminal/discovery/ark_model.py b/gamestonk_terminal/discovery/ark_model.py old mode 100755 new mode 100644 index e48988dee60..0453c927a04 --- a/gamestonk_terminal/discovery/ark_model.py +++ b/gamestonk_terminal/discovery/ark_model.py @@ -14,7 +14,7 @@ from gamestonk_terminal.helper_funcs import get_user_agent def get_ark_orders() -> DataFrame: - """Returns a ARK orders as a Dataframe + """Returns ARK orders in a Dataframe Returns ------- diff --git a/gamestonk_terminal/discovery/disc_controller.py b/gamestonk_terminal/discovery/disc_controller.py index 4cafd4bff4a..9579cbdee22 100644 --- a/gamestonk_terminal/discovery/disc_controller.py +++ b/gamestonk_terminal/discovery/disc_controller.py @@ -122,7 +122,7 @@ class DiscoveryController: def call_orders(self, other_args: List[str]): """Process orders command""" - fidelity_view.orders(other_args) + fidelity_view.orders_view(other_args) def call_ark_orders(self, other_args: List[str]): """Process ark_orders command""" diff --git a/gamestonk_terminal/discovery/fidelity_model.py b/gamestonk_terminal/discovery/fidelity_model.py new file mode 100644 index 00000000000..4d0c625584b --- /dev/null +++ b/gamestonk_terminal/discovery/fidelity_model.py @@ -0,0 +1,93 @@ +""" Fidelity Model """ +__docformat__ = "numpy" + +from typing import Tuple +import requests +from bs4 import BeautifulSoup +import pandas as pd +from pandas.core.frame import DataFrame + +from gamestonk_terminal.helper_funcs import get_user_agent + + +def get_orders() -> Tuple[str, DataFrame]: + """Returns Fidelity orders in a Dataframe + + Returns + ------- + Tuple[str, DataFrame] + First value in the tuple is a Fidelity orders header + Fidelity orders Dataframe with the following columns: + Symbol, Buy / Sell Ratio, Price Change, Company, # Buy Orders, # Sell Orders + + """ + + 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" + ) + + 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('"/>') + ] + ) + else: + l_orders_vals.append(an_order.text.strip()) + + idx += 1 + + # 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 = df_orders[ + [ + "Symbol", + "Buy / Sell Ratio", + "Price Change", + "Company", + "# Buy Orders", + "# Sell Orders", + "Latest News", + ] + ] + + order_header = text_soup_url_orders.findAll("span", {"class": "source"})[ + 0 + ].text.capitalize() + + return order_header, df_orders diff --git a/gamestonk_terminal/discovery/fidelity_view.py b/gamestonk_terminal/discovery/fidelity_view.py index cf81fecedd8..ada56b013bb 100644 --- a/gamestonk_terminal/discovery/fidelity_view.py +++ b/gamestonk_terminal/discovery/fidelity_view.py @@ -1,20 +1,36 @@ +""" Fidelity View """ +__docformat__ = "numpy" + import argparse +from typing import List import re -import requests from colorama import Fore, Style -from bs4 import BeautifulSoup import pandas as pd from gamestonk_terminal.helper_funcs import ( check_positive, - get_user_agent, patch_pandas_text_adjustment, parse_known_args_and_warn, ) from gamestonk_terminal import feature_flags as gtff +from gamestonk_terminal.discovery import fidelity_model + def buy_sell_ratio_color_red_green(val: str) -> str: + """Add color tags to the Buys/Sells ratio cell + + Parameters + ---------- + val : str + Buys/Sells ratio cell + + Returns + ------- + str + Buys/Sells ratio cell with color tags + """ + buy_sell_match = re.match(r"(\d+)% Buys, (\d+)% Sells", val, re.M | re.I) if not buy_sell_match: @@ -32,6 +48,19 @@ def buy_sell_ratio_color_red_green(val: str) -> str: def price_change_color_red_green(val: str) -> str: + """Add color tags to the price change cell + + Parameters + ---------- + val : str + Price change cell + + Returns + ------- + str + Price change cell with color tags + """ + val_float = float(val.split(" ")[0]) if val_float > 0: color = Fore.GREEN @@ -40,7 +69,15 @@ def price_change_color_red_green(val: str) -> str: return color + val + Style.RESET_ALL -def orders(l_args): +def orders_view(other_args: List[str]): + """Prints a table with the last N orders by Fidelity customers + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "10"] + """ + parser = argparse.ArgumentParser( add_help=False, prog="orders", @@ -62,78 +99,13 @@ def orders(l_args): help="Number of top ordered stocks to be printed.", ) - 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_orders = ( - "https://eresearch.fidelity.com/eresearch/gotoBL/fidelityTopOrders.jhtml" - ) + order_header, df_orders = fidelity_model.get_orders() - 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('"/>') - ] - ) - else: - l_orders_vals.append(an_order.text.strip()) - - idx += 1 - - # 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 = 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(order_header, ":") pd.set_option("display.max_colwidth", None) diff --git a/tests/test_discovery_fidelity_api.py b/tests/test_discovery_fidelity_api.py index ac6850610a8..7396e54e754 100644 --- a/tests/test_discovery_fidelity_api.py +++ b/tests/test_discovery_fidelity_api.py @@ -4,13 +4,13 @@ import unittest from gamestonk_terminal.discovery.fidelity_view import ( buy_sell_ratio_color_red_green, price_change_color_red_green, - orders, + orders_view, ) class TestDiscoveryFidelityApi(unittest.TestCase): def test_orders(self): - orders([]) + orders_view([]) def test_buy_sell_ratio_color_red_green(self): res = buy_sell_ratio_color_red_green("56% Buys, 44% Sells") From 691da5031747eed0c147db018dd201c1df23cd51 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Sun, 28 Mar 2021 23:28:57 -0700 Subject: [PATCH 07/11] Add docstrings and update list of arguments --- .../discovery/alpha_vantage_view.py | 22 ++++++++++++--- .../discovery/disc_controller.py | 23 ++++++++------- gamestonk_terminal/discovery/finviz_view.py | 16 +++++++++-- .../discovery/seeking_alpha_view.py | 16 +++++++++-- .../discovery/short_interest_view.py | 27 +++++++++++++++--- .../discovery/simply_wallst_view.py | 16 +++++++++-- gamestonk_terminal/discovery/spachero_view.py | 16 +++++++++-- .../discovery/unusual_whales_view.py | 13 +++++++-- .../discovery/yahoo_finance_view.py | 28 ++++++++++++++++--- 9 files changed, 145 insertions(+), 32 deletions(-) diff --git a/gamestonk_terminal/discovery/alpha_vantage_view.py b/gamestonk_terminal/discovery/alpha_vantage_view.py index 57f07cbecae..63bdfee8d51 100644 --- a/gamestonk_terminal/discovery/alpha_vantage_view.py +++ b/gamestonk_terminal/discovery/alpha_vantage_view.py @@ -1,4 +1,8 @@ +""" Alpha Vantage View """ +__docformat__ = "numpy" + import argparse +from typing import List import matplotlib.pyplot as plt from alpha_vantage.sectorperformance import SectorPerformances from gamestonk_terminal import config_terminal as cfg @@ -6,15 +10,25 @@ from gamestonk_terminal.helper_funcs import parse_known_args_and_warn from gamestonk_terminal import feature_flags as gtff -def sectors(l_args): +def sectors_view(other_args: List[str]): + """Opens a bar chart with sector performance + + Parameters + ---------- + other_args : List[str] + argparse other args + """ + parser = argparse.ArgumentParser( add_help=False, 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] + """, ) - 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 diff --git a/gamestonk_terminal/discovery/disc_controller.py b/gamestonk_terminal/discovery/disc_controller.py index 9579cbdee22..a8c44d739e0 100644 --- a/gamestonk_terminal/discovery/disc_controller.py +++ b/gamestonk_terminal/discovery/disc_controller.py @@ -1,3 +1,6 @@ +""" Disc Controller """ +__docformat__ = "numpy" + import argparse from typing import List from prompt_toolkit.completion import NestedCompleter @@ -106,19 +109,19 @@ class DiscoveryController: def call_map(self, other_args: List[str]): """Process map command""" - finviz_view.map_sp500(other_args) + finviz_view.map_sp500_view(other_args) def call_sectors(self, other_args: List[str]): """Process sectors command""" - alpha_vantage_view.sectors(other_args) + alpha_vantage_view.sectors_view(other_args) def call_gainers(self, other_args: List[str]): """Process gainers command""" - yahoo_finance_view.gainers(other_args) + yahoo_finance_view.gainers_view(other_args) def call_losers(self, other_args: List[str]): """Process losers command""" - yahoo_finance_view.losers(other_args) + yahoo_finance_view.losers_view(other_args) def call_orders(self, other_args: List[str]): """Process orders command""" @@ -130,27 +133,27 @@ class DiscoveryController: def call_up_earnings(self, other_args: List[str]): """Process up_earnings command""" - seeking_alpha_view.earnings_release_dates(other_args) + seeking_alpha_view.earnings_release_dates_view(other_args) def call_high_short(self, other_args: List[str]): """Process high_short command""" - short_interest_view.high_short_interest(other_args) + short_interest_view.high_short_interest_view(other_args) def call_low_float(self, other_args: List[str]): """Process low_float command""" - short_interest_view.low_float(other_args) + short_interest_view.low_float_view(other_args) def call_simply_wallst(self, other_args: List[str]): """Process simply_wallst command""" - simply_wallst_view.simply_wallst(other_args) + simply_wallst_view.simply_wallst_view(other_args) def call_spachero(self, other_args: List[str]): """Process spachero command""" - spachero_view.spachero(other_args) + spachero_view.spachero_view(other_args) def call_uwhales(self, other_args: List[str]): """Process uwhales command""" - unusual_whales_view.unusual_whales(other_args) + unusual_whales_view.unusual_whales_view(other_args) def menu(): diff --git a/gamestonk_terminal/discovery/finviz_view.py b/gamestonk_terminal/discovery/finviz_view.py index 980cec8c896..46715342ea2 100644 --- a/gamestonk_terminal/discovery/finviz_view.py +++ b/gamestonk_terminal/discovery/finviz_view.py @@ -1,9 +1,21 @@ +""" Finviz View """ +__docformat__ = "numpy" + import argparse +from typing import List import webbrowser from gamestonk_terminal.helper_funcs import parse_known_args_and_warn -def map_sp500(l_args): +def map_sp500_view(other_args: List[str]): + """Opens Finviz website in a browser + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-p", "6m", "-t", "sp500"] + """ + parser = argparse.ArgumentParser( add_help=False, prog="map", @@ -38,7 +50,7 @@ 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"} - 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 diff --git a/gamestonk_terminal/discovery/seeking_alpha_view.py b/gamestonk_terminal/discovery/seeking_alpha_view.py index 85e3d2180cd..61d3866d0bc 100644 --- a/gamestonk_terminal/discovery/seeking_alpha_view.py +++ b/gamestonk_terminal/discovery/seeking_alpha_view.py @@ -1,4 +1,8 @@ +""" Seeking Alpha View """ +__docformat__ = "numpy" + import argparse +from typing import List import requests from bs4 import BeautifulSoup import pandas as pd @@ -9,7 +13,15 @@ from gamestonk_terminal.helper_funcs import ( ) -def earnings_release_dates(l_args): +def earnings_release_dates_view(other_args: List[str]): + """Prints a data frame with earnings release dates + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-p", "20", "-n", "5"] + """ + parser = argparse.ArgumentParser( add_help=False, prog="up_earnings", @@ -35,7 +47,7 @@ def earnings_release_dates(l_args): help="Number of upcoming earnings release dates to print", ) - 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 diff --git a/gamestonk_terminal/discovery/short_interest_view.py b/gamestonk_terminal/discovery/short_interest_view.py index 0bb04eb2aff..67a0888c4b1 100644 --- a/gamestonk_terminal/discovery/short_interest_view.py +++ b/gamestonk_terminal/discovery/short_interest_view.py @@ -1,4 +1,8 @@ +""" Short Interest View """ +__docformat__ = "numpy" + import argparse +from typing import List import requests from bs4 import BeautifulSoup import pandas as pd @@ -9,7 +13,14 @@ from gamestonk_terminal.helper_funcs import ( ) -def high_short_interest(l_args): +def high_short_interest_view(other_args: List[str]): + """Prints top N shorted stocks + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "20"] + """ parser = argparse.ArgumentParser( add_help=False, prog="high_short", @@ -32,7 +43,7 @@ def high_short_interest(l_args): help="Number of top stocks to print.", ) - 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 @@ -77,7 +88,15 @@ def high_short_interest(l_args): print("") -def low_float(l_args): +def low_float_view(other_args: List[str]): + """Prints top N low float stocks + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "20"] + """ + parser = argparse.ArgumentParser( add_help=False, prog="low_float", @@ -100,7 +119,7 @@ def low_float(l_args): help="Number of top stocks to print.", ) - 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 diff --git a/gamestonk_terminal/discovery/simply_wallst_view.py b/gamestonk_terminal/discovery/simply_wallst_view.py index f3b6ed8ae71..fd6a4723382 100644 --- a/gamestonk_terminal/discovery/simply_wallst_view.py +++ b/gamestonk_terminal/discovery/simply_wallst_view.py @@ -1,9 +1,21 @@ +""" Simply Wallst View """ +__docformat__ = "numpy" + import argparse +from typing import List import webbrowser from gamestonk_terminal.helper_funcs import parse_known_args_and_warn -def simply_wallst(l_args): +def simply_wallst_view(other_args: List[str]): + """Opens simplywall.st for a specific industry in a browser + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-i", "banks"] + """ + parser = argparse.ArgumentParser( add_help=False, prog="simply_wallst", @@ -51,7 +63,7 @@ def simply_wallst(l_args): ], ) - 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 diff --git a/gamestonk_terminal/discovery/spachero_view.py b/gamestonk_terminal/discovery/spachero_view.py index b63cb956fd3..32ddf716b54 100644 --- a/gamestonk_terminal/discovery/spachero_view.py +++ b/gamestonk_terminal/discovery/spachero_view.py @@ -1,16 +1,28 @@ +""" Spachero View """ +__docformat__ = "numpy" + import argparse +from typing import List import webbrowser from gamestonk_terminal.helper_funcs import parse_known_args_and_warn -def spachero(l_args): +def spachero_view(other_args: List[str]): + """Opens www.spachero.com website in a browser + + Parameters + ---------- + other_args : List[str] + argparse other args + """ + parser = argparse.ArgumentParser( add_help=False, prog="spachero", description="""Great website for SPACs research. [Source: www.spachero.com]""", ) - 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 diff --git a/gamestonk_terminal/discovery/unusual_whales_view.py b/gamestonk_terminal/discovery/unusual_whales_view.py index 664f856d68a..85ba7a7f1c4 100644 --- a/gamestonk_terminal/discovery/unusual_whales_view.py +++ b/gamestonk_terminal/discovery/unusual_whales_view.py @@ -1,16 +1,25 @@ import argparse +from typing import List import webbrowser from gamestonk_terminal.helper_funcs import parse_known_args_and_warn -def unusual_whales(l_args): +def unusual_whales_view(other_args: List[str]): + """Opens unusualwhales.com in a browser + + Parameters + ---------- + other_args : List[str] + argparse other args + """ + parser = argparse.ArgumentParser( add_help=False, prog="uwhales", description="""Good website for SPACs research. [Source: www.unusualwhales.com]""", ) - 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 diff --git a/gamestonk_terminal/discovery/yahoo_finance_view.py b/gamestonk_terminal/discovery/yahoo_finance_view.py index b275764de36..9bf10442bbb 100644 --- a/gamestonk_terminal/discovery/yahoo_finance_view.py +++ b/gamestonk_terminal/discovery/yahoo_finance_view.py @@ -1,9 +1,21 @@ +""" Yahoo Finance View """ +__docformat__ = "numpy" + import argparse +from typing import List import pandas as pd from gamestonk_terminal.helper_funcs import parse_known_args_and_warn -def gainers(l_args): +def gainers_view(other_args: List[str]): + """Prints top N gainers + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "20"] + """ + parser = argparse.ArgumentParser( add_help=False, prog="gainers", @@ -21,7 +33,7 @@ def gainers(l_args): help="Number of the top gainers stocks to retrieve.", ) - 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 @@ -32,7 +44,15 @@ def gainers(l_args): print("") -def losers(l_args): +def losers_view(other_args: List[str]): + """Prints top N loosers + + Parameters + ---------- + other_args : List[str] + argparse other args - ["-n", "20"] + """ + parser = argparse.ArgumentParser( add_help=False, prog="losers", @@ -50,7 +70,7 @@ def losers(l_args): help="Number of the top losers stocks to retrieve.", ) - 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 From a5ba6a2bfea1dd3b03d0162af396c93daf7afb17 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Mon, 29 Mar 2021 00:00:16 -0700 Subject: [PATCH 08/11] Refactor Seeking Alpha and add a model --- .../discovery/seeking_alpha_model.py | 51 +++++++++++++++++++ .../discovery/seeking_alpha_view.py | 30 ++--------- tests/test_discovery_short_interest_api.py | 8 +-- 3 files changed, 58 insertions(+), 31 deletions(-) create mode 100755 gamestonk_terminal/discovery/seeking_alpha_model.py diff --git a/gamestonk_terminal/discovery/seeking_alpha_model.py b/gamestonk_terminal/discovery/seeking_alpha_model.py new file mode 100755 index 00000000000..11ea8f8daa0 --- /dev/null +++ b/gamestonk_terminal/discovery/seeking_alpha_model.py @@ -0,0 +1,51 @@ +""" Seeking Alpha Model """ +__docformat__ = "numpy" + +import requests +from bs4 import BeautifulSoup +import pandas as pd +from pandas.core.frame import DataFrame + +from gamestonk_terminal.helper_funcs import get_user_agent + + +def get_next_earnings(pages: int) -> DataFrame: + """Returns a DataFrame with upcoming earnings + + Parameters + ---------- + pages : int + Number of pages + + Returns + ------- + DataFrame + Upcoming earnings DataFrame + """ + + earnings = list() + for idx in range(0, 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 stock_rows in text_soup_earnings.findAll("tr", {"data-exchange": "NASDAQ"}): + stocks = list() + for a_stock in stock_rows.contents[:3]: + stocks.append(a_stock.text) + earnings.append(stocks) + + df_earnings = pd.DataFrame(earnings, columns=["Ticker", "Name", "Date"]) + df_earnings["Date"] = pd.to_datetime(df_earnings["Date"]) + df_earnings = df_earnings.set_index("Date") + + return df_earnings diff --git a/gamestonk_terminal/discovery/seeking_alpha_view.py b/gamestonk_terminal/discovery/seeking_alpha_view.py index 61d3866d0bc..2965bf79cce 100644 --- a/gamestonk_terminal/discovery/seeking_alpha_view.py +++ b/gamestonk_terminal/discovery/seeking_alpha_view.py @@ -3,15 +3,14 @@ __docformat__ = "numpy" import argparse from typing import List -import requests -from bs4 import BeautifulSoup import pandas as pd from gamestonk_terminal.helper_funcs import ( check_positive, - get_user_agent, parse_known_args_and_warn, ) +from gamestonk_terminal.discovery import seeking_alpha_model + def earnings_release_dates_view(other_args: List[str]): """Prints a data frame with earnings release dates @@ -51,30 +50,7 @@ def earnings_release_dates_view(other_args: List[str]): if not ns_parser: return - 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) - - 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 = seeking_alpha_model.get_next_earnings(ns_parser.n_pages) pd.set_option("display.max_colwidth", None) for n_days, earning_date in enumerate(df_earnings.index.unique()): diff --git a/tests/test_discovery_short_interest_api.py b/tests/test_discovery_short_interest_api.py index 20a80788a42..45946ee5e20 100644 --- a/tests/test_discovery_short_interest_api.py +++ b/tests/test_discovery_short_interest_api.py @@ -9,8 +9,8 @@ from gamestonk_terminal.test_helper import ( # noqa: F401 ) from gamestonk_terminal.discovery.short_interest_view import ( - high_short_interest, - low_float, + high_short_interest_view, + low_float_view, ) assertions = unittest.TestCase("__init__") @@ -27,7 +27,7 @@ class TestDiscoveryShortInterestApi: self, mock_request_get, mock_high_short, expected_result ): mock_request_get.get().text = mock_high_short - high_short_interest(["-n", "10"]) + high_short_interest_view(["-n", "10"]) @mock.patch("gamestonk_terminal.discovery.short_interest_view.requests") @parameterize_from_file( @@ -37,4 +37,4 @@ class TestDiscoveryShortInterestApi: # pylint: disable=unused-argument def test_low_float(self, mock_request_get, mock_low_float, expected_result): mock_request_get.get().text = mock_low_float - low_float(["-n", "10"]) + low_float_view(["-n", "10"]) From af6cc2eb5516657007dae2a65db1a296586f3227 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Mon, 29 Mar 2021 00:25:26 -0700 Subject: [PATCH 09/11] Refactor short interest and add a short interest model --- .../discovery/short_interest_model.py | 105 ++++++++++++++++++ .../discovery/short_interest_view.py | 73 +----------- 2 files changed, 109 insertions(+), 69 deletions(-) create mode 100644 gamestonk_terminal/discovery/short_interest_model.py diff --git a/gamestonk_terminal/discovery/short_interest_model.py b/gamestonk_terminal/discovery/short_interest_model.py new file mode 100644 index 00000000000..edfc89445b9 --- /dev/null +++ b/gamestonk_terminal/discovery/short_interest_model.py @@ -0,0 +1,105 @@ +""" Short Interest View """ +__docformat__ = "numpy" + +import requests +from bs4 import BeautifulSoup +import pandas as pd +from pandas.core.frame import DataFrame + +from gamestonk_terminal.helper_funcs import get_user_agent + + +def get_high_short_interest() -> DataFrame: + """Returns a high short interest DataFrame + + Returns + ------- + DataFrame + High short interest Dataframe with the following columns: + Ticker, Company, Exchange, ShortInt, Float, Outstd, Industry + """ + + url_high_short_interested_stocks = "https://www.highshortinterest.com" + + text_soup_high_short_interested_stocks = BeautifulSoup( + requests.get( + url_high_short_interested_stocks, headers={"User-Agent": get_user_agent()} + ).text, + "lxml", + ) + + a_high_short_interest_header = list() + for high_short_interest_header in text_soup_high_short_interested_stocks.findAll( + "td", {"class": "tblhdr"} + ): + a_high_short_interest_header.append( + high_short_interest_header.text.strip("\n").split("\n")[0] + ) + df_high_short_interest = pd.DataFrame(columns=a_high_short_interest_header) + df_high_short_interest.loc[0] = ["", "", "", "", "", "", ""] + + stock_list_tr = text_soup_high_short_interested_stocks.find_all("tr") + + shorted_stock_data = list() + for a_stock in stock_list_tr: + a_stock_txt = a_stock.text + + if a_stock_txt == "": + continue + + shorted_stock_data = a_stock_txt.split("\n") + + if len(shorted_stock_data) == 8: + df_high_short_interest.loc[ + len(df_high_short_interest.index) + ] = shorted_stock_data[:-1] + + shorted_stock_data = list() + + return df_high_short_interest + + +def get_low_float() -> DataFrame: + """Returns low float DataFrame + + Returns + ------- + DataFrame + Low float DataFrame with the following columns: + Ticker, Company, Exchange, ShortInt, Float, Outstd, Industry + """ + + url_high_short_interested_stocks = "https://www.lowfloat.com" + + text_soup_low_float_stocks = BeautifulSoup( + requests.get( + url_high_short_interested_stocks, headers={"User-Agent": get_user_agent()} + ).text, + "lxml", + ) + + a_low_float_header = list() + for low_float_header in text_soup_low_float_stocks.findAll( + "td", {"class": "tblhdr"} + ): + a_low_float_header.append(low_float_header.text.strip("\n").split("\n")[0]) + df_low_float = pd.DataFrame(columns=a_low_float_header) + df_low_float.loc[0] = ["", "", "", "", "", "", ""] + + stock_list_tr = text_soup_low_float_stocks.find_all("tr") + + low_float_data = list() + for a_stock in stock_list_tr: + a_stock_txt = a_stock.text + + if a_stock_txt == "": + continue + + low_float_data = a_stock_txt.split("\n") + + if len(low_float_data) == 8: + df_low_float.loc[len(df_low_float.index)] = low_float_data[:-1] + + low_float_data = list() + + return df_low_float diff --git a/gamestonk_terminal/discovery/short_interest_view.py b/gamestonk_terminal/discovery/short_interest_view.py index 67a0888c4b1..2fb74704014 100644 --- a/gamestonk_terminal/discovery/short_interest_view.py +++ b/gamestonk_terminal/discovery/short_interest_view.py @@ -3,15 +3,14 @@ __docformat__ = "numpy" import argparse from typing import List -import requests -from bs4 import BeautifulSoup import pandas as pd from gamestonk_terminal.helper_funcs import ( check_positive, - get_user_agent, parse_known_args_and_warn, ) +from gamestonk_terminal.discovery import short_interest_model + def high_short_interest_view(other_args: List[str]): """Prints top N shorted stocks @@ -47,41 +46,7 @@ def high_short_interest_view(other_args: List[str]): if not ns_parser: return - url_high_short_interested_stocks = "https://www.highshortinterest.com" - text_soup_high_short_interested_stocks = BeautifulSoup( - requests.get( - url_high_short_interested_stocks, headers={"User-Agent": get_user_agent()} - ).text, - "lxml", - ) - - a_high_short_interest_header = list() - for high_short_interest_header in text_soup_high_short_interested_stocks.findAll( - "td", {"class": "tblhdr"} - ): - a_high_short_interest_header.append( - high_short_interest_header.text.strip("\n").split("\n")[0] - ) - df_high_short_interest = pd.DataFrame(columns=a_high_short_interest_header) - df_high_short_interest.loc[0] = ["", "", "", "", "", "", ""] - - stock_list_tr = text_soup_high_short_interested_stocks.find_all("tr") - - shorted_stock_data = list() - for a_stock in stock_list_tr: - a_stock_txt = a_stock.text - - if a_stock_txt == "": - continue - - shorted_stock_data = a_stock_txt.split("\n") - - if len(shorted_stock_data) == 8: - df_high_short_interest.loc[ - len(df_high_short_interest.index) - ] = shorted_stock_data[:-1] - - shorted_stock_data = list() + df_high_short_interest = short_interest_model.get_high_short_interest() pd.set_option("display.max_colwidth", None) print(df_high_short_interest.head(n=ns_parser.n_num).to_string(index=False)) @@ -123,37 +88,7 @@ def low_float_view(other_args: List[str]): if not ns_parser: return - url_high_short_interested_stocks = "https://www.lowfloat.com" - text_soup_low_float_stocks = BeautifulSoup( - requests.get( - url_high_short_interested_stocks, headers={"User-Agent": get_user_agent()} - ).text, - "lxml", - ) - - a_low_float_header = list() - for low_float_header in text_soup_low_float_stocks.findAll( - "td", {"class": "tblhdr"} - ): - a_low_float_header.append(low_float_header.text.strip("\n").split("\n")[0]) - df_low_float = pd.DataFrame(columns=a_low_float_header) - df_low_float.loc[0] = ["", "", "", "", "", "", ""] - - stock_list_tr = text_soup_low_float_stocks.find_all("tr") - - low_float_data = list() - for a_stock in stock_list_tr: - a_stock_txt = a_stock.text - - if a_stock_txt == "": - continue - - low_float_data = a_stock_txt.split("\n") - - if len(low_float_data) == 8: - df_low_float.loc[len(df_low_float.index)] = low_float_data[:-1] - - low_float_data = list() + df_low_float = short_interest_model.get_low_float() pd.set_option("display.max_colwidth", None) print(df_low_float.head(n=ns_parser.n_num).to_string(index=False)) From c78364416139612266dac8778bbc1d77eeaa82f3 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Mon, 29 Mar 2021 00:39:11 -0700 Subject: [PATCH 10/11] Updating unit tests --- ...ml => discovery_short_interest_model.yaml} | 112 +++++++++++++++++- tests/test_discovery_short_interest_api.py | 40 ------- tests/test_discovery_short_interest_model.py | 54 +++++++++ 3 files changed, 162 insertions(+), 44 deletions(-) rename tests/data/{discovery_short_interest_api.yaml => discovery_short_interest_model.yaml} (86%) delete mode 100644 tests/test_discovery_short_interest_api.py create mode 100644 tests/test_discovery_short_interest_model.py diff --git a/tests/data/discovery_short_interest_api.yaml b/tests/data/discovery_short_interest_model.yaml similarity index 86% rename from tests/data/discovery_short_interest_api.yaml rename to tests/data/discovery_short_interest_model.yaml index ad58834d8aa..b2b1a5bac95 100755 --- a/tests/data/discovery_short_interest_api.yaml +++ b/tests/data/discovery_short_interest_model.yaml @@ -1,4 +1,4 @@ -test_high_short_interest: +test_get_high_short_interest: base_high_short: mock_high_short: | @@ -628,8 +628,60 @@ test_high_short_interest: - expected_result: "banana" -test_low_float: + expected_result: | + ,Ticker,Company,Exchange,ShortInt,Float,Outstd,Industry + 0,,,,,,, + 1,OTRK,"Ontrak, Inc.",Nasdaq,43.33%,7.53M,17.69M,Healthcare Facilities + 2,BLNK,Blink Charging Co,Nasdaq,41.94%,31.69M,38.95M,Utilities - Electric + 3,ROOT,Root Inc,Nasdaq,41.66%,26.62M,59.66M,Insurance - Multiline & Brokers + 4,GOGO,Gogo Inc,Nasdaq,40.74%,38.90M,86.40M,Communications Services + 5,GTT,GTT Communications Inc,NYSE,40.29%,25.15M,58.82M,IT Services & Consulting + 6,ASO,Academy Sports & Outdoors Inc,Nasdaq,39.91%,31.34M,89.91M,Retailers - Apparel & Accessories + 7,TRIT,Triterras Inc,Nasdaq,35.34%,23.25M,83.20M,Fintech + 8,CLVS,Clovis Oncology Inc,Nasdaq,33.85%,101.89M,104.53M,Biotechnology & Drugs + 9,HPR,HighPoint Resources Corp,NYSE,32.78%,2.21M,4.31M,Oil & Gas - Exploration and Production + 10,GSX,GSX Techedu Inc,NYSE,32.65%,128.57M,254.69M,Personal Services + 11,SENS,Senseonics Holdings Inc,AMEX,31.40%,219.11M,377.88M,Medical Equipment & Supplies + 12,MDGL,Madrigal Pharmaceuticals Inc,Nasdaq,30.69%,8.03M,15.44M,Biotechnology & Medical Research + 13,SDC,SmileDirectClub Inc,Nasdaq,30.55%,101.05M,117.37M,"Medical Equipment, Supplies & Distribution" + 14,AXDX,Accelerate Diagnostics Inc,Nasdaq,28.49%,31.17M,59.33M,Scientific & Technical Instruments + 15,REV,Revlon Inc,NYSE,28.17%,6.74M,53.34M,Personal & Household Products + 16,ICPT,Intercept Pharmaceuticals Inc,Nasdaq,26.59%,27.24M,33.02M,Biotechnology & Medical Research + 17,RUBY,Rubius Therapeutics Inc,Nasdaq,26.40%,35.93M,88.08M,Biotechnology & Medical Research + 18,SKT,Tanger Factory Outlet Centers,NYSE,26.37%,89.76M,93.57M,Real Estate Operations + 19,FUBO,Fubotv Inc,NYSE,26.10%,69.50M,67.56M,Online Services + 20,INO,Inovio Pharmaceuticals Inc,Nasdaq,26.01%,204.08M,207.63M,Biotechnology & Medical Research + 21,PGEN,"Precigen, Inc",Nasdaq,25.88%,90.09M,178.60M,Biotechnology & Medical Research + 22,TR,"Tootsie Roll Industries, Inc.",NYSE,25.67%,14.91M,40.52M,Food Processing + 23,MSTR,MicroStrategy Inc,Nasdaq,25.59%,7.06M,7.62M,Software & Programming + 24,BYND,Beyond Meat Inc,Nasdaq,25.52%,43.53M,62.94M,Food Processing + 25,WKHS,Workhorse Group Inc,Nasdaq,25.49%,116.18M,123.51M,Auto & Truck Manufacturers + 26,PRTS,Carparts.Com Inc,Nasdaq,25.22%,35.23M,48.09M,"Retailers - Auto Vehicles, Parts & Service" + 27,CVNA,Carvana Co,NYSE,24.89%,67.53M,78.33M,Retail (Specialty Non-Apparel) + 28,RVP,"Retractable Technologies, Inc.",AMEX,24.85%,14.71M,33.82M,"Medical Equipment, Supplies & Distribution" + 29,MAC,Macerich Co,NYSE,24.37%,146.11M,149.47M,REITs - Commercial + 30,LCI,"Lannett Company, Inc.",NYSE,24.31%,32.17M,41.40M,Biotechnology & Drugs + 31,KPTI,Karyopharm Therapeutics Inc,Nasdaq,24.26%,63.06M,74.63M,Biotechnology & Medical Research + 32,OPK,Opko Health Inc.,Nasdaq,24.14%,383.89M,670.04M,Biotechnology & Drugs + 33,GME,GameStop Corp.,NYSE,24.10%,42.29M,69.75M,Retail (Technology) + 34,EBIX,"Ebix, Inc.",Nasdaq,23.53%,25.32M,30.96M,Computer Networks + 35,BFI,Burgerfi International Inc,Nasdaq,23.04%,4.02M,17.44M,Restaurants & Bars + 36,HJLI,Hancock Jaffe Laboratories Inc,Nasdaq,22.84%,1.71M,7.73M,"Medical Equipment, Supplies & Distribution" + 37,SRNE,Sorrento Therapeutics Inc,Nasdaq,22.76%,242.64M,280.86M,Biotechnology & Medical Research + 38,SPWR,SunPower Corporation,Nasdaq,22.74%,81.43M,170.56M,Semiconductors + 39,KOSS,Koss Corp,Nasdaq,22.33%,4.24M,8.62M,Audio & Video Equipment + 40,OMER,Omeros Corporation,Nasdaq,22.30%,59.18M,61.93M,Biotechnology & Drugs + 41,VXRT,Vaxart Inc,Nasdaq,22.04%,117.06M,117.77M,Biotechnology & Medical Research + 42,BBBY,Bed Bath & Beyond Inc.,Nasdaq,21.97%,114.09M,126.01M,Retail (Specialty Non-Apparel) + 43,DVAX,Dynavax Technologies Corporati,Nasdaq,21.63%,113.50M,113.26M,Biotechnology & Medical Research + 44,KDMN,Kadmon Holdings Inc,NYSE,21.25%,165.35M,171.82M,Pharmaceuticals + 45,DKS,Dicks Sporting Goods Inc,NYSE,21.16%,59.03M,65.73M,Retailers - Other Specialty + 46,CVM,CEL-SCI Corporation,AMEX,21.10%,38.86M,40.53M,Biotechnology & Medical Research + 47,CRTX,Cortexyme Inc,Nasdaq,20.98%,15.36M,29.50M,Biotechnology & Medical Research + 48,FBRX,Forte Biosciences Inc,Nasdaq,20.90%,1.48M,13.51M,Biotechnology & Medical Research + 49,PETS,Petmed Express Inc,Nasdaq,20.70%,19.59M,20.27M,Retail (Drugs) + 50,IRBT,iRobot Corporation,Nasdaq,20.58%,27.51M,28.20M,Electronic Equipment & Parts +test_get_low_float: base_low_float: mock_low_float: | @@ -1255,4 +1307,56 @@ test_low_float: - expected_result: "banana" \ No newline at end of file + expected_result: | + ,Ticker,Company,Exchange,Float,Outstd,ShortInt,Industry + 0,,,,,,, + 1,DIT,Amcon Distributing Co,AMEX,150K,550K,0.30%,Retail (Grocery) + 2,SEB,Seaboard Corp,AMEX,240K,1.16M,0.71%,Fish/Livestock + 3,HMG,HMG Courtland Properties,AMEX,280K,1.01M,0.01%,Real Estate Operations + 4,BDL,Flanigan's Enterprises Inc,AMEX,380K,1.86M,0.11%,Restaurants + 5,MAYS,JW Mays Inc,Nasdaq,390K,2.02M,0.80%,Real Estate Operations + 6,SENEB,Seneca Foods Corp,Nasdaq,460K,1.72M,0.07%,Food Processing + 7,PNRG,PrimeEnergy Corp,Nasdaq,490K,1.99M,1.90%,Oil & Gas Operations + 8,COHN,Cohen & Company Inc,AMEX,500K,1.33M,1.23%,Diversified Investment Services + 9,VBFC,Village Bank and Trust Financial Corp,Nasdaq,530K,1.47M,0.07%,Regional Banks + 10,MDLY,Medley Management Inc,NYSE,620K,670K,13.84%,Investment Services + 11,RAND,Rand Capital Corp,Nasdaq,640K,2.58M,0.01%,Misc. Financial Services + 12,CVR,Chicago Rivet & Machine Co,AMEX,780K,970K,0.03%,Misc. Fabricated Products + 13,INTG,Intergroup Corp,Nasdaq,780K,2.28M,0.30%,Real Estate Operations + 14,QADB,"QAD, Inc.",Nasdaq,780K,3.33M,0.19%,Software & Programming + 15,CARV,Carver Bancorp Inc,Nasdaq,790K,3.33M,12.71%,S&Ls/Savings Banks + 16,ARL,"American Realty Investors, Inc",NYSE,800K,16.00M,7.45%,Real Estate Operations + 17,IKNX,Ikonics Corp,Nasdaq,830K,1.98M,0.67%,Photography + 18,AAMC,Altisource Asset Management Co,AMEX,910K,2.05M,0.10%,Investment Services + 19,VALU,Value Line Inc,Nasdaq,950K,9.57M,1.36%,Investment Services + 20,MXC,Mexco Energy Corp,AMEX,960K,2.05M,2.92%,Oil & Gas Operations + 21,FFDF,FFD Financial Corp,Nasdaq,980K,990K,0.00%,S&Ls/Savings Banks + 22,VRTA,Vestin Realty Mortgage I Inc,Nasdaq,1.01M,1.30M,0.00%,Real Estate Operations + 23,SQBG,Sequential Brands Group Inc,Nasdaq,1.02M,1.65M,6.33%,Retailers - Apparel & Accessories + 24,SVT,Servotronics Inc,NYSE,1.04M,2.49M,0.14%,Personal & Household Products + 25,LINK,Interlink Electronics Inc,Nasdaq,1.06M,6.60M,0.00%,Electronic Instruments & Controls + 26,DJCO,Daily Journal Corp,Nasdaq,1.08M,1.38M,1.06%,Printing & Publishing + 27,NSEC,National Security Group Inc,Nasdaq,1.08M,2.53M,0.01%,Insurance (Life) + 28,ISIG,"Insignia Systems, Inc.",Nasdaq,1.09M,1.75M,4.36%,Advertising & Marketing + 29,EDRY,EuroDry Ltd,Nasdaq,1.09M,2.35M,1.27%,Water Transportation + 30,TSRI,TSR Inc,Nasdaq,1.10M,1.96M,0.05%,Software & Programming + 31,FFHL,Fuwei Films Holdings Co Ltd,Nasdaq,1.13M,3.27M,1.16%,Fabricated Plastic & Rubber + 32,GTEC,Greenland Technologies Holding,Nasdaq,1.13M,10.16M,12.23%,"Auto, Truck & Motorcycle Parts" + 33,HFBL,Home Federal Bancorp Inc of LA,Nasdaq,1.15M,1.69M,0.01%,S&Ls/Savings Banks + 34,LBPS,4D Pharma PLC,Nasdaq,1.16M,2.63M,0.80%,Pharmaceuticals + 35,ELTK,Eltek Ltd,Nasdaq,1.17M,4.38M,0.10%,Electronic Instruments & Controls + 36,BNSO,Bonso Electronic International Inc,Nasdaq,1.17M,4.89M,0.09%,Misc. Capital Goods + 37,NSYS,Nortech Systems Inc,Nasdaq,1.18M,2.66M,0.48%,Electronic Instruments & Controls + 38,WAFU,Wah Fu Education Group Ltd,Nasdaq,1.18M,4.38M,1.30%,Personal Services + 39,OFED,Oconee Federal Financial,Nasdaq,1.19M,5.60M,1.08%,S&Ls/Savings Banks + 40,ACY,AeroCentury Corp,AMEX,1.20M,1.55M,3.09%,Rental & Leasing + 41,AEI,Alset Ehome International Inc,Nasdaq,1.23M,8.57M,1.76%,Construction & Engineering + 42,MTEX,"Mannatech,Inc.",Nasdaq,1.24M,2.07M,0.13%,Biotechnology & Drugs + 43,KSPN,"Kaspien Holdings, Inc",Nasdaq,1.25M,2.43M,0.72%,Retailers - Other Specialty + 44,QBAK,Qualstar Corp,Nasdaq,1.29M,1.93M,0.00%,Computer Storage Devices + 45,LIVE,Live Ventures Inc,Nasdaq,1.32M,1.56M,0.55%,Computer Services + 46,PNBK,Patriot National Bancorp Inc,Nasdaq,1.32M,3.94M,1.42%,Regional Banks + 47,TCI,Transcontinental Realty Investors,NYSE,1.33M,8.72M,1.68%,Real Estate Operations + 48,DXR,Daxor Corp,AMEX,1.35M,4.09M,1.45%,Healthcare Facilities + 49,HIFS,Hingham Institution for Savings,Nasdaq,1.36M,2.14M,1.67%,S&Ls/Savings Banks + 50,CKX,CKX Lands Inc,AMEX,1.38M,1.94M,0.20%,Oil & Gas Operations diff --git a/tests/test_discovery_short_interest_api.py b/tests/test_discovery_short_interest_api.py deleted file mode 100644 index 45946ee5e20..00000000000 --- a/tests/test_discovery_short_interest_api.py +++ /dev/null @@ -1,40 +0,0 @@ -""" discovery/short_interest_api.py tests """ -import unittest -from unittest import mock - -# pylint: disable=unused-import -from gamestonk_terminal.test_helper import ( # noqa: F401 - parameterize_from_file, - pytest_generate_tests, -) - -from gamestonk_terminal.discovery.short_interest_view import ( - high_short_interest_view, - low_float_view, -) - -assertions = unittest.TestCase("__init__") - - -class TestDiscoveryShortInterestApi: - @mock.patch("gamestonk_terminal.discovery.short_interest_view.requests") - @parameterize_from_file( - "test_high_short_interest", - "../tests/data/discovery_short_interest_api.yaml", - ) - # pylint: disable=unused-argument - def test_high_short_interest( - self, mock_request_get, mock_high_short, expected_result - ): - mock_request_get.get().text = mock_high_short - high_short_interest_view(["-n", "10"]) - - @mock.patch("gamestonk_terminal.discovery.short_interest_view.requests") - @parameterize_from_file( - "test_low_float", - "../tests/data/discovery_short_interest_api.yaml", - ) - # pylint: disable=unused-argument - def test_low_float(self, mock_request_get, mock_low_float, expected_result): - mock_request_get.get().text = mock_low_float - low_float_view(["-n", "10"]) diff --git a/tests/test_discovery_short_interest_model.py b/tests/test_discovery_short_interest_model.py new file mode 100644 index 00000000000..0ac8485596d --- /dev/null +++ b/tests/test_discovery_short_interest_model.py @@ -0,0 +1,54 @@ +""" discovery/short_interest_api.py tests """ +import unittest +from unittest import mock + +# pylint: disable=unused-import +from gamestonk_terminal.test_helper import ( # noqa: F401 + parameterize_from_file, + pytest_generate_tests, +) + +from gamestonk_terminal.discovery.short_interest_model import ( + get_high_short_interest, + get_low_float, +) + +assertions = unittest.TestCase("__init__") + + +class TestDiscoveryShortInterestApi: + @mock.patch("gamestonk_terminal.discovery.short_interest_model.requests") + @parameterize_from_file( + "test_get_high_short_interest", + "../tests/data/discovery_short_interest_model.yaml", + ) + # pylint: disable=unused-argument + def test_get_high_short_interest( + self, mock_request_get, mock_high_short, expected_result + ): + mock_request_get.get().text = mock_high_short + + df_high_short = get_high_short_interest() + + assertions.assertEqual.__self__.maxDiff = None + assertions.assertEqual( + df_high_short.to_csv().replace("\r\n", "\n"), + expected_result.replace("\r\n", "\n"), + ) + + @mock.patch("gamestonk_terminal.discovery.short_interest_model.requests") + @parameterize_from_file( + "test_get_low_float", + "../tests/data/discovery_short_interest_model.yaml", + ) + # pylint: disable=unused-argument + def test_get_low_float(self, mock_request_get, mock_low_float, expected_result): + mock_request_get.get().text = mock_low_float + + df_low_float = get_low_float() + + assertions.assertEqual.__self__.maxDiff = None + assertions.assertEqual( + df_low_float.to_csv().replace("\r\n", "\n"), + expected_result.replace("\r\n", "\n"), + ) \ No newline at end of file From 611369ddf7983c78e5fb0dd488e8559952d85d23 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Mon, 29 Mar 2021 00:43:03 -0700 Subject: [PATCH 11/11] Missing a new line --- tests/test_discovery_short_interest_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_discovery_short_interest_model.py b/tests/test_discovery_short_interest_model.py index 0ac8485596d..254101054cc 100644 --- a/tests/test_discovery_short_interest_model.py +++ b/tests/test_discovery_short_interest_model.py @@ -51,4 +51,4 @@ class TestDiscoveryShortInterestApi: assertions.assertEqual( df_low_float.to_csv().replace("\r\n", "\n"), expected_result.replace("\r\n", "\n"), - ) \ No newline at end of file + )