diff --git a/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_view.py b/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_view.py index b96bdd152b4..e7f7a4dc85d 100644 --- a/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_view.py +++ b/gamestonk_terminal/portfolio/portfolio_optimization/optimizer_view.py @@ -285,9 +285,13 @@ def display_efficient_risk( Optional axes to plot data on """ s_title = f"{d_period[period]} Weights that maximise returns at target volatility: {target_volatility}" - ef_opt, ef = optimizer_model.get_efficient_risk_portfolio( - stocks, period, target_volatility, market_neutral - ) + try: + ef_opt, ef = optimizer_model.get_efficient_risk_portfolio( + stocks, period, target_volatility, market_neutral + ) + except ValueError as e: + console.print(f"[red]{e}[/red]\n") + return weights = {key: value * round(port_value, 5) for key, port_value in ef_opt.items()} if not market_neutral and pie: @@ -332,9 +336,15 @@ def display_efficient_return( Optional axes to plot data on """ s_title = f"{d_period[period]} Weights that minimizes volatility at target return: {target_return}" - ef_opt, ef = optimizer_model.get_efficient_return_portfolio( - stocks, period, target_return, market_neutral - ) + try: + ef_opt, ef = optimizer_model.get_efficient_return_portfolio( + stocks, period, target_return, market_neutral + ) + except ValueError as e: + # This means optimization failed + console.print(f"[red]{e}[/red]\n") + return + weights = {key: value * round(port_value, 5) for key, port_value in ef_opt.items()} if not market_neutral and pie: @@ -382,7 +392,8 @@ def display_ef( sharpes = rets / stds ax.scatter(stds, rets, marker=".", c=sharpes) - plotting.plot_efficient_frontier(ef, ax=ax, show_assets=True) + plotting.plot_efficient_frontier(ef, ax=ax, show_assets=False) + for ticker, ret, std in zip( ef.tickers, ef.expected_returns, np.sqrt(np.diag(ef.cov_matrix)) ): @@ -407,6 +418,9 @@ def display_ef( ax.set_xlim(xmin=min(stds) * 0.8) ax.add_line(line) ax.set_title(f"Efficient Frontier simulating {n_portfolios} portfolios") + + ax.legend(loc="best", scatterpoints=1) + theme.style_primary_axis(ax) if external_axes is None: theme.visualize_output() diff --git a/gamestonk_terminal/portfolio/portfolio_optimization/po_controller.py b/gamestonk_terminal/portfolio/portfolio_optimization/po_controller.py index 81be7e08aa3..9007438eab1 100644 --- a/gamestonk_terminal/portfolio/portfolio_optimization/po_controller.py +++ b/gamestonk_terminal/portfolio/portfolio_optimization/po_controller.py @@ -247,6 +247,7 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 loaded tickers to calculate weights.\n" ) + return optimizer_view.display_equal_weight( stocks=self.tickers, value=ns_parser.value, pie=ns_parser.pie @@ -282,6 +283,7 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 stocks selected to perform calculations." ) + return optimizer_view.display_property_weighting( self.tickers, @@ -320,6 +322,7 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 stocks selected to perform calculations." ) + return optimizer_view.display_property_weighting( self.tickers, @@ -367,6 +370,7 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 stocks selected to perform calculations." ) + return optimizer_view.display_property_weighting( self.tickers, @@ -423,6 +427,7 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 loaded tickers to calculate weights.\n" ) + return optimizer_view.display_max_sharpe( stocks=self.tickers, @@ -471,6 +476,7 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 loaded tickers to calculate weights.\n" ) + return optimizer_view.display_min_volatility( stocks=self.tickers, @@ -535,6 +541,7 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 loaded tickers to calculate weights.\n" ) + return if ns_parser.pie and ns_parser.market_neutral: console.print( @@ -611,11 +618,13 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 loaded tickers to calculate weights.\n" ) + return if ns_parser.pie and ns_parser.market_neutral: console.print( "Cannot show pie chart for market neutral due to negative weights." ) + return optimizer_view.display_efficient_risk( stocks=self.tickers, @@ -685,11 +694,13 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 loaded tickers to calculate weights.\n" ) + return if ns_parser.pie and ns_parser.market_neutral: console.print( "Cannot show pie chart for market neutral due to negative weights." ) + return optimizer_view.display_efficient_return( stocks=self.tickers, @@ -743,6 +754,7 @@ class PortfolioOptimization(BaseController): console.print( "Please have at least 2 loaded tickers to calculate weights.\n" ) + return optimizer_view.display_ef( stocks=self.tickers, diff --git a/tests/gamestonk_terminal/portfolio/portfolio_optimization/test_po_controller.py b/tests/gamestonk_terminal/portfolio/portfolio_optimization/test_po_controller.py index b460d56cd0f..62d63c79026 100644 --- a/tests/gamestonk_terminal/portfolio/portfolio_optimization/test_po_controller.py +++ b/tests/gamestonk_terminal/portfolio/portfolio_optimization/test_po_controller.py @@ -326,6 +326,7 @@ def test_call_func( ) controller = po_controller.PortfolioOptimization(queue=None) + controller.tickers = ["AAPL", "MSFT"] getattr(controller, tested_func)(other_args)