Fix Various portfolio/po issues (#3286)

* Allow for loading without -f flag

* Massive upgrade to params file read/write

* Better handling empty dfs and none in prompt toolkit

* Fix tr typing and handle none error

* Added options

* Fixed tests

Co-authored-by: James Maslek <jmaslek11@gmail.com>
This commit is contained in:
Colin Delahunty
2022-11-04 20:10:50 +01:00
committed by GitHub
parent 2b517bc181
commit 86f2e5622a
13 changed files with 395 additions and 342 deletions

View File

@@ -271,7 +271,7 @@ class NestedCompleter(Completer):
if "-" not in text:
completer = self.options.get(first_term)
else:
if cmd in self.options:
if cmd in self.options and self.options.get(cmd):
completer = self.options.get(cmd).options.get(first_term) # type: ignore
else:
completer = self.options.get(first_term)
@@ -378,16 +378,14 @@ class NestedCompleter(Completer):
if k not in self.flags_processed
}
if (
cmd
and cmd in self.options.keys()
and [
text in val
for val in [
f"{cmd} {opt}" for opt in self.options.get(cmd).options.keys() # type: ignore
]
]
):
command = self.options.get(cmd)
if command:
options = command.options # type: ignore
else:
options = {}
command_options = [f"{cmd} {opt}" for opt in options.keys()]
text_list = [text in val for val in command_options]
if cmd and cmd in self.options.keys() and text_list:
completer = WordCompleter(
list(self.options.get(cmd).options.keys()), # type: ignore
ignore_case=self.ignore_case,

View File

@@ -491,10 +491,8 @@ en:
crypto/disc/games: top blockchain games
crypto/disc/dapps: top decentralized apps
crypto/disc/dex: top decentralized exchanges
crypto/ov/global: global crypto market info
crypto/ov/defi: global DeFi market info
crypto/ov/stables: stablecoins
crypto/ov/exchanges: top crypto exchanges
crypto/ov/exrates: coin exchange rates
crypto/ov/indexes: crypto indexes
crypto/ov/derivatives: crypto derivatives
@@ -968,7 +966,7 @@ en:
portfolio/po/_loaded: Portfolio loaded
portfolio/po/_tickers: Tickers
portfolio/po/_categories: Categories
portfolio/po/file: select portfolio parameter file
portfolio/po/params/load: select portfolio parameter file
portfolio/po/params: specify and show portfolio risk parameters
portfolio/po/_parameter: Parameter file
portfolio/po/_mean_risk_optimization_: Mean Risk Optimization

View File

@@ -461,6 +461,11 @@ def get_mean_risk_portfolio(
threshold=threshold,
method=method,
)
if stock_returns.empty:
console.print(
"[red]Not enough data points in range to run calculations.[/red]\n"
)
return {}, pd.DataFrame()
risk_free_rate = risk_free_rate / time_factor[freq.upper()]

View File

@@ -1186,6 +1186,9 @@ def display_max_sharpe(
value_short=value_short,
)
if stock_returns is None or stock_returns.empty:
return {}
if weights is None:
console.print("\n", "There is no solution with these parameters")
return {}

View File

@@ -4,22 +4,18 @@ __docformat__ = "numpy"
# pylint: disable=C0302, no-else-return
import argparse
import configparser
import logging
import os
from pathlib import Path
from typing import List, Optional
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY
from openbb_terminal import feature_flags as gtff
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import log_and_raise
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.portfolio.portfolio_optimization.parameters import params_view
from openbb_terminal.portfolio.portfolio_optimization.parameters.params_view import (
from openbb_terminal.portfolio.portfolio_optimization.parameters import params_helpers
from openbb_terminal.portfolio.portfolio_optimization.parameters.params_statics import (
AVAILABLE_OPTIONS,
DEFAULT_PARAMETERS,
DEFAULT_BOOL,
@@ -30,29 +26,12 @@ from openbb_terminal.rich_config import console, MenuText
logger = logging.getLogger(__name__)
def check_save_file(file: str) -> str:
"""Argparse type to check parameter file to be saved"""
if file == "defaults.ini":
log_and_raise(
argparse.ArgumentTypeError(
"Cannot overwrite defaults.ini file, please save with a different name"
)
)
else:
if not file.endswith(".ini") and not file.endswith(".xlsx"):
log_and_raise(
argparse.ArgumentTypeError("File to be saved needs to be .ini or .xlsx")
)
return file
class ParametersController(BaseController):
"""Portfolio Optimization Parameters Controller class"""
CHOICES_COMMANDS = [
"set",
"file",
"load",
"save",
"new",
"clear",
@@ -80,46 +59,22 @@ class ParametersController(BaseController):
current_model = ""
current_file = ""
params = configparser.RawConfigParser()
def __init__(
self,
file: str,
queue: List[str] = None,
params=None,
params: Optional[dict] = None,
current_model=None,
):
"""Constructor"""
super().__init__(queue)
self.params: dict = params if params else {}
self.current_file = file
self.current_model = current_model
self.description: Optional[str] = None
self.DEFAULT_PATH = os.path.abspath(
MISCELLANEOUS_DIRECTORY / "portfolio_examples" / "optimization"
)
self.file_types = ["xlsx", "ini"]
self.DATA_FILES = {
filepath.name: filepath
for file_type in self.file_types
for filepath in Path(self.DEFAULT_PATH).rglob(f"*.{file_type}")
if filepath.is_file()
}
if params:
self.params = params
else:
pass
# TODO: Enable .ini reading
# self.params.read(
# os.path.join(
# self.DEFAULT_PATH,
# self.current_file if self.current_file else "defaults.ini",
# )
# )
# self.params.optionxform = str # type: ignore
# self.params = self.params["OPENBB"]
self.DATA_FILES = params_helpers.load_data_files()
if session and gtff.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
@@ -127,9 +82,9 @@ class ParametersController(BaseController):
choices["set"]["--model"] = {c: None for c in self.models}
choices["set"]["-m"] = "--model"
choices["arg"] = {c: None for c in AVAILABLE_OPTIONS}
choices["file"] = {c: {} for c in self.DATA_FILES}
choices["file"]["--file"] = {c: {} for c in self.DATA_FILES}
choices["file"]["-f"] = "--file"
choices["load"] = {c: {} for c in self.DATA_FILES}
choices["load"]["--file"] = {c: {} for c in self.DATA_FILES}
choices["load"]["-f"] = "--file"
choices["save"]["--file"] = None
choices["save"]["-f"] = "--file"
choices["arg"] = {
@@ -145,7 +100,7 @@ class ParametersController(BaseController):
mt = MenuText("portfolio/po/params/")
mt.add_param("_loaded", self.current_file)
mt.add_raw("\n")
mt.add_cmd("file")
mt.add_cmd("load")
mt.add_cmd("save")
mt.add_raw("\n")
mt.add_param("_model", self.current_model or "")
@@ -190,7 +145,7 @@ class ParametersController(BaseController):
return []
@log_start_end(log=logger)
def call_file(self, other_args: List[str]):
def call_load(self, other_args: List[str]):
"""Process load command"""
parser = argparse.ArgumentParser(
add_help=False,
@@ -208,6 +163,8 @@ class ParametersController(BaseController):
help="Parameter file to be used",
)
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:
@@ -235,31 +192,17 @@ class ParametersController(BaseController):
"-f",
"--file",
required=True,
type=check_save_file,
type=params_helpers.check_save_file,
dest="file",
help="Filename to be saved",
)
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:
if ns_parser.file.endswith(".ini"):
# Create file if it does not exist
filepath = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
ns_parser.file,
)
)
Path(filepath)
with open(filepath, "w") as configfile:
self.params.write(configfile)
self.current_file = ns_parser.file
console.print()
elif ns_parser.file.endswith(".xlsx"):
console.print("It is not yet possible to save to .xlsx")
self.current_file = str(params_view.save_file(ns_parser.file, self.params))
console.print()
@log_start_end(log=logger)
def call_clear(self, other_args: List[str]):
@@ -272,11 +215,6 @@ class ParametersController(BaseController):
)
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
if not self.current_file:
console.print(
"[red]Load portfolio risk parameters first using `file`.\n[/red]"
)
return
self.current_model = ""
console.print("")

View File

@@ -0,0 +1,42 @@
import argparse
from typing import Dict
from pathlib import Path
from openbb_terminal.helper_funcs import log_and_raise
from openbb_terminal.core.config import paths
def check_save_file(file: str) -> str:
"""Argparse type to check parameter file to be saved"""
if file == "defaults.ini":
log_and_raise(
argparse.ArgumentTypeError(
"Cannot overwrite defaults.ini file, please save with a different name"
)
)
else:
if not file.endswith(".ini"):
log_and_raise(
argparse.ArgumentTypeError("File to be saved needs to be .ini")
)
return file
def load_data_files() -> Dict[str, Path]:
"""Loads files from the misc directory and from the user's custom exports
Returns
----------
Dict[str, Path]
The dictionary of filenames and their paths
"""
default_path = paths.MISCELLANEOUS_DIRECTORY / "portfolio_examples" / "optimization"
custom_exports = paths.USER_EXPORTS_DIRECTORY / "portfolio"
data_files = {}
for directory in [default_path, custom_exports]:
for file_type in ["xlsx", "ini"]:
for filepath in Path(directory).rglob(f"*.{file_type}"):
if filepath.is_file():
data_files[filepath.name] = filepath
return data_files

View File

@@ -0,0 +1,219 @@
DEFAULT_RANGE = [value / 1000 for value in range(0, 1001)]
DEFAULT_BOOL = ["True", "False"]
AVAILABLE_OPTIONS = {
"historic_period": ["d", "w", "mo", "y", "ytd", "max"],
"start_period": ["Any"],
"end_period": ["Any"],
"log_returns": DEFAULT_BOOL,
"return_frequency": ["d", "w", "m"],
"max_nan": DEFAULT_RANGE,
"threshold_value": DEFAULT_RANGE,
"nan_fill_method": [
"linear",
"time",
"nearest",
"zero",
"slinear",
"quadratic",
"cubic",
],
"risk_free": DEFAULT_RANGE,
"significance_level": DEFAULT_RANGE,
"risk_measure": [
"MV",
"MAD",
"MSV",
"FLPM",
"SLPM",
"CVaR",
"EVaR",
"WR",
"ADD",
"UCI",
"CDaR",
"EDaR",
"MDD",
],
"target_return": DEFAULT_RANGE + [-x for x in DEFAULT_RANGE],
"target_risk": DEFAULT_RANGE + [-x for x in DEFAULT_RANGE],
"expected_return": ["hist", "ewma1", "ewma2"],
"covariance": [
"hist",
"ewma1",
"ewma2",
"ledoit",
"oas",
"shrunk",
"gl",
"jlogo",
"fixed",
"spectral",
"shrink",
],
"smoothing_factor_ewma": DEFAULT_RANGE,
"long_allocation": DEFAULT_RANGE,
"short_allocation": DEFAULT_RANGE,
"risk_aversion": [value / 100 for value in range(-500, 501)],
"amount_portfolios": range(1, 10001),
"random_seed": range(1, 10001),
"tangency": DEFAULT_BOOL,
"risk_parity_model": ["A", "B", "C"],
"penal_factor": DEFAULT_RANGE + [-x for x in DEFAULT_RANGE],
"co_dependence": [
"pearson",
"spearman",
"abs_pearson",
"abs_spearman",
"distance",
"mutual_info",
"tail",
],
"cvar_simulations": range(1, 10001),
"cvar_significance": DEFAULT_RANGE,
"linkage": [
"single",
"complete",
"average",
"weighted",
"centroid",
"ward",
"dbht",
],
"max_clusters": range(1, 101),
"amount_bins": ["KN", "FD", "SC", "HGR", "Integer"],
"alpha_tail": DEFAULT_RANGE,
"leaf_order": DEFAULT_BOOL,
"objective": ["MinRisk", "Utility", "Sharpe", "MaxRet"],
}
DEFAULT_PARAMETERS = [
"historic_period",
"start_period",
"end_period",
"log_returns",
"return_frequency",
"max_nan",
"threshold_value",
"nan_fill_method",
"risk_free",
"significance_level",
]
MODEL_PARAMS = {
"maxsharpe": [
"risk_measure",
"target_return",
"target_risk",
"expected_return",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"short_allocation",
],
"minrisk": [
"risk_measure",
"target_return",
"target_risk",
"expected_return",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"short_allocation",
],
"maxutil": [
"risk_measure",
"target_return",
"target_risk",
"expected_return",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"short_allocation",
"risk_aversion",
],
"maxret": [
"risk_measure",
"target_return",
"target_risk",
"expected_return",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
],
"maxdiv": ["covariance", "long_allocation"],
"maxdecorr": ["covariance", "long_allocation"],
"ef": [
"risk_measure",
"long_allocation",
"short_allocation",
"amount_portfolios",
"random_seed",
"tangency",
],
"equal": ["risk_measure", "long_allocation"],
"mktcap": ["risk_measure", "long_allocation"],
"dividend": ["risk_measure", "long_allocation"],
"riskparity": [
"risk_measure",
"target_return",
"long_allocation",
"risk_contribution",
],
"relriskparity": [
"risk_measure",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"risk_contribution",
"risk_parity_model",
"penal_factor",
],
"hrp": [
"risk_measure",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"co_dependence",
"cvar_simulations",
"cvar_significance",
"linkage",
"amount_clusters",
"max_clusters",
"amount_bins",
"alpha_tail",
"leaf_order",
"objective",
],
"herc": [
"risk_measure",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"co_dependence",
"cvar_simulations",
"cvar_significance",
"linkage",
"amount_clusters",
"max_clusters",
"amount_bins",
"alpha_tail",
"leaf_order",
"objective",
],
"nco": [
"risk_measure",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"co_dependence",
"cvar_simulations",
"cvar_significance",
"linkage",
"amount_clusters",
"max_clusters",
"amount_bins",
"alpha_tail",
"leaf_order",
"objective",
],
}

View File

@@ -1,271 +1,60 @@
import configparser
from typing import Tuple
from pathlib import Path
import pandas as pd
from openbb_terminal.helper_funcs import print_rich_table
from openbb_terminal.portfolio.portfolio_optimization import excel_model
from openbb_terminal.rich_config import console
DEFAULT_RANGE = [value / 1000 for value in range(0, 1001)]
DEFAULT_BOOL = ["True", "False"]
AVAILABLE_OPTIONS = {
"historic_period": ["d", "w", "mo", "y", "ytd", "max"],
"start_period": ["Any"],
"end_period": ["Any"],
"log_returns": DEFAULT_BOOL,
"return_frequency": ["d", "w", "m"],
"max_nan": DEFAULT_RANGE,
"threshold_value": DEFAULT_RANGE,
"nan_fill_method": [
"linear",
"time",
"nearest",
"zero",
"slinear",
"quadratic",
"cubic",
],
"risk_free": DEFAULT_RANGE,
"significance_level": DEFAULT_RANGE,
"risk_measure": [
"MV",
"MAD",
"MSV",
"FLPM",
"SLPM",
"CVaR",
"EVaR",
"WR",
"ADD",
"UCI",
"CDaR",
"EDaR",
"MDD",
],
"target_return": DEFAULT_RANGE + [-x for x in DEFAULT_RANGE],
"target_risk": DEFAULT_RANGE + [-x for x in DEFAULT_RANGE],
"expected_return": ["hist", "ewma1", "ewma2"],
"covariance": [
"hist",
"ewma1",
"ewma2",
"ledoit",
"oas",
"shrunk",
"gl",
"jlogo",
"fixed",
"spectral",
"shrink",
],
"smoothing_factor_ewma": DEFAULT_RANGE,
"long_allocation": DEFAULT_RANGE,
"short_allocation": DEFAULT_RANGE,
"risk_aversion": [value / 100 for value in range(-500, 501)],
"amount_portfolios": range(1, 10001),
"random_seed": range(1, 10001),
"tangency": DEFAULT_BOOL,
"risk_parity_model": ["A", "B", "C"],
"penal_factor": DEFAULT_RANGE + [-x for x in DEFAULT_RANGE],
"co_dependence": [
"pearson",
"spearman",
"abs_pearson",
"abs_spearman",
"distance",
"mutual_info",
"tail",
],
"cvar_simulations": range(1, 10001),
"cvar_significance": DEFAULT_RANGE,
"linkage": [
"single",
"complete",
"average",
"weighted",
"centroid",
"ward",
"dbht",
],
"max_clusters": range(1, 101),
"amount_bins": ["KN", "FD", "SC", "HGR", "Integer"],
"alpha_tail": DEFAULT_RANGE,
"leaf_order": DEFAULT_BOOL,
"objective": ["MinRisk", "Utility", "Sharpe", "MaxRet"],
}
DEFAULT_PARAMETERS = [
"historic_period",
"start_period",
"end_period",
"log_returns",
"return_frequency",
"max_nan",
"threshold_value",
"nan_fill_method",
"risk_free",
"significance_level",
]
MODEL_PARAMS = {
"maxsharpe": [
"risk_measure",
"target_return",
"target_risk",
"expected_return",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"short_allocation",
],
"minrisk": [
"risk_measure",
"target_return",
"target_risk",
"expected_return",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"short_allocation",
],
"maxutil": [
"risk_measure",
"target_return",
"target_risk",
"expected_return",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"short_allocation",
"risk_aversion",
],
"maxret": [
"risk_measure",
"target_return",
"target_risk",
"expected_return",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
],
"maxdiv": ["covariance", "long_allocation"],
"maxdecorr": ["covariance", "long_allocation"],
"ef": [
"risk_measure",
"long_allocation",
"short_allocation",
"amount_portfolios",
"random_seed",
"tangency",
],
"equal": ["risk_measure", "long_allocation"],
"mktcap": ["risk_measure", "long_allocation"],
"dividend": ["risk_measure", "long_allocation"],
"riskparity": [
"risk_measure",
"target_return",
"long_allocation",
"risk_contribution",
],
"relriskparity": [
"risk_measure",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"risk_contribution",
"risk_parity_model",
"penal_factor",
],
"hrp": [
"risk_measure",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"co_dependence",
"cvar_simulations",
"cvar_significance",
"linkage",
"amount_clusters",
"max_clusters",
"amount_bins",
"alpha_tail",
"leaf_order",
"objective",
],
"herc": [
"risk_measure",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"co_dependence",
"cvar_simulations",
"cvar_significance",
"linkage",
"amount_clusters",
"max_clusters",
"amount_bins",
"alpha_tail",
"leaf_order",
"objective",
],
"nco": [
"risk_measure",
"covariance",
"smoothing_factor_ewma",
"long_allocation",
"co_dependence",
"cvar_simulations",
"cvar_significance",
"linkage",
"amount_clusters",
"max_clusters",
"amount_bins",
"alpha_tail",
"leaf_order",
"objective",
],
}
from openbb_terminal.portfolio.portfolio_optimization.parameters import params_statics
from openbb_terminal.core.config import paths
def load_file(file_location=None):
def load_file(path: str = "") -> Tuple[dict, str]:
"""
Loads in the configuration file and return the parameters in a dictionary including the model if available.
Loads in the configuration file and return the parameters in a dictionary including the model
if available.
Parameters
----------
file_location: str
path: str
The location of the file to be loaded in either xlsx or ini.
Returns
-------
Return the parameters and the model, if available.
Tuple[dict, str]
Return the parameters and the model, if available.
"""
if str(file_location).endswith(".ini"):
params = configparser.RawConfigParser()
params.read(file_location)
params.optionxform = str # type: ignore
params = params["OPENBB"]
if str(path).endswith(".ini"):
params_obj = configparser.RawConfigParser()
params_obj.read(path)
params_obj.optionxform = str # type: ignore
params: dict = dict(params_obj["OPENBB"].items())
if "technique" in params:
current_model = params["technique"]
else:
current_model = None
current_model = ""
elif str(file_location).endswith(".xlsx"):
params, _ = excel_model.load_configuration(file_location)
elif str(path).endswith(".xlsx"):
params, _ = excel_model.load_configuration(path)
current_model = params["technique"]
else:
console.print(
"Can not load in the file due to not being an .ini or .xlsx file."
)
return None, None
return {}, ""
max_len = max(len(k) for k in params.keys())
help_text = "[info]Parameters:[/info]\n"
if current_model:
for k, v in params.items():
all_params = DEFAULT_PARAMETERS + MODEL_PARAMS[current_model]
all_params = (
params_statics.DEFAULT_PARAMETERS
+ params_statics.MODEL_PARAMS[current_model]
)
if k in all_params:
help_text += f" [param]{k}{' ' * (max_len - len(k))} :[/param] {v}\n"
else:
@@ -277,6 +66,26 @@ def load_file(file_location=None):
return params, current_model
def save_file(path: str, params: dict) -> Path:
if not path.endswith(".ini"):
console.print("[red]File to be saved needs to be a .ini file.[/red]\n")
# Create file if it does not exist
base_path = paths.USER_EXPORTS_DIRECTORY / "portfolio"
if not base_path.is_dir():
base_path.mkdir()
filepath = base_path / path
config_parser = configparser.RawConfigParser()
config_parser.add_section("OPENBB")
for key, value in params.items():
config_parser.set("OPENBB", key, value)
with open(filepath, "w") as configfile:
config_parser.write(configfile)
return filepath
def show_arguments(arguments, description=None):
"""
Show the available arguments and the choices you have for each. If available, also show

View File

@@ -383,6 +383,47 @@ class PortfolioOptimizationController(BaseController):
self.choices["load"] = {c: {} for c in self.DATA_ALLOCATION_FILES}
self.choices["load"]["--file"] = {c: {} for c in self.DATA_ALLOCATION_FILES}
self.choices["load"]["-f"] = "--file"
self.choices["plot"]["--portfolios"] = None
self.choices["plot"]["-pf"] = "--portfolios"
self.choices["plot"]["--pie"] = None
self.choices["plot"]["-pi"] = "--pie"
self.choices["plot"]["--hist"] = None
self.choices["plot"]["-hi"] = "--hist"
self.choices["plot"]["--drawdown"] = None
self.choices["plot"]["-dd"] = "--drawdown"
self.choices["plot"]["--rc-chart"] = None
self.choices["plot"]["-rc"] = "--rc-chart"
self.choices["plot"]["--heat"] = None
self.choices["plot"]["-he"] = "--heat"
self.choices["plot"]["--risk-measure"] = {
c: {} for c in self.MEAN_RISK_CHOICES
}
self.choices["plot"]["-rm"] = "--risk-measure"
self.choices["plot"]["--method"] = {c: {} for c in self.METHOD_CHOICES}
self.choices["plot"]["-mt"] = "--method"
self.choices["plot"]["--categories"] = None
self.choices["plot"]["-ct"] = "--categories"
self.choices["plot"]["--period"] = {c: {} for c in self.PERIOD_CHOICES}
self.choices["plot"]["-p"] = "--period"
self.choices["plot"]["--start"] = None
self.choices["plot"]["-s"] = "--start"
self.choices["plot"]["--end"] = None
self.choices["plot"]["-e"] = "--end"
self.choices["plot"]["--log-returns"] = None
self.choices["plot"]["-lr"] = "--log-returns"
self.choices["plot"]["--freq"] = {c: {} for c in ["d", "w", "m"]}
self.choices["plot"]["--maxnan"] = None
self.choices["plot"]["-mn"] = "--maxnan"
self.choices["plot"]["--threshold"] = None
self.choices["plot"]["-th"] = "--threshold"
self.choices["plot"]["--risk-free-rate"] = None
self.choices["plot"]["-r"] = "--risk-free-rate"
self.choices["plot"]["--alpha"] = None
self.choices["plot"]["-a"] = "--alpha"
self.choices["plot"]["--value"] = None
self.choices["plot"]["-v"] = "--value"
self.choices["rpf"]["--portfolios"] = None
self.choices["rpf"]["--pf"] = "--portfolios"
for fn in models:
self.choices[fn]["-p"] = {c: {} for c in self.PERIOD_CHOICES}
self.choices[fn]["--period"] = {c: {} for c in self.PERIOD_CHOICES}
@@ -469,7 +510,6 @@ class PortfolioOptimizationController(BaseController):
if session and obbff.USE_PROMPT_TOOLKIT:
if self.portfolios:
self.choices["show"] = {c: {} for c in list(self.portfolios.keys())}
self.choices["plot"] = {c: {} for c in list(self.portfolios.keys())}
self.choices = {**self.choices, **self.SUPPORT_CHOICES}
self.completer = NestedCompleter.from_nested_dict(self.choices)
@@ -1315,6 +1355,7 @@ class PortfolioOptimizationController(BaseController):
default=self.params["target_return"]
if "target_return" in self.params
else -1,
type=int,
help="Constraint on minimum level of portfolio's return",
)
parser.add_argument(
@@ -1412,7 +1453,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_max_sharpe(
symbols=self.tickers,
@@ -1595,7 +1636,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_min_risk(
symbols=self.tickers,
@@ -1790,7 +1831,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_max_util(
symbols=self.tickers,
@@ -1977,7 +2018,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_max_ret(
symbols=self.tickers,
@@ -2133,7 +2174,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_max_div(
symbols=self.tickers,
@@ -2277,7 +2318,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_max_decorr(
symbols=self.tickers,
@@ -2499,7 +2540,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_black_litterman(
symbols=self.tickers,
@@ -2779,7 +2820,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_risk_parity(
symbols=self.tickers,
@@ -2937,7 +2978,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_rel_risk_parity(
symbols=self.tickers,
@@ -3220,7 +3261,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_hrp(
symbols=self.tickers,
@@ -3521,7 +3562,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_herc(
symbols=self.tickers,
@@ -3776,7 +3817,7 @@ class PortfolioOptimizationController(BaseController):
if "historic_period_sa" in vars(ns_parser):
table = False
console.print("Optimization can take time. Please be patient...")
console.print("Optimization can take time. Please be patient...\n")
weights = optimizer_view.display_nco(
symbols=self.tickers,

View File

@@ -5,7 +5,7 @@ Portfolio loaded:
Tickers:
Categories:
file select portfolio parameter file
file portfolio/po/file
> params specify and show portfolio risk parameters
Parameter file: