From db693be042ad1066aeb8a216c4027f9ead71063f Mon Sep 17 00:00:00 2001 From: Diogo Sousa Date: Sun, 5 Feb 2023 13:11:47 +0000 Subject: [PATCH] handle some error cases --- .../portfolio/test_portfolio.openbb | 2 + openbb_terminal/portfolio/allocation_model.py | 73 +++++++++++-------- .../portfolio/portfolio_controller.py | 30 +++++--- openbb_terminal/portfolio/portfolio_engine.py | 5 +- openbb_terminal/portfolio/portfolio_view.py | 20 ++++- 5 files changed, 83 insertions(+), 47 deletions(-) diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_portfolio.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_portfolio.openbb index 231ff561866..572a3955c5b 100644 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_portfolio.openbb +++ b/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_portfolio.openbb @@ -2,6 +2,8 @@ portfolio load --example show bench QQQ +bench FAKE_BENCH +bench TSLA alloc assets -t -l 4 alloc sectors -t -l 4 alloc countries -t -l 4 diff --git a/openbb_terminal/portfolio/allocation_model.py b/openbb_terminal/portfolio/allocation_model.py index 9cdf7b933cf..606f06fdef3 100644 --- a/openbb_terminal/portfolio/allocation_model.py +++ b/openbb_terminal/portfolio/allocation_model.py @@ -72,11 +72,16 @@ def get_assets_allocation( pd.DataFrame DataFrame with the portfolio's asset allocations """ - benchmark_assets_allocation = pd.DataFrame(benchmark_info["holdings"]) - benchmark_assets_allocation.rename( - columns={"symbol": "Symbol", "holdingPercent": "Benchmark"}, inplace=True - ) - benchmark_assets_allocation.drop(columns=["holdingName"], inplace=True) + if "holdings" in benchmark_info: + benchmark_assets_allocation = pd.DataFrame(benchmark_info["holdings"]) + benchmark_assets_allocation.rename( + columns={"symbol": "Symbol", "holdingPercent": "Benchmark"}, inplace=True + ) + benchmark_assets_allocation.drop(columns=["holdingName"], inplace=True) + else: + benchmark_assets_allocation = pd.DataFrame( + columns=["Symbol", "Benchmark", "Sector", "Country"] + ) portfolio_assets_allocation = ( portfolio_trades[portfolio_trades["Type"] != "CASH"] @@ -109,34 +114,37 @@ def get_sectors_allocation( Returns ------- pd.DataFrame - DataFrame with regional allocations. + DataFrame with benchmark allocations. pd.DataFrame - DataFrame with country allocations + DataFrame with portfolio allocations """ - benchmark_sectors_allocation = ( - pd.DataFrame.from_dict( - data={ - sector_name: allocation - for sector in benchmark_info["sectorWeightings"] - for sector_name, allocation in sector.items() - }, - orient="index", + if "sectorWeightings" in benchmark_info: + benchmark_sectors_allocation = ( + pd.DataFrame.from_dict( + data={ + sector_name: allocation + for sector in benchmark_info["sectorWeightings"] + for sector_name, allocation in sector.items() + }, + orient="index", + ) + .squeeze() + .sort_values(ascending=False) ) - .squeeze() - .sort_values(ascending=False) - ) - # Prettify sector allocations of benchmark to align with Portfolio Excel - prettified = [ - sector.replace("_", " ").title() - for sector in benchmark_sectors_allocation.index - ] + # Prettify sector allocations of benchmark to align with Portfolio Excel + prettified = [ + sector.replace("_", " ").title() + for sector in benchmark_sectors_allocation.index + ] - benchmark_sectors_allocation.index = prettified - benchmark_sectors_allocation = pd.DataFrame(benchmark_sectors_allocation) - benchmark_sectors_allocation.reset_index(inplace=True) - benchmark_sectors_allocation.columns = ["Sector", "Benchmark"] + benchmark_sectors_allocation.index = prettified + benchmark_sectors_allocation = pd.DataFrame(benchmark_sectors_allocation) + benchmark_sectors_allocation.reset_index(inplace=True) + benchmark_sectors_allocation.columns = ["Sector", "Benchmark"] + else: + benchmark_sectors_allocation = pd.DataFrame(columns=["Sector", "Benchmark"]) # Define portfolio sector allocation # Aggregate sector value for stocks and crypto @@ -243,9 +251,9 @@ def get_countries_allocation( Returns ------- pd.DataFrame - DataFrame with regional allocations. + DataFrame with benchmark allocations. pd.DataFrame - DataFrame with country allocations + DataFrame with portfolio allocations """ benchmark_allocation = get_symbol_allocation( @@ -305,7 +313,7 @@ def get_symbol_allocation( Returns ------- pd.DataFrame - Dictionary with country allocations + Dictionary with category allocations """ if category == "Region": @@ -332,7 +340,10 @@ def get_symbol_allocation( # Collect data from Fidelity about the portfolio composition of the benchmark URL = f"https://screener.fidelity.com/ftgw/etf/goto/snapshot/portfolioComposition.jhtml?symbols={symbol}" html = request(URL).content - df_list = pd.read_html(html) + try: + df_list = pd.read_html(html) + except ValueError: + return pd.DataFrame(columns=[category, col_name]) # Find the ones that contain regions and countries for index, item in enumerate(df_list): diff --git a/openbb_terminal/portfolio/portfolio_controller.py b/openbb_terminal/portfolio/portfolio_controller.py index bfd5be83dcb..3551fe12b57 100644 --- a/openbb_terminal/portfolio/portfolio_controller.py +++ b/openbb_terminal/portfolio/portfolio_controller.py @@ -141,6 +141,7 @@ class PortfolioController(BaseController): self.portfolio_name: str = "" self.benchmark_name: str = "" self.original_benchmark_ticker = "" + self.recalculate_alloc = False self.risk_free_rate = 0 self.portlist: List[str] = os.listdir(self.DEFAULT_HOLDINGS_PATH) self.portfolio = None @@ -447,6 +448,7 @@ class PortfolioController(BaseController): ns_parser.benchmark, ns_parser.benchmark ) self.original_benchmark_ticker = ns_parser.benchmark + self.recalculate_alloc = True @log_start_end(log=logger) def call_alloc(self, other_args: List[str]): @@ -488,27 +490,31 @@ class PortfolioController(BaseController): ): if ns_parser.agg == "assets": portfolio_view.display_assets_allocation( - self.portfolio, - ns_parser.limit, - ns_parser.tables, + portfolio_engine=self.portfolio, + limit=ns_parser.limit, + tables=ns_parser.tables, + recalculate=self.recalculate_alloc, ) elif ns_parser.agg == "sectors": portfolio_view.display_sectors_allocation( - self.portfolio, - ns_parser.limit, - ns_parser.tables, + portfolio_engine=self.portfolio, + limit=ns_parser.limit, + tables=ns_parser.tables, + recalculate=self.recalculate_alloc, ) elif ns_parser.agg == "countries": portfolio_view.display_countries_allocation( - self.portfolio, - ns_parser.limit, - ns_parser.tables, + portfolio_engine=self.portfolio, + limit=ns_parser.limit, + tables=ns_parser.tables, + recalculate=self.recalculate_alloc, ) elif ns_parser.agg == "regions": portfolio_view.display_regions_allocation( - self.portfolio, - ns_parser.limit, - ns_parser.tables, + portfolio_engine=self.portfolio, + limit=ns_parser.limit, + tables=ns_parser.tables, + recalculate=self.recalculate_alloc, ) else: console.print( diff --git a/openbb_terminal/portfolio/portfolio_engine.py b/openbb_terminal/portfolio/portfolio_engine.py index b52f639a5cc..56d3b26410e 100644 --- a/openbb_terminal/portfolio/portfolio_engine.py +++ b/openbb_terminal/portfolio/portfolio_engine.py @@ -118,7 +118,7 @@ class PortfolioEngine: self.tickers_list = None self.tickers: Dict[Any, Any] = {} self.benchmark_ticker: str = "" - self.benchmark_info = None + self.benchmark_info: Dict[Any, Any] = {} self.historical_trade_data = pd.DataFrame() # Portfolio @@ -560,7 +560,8 @@ class PortfolioEngine: p_bar.refresh() try: - self.benchmark_info = yf.Ticker(symbol).info + self.benchmark_info = dict(yf.Ticker(symbol).info) + self.benchmark_info["symbol"] = symbol except Exception as _: # noqa console.print( f"[red]\n\nCould not get info for {symbol}." diff --git a/openbb_terminal/portfolio/portfolio_view.py b/openbb_terminal/portfolio/portfolio_view.py index e5734881216..132ca92a484 100644 --- a/openbb_terminal/portfolio/portfolio_view.py +++ b/openbb_terminal/portfolio/portfolio_view.py @@ -145,6 +145,7 @@ def display_assets_allocation( portfolio_engine: PortfolioEngine, limit: int = 10, tables: bool = False, + recalculate: bool = False, ): """Display portfolio asset allocation compared to the benchmark @@ -152,16 +153,19 @@ def display_assets_allocation( ---------- portfolio_engine: PortfolioEngine Instance of PortfolioEngine class - tables: bool - Whether to include separate asset allocation tables limit: int The amount of assets you wish to show, by default this is set to 10 + tables: bool + Whether to include separate asset allocation tables + recalculate: bool + Whether to recalculate the allocation """ combined, portfolio_allocation, benchmark_allocation = get_assets_allocation( portfolio_engine=portfolio_engine, limit=limit, tables=True, + recalculate=recalculate, ) display_category( @@ -179,6 +183,7 @@ def display_sectors_allocation( portfolio_engine: PortfolioEngine, limit: int = 10, tables: bool = False, + recalculate: bool = False, ): """Display portfolio sector allocation compared to the benchmark @@ -190,12 +195,15 @@ def display_sectors_allocation( The amount of assets you wish to show, by default this is set to 10 tables: bool Whether to include separate asset allocation tables + recalculate: bool + Whether to recalculate the allocation """ combined, portfolio_allocation, benchmark_allocation = get_sectors_allocation( portfolio_engine=portfolio_engine, limit=limit, tables=True, + recalculate=recalculate, ) display_category( @@ -213,6 +221,7 @@ def display_countries_allocation( portfolio_engine: PortfolioEngine, limit: int = 10, tables: bool = False, + recalculate: bool = False, ): """Display portfolio country allocation compared to the benchmark @@ -224,12 +233,15 @@ def display_countries_allocation( The amount of assets you wish to show, by default this is set to 10 tables: bool Whether to include separate asset allocation tables + recalculate: bool + Whether to recalculate the allocation """ combined, portfolio_allocation, benchmark_allocation = get_countries_allocation( portfolio_engine=portfolio_engine, limit=limit, tables=True, + recalculate=recalculate, ) display_category( @@ -247,6 +259,7 @@ def display_regions_allocation( portfolio_engine: PortfolioEngine, limit: int = 10, tables: bool = False, + recalculate: bool = False, ): """Display portfolio region allocation compared to the benchmark @@ -258,12 +271,15 @@ def display_regions_allocation( The amount of assets you wish to show, by default this is set to 10 tables: bool Whether to include separate asset allocation tables + recalculate: bool + Whether to recalculate the allocation """ combined, portfolio_allocation, benchmark_allocation = get_regions_allocation( portfolio_engine=portfolio_engine, limit=limit, tables=True, + recalculate=recalculate, ) display_category(