From 16cbcdd9c1298c326ea9e238fd76747dcad1a5f6 Mon Sep 17 00:00:00 2001 From: Jeroen Bouma Date: Thu, 10 Mar 2022 09:25:36 +0100 Subject: [PATCH] Add indices --- gamestonk_terminal/economy/econdb_model.py | 2 +- .../economy/economy_controller.py | 253 ++++++++++-------- gamestonk_terminal/economy/nasdaq_view.py | 5 +- gamestonk_terminal/economy/yfinance_model.py | 84 ++++++ gamestonk_terminal/economy/yfinance_view.py | 122 +++++++++ 5 files changed, 353 insertions(+), 113 deletions(-) create mode 100644 gamestonk_terminal/economy/yfinance_model.py create mode 100644 gamestonk_terminal/economy/yfinance_view.py diff --git a/gamestonk_terminal/economy/econdb_model.py b/gamestonk_terminal/economy/econdb_model.py index 4fb51d6fe82..c9c6db736d3 100644 --- a/gamestonk_terminal/economy/econdb_model.py +++ b/gamestonk_terminal/economy/econdb_model.py @@ -264,7 +264,7 @@ def get_data(parameter: str, country: str, convert_currency: str = "USD") -> pd. df = ( df * yf.Ticker(f"{convert_currency}{country_currency}=X").history( - period="1d" + start=df.index[0] )["Close"][0] ) diff --git a/gamestonk_terminal/economy/economy_controller.py b/gamestonk_terminal/economy/economy_controller.py index b1850268851..e3471677632 100644 --- a/gamestonk_terminal/economy/economy_controller.py +++ b/gamestonk_terminal/economy/economy_controller.py @@ -1,11 +1,11 @@ """ Econ Controller """ __docformat__ = "numpy" # pylint:disable=too-many-lines,R1710,R0904,C0415 + import argparse import logging import os from typing import List - import pandas as pd from prompt_toolkit.completion import NestedCompleter @@ -20,7 +20,10 @@ from gamestonk_terminal.economy import ( wsj_view, econdb_view, econdb_model, + yfinance_model, + yfinance_view, ) + from gamestonk_terminal.helper_funcs import ( EXPORT_BOTH_RAW_DATA_AND_FIGURES, EXPORT_ONLY_FIGURES_ALLOWED, @@ -42,7 +45,7 @@ class EconomyController(BaseController): "overview", "futures", "macro", - "macrous", + "indices", "valuation", "performance", "spectrum", @@ -145,6 +148,8 @@ class EconomyController(BaseController): c: None for c in ["energy", "metals", "meats", "grains", "softs"] } + choices["indices"] = {c: None for c in yfinance_model.INDICES} + choices["macro"]["-p"] = {c: None for c in econdb_model.PARAMETERS} choices["macro"]["--parameter"] = {c: None for c in econdb_model.PARAMETERS} choices["macro"]["-c"] = {c: None for c in econdb_model.COUNTRY_CODES} @@ -152,9 +157,6 @@ class EconomyController(BaseController): c: None for c in econdb_model.COUNTRY_CODES } - choices["macrous"]["-t"] = {c: None for c in self.macro_us_types} - choices["macrous"]["--type"] = {c: None for c in self.macro_us_types} - choices["valuation"]["-s"] = {c: None for c in self.valuation_sort_cols} choices["valuation"]["--sortby"] = { c: None for c in self.valuation_sort_cols @@ -185,7 +187,7 @@ Overview Macro Data macro collect macro data for a country or countries [src][Source: EconDB][/src] - macrous show United States macro data [src][Source: Alpha Vantage][/src] + indices show the most important indices worldwide [src][Source: Yahoo Finance][/src] Performance & Valuations rtps real-time performance sectors [src][Source: Alpha Vantage][/src] @@ -349,7 +351,7 @@ Index add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="macro", - description="Get a broad selection of macro data from one or multiple companies. [Source: EconDB}", + description="Get a broad selection of macro data from one or multiple companies. [Source: EconDB]", ) parser.add_argument( @@ -449,6 +451,139 @@ Index export=ns_parser.export, ) + @log_start_end(log=logger) + def call_indices(self, other_args: List[str]): + """Process indices command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="indices", + description="Obtain any set of indices and plot them together. With the -si argument the major indices are " + "shown. By using the arguments (for example 'nasdaq' and 'sp500') you can collect data and " + "plot the graphs together. [Source: Yahoo finance / FinanceDatabase]", + ) + + parser.add_argument( + "-i", + "--indices", + nargs="+", + dest="indices", + help="One or multiple indices", + ) + + parser.add_argument( + "-si", + "--show_indices", + dest="show_indices", + help="Show the major indices, their arguments and ticker", + action="store_true", + default=False, + ) + + parser.add_argument( + "-iv", + "--interval", + type=str, + dest="interval", + help="The preferred interval data is shown at. This can be 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, " + "1d, 5d, 1wk, 1mo or 3mo", + choices=[ + "1m", + "2m", + "5m", + "15m", + "30m", + "60m", + "90m", + "1h", + "1d", + "5d", + "1wk", + "1mo", + "3mo", + ], + default="1d", + ) + + parser.add_argument( + "-s", + "--start_date", + dest="start_date", + help="The start date of the data (format: YEAR-MONTH-DAY, i.e. 2010-12-31)", + default="2000-01-01", + ) + + parser.add_argument( + "-e", + "--end_date", + dest="end_date", + help="The end date of the data (format: YEAR-MONTH-DAY, i.e. 2021-06-20)", + default=None, + ) + + parser.add_argument( + "-c", + "--column", + type=str, + dest="column", + help="The column you wish to load in, by default this is the Adjusted Close column", + default="Adj Close", + ) + + parser.add_argument( + "-q", + "--query", + type=str, + nargs="+", + dest="query", + help="Search for indices with given keyword", + ) + + parser.add_argument( + "-l", + "--limit", + type=str, + dest="limit", + help="Takes into account the amount of rows you wish to see for your query ('-q').", + default=10, + ) + + parser.add_argument( + "-r", + "--raw", + dest="raw", + help="Show raw data", + action="store_true", + default=False, + ) + + if other_args and "-" not in other_args[0][0]: + other_args.insert(0, "-i") + ns_parser = parse_known_args_and_warn( + parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED + ) + if ns_parser: + if ns_parser.query and ns_parser.limit: + yfinance_view.search_indices(ns_parser.query, ns_parser.limit) + if ns_parser.show_indices: + print_rich_table( + pd.DataFrame.from_dict(yfinance_model.INDICES, orient="index"), + show_index=True, + index_name="Argument", + headers=["Name", "Ticker"], + title="Major Indices", + ) + elif ns_parser.indices: + yfinance_view.show_indices( + indices=ns_parser.indices, + interval=ns_parser.interval, + start_date=ns_parser.start_date, + end_date=ns_parser.end_date, + column=ns_parser.column, + raw=ns_parser.raw, + export=ns_parser.export, + ) + @log_start_end(log=logger) def call_map(self, other_args: List[str]): """Process map command""" @@ -663,110 +798,6 @@ Index export=ns_parser.export, ) - @log_start_end(log=logger) - def call_macrous(self, other_args: List[str]): - """Process macrous command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="macrous", - description=""" - Show a large selection of US Macro data including Real Gross Domestic Product (GDP), - Inflation Rates (INF), Consumer Price Index (CPI), Treasury Yields (TYLD) and - Unemployment Rates (UNEMP) [Source: Alpha Vantage] - """, - ) - - parser.add_argument( - "-t", - "--type", - help="Type of macro data you wish to view.", - dest="type", - type=str, - choices=self.macro_us_types, - default="GDP", - ) - - parser.add_argument( - "-i", - "--interval", - help="The interval you wish to show the data for. This is dependent on the type of data.", - dest="interval", - type=str, - choices=self.macro_us_interval, - default="annual", - ) - parser.add_argument( - "-s", - "--start", - help="Start year. Quarterly only goes back to 2002.", - dest="start", - type=int, - default=2010, - ) - - parser.add_argument( - "-m", - "--maturity", - help="Maturity timeline for treasuries (when entering he TYLD parameter)", - dest="maturity", - choices=self.tyld_maturity, - default="5y", - ) - - parser.add_argument( - "--raw", - help="Display raw data", - action="store_true", - dest="raw", - default=False, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - - ns_parser = parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - type_upper = ns_parser.type.upper() - - if type_upper == "GDP": - alphavantage_view.display_real_gdp( - interval=ns_parser.interval, - start_year=ns_parser.start, - raw=ns_parser.raw, - export=ns_parser.export, - ) - elif type_upper == "INF": - alphavantage_view.display_inflation( - start_year=ns_parser.start, - raw=ns_parser.raw, - export=ns_parser.export, - ) - elif type_upper == "CPI": - alphavantage_view.display_cpi( - interval=ns_parser.interval, - start_year=ns_parser.start, - raw=ns_parser.raw, - export=ns_parser.export, - ) - elif type_upper == "TYLD": - alphavantage_view.display_treasury_yield( - interval=ns_parser.interval, - maturity=ns_parser.maturity, - start_date=ns_parser.start, - raw=ns_parser.raw, - export=ns_parser.export, - ) - elif type_upper == "UNEMP": - alphavantage_view.display_unemployment( - start_year=ns_parser.start, - raw=ns_parser.raw, - export=ns_parser.export, - ) - @log_start_end(log=logger) def call_bigmac(self, other_args: List[str]): """Process bigmac command""" diff --git a/gamestonk_terminal/economy/nasdaq_view.py b/gamestonk_terminal/economy/nasdaq_view.py index e58692f8c1f..f14e617ce43 100644 --- a/gamestonk_terminal/economy/nasdaq_view.py +++ b/gamestonk_terminal/economy/nasdaq_view.py @@ -75,7 +75,10 @@ def display_big_mac_index( if raw: print_rich_table( - big_mac, headers=list(big_mac.columns), title="Big Mac Index" + big_mac, + headers=list(big_mac.columns), + title="Big Mac Index", + show_index=True, ) console.print("") diff --git a/gamestonk_terminal/economy/yfinance_model.py b/gamestonk_terminal/economy/yfinance_model.py new file mode 100644 index 00000000000..efe036ab442 --- /dev/null +++ b/gamestonk_terminal/economy/yfinance_model.py @@ -0,0 +1,84 @@ +""" Yahoo Finance Model """ +__docformat__ = "numpy" + +import logging + +import pandas as pd +import yfinance as yf + +from gamestonk_terminal.decorators import log_start_end +from gamestonk_terminal.rich_config import console + +logger = logging.getLogger(__name__) + +INDICES = { + "sp500": {"name": "S&P 500", "ticker": "^GSPC"}, + "nasdaq": {"name": "Nasdaq Composite", "ticker": "^IXIC"}, + "dowjones": {"name": "Dow Jones Industrial Average", "ticker": "^DJI"}, + "vix": {"name": "CBOE Volatility Index", "ticker": "^VIX"}, + "russel": {"name": "Russel 2000 Index", "ticker": "^RUT"}, + "tsx": {"name": "TSX Composite", "ticker": "^GSPTSE"}, + "nikkei": {"name": "Nikkei 255 Stock Average", "ticker": "^N225"}, + "shanghai": {"name": "Shanghai Composite Index", "ticker": "000001.SS"}, + "ftse100": {"name": "FTSE 100 Index ('footsie')", "ticker": "^FTSE"}, + "stoxx50": {"name": "Euro STOXX 50", "ticker": "^STOXX50E"}, + "dax": {"name": "DAX Performance Index", "ticker": "^GDAXI"}, + "cac40": {"name": "CAC 40 Index", "ticker": "^FCHI"}, +} + + +@log_start_end(log=logger) +def get_index( + index: str, + interval: str = "1d", + start_date: int = None, + end_date: int = None, + column: str = "Adj Close", +) -> pd.Series: + """Obtain data on any index [Source: Yahoo Finance] + + Parameters + ---------- + index: str + The index you wish to collect data for. + start_date : str + the selected country + end_date : bool + The currency you wish to convert the data to. + interval : str + Valid intervals: 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, 1d, 5d, 1wk, 1mo or 3mo + Intraday data cannot extend last 60 days + column : str + The column you wish to select, by default this is Adjusted Close. + + Returns + ---------- + pd.Series + A series with the requested index + """ + if index in INDICES: + ticker = INDICES[index]["ticker"] + else: + ticker = index + + index_data = yf.download( + ticker, + start=start_date, + end=end_date, + interval=interval, + progress=False, + show_errors=False, + ) + + if column not in index_data.columns: + return console.print( + f"The chosen column is not available for {ticker}. Please choose " + f"between: {', '.join(index_data.columns)}" + ) + if index_data.empty or len(index_data) < 2: + return console.print( + f"The chosen index {ticker}, returns no data. Please check if " + f"there is any data available." + ) + + return index_data[column] diff --git a/gamestonk_terminal/economy/yfinance_view.py b/gamestonk_terminal/economy/yfinance_view.py new file mode 100644 index 00000000000..47146ce10ba --- /dev/null +++ b/gamestonk_terminal/economy/yfinance_view.py @@ -0,0 +1,122 @@ +""" EconDB View """ +__docformat__ = "numpy" + +import logging +from typing import Optional, List + +import pandas as pd +from matplotlib import pyplot as plt +import financedatabase as fd + +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.config_terminal import theme +from gamestonk_terminal.decorators import log_start_end +from gamestonk_terminal.economy.yfinance_model import get_index, INDICES +from gamestonk_terminal.helper_funcs import plot_autoscale, print_rich_table + +logger = logging.getLogger(__name__) + + +@log_start_end(log=logger) +def show_indices( + indices: list, + interval: str = "1d", + start_date: int = None, + end_date: int = None, + column: str = "Adj Close", + raw: bool = False, + external_axes: Optional[List[plt.axes]] = None, + export: str = "", +): + """Load (and show) the selected indices over time [Source: Yahoo Finance] + + Parameters + ---------- + indices: list + A list of indices you wish to load (and plot). + interval: str + Valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo + Intraday data cannot extend last 60 days + start_date : str + The starting date, format "YEAR-MONTH-DAY", i.e. 2010-12-31. + end_date : str + The end date, format "YEAR-MONTH-DAY", i.e. 2020-06-05. + raw : bool + Whether to display the raw output. + external_axes: Optional[List[plt.axes]] + External axes to plot on + export : str + Export data to csv,json,xlsx or png,jpg,pdf,svg file + + Returns + ---------- + Plots the Series. + """ + indices_data: pd.DataFrame = pd.DataFrame() + + if external_axes is None: + _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + else: + ax = external_axes[0] + + for index in indices: + indices_data[index] = get_index(index, interval, start_date, end_date, column) + + if index in INDICES: + label = INDICES[index]["name"] + else: + label = index + + if not indices_data[index].empty: + ax.plot(indices_data[index], label=label) + + ax.set_title("Indices") + ax.legend() + + if raw: + print_rich_table( + indices_data.fillna("-").iloc[-10:], + headers=list(indices_data.columns), + show_index=True, + title=f"Indices [columns: {column}", + ) + + if export: + print("Doing nothing!") + + theme.style_primary_axis(ax) + + if external_axes is None: + theme.visualize_output() + + +@log_start_end(log=logger) +def search_indices(keyword: list, limit: int = 10): + """Load (and show) the selected indices over time [Source: Yahoo Finance] + + Parameters + ---------- + keyword: list + The keyword you wish to search for. This can include spaces. + limit: int + The amount of views you want to show, by default this is set to 10. + + Returns + ---------- + Shows a rich table with the available options. + """ + keyword_adjusted = " ".join(keyword) + + indices = fd.select_indices() + + queried_indices = pd.DataFrame.from_dict( + fd.search_products(indices, keyword_adjusted, "short_name"), orient="index" + ) + + print_rich_table( + queried_indices.iloc[:limit], + show_index=True, + index_name="ticker", + headers=queried_indices.columns, + title=f"Queried Indices with keyword {keyword_adjusted}", + )