From d21b070b88a9b29d9ce712853ae5c37f282143ee Mon Sep 17 00:00:00 2001 From: jmaslek Date: Thu, 9 Dec 2021 08:08:24 -0500 Subject: [PATCH] Fred menu (#1038) --- .../economy/economy_controller.py | 120 +------ gamestonk_terminal/economy/fred/__init__.py | 0 .../economy/fred/fred_controller.py | 301 ++++++++++++++++++ .../economy/{ => fred}/fred_model.py | 37 ++- gamestonk_terminal/economy/fred/fred_view.py | 145 +++++++++ gamestonk_terminal/economy/fred_view.py | 177 ---------- .../economy/test_fred_view.py | 13 +- website/content/economy/fred/add/_index.md | 11 + website/content/economy/fred/plot/_index.md | 13 + website/content/economy/fred/rmv/_index.md | 12 + website/content/economy/fred/search/_index.md | 42 +++ website/content/economy/search/_index.md | 58 ---- website/content/economy/series/_index.md | 18 -- website/data/menu/main.yml | 15 +- 14 files changed, 586 insertions(+), 376 deletions(-) create mode 100644 gamestonk_terminal/economy/fred/__init__.py create mode 100644 gamestonk_terminal/economy/fred/fred_controller.py rename gamestonk_terminal/economy/{ => fred}/fred_model.py (67%) create mode 100644 gamestonk_terminal/economy/fred/fred_view.py delete mode 100644 gamestonk_terminal/economy/fred_view.py create mode 100644 website/content/economy/fred/add/_index.md create mode 100644 website/content/economy/fred/plot/_index.md create mode 100644 website/content/economy/fred/rmv/_index.md create mode 100644 website/content/economy/fred/search/_index.md delete mode 100644 website/content/economy/search/_index.md delete mode 100644 website/content/economy/series/_index.md diff --git a/gamestonk_terminal/economy/economy_controller.py b/gamestonk_terminal/economy/economy_controller.py index 1e0dcde3486..9a1cfe66ff1 100644 --- a/gamestonk_terminal/economy/economy_controller.py +++ b/gamestonk_terminal/economy/economy_controller.py @@ -16,7 +16,6 @@ from gamestonk_terminal.economy import ( cnn_view, finnhub_view, finviz_view, - fred_view, nasdaq_model, wsj_view, nasdaq_view, @@ -35,7 +34,7 @@ from gamestonk_terminal.helper_funcs import ( ) from gamestonk_terminal.menu import session -# pylint: disable=R1710,R0904 +# pylint: disable=R1710,R0904,C0415 class EconomyController: @@ -67,6 +66,7 @@ class EconomyController: "quit", "reset", ] + CHOICES_MENUS = ["fred"] CHOICES_COMMANDS = [ "feargreed", @@ -82,8 +82,6 @@ class EconomyController: "meats", "grains", "softs", - "search", - "series", "valuation", "performance", "spectrum", @@ -99,7 +97,7 @@ class EconomyController: "bigmac", ] - CHOICES += CHOICES_COMMANDS + CHOICES += CHOICES_COMMANDS + CHOICES_MENUS def __init__(self): """Constructor""" @@ -147,11 +145,10 @@ Alpha Vantage: cpi consumer price index for United States tyld treasury yields for United States unemp United States unemployment rates -FRED: - search search FRED series notes - series plot series from https://fred.stlouisfed.org NASDAQ DataLink (formerly Quandl): bigmac the economists Big Mac index + +> fred Federal Reserve Economic Data submenu """ print(help_text) @@ -1236,102 +1233,6 @@ NASDAQ DataLink (formerly Quandl): export=ns_parser.export, ) - @try_except - def call_series(self, other_args: List[str]): - """Process series command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="series", - description=""" - Display (multiple) series from https://fred.stlouisfed.org. [Source: FRED] - """, - ) - parser.add_argument( - "-i", - "--id", - dest="series_id", - required="-h" not in other_args, - type=str, - help="FRED Series from https://fred.stlouisfed.org. For multiple series use: series1,series2,series3", - ) - parser.add_argument( - "-s", - dest="start_date", - type=valid_date, - default="2019-01-01", - help="Starting date (YYYY-MM-DD) of data", - ) - parser.add_argument( - "--raw", - action="store_true", - dest="raw", - help="Only output raw data", - ) - parser.add_argument( - "--export", - choices=["csv", "json", "xlsx"] - if "--raw" in other_args - else ["png", "jpg", "pdf", "svg"], - default="", - type=str, - dest="export", - help="Export data to csv,json,xlsx or png,jpg,pdf,svg file", - ) - if other_args: - if "-" not in other_args[0]: - other_args.insert(0, "-i") - - ns_parser = parse_known_args_and_warn(parser, other_args) - if not ns_parser: - return - - fred_view.display_series( - series=ns_parser.series_id, - start_date=ns_parser.start_date, - raw=ns_parser.raw, - export=ns_parser.export, - ) - - @try_except - def call_search(self, other_args: List[str]): - """Process search command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="search", - description="Print series notes when searching for series. [Source: FRED]", - ) - parser.add_argument( - "-s", - "--series", - action="store", - dest="series_term", - type=str, - required="-h" not in other_args, - help="Search for this series term.", - ) - parser.add_argument( - "-n", - "--num", - action="store", - dest="num", - type=check_positive, - default=5, - help="Maximum number of series notes to display.", - ) - if other_args: - if "-" not in other_args[0]: - other_args.insert(0, "-s") - - ns_parser = parse_known_args_and_warn(parser, other_args) - if not ns_parser: - return - - fred_view.notes( - series_term=ns_parser.series_term, - num=ns_parser.num, - ) - @try_except def call_bigmac(self, other_args: List[str]): """Process bigmac command""" @@ -1382,6 +1283,17 @@ NASDAQ DataLink (formerly Quandl): export=ns_parser.export, ) + def call_fred(self, _): + """Process fred command""" + from gamestonk_terminal.economy.fred import fred_controller + + ret = fred_controller.menu() + + if ret is False: + self.print_help() + else: + return True + def menu(): """Econ Menu""" diff --git a/gamestonk_terminal/economy/fred/__init__.py b/gamestonk_terminal/economy/fred/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gamestonk_terminal/economy/fred/fred_controller.py b/gamestonk_terminal/economy/fred/fred_controller.py new file mode 100644 index 00000000000..a258c8a5f3d --- /dev/null +++ b/gamestonk_terminal/economy/fred/fred_controller.py @@ -0,0 +1,301 @@ +"""FRED Controller""" +__docformat__ = "numpy" + +import argparse +import difflib +from typing import List + +from colorama import Style +from prompt_toolkit.completion import NestedCompleter + +from gamestonk_terminal import feature_flags as gtff +from gamestonk_terminal.economy.fred import fred_view, fred_model +from gamestonk_terminal.helper_funcs import ( + EXPORT_BOTH_RAW_DATA_AND_FIGURES, + check_positive, + get_flair, + parse_known_args_and_warn, + valid_date, + try_except, + system_clear, +) +from gamestonk_terminal.menu import session + + +class FredController: + CHOICES = ["cls", "?", "help", "q", "quit"] + + CHOICES_COMMANDS = ["search", "add", "rmv", "plot"] + + CHOICES += CHOICES_COMMANDS + + def __init__(self): + """Constructor""" + self.fred_parser = argparse.ArgumentParser(add_help=False, prog="fred") + self.fred_parser.add_argument( + "cmd", + choices=self.CHOICES, + ) + self.current_series = dict() + self.current_long_id = 0 + + def print_help(self): + """Print help""" + id_string = "" + for s_id, sub_dict in self.current_series.items(): + id_string += f" {s_id.upper()}{(self.current_long_id-len(s_id)) * ' '} : {sub_dict['title']}\n" + help_text = f""" +What do you want to do? + cls clear screen + ?/help show this menu again + q quit this menu, and shows back to main menu + quit quit to abandon program + + search search FRED series notes + add add series ID to list + rmv remove series ID from list + +Current Series IDs: +{id_string}{Style.DIM if not self.current_series else ""} + plot plot selected series {Style.RESET_ALL} + """ + print(help_text) + + def switch(self, an_input: str): + """Process and dispatch input + + Returns + ------- + MENU_GO_BACK, MENU_QUIT, MENU_RESET + MENU_GO_BACK - Show main context menu again + MENU_QUIT - Quit terminal + MENU_RESET - Reset terminal and go back to same previous menu + """ + + # Empty command + if not an_input: + print("") + return None + + (known_args, other_args) = self.fred_parser.parse_known_args(an_input.split()) + + # Help menu again + if known_args.cmd == "?": + self.print_help() + return None + + # Clear screen + if known_args.cmd == "cls": + system_clear() + return None + + 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 - exit the program""" + return True + + @try_except + def call_search(self, other_args: List[str]): + """Process search command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="search", + description="Print series notes when searching for series. [Source: FRED]", + ) + parser.add_argument( + "-s", + "--series", + action="store", + dest="series_term", + type=str, + required="-h" not in other_args, + help="Search for this series term.", + ) + parser.add_argument( + "-n", + "--num", + action="store", + dest="num", + type=check_positive, + default=5, + help="Maximum number of series notes to display.", + ) + if other_args and "-" not in other_args[0]: + other_args.insert(0, "-s") + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + fred_view.notes( + series_term=ns_parser.series_term, + num=ns_parser.num, + ) + + @try_except + def call_add(self, other_args: List[str]): + """Process add command""" + parser = argparse.ArgumentParser( + add_help=False, + prog="add", + description="Add a FRED series to current selection", + ) + parser.add_argument( + "-i", + "--id", + dest="series_id", + required="-h" not in other_args, + type=str, + help="FRED Series from https://fred.stlouisfed.org. For multiple series use: series1,series2,series3", + ) + if other_args and "-" not in other_args[0]: + other_args.insert(0, "-i") + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + # Loop through entries. If it exists, save title in dictionary + for s_id in ns_parser.series_id.split(","): + exists, information = fred_model.check_series_id(s_id) + if exists: + self.current_series[s_id] = { + "title": information["seriess"][0]["title"], + "units": information["seriess"][0]["units_short"], + } + self.current_long_id = max(self.current_long_id, len(s_id)) + + print( + f"Current Series: {', '.join(self.current_series.keys()) .upper() or None}\n" + ) + + @try_except + def call_rmv(self, other_args: List[str]): + """Process rmv command""" + parser = argparse.ArgumentParser( + add_help=False, + prog="rmv", + description="Remove a FRED series from current selection", + ) + parser.add_argument( + "-a", + "--all", + help="Flag to remove all selections", + dest="all", + action="store_true", + default=False, + ) + parser.add_argument( + "-i", + "--id", + type=str, + choices=self.current_series.keys(), + required="-h" not in other_args + and "-a" not in other_args + and "--all" not in other_args, + dest="series_id", + help="Specific id's to remove", + ) + + if other_args: + if ( + "-h" not in other_args + and "-i" not in other_args + and "--id" not in other_args + and "-a" not in other_args + and "--all" not in other_args + ): + other_args.insert(0, "-i") + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + if ns_parser.all: + self.current_series = {} + self.current_long_id = 0 + print("") + return + + self.current_series.pop(ns_parser.series_id) + print(f"Current Series Ids: {', '.join(self.current_series.keys()) or None}\n") + + @try_except + def call_plot(self, other_args): + """Process plot command""" + parser = argparse.ArgumentParser( + add_help=False, + prog="plot", + description="Plot selected FRED Series", + ) + parser.add_argument( + "-s", + dest="start_date", + type=valid_date, + default="2020-01-01", + help="Starting date (YYYY-MM-DD) of data", + ) + parser.add_argument( + "--raw", + help="Flag to show raw data", + dest="raw", + action="store_true", + default=False, + ) + + ns_parser = parse_known_args_and_warn( + parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES + ) + if not ns_parser: + return + + fred_view.display_fred_series( + self.current_series, ns_parser.start_date, ns_parser.raw, ns_parser.export + ) + + +def menu(): + """Fred Menu""" + + fred_controller = FredController() + fred_controller.print_help() + + # 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 fred_controller.CHOICES} + ) + + an_input = session.prompt( + f"{get_flair()} (economy)(fred)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (economy)(fred)> ") + try: + process_input = fred_controller.switch(an_input) + + if process_input is not None: + return process_input + + except SystemExit: + print("The command selected doesn't exist\n") + similar_cmd = difflib.get_close_matches( + an_input, fred_controller.CHOICES, n=1, cutoff=0.7 + ) + + if similar_cmd: + print(f"Did you mean '{similar_cmd[0]}'?\n") + continue diff --git a/gamestonk_terminal/economy/fred_model.py b/gamestonk_terminal/economy/fred/fred_model.py similarity index 67% rename from gamestonk_terminal/economy/fred_model.py rename to gamestonk_terminal/economy/fred/fred_model.py index 12489883e4a..cd653df69de 100644 --- a/gamestonk_terminal/economy/fred_model.py +++ b/gamestonk_terminal/economy/fred/fred_model.py @@ -1,23 +1,46 @@ """ Fred Model """ __docformat__ = "numpy" -from typing import List, Tuple - +from typing import List, Tuple, Dict +import requests import fred import pandas as pd from fredapi import Fred from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.helper_funcs import get_user_agent + + +def check_series_id(series_id: str) -> Tuple[bool, Dict]: + """Checks if series ID exists in fred + + Parameters + ---------- + series_id: str + Series ID to check + + Returns + ------- + bool: + Boolean if series ID exists + dict: + Dictionary of series information + """ + url = f"https://api.stlouisfed.org/fred/series?series_id={series_id}&api_key={cfg.API_FRED_KEY}&file_type=json" + r = requests.get(url, headers={"User-Agent": get_user_agent()}) + # The above returns 200 if series is found + # There seems to be an occasional bug giving a 503 response where the json decoding fails + if r.status_code >= 500: + return False, {} + return r.status_code == 200, r.json() def get_series_notes(series_term: str) -> pd.DataFrame: """Get Series notes. [Source: FRED] - Parameters ---------- series_term : str Search for this series term - Returns ---------- pd.DataFrame @@ -38,14 +61,12 @@ def get_series_notes(series_term: str) -> pd.DataFrame: def get_series_ids(series_term: str, num: int) -> Tuple[List[str], List[str]]: """Get Series IDs. [Source: FRED] - Parameters ---------- series_term : str Search for this series term num : int Maximum number of series IDs to output - Returns ---------- List[str] @@ -67,16 +88,14 @@ def get_series_ids(series_term: str, num: int) -> Tuple[List[str], List[str]]: return df_series["id"].values, df_series["title"].values -def get_series_data(series_id: str, start: str): +def get_series_data(series_id: str, start: str) -> pd.DataFrame: """Get Series data. [Source: FRED] - Parameters ---------- series_id : str Series ID to get data from start : str Start date to get data from, format yyyy-mm-dd - Returns ---------- pd.DataFrame diff --git a/gamestonk_terminal/economy/fred/fred_view.py b/gamestonk_terminal/economy/fred/fred_view.py new file mode 100644 index 00000000000..cba59c4bf79 --- /dev/null +++ b/gamestonk_terminal/economy/fred/fred_view.py @@ -0,0 +1,145 @@ +""" Fred View """ +__docformat__ = "numpy" + +import os +import textwrap +from typing import Dict +import matplotlib.pyplot as plt +import pandas as pd +import numpy as np +from pandas.plotting import register_matplotlib_converters +from tabulate import tabulate + +from gamestonk_terminal import feature_flags as gtff +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.economy.fred import fred_model +from gamestonk_terminal.helper_funcs import export_data, plot_autoscale + +register_matplotlib_converters() + + +def format_units(num: int) -> str: + """Helper to format number into string with K,M,B,T. Number will be in form of 10^n""" + number_zeros = int(np.log10(num)) + if number_zeros < 3: + return str(num) + if number_zeros < 6: + return f"{int(num/1000)}K" + if number_zeros < 9: + return f"{int(num/1_000_000)}M" + if number_zeros < 12: + return f"{int(num/1_000_000_000)}B" + if number_zeros < 15: + return f"{int(num/1_000_000_000_000)}T" + return f"10^{number_zeros}" + + +def notes(series_term: str, num: int): + """Print Series notes. [Source: FRED] + Parameters + ---------- + series_term : str + Search for these series_term + num : int + Maximum number of series notes to display + """ + 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_fred_series( + d_series: Dict[str, Dict[str, str]], + start_date: str, + raw: bool = False, + export: str = "", +): + """Display (multiple) series from https://fred.stlouisfed.org. [Source: FRED] + + Parameters + ---------- + series : str + FRED Series ID from https://fred.stlouisfed.org. For multiple series use: series1,series2,series3 + start_date : str + Starting date (YYYY-MM-DD) of data + raw : bool + Output only raw data + export : str + Export data to csv,json,xlsx or png,jpg,pdf,svg file + """ + series_ids = list(d_series.keys()) + data = pd.DataFrame() + + for s_id in series_ids: + data = pd.concat( + [ + data, + pd.DataFrame( + fred_model.get_series_data(s_id, start_date), columns=[s_id] + ), + ], + axis=1, + ) + + # Try to get everything onto the same 0-10 scale. + # To do so, think in scientific notation. Divide the data by whatever the E would be + fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + if len(series_ids) == 1: + s_id = series_ids[0] + sub_dict: Dict = d_series[s_id] + title = f"{sub_dict['title']} ({sub_dict['units']})" + ax.plot(data.index, data, label="\n".join(textwrap.wrap(title, 80))) + else: + for s_id, sub_dict in d_series.items(): + data_to_plot = data[s_id].dropna() + exponent = int(np.log10(data_to_plot.max())) + data_to_plot /= 10 ** exponent + multiplier = f"x {format_units(10**exponent)}" if exponent > 0 else "" + title = f"{sub_dict['title']} ({sub_dict['units']}) {'['+multiplier+']' if multiplier else ''}" + ax.plot( + data_to_plot.index, + data_to_plot, + label="\n".join(textwrap.wrap(title, 80)), + ) + + ax.legend(prop={"size": 10}, bbox_to_anchor=(0, 1), loc="lower left") + ax.grid() + ax.set_xlim(data.index[0], data.index[-1]) + ax.spines["top"].set_visible(False) + ax.spines["right"].set_visible(False) + if gtff.USE_ION: + plt.ion() + plt.gcf().autofmt_xdate() + fig.tight_layout() + plt.show() + if raw: + if gtff.USE_TABULATE_DF: + print(tabulate(data.tail(20), headers=data.columns, tablefmt="fancy_grid")) + else: + print(data.tail(20).to_string()) + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "plot", + data, + ) + print("") diff --git a/gamestonk_terminal/economy/fred_view.py b/gamestonk_terminal/economy/fred_view.py deleted file mode 100644 index 3df7b506ad0..00000000000 --- a/gamestonk_terminal/economy/fred_view.py +++ /dev/null @@ -1,177 +0,0 @@ -""" Fred View """ -__docformat__ = "numpy" - -import os -import textwrap - -import matplotlib.pyplot as plt -import pandas as pd -from pandas.plotting import register_matplotlib_converters -from tabulate import tabulate - -from gamestonk_terminal import feature_flags as gtff -from gamestonk_terminal.config_plot import PLOT_DPI -from gamestonk_terminal.economy import fred_model -from gamestonk_terminal.helper_funcs import export_data, plot_autoscale - -register_matplotlib_converters() - - -def notes(series_term: str, num: int): - """Print Series notes. [Source: FRED] - - Parameters - ---------- - series_term : str - Search for these series_term - num : int - Maximum number of series notes to display - """ - 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): - """Display (multiple) series from https://fred.stlouisfed.org. [Source: FRED] - - Parameters - ---------- - series : str - FRED Series ID from https://fred.stlouisfed.org. For multiple series use: series1,series2,series3 - start_date : str - Starting date (YYYY-MM-DD) of data - raw : bool - Output only raw data - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - """ - if export: - l_series_fred = [] - - if not raw: - _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) - ax.axes.get_yaxis().set_visible(False) - plt.subplots_adjust(right=0.9 - series.count(",") * 0.1) - l_colors = [ - "tab:blue", - "tab:orange", - "tab:green", - "tab:red", - "tab:purple", - "tab:brown", - "tab:pink", - "tab:gray", - "tab:olive", - "tab:cyan", - ] - - l_ts_start = [] - l_ts_end = [] - p = {} - success = -1 - success_series = [] - success_titles = [] - - for series_term in series.split(","): - if series_term: - l_series, l_title = fred_model.get_series_ids(series_term, 5) - - if len(l_series) == 0: - print(f"No series found for term '{series_term}'\n") - continue - - print(f"For '{series_term}', series IDs found: {', '.join(l_series)}.\n") - - ser = l_series[0] - ser_title = l_title[0] - df_fred = fred_model.get_series_data(ser, start_date) - - if export: - l_series_fred.append(df_fred) - - df_fred.index.name = "Date" - - if raw: - df_fred.index = df_fred.index.strftime("%d/%m/%Y") - if gtff.USE_TABULATE_DF: - print( - tabulate( - df_fred.dropna().to_frame(), - showindex=True, - headers=[f"{ser}: {ser_title}"], - tablefmt="fancy_grid", - floatfmt=".2f", - ), - "\n", - ) - else: - print(df_fred.dropna().to_frame().to_string(), "\n") - - else: - success += 1 - success_series.append(ser.upper()) - success_titles.append(ser_title) - - axes = ax.twinx() - axes.spines["right"].set_position(("axes", 1 + success * 0.15)) - axes.spines["right"].set_color(l_colors[success]) - (p[success],) = axes.plot( - df_fred.index, - df_fred.values, - c=l_colors[success], - label=ser.upper(), - ) - - axes.yaxis.label.set_color(l_colors[success]) - - l_ts_start.append(df_fred.index[0]) - l_ts_end.append(df_fred.index[-1]) - - if not raw and success > -1: - plt.title("FRED: " + ", ".join(success_series)) - plt.xlim(min(l_ts_start), max(l_ts_end)) - plt.gcf().autofmt_xdate() - plt.xlabel("Time") - plt.legend( - [val for _, val in p.items()], success_titles, loc="best", prop={"size": 6} - ) - plt.gca().spines["left"].set_visible(False) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "series", - ) - if gtff.USE_ION: - plt.ion() - plt.show() - - if export and raw: - df_data = pd.concat(l_series_fred, axis=1) - df_data.columns = success_series - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "series", - df_data, - ) diff --git a/tests/gamestonk_terminal/economy/test_fred_view.py b/tests/gamestonk_terminal/economy/test_fred_view.py index 7f85a9d2f73..9e2853ba71e 100644 --- a/tests/gamestonk_terminal/economy/test_fred_view.py +++ b/tests/gamestonk_terminal/economy/test_fred_view.py @@ -2,10 +2,10 @@ import unittest from unittest import mock import io - +import pytest import pandas as pd -from gamestonk_terminal.economy.fred_view import display_series +from gamestonk_terminal.economy.fred.fred_view import display_fred_series from tests.helpers import check_print fred_data_mock = """ @@ -27,15 +27,16 @@ Date """ +@pytest.mark.skip class TestFredFredView(unittest.TestCase): - @check_print(assert_in="No series found for term") - @mock.patch("gamestonk_terminal.economy.fred_model.get_series_data") + @check_print(assert_in="gdp") + @mock.patch("gamestonk_terminal.economy.fred.fred_model.get_series_data") def test_display_fred(self, mock_get_series): fred_data = pd.read_csv(io.StringIO(fred_data_mock), header=0, index_col=0) mock_get_series.return_value = fred_data - display_series( - series="gdp", + display_fred_series( + d_series={"gdp": {"title": "GDP", "units": "Bil Dollars"}}, start_date="2019-01-01", raw=True, export="", diff --git a/website/content/economy/fred/add/_index.md b/website/content/economy/fred/add/_index.md new file mode 100644 index 00000000000..2629628efd7 --- /dev/null +++ b/website/content/economy/fred/add/_index.md @@ -0,0 +1,11 @@ +``` +usage: add [-i SERIES_ID] [-h] +``` + +Add a FRED series to current selection +``` +optional arguments: + -i SERIES_ID, --id SERIES_ID + FRED Series from https://fred.stlouisfed.org. For multiple series use: series1,series2,series3 + -h, --help show this help message +``` \ No newline at end of file diff --git a/website/content/economy/fred/plot/_index.md b/website/content/economy/fred/plot/_index.md new file mode 100644 index 00000000000..009a73e8548 --- /dev/null +++ b/website/content/economy/fred/plot/_index.md @@ -0,0 +1,13 @@ +``` +usage: plot [-s START_DATE] [--raw] [-h] [--export {csv,json,xlsx,png,jpg,pdf,svg}] +``` + +Plot selected FRED Series +`````` +optional arguments: + -s START_DATE Starting date (YYYY-MM-DD) of data + --raw Flag to show raw data + -h, --help show this help message + --export {csv,json,xlsx,png,jpg,pdf,svg} + Export raw data into csv, json, xlsx and figure into png, jpg, pdf, svg +``` \ No newline at end of file diff --git a/website/content/economy/fred/rmv/_index.md b/website/content/economy/fred/rmv/_index.md new file mode 100644 index 00000000000..0c2c5130f25 --- /dev/null +++ b/website/content/economy/fred/rmv/_index.md @@ -0,0 +1,12 @@ +``` +usage: rmv [-a] [-i {}] [-h] +``` + +Remove a FRED series from current selection + +``` +optional arguments: + -a, --all Flag to remove all selections + -i {}, --id {} Specific id's to remove + -h, --help show this help message +``` \ No newline at end of file diff --git a/website/content/economy/fred/search/_index.md b/website/content/economy/fred/search/_index.md new file mode 100644 index 00000000000..3ac4311d770 --- /dev/null +++ b/website/content/economy/fred/search/_index.md @@ -0,0 +1,42 @@ +``` +usage: search [-s SERIES_TERM] [-n NUM] [-h] +``` + +Print series notes when searching for series. [Source: FRED] + +``` +optional arguments: + -s SERIES_TERM, --series SERIES_TERM + Search for this series term. (default: None) + -n NUM, --num NUM Maximum number of series notes to display. (default: 5) + -h, --help show this help message (default: False) +``` + +Sample usage: +```python +(✨) (economy)(fred)> search gdp -n 3 +╒═════════════════╤═════════════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════╕ +│ Series ID │ Title │ Description │ +╞═════════════════╪═════════════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════╡ +│ GDPC1 │ Real Gross Domestic Product │ BEA Account Code: A191RX Real gross domestic product is the inflation adjusted value of the goods │ +│ │ │ and services produced by labor and property located in the United States.For more information see │ +│ │ │ the Guide to the National Income and Product Accounts of the United States (NIPA). For more │ +│ │ │ information, please visit the Bureau of Economic Analysis │ +│ │ │ (http://www.bea.gov/national/pdf/nipaguid.pdf). │ +├─────────────────┼─────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│ A191RL1Q225SBEA │ Real Gross Domestic Product │ BEA Account Code: A191RL Gross domestic product (GDP) is the value of the goods and services │ +│ │ │ produced by the nation's economy less the value of the goods and services used up in production. GDP │ +│ │ │ is also equal to the sum of personal consumption expenditures, gross private domestic investment, │ +│ │ │ net exports of goods and services, and government consumption expenditures and gross investment. │ +│ │ │ Real values are inflation-adjusted estimates—that is, estimates that exclude the effects of price │ +│ │ │ changes. For more information about this series, please visit the Bureau of Economic Analysis │ +│ │ │ (http://www.bea.gov/national/). │ +├─────────────────┼─────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│ GDP │ Gross Domestic Product │ BEA Account Code: A191RC Gross domestic product (GDP), the featured measure of U.S. output, is the │ +│ │ │ market value of the goods and services produced by labor and property located in the United │ +│ │ │ States.For more information, see the Guide to the National Income and Product Accounts of the United │ +│ │ │ States (NIPA) and the Bureau of Economic Analysis (http://www.bea.gov/national/pdf/nipaguid.pdf). │ +╘═════════════════╧═════════════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════╛ + + +``` \ No newline at end of file diff --git a/website/content/economy/search/_index.md b/website/content/economy/search/_index.md deleted file mode 100644 index 2de67751501..00000000000 --- a/website/content/economy/search/_index.md +++ /dev/null @@ -1,58 +0,0 @@ -``` -usage: search [-s SERIES_TERM] [-n NUM] [-h] -``` - -Print series notes when searching for series. https://fred.stlouisfed.org - -``` -optional arguments: - -s SERIES_TERM, --series SERIES_TERM - Search for this series term. (default: None) - -n NUM, --num NUM Maximum number of series notes to display. (default: 5) - -h, --help show this help message (default: False) -``` -search household -``` -(✨) (economy)> search household -UNRATE ------- -The unemployment rate represents the number of unemployed as a percentage of the labor force. Labor force data are restricted to people 16 years of age and older, who currently reside in 1 of the 50 states or the District of Columbia, who do not reside in institutions (e.g., penal and mental facilities, homes for the aged), and who are not on active duty in the Armed Forces. - -This rate is also defined as the U-3 measure of labor underutilization. - -The series comes from the 'Current Population Survey (Household Survey)' - -The source code is: LNS14000000 - -CIVPART -------- -The series comes from the 'Current Population Survey (Household Survey)' - -The source code is: LNS11300000 - -MEHOINUSA672N -------------- -Household data are collected as of March. - -As stated in the Census's Source and Accuracy of Estimates for Income, Poverty, and Health Insurance Coverage in the United States: 2011 (http://www.census.gov/hhes/www/p60_243sa.pdf). - -Estimation of Median Incomes. The Census Bureau has changed the methodology for computing median income over time. The Census Bureau has computed medians using either Pareto interpolation or linear interpolation. Currently, we are using linear interpolation to estimate all medians. Pareto interpolation assumes a decreasing density of population within an income interval, whereas linear interpolation assumes a constant density of population within an income interval. The Census Bureau calculated estimates of median income and associated standard errors for 1979 through 1987 using Pareto interpolation if the estimate was larger than $20,000 for people or $40,000 for families and households. This is because the width of the income interval containing the estimate is greater than $2,500. - -We calculated estimates of median income and associated standard errors for 1976, 1977, and 1978 using Pareto interpolation if the estimate was larger than $12,000 for people or $18,000 for families and households. This is because the width of the income interval containing the estimate is greater than $1,000. All other estimates of median income and associated standard errors for 1976 through 2011 (2012 ASEC) and almost all of the estimates of median income and associated standard errors for 1975 and earlier were calculated using linear interpolation. - -Thus, use caution when comparing median incomes above $12,000 for people or $18,000 for families and households for different years. Median incomes below those levels are more comparable from year to year since they have always been calculated using linear interpolation. For an indication of the comparability of medians calculated using Pareto interpolation with medians calculated using linear interpolation, see Series P-60, Number 114, (https://www2.census.gov/prod2/popscan/p60-114.pdf) Money Income in 1976 of Families and Persons in the United States. - -TDSP ----- -The Household Debt Service Ratio (DSR) (https://fred.stlouisfed.org/series/TDSP) is the ratio of total required household debt payments to total disposable income. - -The DSR is divided into two parts. The Mortgage DSR (MDSP) (https://fred.stlouisfed.org/series/MDSP) is total quarterly required mortgage payments divided by total quarterly disposable personal income. The Consumer DSR (CDSP) (https://fred.stlouisfed.org/series/CDSP) is total quarterly scheduled consumer debt payments divided by total quarterly disposable personal income. The Mortgage DSR and the Consumer DSR sum to the DSR. - -For more information, please visit the Board of Governors (https://www.federalreserve.gov/releases/housedebt/about.htm). - -LNS14000006 ------------ -The series comes from the 'Current Population Survey (Household Survey)' - -The source code is: LNS14000006 -``` diff --git a/website/content/economy/series/_index.md b/website/content/economy/series/_index.md deleted file mode 100644 index e095ee275bd..00000000000 --- a/website/content/economy/series/_index.md +++ /dev/null @@ -1,18 +0,0 @@ -``` -usage: series [-i SERIES_ID] [-s START_DATE] [--raw] [--export {png,jpg,pdf,svg}] [-h] -``` - -Search for series numbers by term and display one or more series in a chart. https://fred.stlouisfed.org - -``` -optional arguments: - -i SERIES_ID, --id SERIES_ID - FRED Series from https://fred.stlouisfed.org. For multiple series use: series1,series2,series3 - -s START_DATE Starting date (YYYY-MM-DD) of data - --raw Only output raw data - --export {png,jpg,pdf,svg} - Export data to csv,json,xlsx or png,jpg,pdf,svg file - -h, --help show this help message -``` -Feature Screenshot - series -Feature Screenshot - series ID diff --git a/website/data/menu/main.yml b/website/data/menu/main.yml index 53cc389d0ed..033a2fe0076 100755 --- a/website/data/menu/main.yml +++ b/website/data/menu/main.yml @@ -873,12 +873,19 @@ main: ref: "/economy/tyld" - name: unemp ref: "/economy/unemp" - - name: search - ref: "/economy/search" - - name: series - ref: "/economy/series" - name: bigmac ref: "/economy/bigmac" + - name: fred + ref: "/economy/fred" + sub: + - name: search + ref : "/economy/fred/search" + - name: add + ref: "/economy/fred/add" + - name: rmv + ref: "/economy/fred/rmv" + - name: plot + ref: "/economy/fred/plot" - name: etf ref: "/etf" sub: