From 4dd80e2b54c1d8442e5cdcd339a67d3154768d97 Mon Sep 17 00:00:00 2001 From: Diogo Sousa Date: Wed, 1 Feb 2023 11:47:59 +0000 Subject: [PATCH] remove mixed portfolio changes --- .../portfolio/attribution_model.py | 7 +-- .../portfolio/portfolio_controller.py | 54 +++++++++---------- openbb_terminal/portfolio/portfolio_engine.py | 46 ++++------------ openbb_terminal/portfolio/portfolio_model.py | 18 ++----- 4 files changed, 38 insertions(+), 87 deletions(-) diff --git a/openbb_terminal/portfolio/attribution_model.py b/openbb_terminal/portfolio/attribution_model.py index 334b2f18652..dfa815b99de 100644 --- a/openbb_terminal/portfolio/attribution_model.py +++ b/openbb_terminal/portfolio/attribution_model.py @@ -9,7 +9,6 @@ from typing import Dict import yfinance as yf import pandas as pd from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console logger = logging.getLogger(__name__) @@ -69,11 +68,7 @@ def get_spy_sector_contributions( # Load in info sp500_tickers_data = get_daily_sector_prices(start_date, end_date) - try: - weight_data = yf.Ticker(sectors_ticker).info["sectorWeightings"] - except Exception as _: # noqa - console.print("[red]Could not get info for S&P 500.") - return pd.DataFrame() + weight_data = yf.Ticker(sectors_ticker).info["sectorWeightings"] # reformat Data weights: Dict[str, dict] = {"SPY": {}} diff --git a/openbb_terminal/portfolio/portfolio_controller.py b/openbb_terminal/portfolio/portfolio_controller.py index 77829244746..938908fd481 100644 --- a/openbb_terminal/portfolio/portfolio_controller.py +++ b/openbb_terminal/portfolio/portfolio_controller.py @@ -82,7 +82,6 @@ class PortfolioController(BaseController): CHOICES_MENUS = [ "bro", "po", - "pa", ] VALID_DISTRIBUTIONS = ["laplace", "student_t", "logistic", "normal"] AGGREGATION_METRICS = ["assets", "sectors", "countries", "regions"] @@ -121,15 +120,6 @@ class PortfolioController(BaseController): for file_type in self.file_types for filepath in self.DEFAULT_HOLDINGS_PATH.rglob(f"*.{file_type}") } - self.DATA_HOLDINGS_FILES.update( - { - filepath.name: filepath - for file_type in self.file_types - for filepath in ( - MISCELLANEOUS_DIRECTORY / "portfolio_examples" / "holdings" - ).rglob(f"*.{file_type}") - } - ) self.portfolio_df = pd.DataFrame( columns=[ @@ -160,10 +150,6 @@ class PortfolioController(BaseController): self.update_choices() choices: dict = self.choices_default self.choices = choices - self.choices["bench"] = { - "--benchmark": {c: None for c in statics.BENCHMARK_CHOICES}, - "-b": "--benchmark", - } self.completer = NestedCompleter.from_nested_dict(choices) def update_choices(self): @@ -228,7 +214,7 @@ class PortfolioController(BaseController): > bro brokers holdings, \t\t supports: robinhood, ally, degiro, coinbase > po portfolio optimization, \t optimize your portfolio weights efficiently[/menu] [cmds] - load load data into the portfolio + load load transactions into the portfolio (use load --example for an example) show show existing transactions bench define the benchmark [/cmds] @@ -318,7 +304,6 @@ class PortfolioController(BaseController): "--file", type=str, dest="file", - required="-h" not in other_args, help="The file to be loaded", choices={c: {} for c in self.DATA_HOLDINGS_FILES}, metavar="FILE", @@ -338,12 +323,28 @@ class PortfolioController(BaseController): dest="risk_free_rate", help="Set the risk free rate.", ) + parser.add_argument( + "-e", + "--example", + help="Run an example holdings file to understand how the portfolio menu can be used.", + dest="example", + action="store_true", + default=False, + ) if other_args and "-" not in other_args[0][0]: other_args.insert(0, "-f") ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser and ns_parser.file: - if ns_parser.file in self.DATA_HOLDINGS_FILES: + if ns_parser and ns_parser.file or ns_parser and ns_parser.example: + if ns_parser.example: + file_location = ( + MISCELLANEOUS_DIRECTORY / "portfolio" / "holdings_example.xlsx" + ) + console.print( + "[green]Loading an example, please type `about` " + "to learn how to create your own Portfolio Excel sheet.[/green]\n" + ) + elif ns_parser.file in self.DATA_HOLDINGS_FILES: file_location = self.DATA_HOLDINGS_FILES[ns_parser.file] else: file_location = ns_parser.file # type: ignore @@ -356,6 +357,8 @@ class PortfolioController(BaseController): if ns_parser.name: self.portfolio_name = ns_parser.name + elif ns_parser.example: + self.portfolio_name = "OpenBB Example Portfolio" else: self.portfolio_name = ns_parser.file console.print( @@ -415,6 +418,7 @@ class PortfolioController(BaseController): dest="benchmark", required="-h" not in other_args, help="Set the benchmark for the portfolio. By default, this is SPDR S&P 500 ETF Trust (SPY).", + choices={c: {} for c in statics.BENCHMARK_CHOICES}, metavar="BENCHMARK", ) parser.add_argument( @@ -435,13 +439,9 @@ class PortfolioController(BaseController): "[red]Please first load transactions file using 'load'[/red]" ) else: - if self.portfolio.set_benchmark( - ns_parser.benchmark, ns_parser.full_shares - ): - self.benchmark_name = statics.BENCHMARK_CHOICES.get( - ns_parser.benchmark, ns_parser.benchmark - ) - self.original_benchmark_ticker = ns_parser.benchmark + self.benchmark_name = statics.BENCHMARK_CHOICES.get(ns_parser.benchmark) + self.original_benchmark_ticker = ns_parser.benchmark + self.portfolio.set_benchmark(ns_parser.benchmark, ns_parser.full_shares) @log_start_end(log=logger) def call_alloc(self, other_args: List[str]): @@ -580,13 +580,9 @@ class PortfolioController(BaseController): bench_result = attribution_model.get_spy_sector_contributions( start_date, end_date ) - if bench_result.empty: - return portfolio_result = attribution_model.get_portfolio_sector_contributions( start_date, self.portfolio.portfolio_trades ) - if portfolio_result.empty: - return # relative results - the proportions of return attribution if ns_parser.type == "relative": diff --git a/openbb_terminal/portfolio/portfolio_engine.py b/openbb_terminal/portfolio/portfolio_engine.py index c845f6584ae..96b7b628d0a 100644 --- a/openbb_terminal/portfolio/portfolio_engine.py +++ b/openbb_terminal/portfolio/portfolio_engine.py @@ -21,8 +21,7 @@ from openbb_terminal.portfolio.allocation_model import get_allocation from openbb_terminal.terminal_helper import suppress_stdout # pylint: disable=E1136,W0201,R0902,C0302 -# pylint: disable=unsupported-assignment-operation,redefined-outer-name -# pylint: too-many-public-methods, consider-using-f-string,disable=raise-missing-from +# pylint: disable=unsupported-assignment-operation,redefined-outer-name,too-many-public-methods, consider-using-f-string logger = logging.getLogger(__name__) @@ -496,7 +495,7 @@ class PortfolioEngine: ] = info_list @log_start_end(log=logger) - def set_benchmark(self, symbol: str = "SPY", full_shares: bool = False) -> bool: + def set_benchmark(self, symbol: str = "SPY", full_shares: bool = False): """Load benchmark into portfolio. Parameters @@ -506,15 +505,12 @@ class PortfolioEngine: full_shares: bool Whether to mimic the portfolio trades exactly (partial shares) or round down the quantity to the nearest number - - Returns - ------- - bool - True if successful, False otherwise """ p_bar = tqdm(range(4), desc=" Loading benchmark") + self.benchmark_ticker = symbol + self.benchmark_historical_prices = yf.download( symbol, start=self.inception_date - datetime.timedelta(days=1), @@ -523,15 +519,6 @@ class PortfolioEngine: ignore_tz=True, )["Adj Close"] - if self.benchmark_historical_prices.empty: - console.print( - f"\n[red]Could not download benchmark data for {symbol}." - " Choose another symbol.\n[/red]" - ) - return False - - self.benchmark_ticker = symbol - p_bar.n += 1 p_bar.refresh() @@ -551,6 +538,11 @@ class PortfolioEngine: p_bar.refresh() self.benchmark_returns = self.benchmark_historical_prices.pct_change().dropna() + self.benchmark_info = yf.Ticker(symbol).info + + p_bar.n += 1 + p_bar.refresh() + ( self.portfolio_returns, self.benchmark_returns, @@ -559,20 +551,6 @@ class PortfolioEngine: p_bar.n += 1 p_bar.refresh() - try: - self.benchmark_info = yf.Ticker(symbol).info - except Exception as _: # noqa - console.print( - f"[red]\n\nCould not get info for {symbol}." - " This affects 'alloc' command.[/red]\n" - ) - return False - - p_bar.n += 1 - p_bar.refresh() - - return True - @log_start_end(log=logger) def __mimic_trades_for_benchmark(self, full_shares: bool = False): """Mimic trades from the transactions based on chosen benchmark assuming partial shares @@ -928,12 +906,6 @@ class PortfolioEngine: Flag to force recalculate allocation if already exists """ - if not self.benchmark_info: - return - - if self.portfolio_trades.empty: - return - if category == "Asset": if ( self.benchmark_assets_allocation.empty diff --git a/openbb_terminal/portfolio/portfolio_model.py b/openbb_terminal/portfolio/portfolio_model.py index c9e8155f08e..6cd87b810b9 100644 --- a/openbb_terminal/portfolio/portfolio_model.py +++ b/openbb_terminal/portfolio/portfolio_model.py @@ -58,8 +58,8 @@ def generate_portfolio( transactions = PortfolioEngine.read_transactions(transactions_file_path) portfolio_engine = PortfolioEngine(transactions) portfolio_engine.generate_portfolio_data() - portfolio_engine.set_risk_free_rate(risk_free_rate) portfolio_engine.set_benchmark(symbol=benchmark_symbol, full_shares=full_shares) + portfolio_engine.set_risk_free_rate(risk_free_rate) return portfolio_engine @@ -92,7 +92,7 @@ def get_transactions(portfolio_engine: PortfolioEngine) -> pd.DataFrame: @log_start_end(log=logger) def set_benchmark( portfolio_engine: PortfolioEngine, symbol: str, full_shares: bool = False -) -> bool: +): """Load benchmark into portfolio Parameters @@ -106,11 +106,6 @@ def set_benchmark( Whether to mimic the portfolio trades exactly (partial shares) or round down the quantity to the nearest number - Returns - ------- - bool - True if successful, False otherwise - Examples -------- >>> from openbb_terminal.sdk import openbb @@ -118,7 +113,7 @@ def set_benchmark( >>> output = openbb.portfolio.bench(p, symbol="SPY") """ - return portfolio_engine.set_benchmark(symbol=symbol, full_shares=full_shares) + portfolio_engine.set_benchmark(symbol=symbol, full_shares=full_shares) @log_start_end(log=logger) @@ -435,13 +430,6 @@ def join_allocation( pd.DataFrame DataFrame with portfolio and benchmark allocations """ - - if portfolio.empty: - portfolio = pd.DataFrame(columns=[column, "Portfolio"]) - - if benchmark.empty: - benchmark = pd.DataFrame(columns=[column, "Benchmark"]) - combined = pd.merge(portfolio, benchmark, on=column, how="left") combined["Difference"] = combined["Portfolio"] - combined["Benchmark"] combined = combined.replace(np.nan, "-")