mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-30 05:52:23 +00:00
add basic portfolio optimization controller
This commit is contained in:
@@ -15,6 +15,7 @@ from gamestonk_terminal.comparison_analysis import yahoo_finance_api as yf_api
|
||||
from gamestonk_terminal.comparison_analysis import market_watch_api as mw_api
|
||||
from gamestonk_terminal.comparison_analysis import finbrain_api as f_api
|
||||
from gamestonk_terminal.comparison_analysis import finviz_compare_view
|
||||
from gamestonk_terminal.portfolio_optimization import port_optimization_controller
|
||||
from gamestonk_terminal.menu import session
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
@@ -42,6 +43,7 @@ class ComparisonAnalysisController:
|
||||
"ownership",
|
||||
"performance",
|
||||
"technical",
|
||||
"po"
|
||||
]
|
||||
|
||||
def __init__(
|
||||
@@ -107,6 +109,8 @@ class ComparisonAnalysisController:
|
||||
print(" performance brief performance comparison")
|
||||
print(" technical brief technical comparison")
|
||||
print("")
|
||||
print(" po portfolio optimzation from selected tickers")
|
||||
print("")
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@@ -312,6 +316,10 @@ class ComparisonAnalysisController:
|
||||
"""Process technical command"""
|
||||
finviz_compare_view.screener(other_args, "technical", self.ticker, self.similar)
|
||||
|
||||
def call_po(self, _):
|
||||
"""Open Portfolio Optimization menu with ticker and similar"""
|
||||
port_optimization_controller.menu([self.ticker] + self.similar)
|
||||
|
||||
|
||||
def menu(stock: pd.DataFrame, ticker: str, start: datetime, interval: str):
|
||||
"""Comparison Analysis Menu"""
|
||||
|
||||
@@ -60,6 +60,7 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open):
|
||||
)
|
||||
print(" pa portfolio analysis, \t\t supports: robinhood, alpaca, ally ")
|
||||
print(" crypto cryptocurrencies, \t\t uses coingecko api")
|
||||
print(" po portfolio analysis, \t\t UPDATE ME BEFORE PR")
|
||||
|
||||
if s_ticker:
|
||||
print(
|
||||
|
||||
16
gamestonk_terminal/portfolio_optimization/README.md
Normal file
16
gamestonk_terminal/portfolio_optimization/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Portfolio Optimization
|
||||
|
||||
This menu aims to discover optimal portfolios for selected stocks
|
||||
|
||||
|
||||
###add
|
||||
Adds tickers to current list for optimization
|
||||
````
|
||||
usage: add ticker1,ticker2,...
|
||||
````
|
||||
###optimize
|
||||
Runs optimization
|
||||
````
|
||||
usage: optimize
|
||||
````
|
||||
|
||||
28
gamestonk_terminal/portfolio_optimization/port_opt_api.py
Normal file
28
gamestonk_terminal/portfolio_optimization/port_opt_api.py
Normal file
@@ -0,0 +1,28 @@
|
||||
""" Portfolio Optimization Functions"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
|
||||
def optimize(list_of_stocks:List, other_args:List[str]):
|
||||
"""
|
||||
|
||||
Parameters
|
||||
----------
|
||||
list_of_stocks: List[str]
|
||||
List of tickers to be included in optimization
|
||||
|
||||
Returns
|
||||
-------
|
||||
weights : dict
|
||||
Dictionary of weights where keys are the tickers
|
||||
|
||||
"""
|
||||
weights = {}
|
||||
n_stocks = len(list_of_stocks)
|
||||
for stock in list_of_stocks:
|
||||
weights[stock] = round(1/n_stocks, 5)
|
||||
|
||||
return weights
|
||||
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
"""Portfolio Optimization Controller Module"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
|
||||
import argparse
|
||||
from typing import List, Set
|
||||
from datetime import datetime
|
||||
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import get_flair, parse_known_args_and_warn
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.portfolio_optimization import port_opt_api as po_api
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
class PortfolioOptimization:
|
||||
|
||||
CHOICES = ["help",
|
||||
"q",
|
||||
"quit",
|
||||
"clear",
|
||||
"add",
|
||||
"optimize"]
|
||||
|
||||
def __init__(self,
|
||||
tickers:Set[str] = None
|
||||
):
|
||||
"""
|
||||
Construct Portfolio Optimization
|
||||
"""
|
||||
|
||||
self.po_parser = argparse.ArgumentParser(add_help=False, prog="po")
|
||||
self.po_parser.add_argument("cmd", choices=self.CHOICES )
|
||||
self.tickers = set(tickers)
|
||||
|
||||
@staticmethod
|
||||
def print_help(tickers: Set[str]):
|
||||
"""Print help"""
|
||||
print("\nPortfolio Optimization:")
|
||||
print(" help show this menu again")
|
||||
print(
|
||||
" q quit this menu, and shows back to main menu"
|
||||
)
|
||||
print(" quit quit to abandon program")
|
||||
print(
|
||||
f"\nCurrent Tickers: {('None', ', '.join(tickers))[bool(tickers)]}"
|
||||
)
|
||||
print("")
|
||||
print(" add add ticker to optimize")
|
||||
print(" optimize run optimization")
|
||||
print("")
|
||||
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
"""
|
||||
(known_args, other_args) = self.po_parser.parse_known_args(an_input.split())
|
||||
|
||||
return getattr(
|
||||
self, "call_" + known_args.cmd, lambda: "Command not recognized!"
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help(self.tickers)
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
|
||||
def call_add(self, other_args:List[str]):
|
||||
self.add_stocks(self, other_args)
|
||||
|
||||
def call_optimize(self, other_args:List[str]):
|
||||
weights = po_api.optimize(self.tickers, other_args)
|
||||
print("Optimal Weights for Equal Weighting:")
|
||||
print(weights)
|
||||
print("")
|
||||
|
||||
@staticmethod
|
||||
def add_stocks(self, other_args: List[str]):
|
||||
|
||||
""" Add ticker to current list for optimization"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="add",
|
||||
description="""Add tickers for optimizing.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--tickers",
|
||||
dest="add_tickers",
|
||||
type=lambda s: [str(item).upper() for item in s.split(",")],
|
||||
default=[],
|
||||
help="add tickers to optimzation.",
|
||||
)
|
||||
try:
|
||||
# For the case where a user uses: 'select NIO,XPEV,LI'
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
for ticker in ns_parser.add_tickers:
|
||||
self.tickers.add(ticker)
|
||||
|
||||
|
||||
print(
|
||||
f"\nCurrent Tickers: {('None', ', '.join(self.tickers))[bool(self.tickers)]}"
|
||||
)
|
||||
print("")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
print("")
|
||||
|
||||
|
||||
def menu(tickers:List[str]):
|
||||
"""Portfolio Optimization Menu"""
|
||||
if tickers == ['']:
|
||||
tickers = []
|
||||
po_controller = PortfolioOptimization(tickers)
|
||||
po_controller.call_help(tickers)
|
||||
|
||||
while True:
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in po_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (po)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (po)> ")
|
||||
|
||||
try:
|
||||
process_input = po_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
|
||||
@@ -31,6 +31,7 @@ from gamestonk_terminal.residuals_analysis import ra_controller
|
||||
from gamestonk_terminal.portfolio import port_controller
|
||||
from gamestonk_terminal.cryptocurrency import crypto_controller
|
||||
from gamestonk_terminal.screener import screener_controller
|
||||
from gamestonk_terminal.portfolio_optimization import port_optimization_controller as po_controller
|
||||
|
||||
# import warnings
|
||||
# warnings.simplefilter("always")
|
||||
@@ -88,7 +89,9 @@ def main():
|
||||
"pa",
|
||||
"crypto",
|
||||
"ra",
|
||||
"po"
|
||||
]
|
||||
|
||||
menu_parser.add_argument("opt", choices=choices)
|
||||
completer = NestedCompleter.from_nested_dict({c: None for c in choices})
|
||||
|
||||
@@ -242,6 +245,9 @@ def main():
|
||||
elif ns_known_args.opt == "crypto":
|
||||
b_quit = crypto_controller.menu()
|
||||
|
||||
elif ns_known_args.opt == "po":
|
||||
b_quit = po_controller.menu([s_ticker])
|
||||
|
||||
elif ns_known_args.opt == "pred":
|
||||
|
||||
if not gtff.ENABLE_PREDICT:
|
||||
|
||||
Reference in New Issue
Block a user