Portfolio optimization controller/sdk fixes (#3604)

* fix plot and show

* clean duplicated code

* fix msg

* fix if no portfolios

* improve error msg

* fix msg and add integration test

* final fixes

* Portfolio/po | alloc : fix paths

* Linting

Co-authored-by: Chavithra PARANA <chavithra@gmail.com>
This commit is contained in:
montezdesousa
2022-11-27 19:48:48 +00:00
committed by GitHub
parent 42fd915143
commit 2ef3f86b83
7 changed files with 409 additions and 209 deletions

View File

@@ -0,0 +1,54 @@
portfolio
po
show
rpf
show -pf a,b
show -ct a,b
show -pf a,b -ct c,d
plot
plot -pf a,b
plot -ct a,b
plot -pf a,b -ct c,d
load --file 50_30_10_10_Portfolio.xlsx
show -pf a,b -ct c,d
show -pf a
show -ct c
plot -pf a,b -ct c,d
plot -pf a
plot -ct c
equal
show
show -pf name_0
show -ct sector
show -pf name_0 -ct sector
plot
plot -pf name_0
plot -ct sector
plot -pf name_0 -ct sector
plot -pf name_0 -ct sector -pi
plot -pf name_0 -ct sector -hi
plot -pf name_0 -ct sector -dd
plot -pf name_0 -ct sector -rc
plot -pf name_0 -ct sector -he
maxsharpe
show -pf name_0,maxsharpe_1 -ct sector,country
show -pf name_0,maxsharpe_1,a -ct c,sector,currency,current_invested_amount
plot -pf name_0,maxsharpe_1 -ct c,sector,country,current_invested_amount -pi
plot -pf name_0,maxsharpe_1 -ct c,sector,current_invested_amount -hi
plot -pf name_0,maxsharpe_1 -ct c,sector,current_invested_amount -dd
plot -pf name_0,maxsharpe_1 -ct c,sector,current_invested_amount -rc
plot -pf name_0,maxsharpe_1 -ct c,sector,current_invested_amount -he
..
load --file market.csv
po
equal
show -pf name_0
show -pf name_0 -ct a,b
plot -pf name_0 -ct sector -pi
plot -pf name_0 -ct sector -hi
plot -pf name_0 -ct sector -dd
plot -pf name_0 -ct sector -rc
plot -pf name_0 -ct sector -he
maxsharpe
rpf -pf a,b,c
rpf -pf name_0,maxsharpe_1

View File

@@ -2904,17 +2904,15 @@ def get_categories(
if not weights:
return pd.DataFrame()
if column == "CURRENT_INVESTED_AMOUNT":
return pd.DataFrame()
df = pd.DataFrame.from_dict(
data=weights, orient="index", columns=["value"], dtype=float
)
categories_df = pd.DataFrame.from_dict(data=categories, dtype=float)
col = list(categories_df.columns).index(column)
col_inv_amt = list(categories_df.columns).index("CURRENT_INVESTED_AMOUNT")
col_crncy = list(categories_df.columns).index("CURRENCY")
categories_df = df.join(
categories_df.iloc[:, [col, col_inv_amt, col_crncy]], how="inner"
)
categories_df = df.join(categories_df)
categories_df.set_index(column, inplace=True)
categories_df.groupby(level=0).sum()

View File

@@ -416,6 +416,10 @@ def display_categories(
title to display
"""
if column == "CURRENT_INVESTED_AMOUNT":
console.print(f"[yellow]'{column}' cannot be displayed as a category.[/yellow]")
return
df = optimizer_model.get_categories(weights, categories, column)
if df.empty:
@@ -452,12 +456,13 @@ def display_categories(
)
)
df.reset_index(inplace=True)
df.set_index("CURRENCY", inplace=True)
df.reset_index(level=1, inplace=True)
headers = [s.title() for s in list(df.columns)]
show_index = True
if column == "CURRENCY":
show_index = False
headers = list(df.columns)
headers = [s.title() for s in headers]
print_rich_table(df, headers=headers, show_index=True, title=title)
print_rich_table(df, headers=headers, show_index=show_index, title=title)
@log_start_end(log=logger)
@@ -3934,6 +3939,11 @@ def additional_plots(
theme.visualize_output()
if heat:
if len(weights) == 1:
console.print(f"Heatmap needs at least two values for '{category}'.")
return
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
else:
@@ -4004,7 +4014,7 @@ def additional_plots(
theme.visualize_output(force_tight_layout=True)
def display_show(weights: Dict, tables: List[str], categories: Dict[Any, Any]):
def display_show(weights: Dict, tables: List[str], categories_dict: Dict[Any, Any]):
"""Display the results of the optimization.
Parameters
@@ -4013,7 +4023,7 @@ def display_show(weights: Dict, tables: List[str], categories: Dict[Any, Any]):
Dictionary of weights.
tables : List[str]
List of tables to display.
categories : Dict[Any, Any]
categories_dict : Dict[Any, Any]
Dictionary of categories.
"""
@@ -4023,7 +4033,7 @@ def display_show(weights: Dict, tables: List[str], categories: Dict[Any, Any]):
console.print("")
display_categories(
weights=weights,
categories=categories,
categories=categories_dict,
column=t,
title="Category - " + t.title(),
)

View File

@@ -5,7 +5,7 @@ __docformat__ = "numpy"
import argparse
import logging
from typing import List, Dict
from typing import List, Dict, Tuple
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
@@ -60,9 +60,106 @@ def add_arguments(parser_update, parser, not_in_list):
)
def check_input(
input_type: str, input_list: List[str], available_list: List[str]
) -> List[str]:
"""Check if input is valid
Parameters
----------
input_type : str
Type of input
input_list : List[str]
List of input
available_list : List[str]
List of available input
Returns
-------
List[str]
Valid categories
"""
valid: List[str] = []
for i in input_list:
if i in available_list:
valid.append(i)
else:
console.print(f"[red]{input_type} '{i}' not available.[/red]\n")
return valid
def get_valid_portfolio_categories(
input_portfolios: List[str],
available_portfolios: Dict,
input_categories: List[str],
available_categories: Dict,
) -> Tuple[List[str], List[str]]:
"""Get valid portfolios and categories
Parameters
----------
input_portfolios : List[str]
List of input portfolios
available_portfolios : Dict
Dict of available portfolios
input_categories : List[str]
List of input categories
available_categories : Dict
Dict of available categories
Returns
-------
Tuple[List[str], List[str]]
Valid portfolios and categories
"""
portfolios_list = list(set(available_portfolios.keys()))
categories_list = list(set(available_categories.keys()))
if not portfolios_list:
portfolio_msg = "None. Perform some optimization to build a portfolio."
else:
if not input_portfolios:
console.print("[yellow]Please select at least one portfolio.[/yellow]\n")
portfolio_msg = ", ".join(portfolios_list)
if not categories_list:
categories_msg = "None. Attribute some categories in the loaded file."
else:
categories_msg = ", ".join(categories_list)
console.print(
f"[yellow]Current Portfolios: [/yellow]{portfolio_msg}\n",
)
console.print(
f"[yellow]Current Categories: [/yellow]{categories_msg}\n",
)
valid_portfolios = check_input(
input_type="Portfolio",
input_list=input_portfolios,
available_list=portfolios_list,
)
valid_categories = check_input(
input_type="Category",
input_list=input_categories,
available_list=categories_list,
)
return valid_portfolios, valid_categories
class PortfolioOptimizationController(BaseController):
"""Portfolio Optimization Controller class"""
DEFAULT_PORTFOLIO_DIRECTORY = MISCELLANEOUS_DIRECTORY / "portfolio_examples"
DEFAULT_ALLOCATION_DIRECTORY = DEFAULT_PORTFOLIO_DIRECTORY / "allocation"
DEFAULT_OPTIMIZATION_DIRECTORY = DEFAULT_PORTFOLIO_DIRECTORY / "optimization"
FILE_TYPE_LIST = ["xlsx", "ini"]
CHOICES_COMMANDS = [
"show",
"rpf",
@@ -94,6 +191,45 @@ class PortfolioOptimizationController(BaseController):
files_available: List = list()
@classmethod
def build_allocation_file_map(cls) -> dict:
allocation_file_map = {
filepath.name: filepath
for file_type in cls.FILE_TYPE_LIST
for filepath in cls.DEFAULT_ALLOCATION_DIRECTORY.rglob(f"*.{file_type}")
}
allocation_file_map.update(
{
filepath.name: filepath
for file_type in cls.FILE_TYPE_LIST
for filepath in (USER_PORTFOLIO_DATA_DIRECTORY / "allocation").rglob(
f"*.{file_type}"
)
}
)
return allocation_file_map
@classmethod
def build_optimization_file_map(cls) -> dict:
optimization_file_map = {
filepath.name: filepath
for file_type in cls.FILE_TYPE_LIST
for filepath in cls.DEFAULT_OPTIMIZATION_DIRECTORY.rglob(f"*.{file_type}")
}
optimization_file_map.update(
{
filepath.name: filepath
for file_type in cls.FILE_TYPE_LIST
for filepath in (USER_PORTFOLIO_DATA_DIRECTORY / "optimization").rglob(
f"*.{file_type}"
)
}
)
return optimization_file_map
def __init__(
self,
tickers: List[str] = None,
@@ -118,49 +254,18 @@ class PortfolioOptimizationController(BaseController):
if categories:
self.categories = dict(categories)
self.available_categories = list(self.categories.keys())
else:
self.categories = dict()
self.available_categories = list()
self.count = 0
self.current_portfolio = ""
self.file_types = ["xlsx", "ini"]
self.DEFAULT_ALLOCATION_PATH = USER_PORTFOLIO_DATA_DIRECTORY / "allocation"
self.DATA_ALLOCATION_FILES = {
filepath.name: filepath
for file_type in self.file_types
for filepath in self.DEFAULT_ALLOCATION_PATH.rglob(f"*.{file_type}")
}
self.DATA_ALLOCATION_FILES.update(
{
filepath.name: filepath
for file_type in self.file_types
for filepath in (
MISCELLANEOUS_DIRECTORY / "portfolio_examples" / "allocation"
).rglob(f"*.{file_type}")
}
)
self.allocation_file_map = self.build_allocation_file_map()
self.optimization_file_map = self.build_optimization_file_map()
self.current_file = ""
self.DEFAULT_OPTIMIZATION_PATH = USER_PORTFOLIO_DATA_DIRECTORY / "optimization"
self.DATA_OPTIMIZATION_FILES = {
filepath.name: filepath
for file_type in self.file_types
for filepath in self.DEFAULT_OPTIMIZATION_PATH.rglob(f"*.{file_type}")
}
self.DATA_OPTIMIZATION_FILES.update(
{
filepath.name: filepath
for file_type in self.file_types
for filepath in (
MISCELLANEOUS_DIRECTORY / "portfolio_examples" / "optimization"
).rglob(f"*.{file_type}")
}
)
self.params: Dict = {}
@@ -185,7 +290,7 @@ class PortfolioOptimizationController(BaseController):
mt.add_param("_loaded", self.current_portfolio or "")
mt.add_raw("\n")
mt.add_param("_tickers", ", ".join(self.tickers))
mt.add_param("_categories", ", ".join(self.categories.keys()))
mt.add_param("_categories", ", ".join(self.available_categories))
mt.add_raw("\n")
mt.add_cmd("file")
mt.add_menu("params")
@@ -302,7 +407,7 @@ class PortfolioOptimizationController(BaseController):
"--categories",
dest="categories",
type=lambda s: [str(item).upper() for item in s.split(",")],
default=["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"],
default=self.available_categories,
help="Show selected categories",
)
if p:
@@ -463,7 +568,7 @@ class PortfolioOptimizationController(BaseController):
nargs="+",
dest="file",
help="Parameter file to be used",
choices=self.DATA_OPTIMIZATION_FILES,
choices=self.optimization_file_map.keys(),
metavar="FILE",
)
if other_args and "-" not in other_args[0][0]:
@@ -474,8 +579,8 @@ class PortfolioOptimizationController(BaseController):
if ns_parser:
self.current_file = " ".join(ns_parser.file)
if self.current_file in self.DATA_OPTIMIZATION_FILES:
file_location = self.DATA_OPTIMIZATION_FILES[self.current_file]
if self.current_file in self.optimization_file_map:
file_location = self.optimization_file_map[self.current_file]
else:
file_location = self.current_file # type: ignore
@@ -521,26 +626,22 @@ class PortfolioOptimizationController(BaseController):
parser = self.po_parser(parser, ct=True)
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
portfolios = set(self.portfolios.keys())
portfolios, categories = get_valid_portfolio_categories(
input_portfolios=ns_parser.portfolios,
available_portfolios=self.portfolios,
input_categories=ns_parser.categories,
available_categories=self.categories,
)
if not ns_parser.portfolios:
console.print(
f"[yellow]Current Portfolios:[/yellow] {('None', ', '.join(portfolios))[bool(portfolios)]}"
for p in portfolios:
console.print("[yellow]Portfolio[/yellow]: " + p + "\n")
optimizer_view.display_show(
weights=self.portfolios[p],
tables=categories,
categories_dict=self.categories,
)
c = ("None", ", ".join(ns_parser.categories))[
bool(ns_parser.categories)
]
console.print(f"\n[yellow]Current Categories:[/yellow] {c}")
for portfolio in ns_parser.portfolios:
if portfolio in portfolios:
console.print("[yellow]Portfolio[/yellow]: " + portfolio + "\n")
optimizer_view.display_show(
weights=self.portfolios[portfolio],
tables=ns_parser.categories,
categories=self.categories,
)
@log_start_end(log=logger)
def call_rpf(self, other_args: List[str]):
@@ -569,10 +670,13 @@ class PortfolioOptimizationController(BaseController):
if portfolio in portfolios:
self.portfolios.pop(portfolio)
portfolios.remove(portfolio)
console.print(f"[yellow]Removed '{portfolio}'.[/yellow]")
else:
console.print(f"[red]Portfolio '{portfolio}' does not exist.[/red]")
if self.portfolios:
console.print(
f"\nCurrent Portfolios: {('None', ', '.join(portfolios))[bool(portfolios)]}"
f"\n[yellow]Current Portfolios: [/yellow]{('None', ', '.join(portfolios))[bool(portfolios)]}"
)
self.update_runtime_choices()
@@ -592,7 +696,7 @@ class PortfolioOptimizationController(BaseController):
nargs="+",
dest="file",
help="Allocation file to be used",
choices=self.DATA_OPTIMIZATION_FILES,
choices=self.allocation_file_map.keys(),
metavar="FILE",
)
if other_args and "-" not in other_args[0][0]:
@@ -602,12 +706,15 @@ class PortfolioOptimizationController(BaseController):
if ns_parser:
filename = " ".join(ns_parser.file)
if filename in self.DATA_ALLOCATION_FILES:
file_location = self.DATA_ALLOCATION_FILES[filename]
if filename in self.allocation_file_map:
file_location = self.allocation_file_map[filename]
else:
file_location = filename # type: ignore
self.tickers, self.categories = excel_model.load_allocation(file_location)
self.available_categories = list(self.categories.keys())
if "CURRENT_INVESTED_AMOUNT" in self.available_categories:
self.available_categories.remove("CURRENT_INVESTED_AMOUNT")
self.portfolios = dict()
self.update_runtime_choices()
self.current_portfolio = filename
@@ -690,13 +797,28 @@ class PortfolioOptimizationController(BaseController):
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
if len(self.tickers) < 2:
portfolios, categories = get_valid_portfolio_categories(
input_portfolios=ns_parser.portfolios,
available_portfolios=self.portfolios,
input_categories=ns_parser.categories,
available_categories=self.categories,
)
if not portfolios:
return
if not (
ns_parser.pie
or ns_parser.hist
or ns_parser.dd
or ns_parser.rc_chart
or ns_parser.heat
):
console.print(
"Please have at least 2 loaded tickers to calculate weights.\n"
"[yellow]Please select at least one chart to plot[/yellow]",
"[yellow]from the following: -pi, -hi, -dd, -rc, -he.[/yellow]",
)
return
portfolios = ns_parser.portfolios
stocks = []
for i in portfolios:
stocks += list(self.portfolios[i].keys())
@@ -716,34 +838,46 @@ class PortfolioOptimizationController(BaseController):
value=1,
)
categories = ns_parser.categories
if ns_parser.hist or ns_parser.dd:
for i in portfolios:
weights = self.portfolios[i]
weights = dict(
sorted(weights.items(), key=lambda x: x[1], reverse=True)
)
stocks = list(weights.keys())
for i in portfolios:
weights = self.portfolios[i]
weights = dict(
sorted(weights.items(), key=lambda x: x[1], reverse=True)
)
stocks = list(weights.keys())
optimizer_view.additional_plots(
weights=weights,
data=stock_returns[stocks],
category=None,
title_opt=i,
freq=ns_parser.return_frequency,
risk_measure=ns_parser.risk_measure.lower(),
risk_free_rate=ns_parser.risk_free,
alpha=ns_parser.significance_level,
a_sim=100,
beta=ns_parser.significance_level,
b_sim=100,
pie=ns_parser.pie,
hist=ns_parser.hist,
dd=ns_parser.dd,
rc_chart=ns_parser.rc_chart,
heat=ns_parser.heat,
external_axes=None,
)
if ns_parser.pie or ns_parser.heat or ns_parser.rc_chart:
# hist and dd are transversal to all categories
optimizer_view.additional_plots(
weights=weights,
data=stock_returns[stocks],
title_opt=i,
freq=ns_parser.return_frequency,
risk_measure=ns_parser.risk_measure.lower(),
risk_free_rate=ns_parser.risk_free,
alpha=ns_parser.significance_level,
a_sim=100,
beta=ns_parser.significance_level,
b_sim=100,
hist=ns_parser.hist,
dd=ns_parser.dd,
)
if ns_parser.pie or ns_parser.rc_chart or ns_parser.heat:
if not categories:
console.print(
"[yellow]Categories must be provided to use -pi, -rc or -he.[/yellow]"
)
return
for i in portfolios:
weights = self.portfolios[i]
weights = dict(
sorted(weights.items(), key=lambda x: x[1], reverse=True)
)
stocks = list(weights.keys())
# pie, rc_chart and heat apply to each category
for category in categories:
filtered_categories = dict(
filter(
@@ -764,11 +898,8 @@ class PortfolioOptimizationController(BaseController):
beta=ns_parser.significance_level,
b_sim=100,
pie=ns_parser.pie,
hist=ns_parser.hist,
dd=ns_parser.dd,
rc_chart=ns_parser.rc_chart,
heat=ns_parser.heat,
external_axes=None,
)
@log_start_end(log=logger)

View File

@@ -124,7 +124,12 @@ class PoEngine:
List[str]
Available categories
"""
return list(self._categories.keys())
available_categories = list(self._categories.keys())
if "CURRENT_INVESTED_AMOUNT" in available_categories:
available_categories.remove("CURRENT_INVESTED_AMOUNT")
return available_categories
def get_category(self, category: str = None) -> Dict[str, str]:
"""Get the category

View File

@@ -88,16 +88,16 @@ def generate_portfolio(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p)
@@ -385,16 +385,16 @@ def get_maxsharpe(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.maxsharpe(portfolio_engine=p)
@@ -531,16 +531,16 @@ def get_minrisk(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.minrisk(portfolio_engine=p)
@@ -677,16 +677,16 @@ def get_maxutil(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.maxutil(portfolio_engine=p)
@@ -823,16 +823,16 @@ def get_maxret(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.maxret(portfolio_engine=p)
@@ -946,16 +946,16 @@ def get_maxdiv(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.maxdiv(portfolio_engine=p)
@@ -1047,16 +1047,16 @@ def get_maxdecorr(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.maxdecorr(portfolio_engine=p)
@@ -1157,16 +1157,16 @@ def get_blacklitterman(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.blacklitterman(portfolio_engine=p)
@@ -1283,16 +1283,16 @@ def get_ef(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.ef(portfolio_engine=p)
@@ -1414,16 +1414,16 @@ def get_riskparity(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.riskparity(portfolio_engine=p)
@@ -1558,16 +1558,16 @@ def get_relriskparity(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.relriskparity(portfolio_engine=p)
@@ -1759,16 +1759,16 @@ def get_hrp(portfolio_engine: PoEngine = None, **kwargs) -> Tuple[pd.DataFrame,
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.hrp(portfolio_engine=p)
@@ -1958,16 +1958,16 @@ def get_herc(portfolio_engine: PoEngine = None, **kwargs) -> Tuple[pd.DataFrame,
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.herc(portfolio_engine=p)
@@ -2157,16 +2157,16 @@ def get_nco(portfolio_engine: PoEngine = None, **kwargs) -> Tuple[pd.DataFrame,
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.nco(portfolio_engine=p)
@@ -2234,16 +2234,16 @@ def get_equal(portfolio_engine: PoEngine = None, **kwargs) -> Tuple[pd.DataFrame
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p)
@@ -2315,16 +2315,16 @@ def get_mktcap(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.mktcap(portfolio_engine=p)
@@ -2396,16 +2396,16 @@ def get_dividend(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.dividend(portfolio_engine=p)
@@ -2482,16 +2482,16 @@ def get_property(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.property(portfolio_engine=p)
@@ -2556,21 +2556,21 @@ def show(
"MSFT": "INFORMATION TECHNOLOGY",
"AMZN": "CONSUMER DISCRETIONARY",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
"CURRENCY": {
"AAPL": "USD",
"MSFT": "USD",
"AMZN": "USD",
},
"CURRENT_INVESTED_AMOUNT": {
"AAPL": "100000.0",
"MSFT": "200000.0",
"AMZN": "300000.0",
},
}
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p)
>>> p.get_available_categories()
['SECTOR']
['SECTOR', 'CURRENCY']
>>> weights_df, category_df = openbb.portfolio.po.show(portfolio_engine=p, category="SECTOR")
>>> from openbb_terminal.sdk import openbb
@@ -2581,7 +2581,6 @@ def show(
'SECTOR',
'INDUSTRY',
'COUNTRY',
'CURRENT_INVESTED_AMOUNT',
'CURRENCY']
>>> weights_df, category_df = openbb.portfolio.po.show(portfolio_engine=p, category="ASSET_CLASS")
"""

View File

@@ -280,7 +280,7 @@ def display_plot(portfolio_engine: PoEngine = None, chart_type: str = "pie", **k
>>> p = openbb.portfolio.po.load(symbols_categories=d)
>>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p)
>>> p.get_available_categories()
['SECTOR']
['SECTOR', 'CURRENCY']
>>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="pie")
>>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="hist")
>>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="dd")
@@ -295,7 +295,6 @@ def display_plot(portfolio_engine: PoEngine = None, chart_type: str = "pie", **k
'SECTOR',
'INDUSTRY',
'COUNTRY',
'CURRENT_INVESTED_AMOUNT',
'CURRENCY']
>>> openbb.portfolio.po.plot(portfolio_engine=p, category="ASSET_CLASS", chart_type="pie")
>>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="hist")
@@ -403,6 +402,10 @@ def display_heat(**kwargs):
title = kwargs.get("title", "")
external_axes = kwargs.get("external_axes", None)
if len(weights) == 1:
console.print(f"Heatmap needs at least two values for '{category}'.")
return
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
else: