From 3e35a6acd7e73957ccdddb0d67e9c037c53804c8 Mon Sep 17 00:00:00 2001 From: jmaslek Date: Tue, 7 Dec 2021 12:12:47 -0500 Subject: [PATCH] Improve economy menu (#1036) * Improve econ search command * Clean up finviz commands * Add sorting on all the futures and fix the table column names * Improve some plotting features * review comments --- gamestonk_terminal/config_terminal.py | 2 +- .../economy/alphavantage_view.py | 2 +- .../economy/economy_controller.py | 199 +++++++++++++--- gamestonk_terminal/economy/finviz_view.py | 219 +++++++++++------- gamestonk_terminal/economy/fred_model.py | 33 +-- gamestonk_terminal/economy/fred_view.py | 24 +- gamestonk_terminal/economy/nasdaq_view.py | 11 +- 7 files changed, 344 insertions(+), 146 deletions(-) diff --git a/gamestonk_terminal/config_terminal.py b/gamestonk_terminal/config_terminal.py index c495369d4fb..c942695c9fe 100644 --- a/gamestonk_terminal/config_terminal.py +++ b/gamestonk_terminal/config_terminal.py @@ -9,7 +9,7 @@ if env_files: # Choose one of: stocks, options, crypto, economy, etf, portfolio, forex DEFAULT_CONTEXT = "" -# Choose one of: developer, user +# Set to True to see full stack traces for debugging/error reporting DEBUG_MODE = False # By default the jupyter notebook will be run on port 8888 diff --git a/gamestonk_terminal/economy/alphavantage_view.py b/gamestonk_terminal/economy/alphavantage_view.py index 75e8ada36f3..d0fc973b3b6 100644 --- a/gamestonk_terminal/economy/alphavantage_view.py +++ b/gamestonk_terminal/economy/alphavantage_view.py @@ -241,7 +241,7 @@ def display_cpi( fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI) ax.plot(cpi.date, cpi.CPI, marker="o", c="dodgerblue") ax.set_xlabel("Date") - ax.set_title(f"{int_string} CPI from {year_str}") + ax.set_title(f"{int_string} Consumer Price Index from {year_str}") ax.set_ylabel("CPI ") ax.grid("on") fig.tight_layout() diff --git a/gamestonk_terminal/economy/economy_controller.py b/gamestonk_terminal/economy/economy_controller.py index fef6bd6d04a..1e0dcde3486 100644 --- a/gamestonk_terminal/economy/economy_controller.py +++ b/gamestonk_terminal/economy/economy_controller.py @@ -7,6 +7,7 @@ from datetime import datetime, timedelta import os from typing import List +import pandas as pd from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal import feature_flags as gtff @@ -69,7 +70,6 @@ class EconomyController: CHOICES_COMMANDS = [ "feargreed", - "events", "overview", "indices", "futures", @@ -122,8 +122,6 @@ What do you want to do? CNN: feargreed CNN Fear and Greed Index -Finnhub: - events economic impact events Wall St. Journal: overview market data overview indices US indices overview @@ -471,6 +469,22 @@ NASDAQ DataLink (formerly Quandl): prog="energy", description="Energy future overview. [Source: Finviz]", ) + parser.add_argument( + "-s", + "--sortby", + dest="sort_col", + type=str, + choices=["ticker", "last", "change", "prevClose"], + default="ticker", + ) + parser.add_argument( + "-a", + "-ascend", + dest="ascend", + help="Flag to sort in ascending order", + action="store_true", + default=False, + ) parser.add_argument( "--export", choices=["csv", "json", "xlsx"], @@ -485,6 +499,8 @@ NASDAQ DataLink (formerly Quandl): finviz_view.display_future( future_type="Energy", + sort_col=ns_parser.sort_col, + ascending=ns_parser.ascend, export=ns_parser.export, ) @@ -497,6 +513,22 @@ NASDAQ DataLink (formerly Quandl): prog="metals", description="Metals future overview. [Source: Finviz]", ) + parser.add_argument( + "-s", + "--sortby", + dest="sort_col", + type=str, + choices=["ticker", "last", "change", "prevClose"], + default="ticker", + ) + parser.add_argument( + "-a", + "-ascend", + dest="ascend", + help="Flag to sort in ascending order", + action="store_true", + default=False, + ) parser.add_argument( "--export", choices=["csv", "json", "xlsx"], @@ -511,6 +543,8 @@ NASDAQ DataLink (formerly Quandl): finviz_view.display_future( future_type="Metals", + sort_col=ns_parser.sort_col, + ascending=ns_parser.ascend, export=ns_parser.export, ) @@ -523,6 +557,22 @@ NASDAQ DataLink (formerly Quandl): prog="meats", description="Meats future overview. [Source: Finviz]", ) + parser.add_argument( + "-s", + "--sortby", + dest="sort_col", + type=str, + choices=["ticker", "last", "change", "prevClose"], + default="ticker", + ) + parser.add_argument( + "-a", + "-ascend", + dest="ascend", + help="Flag to sort in ascending order", + action="store_true", + default=False, + ) parser.add_argument( "--export", choices=["csv", "json", "xlsx"], @@ -537,6 +587,8 @@ NASDAQ DataLink (formerly Quandl): finviz_view.display_future( future_type="Meats", + sort_col=ns_parser.sort_col, + ascending=ns_parser.ascend, export=ns_parser.export, ) @@ -549,6 +601,22 @@ NASDAQ DataLink (formerly Quandl): prog="grains", description="Grains future overview. [Source: Finviz]", ) + parser.add_argument( + "-s", + "--sortby", + dest="sort_col", + type=str, + choices=["ticker", "last", "change", "prevClose"], + default="ticker", + ) + parser.add_argument( + "-a", + "-ascend", + dest="ascend", + help="Flag to sort in ascending order", + action="store_true", + default=False, + ) parser.add_argument( "--export", choices=["csv", "json", "xlsx"], @@ -563,6 +631,8 @@ NASDAQ DataLink (formerly Quandl): finviz_view.display_future( future_type="Grains", + sort_col=ns_parser.sort_col, + ascending=ns_parser.ascend, export=ns_parser.export, ) @@ -575,6 +645,22 @@ NASDAQ DataLink (formerly Quandl): prog="softs", description="Softs future overview. [Source: Finviz]", ) + parser.add_argument( + "-s", + "--sortby", + dest="sort_col", + type=str, + choices=["ticker", "last", "change", "prevClose"], + default="ticker", + ) + parser.add_argument( + "-a", + "-ascend", + dest="ascend", + help="Flag to sort in ascending order", + action="store_true", + default=False, + ) parser.add_argument( "--export", choices=["csv", "json", "xlsx"], @@ -589,6 +675,8 @@ NASDAQ DataLink (formerly Quandl): finviz_view.display_future( future_type="Softs", + sort_col=ns_parser.sort_col, + ascending=ns_parser.ascend, export=ns_parser.export, ) @@ -648,10 +736,42 @@ NASDAQ DataLink (formerly Quandl): "-g", "--group", type=str, - default="Sector", + default="sector", + nargs="+", dest="group", help="Data group (sector, industry or country)", - choices=list(self.d_GROUPS.keys()), + ) + parser.add_argument( + "-s", + "--sortby", + dest="sort_col", + type=str, + choices=[ + "Name", + "MarketCap", + "P/E", + "FwdP/E", + "PEG", + "P/S", + "P/B", + "P/C", + "P/FCF", + "EPSpast5Y", + "EPSnext5Y", + "Salespast5Y", + "Change", + "Volume", + ], + default="Name", + help="Column to sort by", + ) + parser.add_argument( + "-a", + "-ascend", + dest="ascend", + help="Flag to sort in ascending order", + action="store_true", + default=False, ) parser.add_argument( "--export", @@ -661,18 +781,22 @@ NASDAQ DataLink (formerly Quandl): dest="export", help="Export dataframe data to csv,json,xlsx file", ) - if other_args: - if "-" not in other_args[0]: - other_args.insert(0, "-g") - other_args = [other_args[0], " ".join(other_args[1:])] + if other_args and "-" not in other_args[0]: + other_args.insert(0, "-g") ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return - finviz_view.view_group_data( - s_group=self.d_GROUPS[ns_parser.group], - data_type="valuation", + group = ( + " ".join(ns_parser.group) + if isinstance(ns_parser.group, list) + else ns_parser.group + ) + finviz_view.display_valuation( + s_group=self.d_GROUPS[group], + sort_col=ns_parser.sort_col, + ascending=ns_parser.ascend, export=ns_parser.export, ) @@ -691,10 +815,10 @@ NASDAQ DataLink (formerly Quandl): "-g", "--group", type=str, - default="Sector", + default="sector", + nargs="+", dest="group", help="Data group (sector, industry or country)", - choices=list(self.d_GROUPS.keys()), ) parser.add_argument( "-s", @@ -735,14 +859,16 @@ NASDAQ DataLink (formerly Quandl): ) if other_args and "-" not in other_args[0]: other_args.insert(0, "-g") - ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return - - finviz_view.view_group_data( - s_group=self.d_GROUPS[ns_parser.group], - data_type="performance", + group = ( + " ".join(ns_parser.group) + if isinstance(ns_parser.group, list) + else ns_parser.group + ) + finviz_view.display_performance( + s_group=self.d_GROUPS[group], sort_col=ns_parser.sort_col, ascending=ns_parser.ascend, export=ns_parser.export, @@ -764,9 +890,9 @@ NASDAQ DataLink (formerly Quandl): "--group", type=str, default="sector", + nargs="+", dest="group", help="Data group (sector, industry or country)", - choices=list(self.d_GROUPS.keys()), ) parser.add_argument( "--export", @@ -776,23 +902,22 @@ NASDAQ DataLink (formerly Quandl): dest="export", help="Export plot to png,jpg,pdf,svg file", ) - if other_args: - if "-" not in other_args[0]: - other_args.insert(0, "-g") - other_args = [other_args[0], " ".join(other_args[1:])] + if other_args and "-" not in other_args[0]: + other_args.insert(0, "-g") ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return - - finviz_view.view_group_data( - s_group=self.d_GROUPS[ns_parser.group], - data_type="spectrum", - export="", + group = ( + " ".join(ns_parser.group) + if isinstance(ns_parser.group, list) + else ns_parser.group ) + finviz_view.display_spectrum(s_group=self.d_GROUPS[group]) + # Due to Finviz implementation of Spectrum, we delete the generated spectrum figure # after saving it and displaying it to the user - os.remove(ns_parser.group + ".jpg") + os.remove(self.d_GROUPS[group] + ".jpg") @try_except def call_rtps(self, other_args: List[str]): @@ -1054,6 +1179,8 @@ NASDAQ DataLink (formerly Quandl): default=False, ) + if other_args and "-m" not in other_args[0]: + other_args.insert(0, "-m") ns_parser = parse_known_args_and_warn( parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES ) @@ -1216,6 +1343,13 @@ NASDAQ DataLink (formerly Quandl): Get historical Big Mac Index [Nasdaq Data Link] """, ) + parser.add_argument( + "--codes", + help="Flag to show all country codes", + dest="codes", + action="store_true", + default=False, + ) parser.add_argument( "-c", "--countries", @@ -1237,6 +1371,11 @@ NASDAQ DataLink (formerly Quandl): if not ns_parser: return + if ns_parser.codes: + file = os.path.join(os.path.dirname(__file__), "NASDAQ_CountryCodes.csv") + print(pd.read_csv(file, index_col=0).to_string(index=False), "\n") + return + nasdaq_view.display_big_mac_index( country_codes=ns_parser.countries, raw=ns_parser.raw, diff --git a/gamestonk_terminal/economy/finviz_view.py b/gamestonk_terminal/economy/finviz_view.py index 7b75519b65b..5a27b240e89 100644 --- a/gamestonk_terminal/economy/finviz_view.py +++ b/gamestonk_terminal/economy/finviz_view.py @@ -7,7 +7,6 @@ import pandas as pd from PIL import Image from tabulate import tabulate -from matplotlib import pyplot as plt from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.economy import finviz_model @@ -34,21 +33,18 @@ def map_sp500_view(period: str, map_type: str): print("") -def view_group_data( +def display_performance( s_group: str, - data_type: str, sort_col: str = "Name", - ascending: bool = False, + ascending: bool = True, export: str = "", ): - """View group (sectors, industry or country) valuation/performance/spectrum data. [Source: Finviz] + """View group (sectors, industry or country) performance data. [Source: Finviz] Parameters ---------- s_group : str group between sectors, industry or country - data_type : str - select data type to see data between valuation, performance and spectrum sort_col : str Column to sort by ascending : bool @@ -56,89 +52,148 @@ def view_group_data( export : str Export data to csv,json,xlsx or png,jpg,pdf,svg file """ - if data_type in ("valuation", "performance"): - df_group = finviz_model.get_valuation_performance_data(s_group, data_type) - df_group = df_group.rename( - columns={ - "Perf Week": "Week", - "Perf Month": "Month", - "Perf Quart": "3Month", - "Perf Half": "6Month", - "Perf Year": "1Year", - "Perf YTD": "YTD", - "Avg Volume": "AvgVolume", - "Rel Volume": "RelVolume", - } - ) - df_group["Week"] = df_group["Week"].apply(lambda x: float(x.strip("%")) / 100) - df_group = df_group.sort_values(by=sort_col, ascending=ascending) - df_group["Volume"] = df_group["Volume"] / 1_000_000 - df_group["AvgVolume"] = df_group["AvgVolume"] / 1_000_000 - df_group = df_group.rename( - columns={"Volume": "Volume (1M)", "AvgVolume": "AvgVolume (1M)"} - ) - if gtff.USE_TABULATE_DF: - print( - tabulate( - df_group.fillna(""), - showindex=False, - headers=df_group.columns, - tablefmt="fancy_grid", - floatfmt=[ - "", - ".3f", - ".3f", - ".3f", - ".3f", - ".3f", - ".3f", - ".2f", - ".0f", - ".2f", - ".2f", - ".0f", - ], - ) - ) - else: - print(df_group.fillna("").to_string(index=False)) - print("") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - data_type, - df_group, - ) - - elif data_type == "spectrum": - finviz_model.get_spectrum_data(s_group) - print("") - - img = Image.open(s_group + ".jpg") - plt.imshow(img) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "spectrum", - ) - - img.show() - - else: + df_group = finviz_model.get_valuation_performance_data(s_group, "performance") + df_group = df_group.rename( + columns={ + "Perf Week": "Week", + "Perf Month": "Month", + "Perf Quart": "3Month", + "Perf Half": "6Month", + "Perf Year": "1Year", + "Perf YTD": "YTD", + "Avg Volume": "AvgVolume", + "Rel Volume": "RelVolume", + } + ) + df_group["Week"] = df_group["Week"].apply(lambda x: float(x.strip("%")) / 100) + df_group = df_group.sort_values(by=sort_col, ascending=ascending) + df_group["Volume"] = df_group["Volume"] / 1_000_000 + df_group["AvgVolume"] = df_group["AvgVolume"] / 1_000_000 + df_group = df_group.rename( + columns={"Volume": "Volume (1M)", "AvgVolume": "AvgVolume (1M)"} + ) + if gtff.USE_TABULATE_DF: print( - "Invalid data type. Choose between valuation, performance and spectrum.\n" + tabulate( + df_group.fillna(""), + showindex=False, + headers=df_group.columns, + tablefmt="fancy_grid", + floatfmt=[ + "", + ".3f", + ".3f", + ".3f", + ".3f", + ".3f", + ".3f", + ".2f", + ".0f", + ".2f", + ".2f", + ".0f", + ], + ) ) + else: + print(df_group.fillna("").to_string(index=False)) + print("") + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "performance", + df_group, + ) -def display_future(future_type: str = "Indices", export: str = ""): +def display_valuation( + s_group: str, + sort_col: str = "Name", + ascending: bool = True, + export: str = "", +): + """View group (sectors, industry or country) valuation data. [Source: Finviz] + + Parameters + ---------- + s_group : str + group between sectors, industry or country + sort_col : str + Column to sort by + ascending : bool + Flag to sort in ascending order + export : str + Export data to csv,json,xlsx or png,jpg,pdf,svg file + """ + df_group = finviz_model.get_valuation_performance_data(s_group, "valuation") + df_group["Market Cap"] = df_group["Market Cap"].apply(lambda x: float(x.strip("B"))) + df_group.columns = [col.replace(" ", "") for col in df_group.columns] + df_group = df_group.sort_values(by=sort_col, ascending=ascending) + df_group["Volume"] = df_group["Volume"] / 1_000_000 + df_group = df_group.rename(columns={"Volume": "Volume (1M)"}) + if gtff.USE_TABULATE_DF: + print( + tabulate( + df_group.fillna(""), + showindex=False, + headers=df_group.columns, + tablefmt="fancy_grid", + floatfmt=".2f", + ) + ) + else: + print(df_group.fillna("").to_string(index=False)) + print("") + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "valuation", + df_group, + ) + + +def display_spectrum(s_group: str, export: str = ""): + """Display finviz spectrum in system viewer [Source: Finviz] + + Parameters + ---------- + s_group: str + group between sectors, industry or country + export: str + Format to export data + """ + finviz_model.get_spectrum_data(s_group) + print("") + + img = Image.open(s_group + ".jpg") + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "spectrum", + ) + + img.show() + + +def display_future( + future_type: str = "Indices", + sort_col: str = "ticker", + ascending: bool = False, + export: str = "", +): """Display table of a particular future type. [Source: Finviz] Parameters ---------- future_type : str From the following: Indices, Energy, Metals, Meats, Grains, Softs, Bonds, Currencies + sort_col : str + Column to sort by + ascending : bool + Flag to sort in ascending order export : str Export data to csv,json,xlsx or png,jpg,pdf,svg file """ @@ -146,14 +201,14 @@ def display_future(future_type: str = "Indices", export: str = ""): df = pd.DataFrame(d_futures[future_type]) df = df.set_index("label") - + df = df.sort_values(by=sort_col, ascending=ascending) if gtff.USE_TABULATE_DF: print( tabulate( df[["prevClose", "last", "change"]].fillna(""), showindex=True, floatfmt=".2f", - headers=df.columns, + headers=["prevClose", "last", "change (%)"], tablefmt="fancy_grid", ) ) diff --git a/gamestonk_terminal/economy/fred_model.py b/gamestonk_terminal/economy/fred_model.py index 3201d9f7e78..12489883e4a 100644 --- a/gamestonk_terminal/economy/fred_model.py +++ b/gamestonk_terminal/economy/fred_model.py @@ -1,4 +1,4 @@ -""" Fred View """ +""" Fred Model """ __docformat__ = "numpy" from typing import List, Tuple @@ -10,45 +10,30 @@ from fredapi import Fred from gamestonk_terminal import config_terminal as cfg -def get_series_notes(series_term: str, num: int) -> str: +def get_series_notes(series_term: str) -> pd.DataFrame: """Get Series notes. [Source: FRED] Parameters ---------- series_term : str Search for this series term - num : int - Maximum number of series notes to display Returns ---------- - notes : str - Series notes output + pd.DataFrame + DataFrame of matched series """ fred.key(cfg.API_FRED_KEY) d_series = fred.search(series_term) if "seriess" not in d_series: - return "No Series found using this term!\n" - + return pd.DataFrame() + if not d_series["seriess"]: + return pd.DataFrame() df_fred = pd.DataFrame(d_series["seriess"]) - if df_fred.empty: - return "No Series found using this term!\n" - - df_fred = df_fred.sort_values(by=["popularity"], ascending=False).head(num) - - notes = "" - for _, series in df_fred.iterrows(): - if series["notes"]: - notes += series["id"] + "\n" - notes += "-" * len(series["id"]) + "\n" - notes += series["notes"] + "\n\n" - - if not notes: - return "Series notes not found!\n" - - return notes + df_fred["notes"] = df_fred["notes"].fillna("No description provided.") + return df_fred def get_series_ids(series_term: str, num: int) -> Tuple[List[str], List[str]]: diff --git a/gamestonk_terminal/economy/fred_view.py b/gamestonk_terminal/economy/fred_view.py index fa8667ef704..3df7b506ad0 100644 --- a/gamestonk_terminal/economy/fred_view.py +++ b/gamestonk_terminal/economy/fred_view.py @@ -2,6 +2,7 @@ __docformat__ = "numpy" import os +import textwrap import matplotlib.pyplot as plt import pandas as pd @@ -26,7 +27,28 @@ def notes(series_term: str, num: int): num : int Maximum number of series notes to display """ - print(fred_model.get_series_notes(series_term, num)) + df_search = fred_model.get_series_notes(series_term) + if df_search.empty: + print("No matches found. \n") + return + df_search["notes"] = df_search["notes"].apply( + lambda x: "\n".join(textwrap.wrap(x, width=100)) if isinstance(x, str) else x + ) + df_search["title"] = df_search["title"].apply( + lambda x: "\n".join(textwrap.wrap(x, width=50)) if isinstance(x, str) else x + ) + if gtff.USE_TABULATE_DF: + print( + tabulate( + df_search[["id", "title", "notes"]].head(num), + tablefmt="fancy_grid", + headers=["Series ID", "Title", "Description"], + showindex=False, + ) + ) + else: + print(df_search[["id", "title", "notes"]].head(num).to_string(index=False)) + print("") def display_series(series: str, start_date: str, raw: bool, export: str): diff --git a/gamestonk_terminal/economy/nasdaq_view.py b/gamestonk_terminal/economy/nasdaq_view.py index dc605bcd9c1..710ac2253ed 100644 --- a/gamestonk_terminal/economy/nasdaq_view.py +++ b/gamestonk_terminal/economy/nasdaq_view.py @@ -27,7 +27,6 @@ def display_big_mac_index( export : str, optional Format data, by default "" """ - big_mac = pd.DataFrame() df_cols = ["Date"] df_cols.extend(country_codes) big_mac = pd.DataFrame(columns=df_cols) @@ -37,8 +36,10 @@ def display_big_mac_index( big_mac["Date"] = df1["Date"] big_mac.set_index("Date", inplace=True) fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) - big_mac.plot(ax=ax) + big_mac.plot(ax=ax, marker="o") + ax.legend(bbox_to_anchor=(1, 1)) ax.set_title("Big Mac Index (USD)") + ax.set_ylabel("Price of Big Mac in USD") ax.grid("on") if gtff.USE_ION: plt.ion() @@ -47,11 +48,7 @@ def display_big_mac_index( if raw: if gtff.USE_TABULATE_DF: - print( - tabulate( - big_mac.head(20), headers=big_mac.columns, tablefmt="fancy_grid" - ) - ) + print(tabulate(big_mac, headers=big_mac.columns, tablefmt="fancy_grid")) else: print(big_mac.head(20).to_string())