diff --git a/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_helper.py b/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_helper.py index a9866b09afd..51bc9480e4f 100644 --- a/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_helper.py +++ b/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_helper.py @@ -2,6 +2,7 @@ __docformat__ = "numpy" import argparse +import requests # These are all the possible yfinance properties l_valid_property_infos = [ @@ -105,3 +106,16 @@ def check_valid_property_type(aproperty: str) -> str: return aproperty raise argparse.ArgumentTypeError(f"{aproperty} is not a valid info") + + +def get_rf() -> float: + """Uses the fiscaldata.gov API to get most recent T-Bill rate""" + try: + base = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service" + end = "/v2/accounting/od/avg_interest_rates" + filters = "?filter=security_desc:eq:Treasury Bills&sort=-record_date" + response = requests.get(base + end + filters) + latest = response.json()["data"][0] + return round(float(latest["avg_interest_rate_amt"]) / 100, 8) + except Exception: + return 0.02 diff --git a/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_model.py b/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_model.py index aa995b9451b..636d4696d8b 100644 --- a/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_model.py +++ b/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_model.py @@ -65,7 +65,7 @@ def get_property_weights( def get_maxsharpe_portfolio( - stocks: List[str], period: str = "3mo", rfrate: float = 0.02 + stocks: List[str], period: str, rfrate: float ) -> Tuple[Dict, EfficientFrontier]: """Generate weights for max sharpe portfolio diff --git a/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_view.py b/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_view.py index 454271d0478..9c8430f91a2 100644 --- a/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_view.py +++ b/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_view.py @@ -14,6 +14,7 @@ from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.config_plot import PLOT_DPI from gamestonk_terminal.helper_funcs import plot_autoscale from gamestonk_terminal.portfolio.portfolio_optimization import optimizer_model +from gamestonk_terminal.portfolio.portfolio_optimization.optimizer_helper import get_rf d_period = { "1d": "[1 Day]", @@ -131,8 +132,8 @@ def display_property_weighting( def display_max_sharpe( stocks: List[str], period: str, - value: float = 1.0, - rfrate: float = 0.02, + value: float, + rfrate: float, pie: bool = False, ): """Display portfolio that maximizes Sharpe Ratio over stocks @@ -146,11 +147,12 @@ def display_max_sharpe( value : float, optional Amount to allocate to portfolio, by default 1.0 rfrate : float, optional - Risk Free Rate, by default 0.02 + Risk Free Rate, by default current US T-Bill rate pie : bool, optional Boolean to show weights as a pie chart, by default False """ - s_title = f"{d_period[period]} Weights that maximize Sharpe ratio with risk free level of {rfrate}" + p = d_period[period] + s_title = f"{p} Weights that maximize Sharpe ratio with risk free level of {rfrate*100:.2f}%" ef_opt, ef = optimizer_model.get_maxsharpe_portfolio(stocks, period, rfrate) weights = {key: value * round(port_value, 5) for key, port_value in ef_opt.items()} if pie: @@ -158,7 +160,7 @@ def display_max_sharpe( else: print("\n", s_title) display_weights(weights) - ef.portfolio_performance(verbose=True) + ef.portfolio_performance(verbose=True, risk_free_rate=rfrate) print("") @@ -337,8 +339,11 @@ def display_ef(stocks: List[str], period: str = "3mo", n_portfolios: int = 300): ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r") plotting.plot_efficient_frontier(ef, ax=ax, show_assets=True) # Find the tangency portfolio - ef.max_sharpe() - ret_sharpe, std_sharpe, _ = ef.portfolio_performance() + rfrate = get_rf() + ef.max_sharpe(risk_free_rate=rfrate) + ret_sharpe, std_sharpe, _ = ef.portfolio_performance( + verbose=True, risk_free_rate=rfrate + ) ax.scatter(std_sharpe, ret_sharpe, marker="*", s=100, c="r", label="Max Sharpe") ax.set_title(f"Efficient Frontier simulating {n_portfolios} portfolios") ax.legend() diff --git a/gamestonk_terminal/portfolio/portfolio_optimization/po_controller.py b/gamestonk_terminal/portfolio/portfolio_optimization/po_controller.py index 8df490e64b3..a298356c703 100644 --- a/gamestonk_terminal/portfolio/portfolio_optimization/po_controller.py +++ b/gamestonk_terminal/portfolio/portfolio_optimization/po_controller.py @@ -18,6 +18,8 @@ from gamestonk_terminal.portfolio.portfolio_optimization import ( optimizer_helper, ) +from gamestonk_terminal.portfolio.portfolio_optimization.optimizer_helper import get_rf + period_choices = [ "1d", "5d", @@ -372,7 +374,7 @@ Mean Variance Optimization: "--risk-free-rate", type=float, dest="risk_free_rate", - default=0.02, + default=get_rf(), help="""Risk-free rate of borrowing/lending. The period of the risk-free rate should correspond to the frequency of expected returns.""", ) @@ -385,7 +387,6 @@ Mean Variance Optimization: if len(self.tickers) < 2: print("Please have at least 2 loaded tickers to calculate weights.\n") return - optimizer_view.display_max_sharpe( stocks=self.tickers, period=ns_parser.period,