mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-08 18:32:21 +00:00
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:
@@ -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,
|
||||
|
||||
4
openbb_terminal/miscellaneous/i18n/en.yml
vendored
4
openbb_terminal/miscellaneous/i18n/en.yml
vendored
@@ -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
|
||||
|
||||
@@ -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()]
|
||||
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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("")
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
],
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user