diff --git a/gamestonk_terminal/stocks/dark_pool_shorts/dps_controller.py b/gamestonk_terminal/stocks/dark_pool_shorts/dps_controller.py index cd007570bc0..874cf2d258e 100644 --- a/gamestonk_terminal/stocks/dark_pool_shorts/dps_controller.py +++ b/gamestonk_terminal/stocks/dark_pool_shorts/dps_controller.py @@ -1,4 +1,4 @@ -""" Disc Controller """ +""" Dark Pool Shorts Controller """ __docformat__ = "numpy" import argparse @@ -266,12 +266,33 @@ Quandl/Stockgrid: default=5, help="List of tickers from most promising with better linear regression slope.", ) + parser.add_argument( + "--tier", + action="store", + dest="tier", + type=str, + choices=["T1", "T2", "OTCE"], + default="", + help="Tier to process data from.", + ) + parser.add_argument( + "--export", + choices=["csv", "json", "xlsx"], + default="", + dest="export", + help="Export dataframe data to csv,json,xlsx file", + ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return - finra_view.darkpool_otc(num=ns_parser.n_num, promising=ns_parser.n_top) + finra_view.darkpool_otc( + num=ns_parser.n_num, + promising=ns_parser.n_top, + tier=ns_parser.tier, + export=ns_parser.export, + ) except Exception as e: print(e, "\n") @@ -387,6 +408,13 @@ Quandl/Stockgrid: prog="dpotc", description="Display barchart of dark pool (ATS) and OTC (Non ATS) data. [Source: FINRA]", ) + parser.add_argument( + "--export", + choices=["csv", "json", "xlsx"], + default="", + dest="export", + help="Export dataframe data to csv,json,xlsx file", + ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: @@ -395,7 +423,10 @@ Quandl/Stockgrid: print("No ticker loaded.\n") return - finra_view.darkpool_ats_otc(ticker=self.ticker) + finra_view.darkpool_ats_otc( + ticker=self.ticker, + export=ns_parser.export, + ) except Exception as e: print(e, "\n") @@ -546,7 +577,7 @@ Quandl/Stockgrid: action="store", dest="n_days", type=check_positive, - default=10, + default=10 if "-r" in other_args else 120, help="Number of latest days to print data.", ) else: @@ -558,13 +589,13 @@ Quandl/Stockgrid: default=10 if "-r" in other_args else 120, dest="num", ) - parser.add_argument( - "-r", - action="store_true", - default=False, - help="Flag to print raw data instead", - dest="raw", - ) + parser.add_argument( + "-r", + action="store_true", + default=False, + help="Flag to print raw data instead", + dest="raw", + ) parser.add_argument( "--export", choices=["csv", "json", "xlsx"], @@ -583,9 +614,9 @@ Quandl/Stockgrid: if "quandl" in other_args: quandl_view.short_interest( ticker=self.ticker, - start=self.start, nyse=ns_parser.b_nyse, days=ns_parser.n_days, + raw=ns_parser.raw, export=ns_parser.export, ) else: diff --git a/gamestonk_terminal/stocks/dark_pool_shorts/finra_model.py b/gamestonk_terminal/stocks/dark_pool_shorts/finra_model.py index 4c20ccf521a..577d9dbad51 100644 --- a/gamestonk_terminal/stocks/dark_pool_shorts/finra_model.py +++ b/gamestonk_terminal/stocks/dark_pool_shorts/finra_model.py @@ -199,13 +199,15 @@ def getFINRAdata( ) -def getATSdata(num_tickers_to_filter: int) -> Tuple[pd.DataFrame, Dict]: +def getATSdata(num_tickers_to_filter: int, tier_ats: str) -> Tuple[pd.DataFrame, Dict]: """Get all FINRA ATS data, and parse most promising tickers based on linear regression Parameters ---------- num_tickers_to_filter : int Number of tickers to filter from entire ATS data based on the sum of the total weekly shares quantity + tier_ats : int + Tier to process data from Returns ------- @@ -214,7 +216,10 @@ def getATSdata(num_tickers_to_filter: int) -> Tuple[pd.DataFrame, Dict]: Dict Tickers from Dark Pools with better regression slope """ - tiers = ["T1", "T2", "OTCE"] + if tier_ats: + tiers = [tier_ats] + else: + tiers = ["T1", "T2", "OTCE"] df_ats = pd.DataFrame() for tier in tiers: diff --git a/gamestonk_terminal/stocks/dark_pool_shorts/finra_view.py b/gamestonk_terminal/stocks/dark_pool_shorts/finra_view.py index cdaae58b0ae..c127f87fc0b 100644 --- a/gamestonk_terminal/stocks/dark_pool_shorts/finra_view.py +++ b/gamestonk_terminal/stocks/dark_pool_shorts/finra_view.py @@ -1,13 +1,15 @@ """ FINRA View """ __docformat__ = "numpy" +import os from typing import List import pandas as pd from matplotlib import pyplot as plt +import matplotlib.dates as mdates from gamestonk_terminal.stocks.dark_pool_shorts import finra_model from gamestonk_terminal.config_plot import PLOT_DPI from gamestonk_terminal import feature_flags as gtff -from gamestonk_terminal.helper_funcs import plot_autoscale +from gamestonk_terminal.helper_funcs import plot_autoscale, export_data def plot_dark_pools(ticker: str, ats: pd.DataFrame, otc: pd.DataFrame): @@ -83,6 +85,8 @@ def plot_dark_pools(ticker: str, ats: pd.DataFrame, otc: pd.DataFrame): plt.ylabel("Shares per Trade") plt.grid(b=True, which="major", color="#666666", linestyle="-", alpha=0.2) plt.gcf().autofmt_xdate() + plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%m/%d")) + plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=4)) plt.xlabel("Weeks") if gtff.USE_ION: @@ -91,13 +95,15 @@ def plot_dark_pools(ticker: str, ats: pd.DataFrame, otc: pd.DataFrame): plt.show() -def darkpool_ats_otc(ticker: str): - """Display barchart of dark pool (ATS) and OTC (Non ATS) data +def darkpool_ats_otc(ticker: str, export: str): + """Display barchart of dark pool (ATS) and OTC (Non ATS) data. [Source: FINRA] Parameters ---------- ticker : str Stock ticker + export : str + Export dataframe data to csv,json,xlsx file """ df_ats, df_otc = finra_model.getTickerFINRAdata(ticker) @@ -107,6 +113,19 @@ def darkpool_ats_otc(ticker: str): plot_dark_pools(ticker, df_ats, df_otc) print("") + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "dpotc_ats", + df_ats, + ) + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "dpotc_otc", + df_otc, + ) + def plot_dark_pools_ats(ats: pd.DataFrame, top_ats_tickers: List): """Plots promising tickers based on growing ATS data @@ -142,8 +161,8 @@ def plot_dark_pools_ats(ats: pd.DataFrame, top_ats_tickers: List): plt.show() -def darkpool_otc(num: int, promising: int): - """Display dark pool (ATS) data of tickers with growing trades activity +def darkpool_otc(num: int, promising: int, tier: str, export: str): + """Display dark pool (ATS) data of tickers with growing trades activity. [Source: FINRA] Parameters ---------- @@ -153,8 +172,12 @@ def darkpool_otc(num: int, promising: int): promising : int Number of tickers to display from most promising with better linear regression slope + tier : int + Tier to process data from + export : str + Export dataframe data to csv,json,xlsx file """ - df_ats, d_ats_reg = finra_model.getATSdata(num) + df_ats, d_ats_reg = finra_model.getATSdata(num, tier) top_ats_tickers = list( dict(sorted(d_ats_reg.items(), key=lambda item: item[1], reverse=True)).keys() @@ -162,3 +185,10 @@ def darkpool_otc(num: int, promising: int): plot_dark_pools_ats(df_ats, top_ats_tickers) print("") + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "prom", + df_ats, + ) diff --git a/gamestonk_terminal/stocks/dark_pool_shorts/quandl_view.py b/gamestonk_terminal/stocks/dark_pool_shorts/quandl_view.py index b4239cb65f6..a57e0759183 100644 --- a/gamestonk_terminal/stocks/dark_pool_shorts/quandl_view.py +++ b/gamestonk_terminal/stocks/dark_pool_shorts/quandl_view.py @@ -5,6 +5,7 @@ import os import matplotlib.ticker from matplotlib import pyplot as plt import pandas as pd +from tabulate import tabulate from gamestonk_terminal.stocks.dark_pool_shorts import quandl_model from gamestonk_terminal.helper_funcs import ( long_number_format, @@ -12,9 +13,7 @@ from gamestonk_terminal.helper_funcs import ( ) -def plot_short_interest( - ticker: str, start: str, nyse: bool, days: int, df_short_interest: pd.DataFrame -): +def plot_short_interest(ticker: str, nyse: bool, df_short_interest: pd.DataFrame): """Plots the short interest of a stock. This corresponds to the number of shares that have been sold short but have not yet been covered or closed out. Either NASDAQ or NYSE [Source: Quandl] @@ -27,21 +26,9 @@ def plot_short_interest( start date to start displaying short interest volume nyse : bool data from NYSE if true, otherwise NASDAQ - days : int - Number of past days to show short interest df_short_interest: pd.DataFrame Short interest dataframe """ - df_short_interest = df_short_interest[start:] # type: ignore - df_short_interest.columns = [ - "".join(" " + char if char.isupper() else char.strip() for char in idx).strip() - for idx in df_short_interest.columns.tolist() - ] - df_short_interest["% of Volume Shorted"] = round( - 100 * df_short_interest["Short Volume"] / df_short_interest["Total Volume"], - 2, - ) - _, ax = plt.subplots() ax.bar(df_short_interest.index, df_short_interest["Short Volume"], 0.3, color="r") ax.bar( @@ -53,15 +40,9 @@ def plot_short_interest( ) ax.set_ylabel("Shares") ax.set_xlabel("Date") + ax.set_title(f"{('NASDAQ', 'NYSE')[nyse]} Short Interest on {ticker}") - if start: - ax.set_title( - f"{('NASDAQ', 'NYSE')[nyse]} Short Interest on {ticker} from {start.date()}" # type: ignore - ) - else: - ax.set_title(f"{('NASDAQ', 'NYSE')[nyse]} Short Interest on {ticker}") - - ax.legend(labels=["Short Volume", "Total Volume"]) + ax.legend(labels=["Short Volume", "Total Volume"], loc="best") ax.tick_params(axis="both", which="major") ax.yaxis.set_major_formatter(matplotlib.ticker.EngFormatter()) ax_twin = ax.twinx() @@ -75,21 +56,12 @@ def plot_short_interest( ax_twin.tick_params(axis="y", which="major", color="green") ax_twin.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter("%.0f%%")) plt.xlim([df_short_interest.index[0], df_short_interest.index[-1]]) + plt.gcf().autofmt_xdate() - df_short_interest["% of Volume Shorted"] = df_short_interest[ - "% of Volume Shorted" - ].apply(lambda x: f"{x/100:.2%}") - df_short_interest = df_short_interest.applymap( - lambda x: long_number_format(x) - ).sort_index(ascending=False) - - pd.set_option("display.max_colwidth", 70) - print(df_short_interest.head(n=days).to_string()) - print("") plt.show() -def short_interest(ticker: str, start: str, nyse: bool, days: int, export: str): +def short_interest(ticker: str, nyse: bool, days: int, raw: bool, export: str): """Plots the short interest of a stock. This corresponds to the number of shares that have been sold short but have not yet been covered or closed out. Either NASDAQ or NYSE [Source: Quandl] @@ -98,22 +70,59 @@ def short_interest(ticker: str, start: str, nyse: bool, days: int, export: str): ---------- ticker : str ticker to get short interest from - start : str - start date to start displaying short interest volume nyse : bool data from NYSE if true, otherwise NASDAQ days : int Number of past days to show short interest + raw : bool + Flag to print raw data instead export : str Export dataframe data to csv,json,xlsx file """ df_short_interest = quandl_model.get_short_interest(ticker, nyse) - plot_short_interest(ticker, start, nyse, days, df_short_interest) + df_short_interest = df_short_interest.tail(days) + + df_short_interest.columns = [ + "".join(" " + char if char.isupper() else char.strip() for char in idx).strip() + for idx in df_short_interest.columns.tolist() + ] + pd.options.mode.chained_assignment = None + vol_pct = ( + 100 + * df_short_interest["Short Volume"].values + / df_short_interest["Total Volume"].values + ) + df_short_interest["% of Volume Shorted"] = [round(pct, 2) for pct in vol_pct] + + if raw: + df_short_interest["% of Volume Shorted"] = df_short_interest[ + "% of Volume Shorted" + ].apply(lambda x: f"{x/100:.2%}") + df_short_interest = df_short_interest.applymap( + lambda x: long_number_format(x) + ).sort_index(ascending=False) + + df_short_interest.index = df_short_interest.index.date + + print( + tabulate( + df_short_interest, + headers=df_short_interest.columns, + floatfmt=".2f", + showindex=True, + tablefmt="fancy_grid", + ) + ) + + else: + plot_short_interest(ticker, nyse, df_short_interest) + + print("") export_data( export, os.path.dirname(os.path.abspath(__file__)), - "shortint(quandl)", + "psi(quandl)", df_short_interest, ) diff --git a/gamestonk_terminal/stocks/due_diligence/market_watch_view.py b/gamestonk_terminal/stocks/due_diligence/market_watch_view.py deleted file mode 100644 index 69f74b736a9..00000000000 --- a/gamestonk_terminal/stocks/due_diligence/market_watch_view.py +++ /dev/null @@ -1,95 +0,0 @@ -""" Market Watch View """ -__docformat__ = "numpy" - -import argparse -from typing import List -import requests -import pandas as pd -from bs4 import BeautifulSoup -from gamestonk_terminal.helper_funcs import ( - check_positive, - get_user_agent, - parse_known_args_and_warn, -) - -# pylint: disable=too-many-branches - - -def sec_fillings(other_args: List[str], ticker: str): - """Display SEC filings for a given stock ticker - - Parameters - ---------- - other_args : List[str] - argparse other args - ["-n", "10"] - ticker : str - Stock ticker - """ - - parser = argparse.ArgumentParser( - add_help=False, - prog="sec", - description=""" - Prints SEC filings of the company. The following fields are expected: Filing Date, - Document Date, Type, Category, Amended, and Link. [Source: Market Watch] - """, - ) - - parser.add_argument( - "-n", - "--num", - action="store", - dest="n_num", - type=check_positive, - default=5, - help="number of latest SEC filings.", - ) - - try: - ns_parser = parse_known_args_and_warn(parser, other_args) - if not ns_parser: - return - - pd.set_option("display.max_colwidth", None) - - url_financials = f"https://www.marketwatch.com/investing/stock/{ticker}/financials/secfilings" - - text_soup_financials = BeautifulSoup( - requests.get(url_financials, headers={"User-Agent": get_user_agent()}).text, - "lxml", - ) - - # a_financials_header = [] - df_financials = None - b_ready_to_process_info = False - soup_financials = text_soup_financials.findAll("tr", {"class": "table__row"}) - for financials_info in soup_financials: - a_financials = financials_info.text.split("\n") - - # If header has been processed and dataframe created ready to populate the SEC information - if b_ready_to_process_info: - l_financials_info = [a_financials[2]] - l_financials_info.extend(a_financials[5:-1]) - l_financials_info.append(financials_info.a["href"]) - # Append data values to financials - df_financials.loc[len(df_financials.index)] = l_financials_info # type: ignore - - if "Filing Date" in a_financials: - l_financials_header = [a_financials[2]] - l_financials_header.extend(a_financials[5:-1]) - l_financials_header.append("Link") - - df_financials = pd.DataFrame(columns=l_financials_header) - df_financials.set_index("Filing Date") - b_ready_to_process_info = True - - # Set Filing Date as index - df_financials = df_financials.set_index("Filing Date") # type: ignore - - print(df_financials.head(n=ns_parser.n_num).to_string()) - print("") - - except Exception as e: - print(e) - print("") - return