Add multi-plotting functionality as well as robustness of functions

This commit is contained in:
Jeroen Bouma
2022-03-14 13:47:00 +01:00
parent 583b963552
commit cdae8f49d2
7 changed files with 591 additions and 184 deletions

View File

@@ -340,6 +340,58 @@ def get_macro_data(
return df
@log_start_end(log=logger)
def get_aggregated_macro_data(
parameters: list,
countries: list,
start_date: int = None,
end_date: int = None,
convert_currency: str = "USD",
) -> pd.DataFrame:
"""This functions groups the data queried from the EconDB database [Source: EconDB]
Parameters
----------
parameters: list
The type of data you wish to acquire
countries : list
the selected country or countries
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.
convert_currency : str
In what currency you wish to convert all values.
Returns
----------
pd.DataFrame
A DataFrame with the requested macro data of all chosen countries
"""
country_data: Dict[Any, Dict[Any, pd.Series]] = {}
for country in countries:
country_data[country] = {}
for parameter in parameters:
country_data[country][parameter] = get_macro_data(
parameter, country, convert_currency
)
if country_data[country][parameter] is not None and start_date or end_date:
country_data[country][parameter] = country_data[country][parameter].loc[
start_date:end_date
]
country_data_df = (
pd.DataFrame.from_dict(country_data, orient="index").stack().to_frame()
)
country_data_df = pd.DataFrame(
country_data_df[0].values.tolist(), index=country_data_df.index
).T
return country_data_df
@log_start_end(log=logger)
def get_treasuries(
instruments: list,

View File

@@ -1,9 +1,10 @@
""" EconDB View """
__docformat__ = "numpy"
# pylint:disable=too-many-arguments
import logging
import os
from typing import Optional, List, Dict, Any
from typing import Optional, List, Dict
import pandas as pd
from matplotlib import pyplot as plt
@@ -58,41 +59,28 @@ def show_macro_data(
----------
Plots the Series.
"""
country_data: Dict[Any, Dict[Any, pd.Series]] = {}
country_data_df = econdb_model.get_aggregated_macro_data(
parameters, countries, start_date, end_date, convert_currency
)
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
else:
ax = external_axes[0]
for country in countries:
country_data[country] = {}
for parameter in parameters:
country_data[country][parameter] = econdb_model.get_macro_data(
parameter, country, convert_currency
)
if country_data[country][parameter] is not None and start_date or end_date:
country_data[country][parameter] = country_data[country][parameter].loc[
start_date:end_date
]
df = pd.DataFrame.from_dict(country_data, orient="index").stack().to_frame()
df = pd.DataFrame(df[0].values.tolist(), index=df.index).T
maximum_value = df.max().max()
maximum_value = country_data_df.max().max()
if maximum_value > 1_000_000_000:
df_rounded = df / 1_000_000_000
df_rounded = country_data_df / 1_000_000_000
denomination = f"[{convert_currency} Billions]"
elif maximum_value > 1_000_000:
df_rounded = df / 1_000_000
df_rounded = country_data_df / 1_000_000
denomination = f"[{convert_currency} Millions]"
elif maximum_value > 1_000:
df_rounded = df / 1_000
df_rounded = country_data_df / 1_000
denomination = f"[{convert_currency} Thousands]"
else:
df_rounded = df
df_rounded = country_data_df
denomination = f"[{convert_currency}]"
for column in df_rounded.columns:
@@ -114,6 +102,8 @@ def show_macro_data(
ax.plot(df_rounded[column])
ax.set_title(f"{parameter_label} of {country_label} {denomination}")
df_rounded.columns = ["_".join(column) for column in df_rounded.columns]
if raw:
print_rich_table(
df_rounded.fillna("-").iloc[-10:],
@@ -138,6 +128,7 @@ def show_treasuries(
frequency: str,
start_date: str = None,
end_date: str = None,
store: bool = False,
raw: bool = False,
external_axes: Optional[List[plt.axes]] = None,
export: str = "",
@@ -156,6 +147,8 @@ def show_treasuries(
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.
store : bool
Whether to prevent plotting the data.
raw : bool
Whether to display the raw output.
external_axes: Optional[List[plt.axes]]
@@ -167,47 +160,48 @@ def show_treasuries(
----------
Plots the Treasury Series.
"""
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
else:
ax = external_axes[0]
treasury_data = econdb_model.get_treasuries(
types, maturities, frequency, start_date, end_date
)
for treasury, maturities_data in treasury_data.items():
for maturity in maturities_data:
ax.plot(maturities_data[maturity], label=f"{treasury} [{maturity}]")
if not store:
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
else:
ax = external_axes[0]
ax.set_title("U.S. Treasuries")
ax.legend()
for treasury, maturities_data in treasury_data.items():
for maturity in maturities_data:
ax.plot(maturities_data[maturity], label=f"{treasury} [{maturity}]")
if raw or export:
df = pd.DataFrame.from_dict(treasury_data, orient="index").stack().to_frame()
df = pd.DataFrame(df[0].values.tolist(), index=df.index).T
df.columns = ["_".join(column) for column in df.columns]
ax.set_title("U.S. Treasuries")
ax.legend()
if raw:
print_rich_table(
df.iloc[-10:],
headers=list(df.columns),
show_index=True,
title="U.S. Treasuries",
)
theme.style_primary_axis(ax)
if export:
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"treasuries_data",
df,
)
if external_axes is None:
theme.visualize_output()
theme.style_primary_axis(ax)
df = pd.DataFrame.from_dict(treasury_data, orient="index").stack().to_frame()
df = pd.DataFrame(df[0].values.tolist(), index=df.index).T
df.columns = ["_".join(column) for column in df.columns]
if external_axes is None:
theme.visualize_output()
if raw:
print_rich_table(
df.iloc[-10:],
headers=list(df.columns),
show_index=True,
title="U.S. Treasuries",
)
if export:
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"treasuries_data",
df,
)
@log_start_end(log=logger)

View File

@@ -6,7 +6,7 @@ import argparse
import logging
import os
from datetime import date
from typing import List, Dict
from typing import List, Dict, Any
import pandas as pd
from prompt_toolkit.completion import NestedCompleter
@@ -26,6 +26,7 @@ from gamestonk_terminal.economy import (
fred_model,
yfinance_model,
yfinance_view,
plot_view,
)
from gamestonk_terminal.helper_funcs import (
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
@@ -52,6 +53,7 @@ class EconomyController(BaseController):
"fred",
"index",
"treasury",
"plot",
"valuation",
"performance",
"spectrum",
@@ -92,6 +94,7 @@ class EconomyController(BaseController):
"unemp",
]
overview_options = ["indices", "usbonds", "glbonds", "currencies"]
futures_options = ["energy", "metals", "meats", "grains", "softs"]
tyld_maturity = ["3m", "5y", "10y", "30y"]
valuation_sort_cols = [
"Name",
@@ -148,43 +151,69 @@ class EconomyController(BaseController):
super().__init__(queue)
self.current_series: Dict = dict()
self.fred_query: pd.Series = pd.Series()
self.DATASETS: Dict[Any, pd.DataFrame] = dict()
if session and gtff.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
choices["overview"] = {c: None for c in self.overview_options}
self.choices: dict = {c: {} for c in self.controller_choices}
self.choices["overview"] = {c: None for c in self.overview_options}
choices["futures"] = {
c: None for c in ["energy", "metals", "meats", "grains", "softs"]
self.choices["futures"] = {c: None for c in self.futures_options}
self.choices["index"] = {c: None for c in yfinance_model.INDICES}
self.choices["macro"]["-p"] = {c: None for c in econdb_model.PARAMETERS}
self.choices["macro"]["--parameter"] = {
c: None for c in econdb_model.PARAMETERS
}
choices["index"] = {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}
choices["macro"]["--countries"] = {
self.choices["macro"]["-c"] = {c: None for c in econdb_model.COUNTRY_CODES}
self.choices["macro"]["--countries"] = {
c: None for c in econdb_model.COUNTRY_CODES
}
choices["valuation"]["-s"] = {c: None for c in self.valuation_sort_cols}
choices["valuation"]["--sortby"] = {
self.choices["valuation"]["-s"] = {
c: None for c in self.valuation_sort_cols
}
self.choices["valuation"]["--sortby"] = {
c: None for c in self.valuation_sort_cols
}
choices["performance"]["-s"] = {c: None for c in self.performance_sort_list}
choices["performance"]["--sortby"] = {
self.choices["performance"]["-s"] = {
c: None for c in self.performance_sort_list
}
self.choices["performance"]["--sortby"] = {
c: None for c in self.performance_sort_list
}
choices["map"]["-p"] = {c: None for c in self.map_period_list}
choices["map"]["--period"] = {c: None for c in self.map_period_list}
self.choices["map"]["-p"] = {c: None for c in self.map_period_list}
self.choices["map"]["--period"] = {c: None for c in self.map_period_list}
choices["feargreed"]["-i"] = {c: None for c in self.fear_greed_indicators}
choices["feargreed"]["--indicator"] = {
self.choices["feargreed"]["-i"] = {
c: None for c in self.fear_greed_indicators
}
self.choices["feargreed"]["--indicator"] = {
c: None for c in self.fear_greed_indicators
}
self.completer = NestedCompleter.from_nested_dict(choices)
self.completer = NestedCompleter.from_nested_dict(self.choices)
def update_runtime_choices(self):
if session and gtff.USE_PROMPT_TOOLKIT:
if not self.fred_query.empty:
self.choices["fred"] = {c: None for c in self.fred_query}
if self.DATASETS:
options = [
option
for _, values in self.DATASETS.items()
for option in values.keys()
]
for argument in ["-y1", "-yaxis1", "-y2", "yaxis2"]:
self.choices["plot"][argument] = {
option: None for option in options
}
self.completer = NestedCompleter.from_nested_dict(self.choices)
def print_help(self):
"""Print help"""
@@ -199,6 +228,7 @@ Macro Data
fred collect macro data from FRED based on a series ID [src][Source: FRED][/src]
index find and plot any (major) index on the market [src][Source: Yahoo Finance][/src]
treasury obtain U.S. treasury rates [src][Source: EconDB][/src]
plot plot data from the above commands together
Performance & Valuations
rtps real-time performance sectors [src][Source: Alpha Vantage][/src]
@@ -361,7 +391,10 @@ Index
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="macro",
description="Get a broad selection of macro data from one or multiple countries. [Source: EconDB]",
description="Get a broad selection of macro data from one or multiple countries. This includes Gross "
"Domestic Product (RGDP & GDP) and the underlying components, Treasury Yields (Y10YD & M3YD), "
"Employment figures (URATE, EMP, AC0I0 and EMRATIO), Government components (e.g. GBAL & GREV), "
"Consumer and Producer Indices (CPI & PPI) and a variety of other indicators. [Source: EconDB]",
)
parser.add_argument(
@@ -421,10 +454,19 @@ Index
"--convert_currency",
dest="convert_currency",
help="Convert the currency of the chosen country to a specified currency. By default, this will be USD "
"unless specified with this command. To find the currencies use the option -sc",
"unless specified with this command. To find the currency symbols use the option -sc",
default="USD",
)
parser.add_argument(
"-st",
"--store",
dest="store",
help="Store the data to be used for plotting with the 'plot' command.",
action="store_true",
default=False,
)
ns_parser = parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, raw=True
)
@@ -442,15 +484,30 @@ Index
headers=["Country", "Currency"],
)
elif ns_parser.parameters and ns_parser.countries:
econdb_view.show_macro_data(
parameters=ns_parser.parameters,
countries=ns_parser.countries,
start_date=ns_parser.start_date,
end_date=ns_parser.end_date,
convert_currency=ns_parser.convert_currency,
raw=ns_parser.raw,
export=ns_parser.export,
)
if ns_parser.store:
self.DATASETS["macro"] = econdb_model.get_aggregated_macro_data(
parameters=ns_parser.parameters,
countries=ns_parser.countries,
start_date=ns_parser.start_date,
end_date=ns_parser.end_date,
convert_currency=ns_parser.convert_currency,
)
self.DATASETS["macro"].columns = [
"_".join(column) for column in self.DATASETS["macro"].columns
]
else:
econdb_view.show_macro_data(
parameters=ns_parser.parameters,
countries=ns_parser.countries,
start_date=ns_parser.start_date,
end_date=ns_parser.end_date,
convert_currency=ns_parser.convert_currency,
raw=ns_parser.raw,
export=ns_parser.export,
)
self.update_runtime_choices()
def call_fred(self, other_args: List[str]):
"""Process fred command"""
@@ -471,21 +528,40 @@ Index
parser.add_argument(
"-s",
"--start_date",
dest="start_date",
type=valid_date,
default="2020-01-01",
help="Starting date (YYYY-MM-DD) of data",
default=None,
)
parser.add_argument(
"-e",
"--end_date",
dest="end_date",
type=valid_date,
help="Ending date (YYYY-MM-DD) of data",
default=None,
)
parser.add_argument(
"-q",
"--query",
nargs="+",
action="store",
dest="query",
type=str,
help="Query the FRED database to obtain Series IDs given the query seaarch term.",
)
parser.add_argument(
"-st",
"--store",
dest="store",
help="Store the data to be used for plotting with the 'plot' command.",
action="store_true",
default=False,
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-p")
ns_parser = parse_known_args_and_warn(
@@ -497,10 +573,13 @@ Index
)
if ns_parser:
if ns_parser.query:
fred_view.notes(
series_term=ns_parser.query,
num=ns_parser.limit,
)
query = " ".join(ns_parser.query)
df_search = fred_model.get_series_notes(query)
fred_view.notes(series_term=query, num=ns_parser.limit)
self.fred_query = df_search["id"].head(ns_parser.limit)
self.update_runtime_choices()
if ns_parser.parameter:
series_dict = {}
for series in ns_parser.parameter:
@@ -512,15 +591,23 @@ Index
"units": information["seriess"][0]["units_short"],
}
self.current_series = {series: series_dict[series]}
self.current_series = {series: series_dict[series]}
fred_view.display_fred_series(
series_dict,
ns_parser.start_date,
ns_parser.raw,
ns_parser.export,
ns_parser.limit,
)
if ns_parser.store:
self.DATASETS["fred"] = fred_model.get_aggregated_series_data(
series_dict, ns_parser.start_date, ns_parser.end_date
)
else:
fred_view.display_fred_series(
d_series=series_dict,
start_date=ns_parser.start_date,
end_date=ns_parser.end_date,
raw=ns_parser.raw,
export=ns_parser.export,
limit=ns_parser.limit,
)
self.update_runtime_choices()
@log_start_end(log=logger)
def call_index(self, other_args: List[str]):
@@ -610,6 +697,15 @@ Index
help="Search for indices with given keyword",
)
parser.add_argument(
"-st",
"--store",
dest="store",
help="Store the data to be used for plotting with the 'plot' command.",
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(
@@ -631,15 +727,29 @@ Index
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,
)
if ns_parser.store:
self.DATASETS["index"] = pd.DataFrame()
for index in ns_parser.indices:
self.DATASETS["index"][index] = yfinance_model.get_index(
index,
interval=ns_parser.interval,
start_date=ns_parser.start_date,
end_date=ns_parser.end_date,
column=ns_parser.column,
)
else:
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,
store=ns_parser.store,
raw=ns_parser.raw,
export=ns_parser.export,
)
self.update_runtime_choices()
@log_start_end(log=logger)
def call_treasury(self, other_args: List[str]):
@@ -650,7 +760,8 @@ Index
prog="treasury",
description="Obtain any set of U.S. treasuries and plot them together. These can be a range of maturities "
"for nominal, inflation-adjusted (on long term average of inflation adjusted) and secondary "
"markets over a lengthy period. [Source: EconDB / FED]",
"markets over a lengthy period. Note: 3-month and 10-year treasury yields for other countries "
"are available via the command 'macro' and parameter 'Y10YD' and 'M3YD'. [Source: EconDB / FED]",
)
parser.add_argument(
@@ -710,6 +821,15 @@ Index
default=date.today(),
)
parser.add_argument(
"-st",
"--store",
dest="store",
help="Store the data to be used for plotting with the 'plot' command.",
action="store_true",
default=False,
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-m")
ns_parser = parse_known_args_and_warn(
@@ -724,15 +844,141 @@ Index
if ns_parser.show_maturities:
econdb_view.show_treasury_maturities(econdb_model.TREASURIES)
elif ns_parser.maturity and ns_parser.type:
econdb_view.show_treasuries(
types=ns_parser.type,
maturities=ns_parser.maturity,
frequency=ns_parser.frequency,
start_date=ns_parser.start_date,
end_date=ns_parser.end_date,
raw=ns_parser.raw,
export=ns_parser.export,
if ns_parser.store:
treasury_data = econdb_model.get_treasuries(
instruments=ns_parser.type,
maturities=ns_parser.maturity,
frequency=ns_parser.frequency,
start_date=ns_parser.start_date,
end_date=ns_parser.end_date,
)
df = (
pd.DataFrame.from_dict(treasury_data, orient="index")
.stack()
.to_frame()
)
self.DATASETS["treasuries"] = pd.DataFrame(
df[0].values.tolist(), index=df.index
).T
self.DATASETS["treasuries"].columns = [
"_".join(column)
for column in self.DATASETS["treasuries"].columns
]
else:
econdb_view.show_treasuries(
types=ns_parser.type,
maturities=ns_parser.maturity,
frequency=ns_parser.frequency,
start_date=ns_parser.start_date,
end_date=ns_parser.end_date,
store=ns_parser.store,
raw=ns_parser.raw,
export=ns_parser.export,
)
self.update_runtime_choices()
@log_start_end(log=logger)
def call_plot(self, other_args: List[str]):
"""Process plot command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="plot",
description="Obtain any set of U.S. treasuries and plot them together. These can be a range of maturities "
"for nominal, inflation-adjusted (on long term average of inflation adjusted) and secondary "
"markets over a lengthy period. Note: 3-month and 10-year treasury yields for other countries "
"are available via the command 'macro' and parameter 'Y10YD' and 'M3YD'. [Source: EconDB / FED]",
)
parser.add_argument(
"-y1",
"--yaxis_1",
nargs="+",
dest="yaxis_1",
help="Select the data you wish to plot on the first y-axis. You can select multiple variables here.",
default="",
)
parser.add_argument(
"-y2",
"--yaxis_2",
nargs="+",
dest="yaxis_2",
help="Select the data you wish to plot on the second y-axis. You can select multiple variables here.",
default="",
)
parser.add_argument(
"-s",
"--show",
dest="show",
help="Show all available options currently collected.",
action="store_true",
default=False,
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-y1")
ns_parser = parse_known_args_and_warn(
parser,
other_args,
export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED,
raw=True,
limit=10,
)
if ns_parser:
if ns_parser.show:
options = {
command: ", ".join(values.keys())
for command, values in self.DATASETS.items()
}
print_rich_table(
pd.DataFrame.from_dict(
options, orient="index", columns=["Options"]
),
show_index=True,
index_name="Command",
title="Options available to plot",
)
else:
dataset_yaxis_1 = pd.DataFrame()
dataset_yaxis_2 = pd.DataFrame()
if ns_parser.yaxis_1:
for variable in ns_parser.yaxis_1:
for _, data in self.DATASETS.items():
if variable in data.columns:
dataset_yaxis_1[variable] = data[variable]
break
if variable not in dataset_yaxis_1:
return console.print(
f"Not able to find any data for the -y1 argument {ns_parser.yaxis_1}. "
f"The currently available options are: "
f"{', '.join(self.choices['-y1'])}"
)
if ns_parser.yaxis_2:
for variable in ns_parser.yaxis_2:
for _, data in self.DATASETS.items():
if variable in data.columns:
dataset_yaxis_2[variable] = data[variable]
break
if variable not in dataset_yaxis_2:
return console.print(
f"Not able to find any data for the -y2 argument {ns_parser.yaxis_2}. "
f"The currently available options are: "
f"{', '.join(self.choices['-y2'])}"
)
if ns_parser.yaxis_1 or ns_parser.yaxis_2:
return plot_view.show_plot(
dataset_yaxis_1=dataset_yaxis_1, dataset_yaxis_2=dataset_yaxis_2
)
console.print()
@log_start_end(log=logger)
def call_map(self, other_args: List[str]):

View File

@@ -136,7 +136,7 @@ def get_series_ids(series_term: str, num: int) -> Tuple[List[str], List[str]]:
@log_start_end(log=logger)
def get_series_data(series_id: str, start: str) -> pd.DataFrame:
def get_series_data(series_id: str, start: str = None, end: str = None) -> pd.DataFrame:
"""Get Series data. [Source: FRED]
Parameters
----------
@@ -144,6 +144,9 @@ def get_series_data(series_id: str, start: str) -> pd.DataFrame:
Series ID to get data from
start : str
Start date to get data from, format yyyy-mm-dd
end : str
End data to get from, format yyyy-mm-dd
Returns
----------
pd.DataFrame
@@ -153,9 +156,43 @@ def get_series_data(series_id: str, start: str) -> pd.DataFrame:
try:
fredapi_client = Fred(cfg.API_FRED_KEY)
df = fredapi_client.get_series(series_id, start)
df = fredapi_client.get_series(series_id, start, end)
# Series does not exist & invalid api keys
except HTTPError as e:
console.print(e)
return df
@log_start_end(log=logger)
def get_aggregated_series_data(
d_series: Dict[str, Dict[str, str]], start: str = None, end: str = None
) -> 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
end : str
End data to get from, format yyyy-mm-dd
Returns
----------
pd.DataFrame
Series data
"""
series_ids = list(d_series.keys())
data = pd.DataFrame()
for s_id in series_ids:
data = pd.concat(
[
data,
pd.DataFrame(get_series_data(s_id, start, end), columns=[s_id]),
],
axis=1,
)
return data

View File

@@ -11,9 +11,9 @@ import numpy as np
import pandas as pd
from pandas.plotting import register_matplotlib_converters
from gamestonk_terminal.decorators import check_api_key
from gamestonk_terminal.config_terminal import theme
from gamestonk_terminal.config_plot import PLOT_DPI
from gamestonk_terminal.config_terminal import theme
from gamestonk_terminal.decorators import check_api_key
from gamestonk_terminal.decorators import log_start_end
from gamestonk_terminal.economy import fred_model
from gamestonk_terminal.helper_funcs import (
@@ -47,8 +47,9 @@ def format_units(num: int) -> str:
@log_start_end(log=logger)
@check_api_key(["API_FRED_KEY"])
def notes(series_term: str, num: int):
def notes(series_term: str, num: int) -> pd.DataFrame:
"""Print Series notes. [Source: FRED]
Parameters
----------
series_term : str
@@ -80,6 +81,7 @@ def notes(series_term: str, num: int):
def display_fred_series(
d_series: Dict[str, Dict[str, str]],
start_date: str,
end_date: str = "",
raw: bool = False,
export: str = "",
limit: int = 10,
@@ -89,10 +91,14 @@ def display_fred_series(
Parameters
----------
series : str
d_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
end_date : str
Ending date (YYYY-MM-DD) of data
store : bool
Whether to prevent plotting the data.
raw : bool
Output only raw data
export : str
@@ -103,61 +109,53 @@ def display_fred_series(
External axes (3 axes are expected in the list), by default None
"""
series_ids = list(d_series.keys())
data = pd.DataFrame()
data = fred_model.get_aggregated_series_data(d_series, start_date, end_date)
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
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
else:
if len(external_axes) != 3:
logger.error("Expected list of 3 axis items")
console.print("[red]Expected list of 3 axis items./n[/red]")
return
(ax,) = external_axes
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))
if len(series_ids) < 5
else title,
)
ax.legend(prop={"size": 10}, bbox_to_anchor=(0, 1), loc="lower left")
if data.empty:
logger.error("No data")
console.print("[red]No data[/red]\n")
console.print("[red]No data available.[/red]\n")
else:
# 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
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
else:
if len(external_axes) != 3:
logger.error("Expected list of 3 axis items")
console.print("[red]Expected list of 3 axis items./n[/red]")
return
(ax,) = external_axes
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))
if len(series_ids) < 5
else title,
)
ax.legend(prop={"size": 10}, bbox_to_anchor=(0, 1), loc="lower left")
ax.set_xlim(data.index[0], data.index[-1])
theme.style_primary_axis(ax)
if external_axes is None:
theme.visualize_output()
data.index = [x.strftime("%Y-%m-%d") for x in data.index]
if raw:
print_rich_table(
data.tail(limit),
@@ -166,9 +164,10 @@ def display_fred_series(
index_name="Date",
)
console.print("")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"plot",
"fred",
data,
)

View File

@@ -0,0 +1,69 @@
""" Plot Controller """
from typing import Optional, List
from matplotlib import pyplot as plt
from gamestonk_terminal.config_plot import PLOT_DPI
from gamestonk_terminal.config_terminal import theme
from gamestonk_terminal.helper_funcs import plot_autoscale
def show_plot(
dataset_yaxis_1, dataset_yaxis_2, external_axes: Optional[List[plt.Axes]] = None
):
"""
The ability to plot any data coming from EconDB, FRED or Yahoo Finance.
Parameters
----------
dataset_yaxis_1: pd.DataFrame
Data you wish to plot on the first y-axis.
dataset_yaxis_2 : pd.DataFrame
Data you wish to plot on the second y-axis.
external_axes: Optional[List[plt.axes]]
External axes to plot on
Returns
----------
Plots the data.
"""
if external_axes is None:
_, ax1 = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
else:
ax1, ax2 = external_axes
color_palette = theme.get_colors()
ax_1_coloring = 0
ax_2_coloring = -1
for column in dataset_yaxis_1:
ax1.plot(
dataset_yaxis_1[column],
label=column.replace("_", " "),
color=color_palette[ax_1_coloring],
)
ax_1_coloring += 1
theme.style_primary_axis(ax1)
ax1.yaxis.set_label_position("left")
ax1.legend()
if not dataset_yaxis_2.empty:
ax2 = ax1.twinx()
for column in dataset_yaxis_2:
ax2.plot(
dataset_yaxis_2[column],
label=column,
color=color_palette[ax_2_coloring],
)
ax_2_coloring += -1
ax2.yaxis.set_label_position("right")
theme.style_twin_axis(ax2)
ax2.legend(loc="upper right")
if external_axes is None:
theme.visualize_output()

View File

@@ -1,5 +1,6 @@
""" EconDB View """
__docformat__ = "numpy"
# pylint:disable=too-many-arguments
import logging
import os
@@ -29,6 +30,7 @@ def show_indices(
start_date: int = None,
end_date: int = None,
column: str = "Adj Close",
store: bool = False,
raw: bool = False,
external_axes: Optional[List[plt.axes]] = None,
export: str = "",
@@ -46,6 +48,10 @@ def show_indices(
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.
column : str
Which column to load in, by default this is the Adjusted Close.
store : bool
Whether to prevent plotting the data.
raw : bool
Whether to display the raw output.
external_axes: Optional[List[plt.axes]]
@@ -59,24 +65,31 @@ def show_indices(
"""
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"]
if not store:
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
else:
label = index
ax = external_axes[0]
if not indices_data[index].empty:
ax.plot(indices_data[index], label=label)
for index in indices:
if index in INDICES:
label = INDICES[index]["name"]
else:
label = index
ax.set_title("Indices")
ax.legend()
if not indices_data[index].empty:
ax.plot(indices_data[index], label=label)
ax.set_title("Indices")
ax.legend()
theme.style_primary_axis(ax)
if external_axes is None:
theme.visualize_output()
if raw:
print_rich_table(
@@ -94,10 +107,7 @@ def show_indices(
indices_data,
)
theme.style_primary_axis(ax)
if external_axes is None:
theme.visualize_output()
return indices_data
@log_start_end(log=logger)