mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-03 07:51:49 +00:00
more refactoring + edit pie chart function
This commit is contained in:
@@ -117,7 +117,7 @@ The usage is:
|
||||
eff_risk [-p PERIOD] [-r --risk RISK_LEVEL] [-v --value VALUE] [--pie]
|
||||
````
|
||||
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
|
||||
* -r/--risk Risk tolerance. 5% is 0.05.
|
||||
* -r/--risk Risk tolerance. Default is 0.1 (10%)
|
||||
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
|
||||
* --pie Flag that displays a pie chart of the allocations.
|
||||
|
||||
@@ -128,15 +128,42 @@ The usage is:
|
||||
eff_ret [-p PERIOD] [-r --return] [-v --value VALUE] [--pie]
|
||||
````
|
||||
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
|
||||
* -r/--return. Desired return. 5% is 0.05.
|
||||
* -r/--return. Desired return. Default is 0.1 (10%)
|
||||
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
|
||||
* --pie Flag that displays a pie chart of the allocations.
|
||||
|
||||
### show_eff
|
||||
This function plots random portfolios basd on their risk and returns and shows the efficient frontier.
|
||||
This function plots random portfolios based on their risk and returns and shows the efficient frontier.
|
||||
The usage is:
|
||||
````
|
||||
show_eff [-p PERIOD] [-n N_PORTFOLIOS]
|
||||
````
|
||||
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
|
||||
* -n Number of portfolios to simulate.
|
||||
* -n Number of portfolios to simulate.
|
||||
|
||||
##Sample Usage
|
||||
In this example, we generate weights for a list of 6 stocks using the eff_ret command. This optimization looks to maximize returns
|
||||
at a given risk level. We start by adding the stocks we want to analyze:
|
||||
````
|
||||
select aapl,amzn,msft,f,gm,ge
|
||||
````
|
||||
Which shows:
|
||||
````
|
||||
Current Tickers: GE, GM, AMZN, AAPL, F, MSFT
|
||||
````
|
||||
To perform the optimization, we will set a target return of 25% (.25). Given how stocks performed during COVID,
|
||||
there is a lot of volatility, so many optimizations may not get low volatility. The module also returns annualized volatility,
|
||||
so the number is your portfolio volatility * `sqrt(252`. Ths optimization will be (including a pie chart!). Note we could also supply a different
|
||||
time period, which changes the expected returns and historical volatility, which changes the optimization. We could also specify a dollar
|
||||
amount that you wish to allocate using the `-v` flag.
|
||||
````
|
||||
eff_ret -r .25 --pie
|
||||
````
|
||||
The console will show (numbers will vary based on when this is done).
|
||||

|
||||
|
||||
And the pie chart:
|
||||
|
||||

|
||||
|
||||
Note that since `AAPL` had zero allocation, it was ommitted from the chart.
|
||||
@@ -2,9 +2,10 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
import math
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import yfinance as yf
|
||||
from pypfopt.efficient_frontier import EfficientFrontier
|
||||
@@ -14,7 +15,15 @@ from gamestonk_terminal.config_plot import PLOT_DPI
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import plot_autoscale, parse_known_args_and_warn
|
||||
|
||||
title_maps = {}
|
||||
title_maps = {
|
||||
"max_sharpe": "Maximum Sharpe Portfolio",
|
||||
"min_volatility": "Minimum Volatility Portfolio",
|
||||
"eff_risk": "Maximum Return Portfolio at Risk = {:1f} %",
|
||||
"eff_ret": "Minimum Volatility Portfolio at Target Return = {:.1f} %",
|
||||
"equal": "Equally Weighted Portfolio",
|
||||
"marketCap": "MarketCap Weighted Portfolio",
|
||||
"dividendYield": "Dividend Yield Weighted Portfolio",
|
||||
}
|
||||
|
||||
|
||||
def process_stocks(list_of_stocks: List[str], period: str = "3mo") -> pd.DataFrame:
|
||||
@@ -71,11 +80,24 @@ def display_weights(weights: dict):
|
||||
if not weights:
|
||||
return
|
||||
weight_df = pd.DataFrame.from_dict(data=weights, orient="index", columns=["value"])
|
||||
weight_df["weight"] = (weight_df["value"] * 100).astype(str) + " %"
|
||||
print(weight_df["weight"])
|
||||
if math.isclose(weight_df.sum()["value"], 1, rel_tol=0.1):
|
||||
weight_df["weight"] = (weight_df["value"] * 100).astype(str).apply(
|
||||
lambda s: s[:6]
|
||||
) + " %"
|
||||
print(pd.DataFrame(weight_df["weight"]))
|
||||
else:
|
||||
print(weight_df)
|
||||
|
||||
|
||||
def pie_chart_weights(weights: dict, optimizer: str):
|
||||
def my_autopct(x):
|
||||
"""Function for autopct of plt.pie. This results in values not being printed in the pie if they are 'too small'"""
|
||||
if x > 4:
|
||||
return f"{x:.2f} %"
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def pie_chart_weights(weights: dict, optimizer: str, value: Optional[float]):
|
||||
"""
|
||||
Show a pie chart of holdings
|
||||
Parameters
|
||||
@@ -83,43 +105,71 @@ def pie_chart_weights(weights: dict, optimizer: str):
|
||||
weights: dict
|
||||
weights to display. Keys are stocks. Values are either weights or values if -v specified
|
||||
optimzer: str
|
||||
Optmization technique used
|
||||
Optimization technique used for title
|
||||
"""
|
||||
|
||||
plt.close("all")
|
||||
if not weights:
|
||||
return
|
||||
|
||||
stocks = list(weights.keys())
|
||||
sizes = list(weights.values())
|
||||
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
stocks = np.array(list(weights.keys()))
|
||||
sizes = np.array(list(weights.values()))
|
||||
|
||||
to_not_include = sizes == 0
|
||||
|
||||
stocks, sizes = stocks[to_not_include == False], sizes[to_not_include == False]
|
||||
total_size = np.sum(sizes)
|
||||
|
||||
leg_labels = [
|
||||
f"{str(a)}: {str(round(100*b/total_size,3))[:4]}%"
|
||||
for a, b in zip(stocks, sizes)
|
||||
]
|
||||
|
||||
if math.isclose(sum(sizes), 1, rel_tol=0.1):
|
||||
wedges, _, autotexts = ax.pie(
|
||||
wedges, _, autotexts = plt.pie(
|
||||
sizes,
|
||||
labels=stocks,
|
||||
autopct="%1.1f%%",
|
||||
autopct=my_autopct,
|
||||
textprops=dict(color="k"),
|
||||
labeldistance=5,
|
||||
explode=[s / (5 * total_size) for s in sizes],
|
||||
normalize=True,
|
||||
shadow=True,
|
||||
)
|
||||
else:
|
||||
wedges, _, autotexts = ax.pie(
|
||||
sizes, labels=stocks, autopct="", textprops=dict(color="k"), labeldistance=5
|
||||
wedges, _, autotexts = plt.pie(
|
||||
sizes,
|
||||
labels=stocks,
|
||||
autopct="",
|
||||
textprops=dict(color="k"),
|
||||
explode=[s / (5 * total_size) for s in sizes],
|
||||
normalize=True,
|
||||
shadow=True,
|
||||
)
|
||||
for i, a in enumerate(autotexts):
|
||||
a.set_text(f"{sizes[i]}")
|
||||
if sizes[i] / total_size > 0.05:
|
||||
a.set_text(f"{sizes[i]:.2f}")
|
||||
else:
|
||||
a.set_text("")
|
||||
|
||||
ax.axis("equal")
|
||||
ax.legend(
|
||||
plt.axis("equal")
|
||||
|
||||
plt.legend(
|
||||
wedges,
|
||||
stocks,
|
||||
leg_labels,
|
||||
title="Stocks",
|
||||
loc="center left",
|
||||
bbox_to_anchor=(0.85, 0, 0.5, 1),
|
||||
)
|
||||
|
||||
plt.setp(autotexts, size=8, weight="bold")
|
||||
ax.set_title("Portfolio Holdings")
|
||||
|
||||
if optimizer in ["eff_ret", "eff_risk"]:
|
||||
plt.title(title_maps[optimizer].format(100 * value))
|
||||
else:
|
||||
plt.title(title_maps[optimizer])
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.show()
|
||||
print("")
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ def equal_weight(stocks: List[str], other_args: List[str]):
|
||||
for stock in stocks:
|
||||
values[stock] = ns_parser.value * round(1 / n_stocks, 5)
|
||||
if ns_parser.pie:
|
||||
pie_chart_weights(values)
|
||||
pie_chart_weights(values, "equal", None)
|
||||
if n_stocks >= 1:
|
||||
print("Equal Weight Portfolio: ")
|
||||
display_weights(values)
|
||||
@@ -147,7 +147,7 @@ def property_weighting(stocks: List[str], property_type: str, other_args: List[s
|
||||
weights[k] = round(v / prop_sum, 5) * ns_parser.value
|
||||
|
||||
if ns_parser.pie:
|
||||
pie_chart_weights(weights)
|
||||
pie_chart_weights(weights, property_type, None)
|
||||
|
||||
if property_type == "marketCap":
|
||||
print("Market Cap Weighted Portfolio: ")
|
||||
@@ -225,6 +225,7 @@ def ef_portfolio(stocks: List[str], port_type: str, other_args: List[str]):
|
||||
key: ns_parser.value * round(value, 5)
|
||||
for key, value in ef_sharpe.items()
|
||||
}
|
||||
val = None
|
||||
print("Weights that maximize Sharpe Ratio:")
|
||||
|
||||
elif port_type == "min_volatility":
|
||||
@@ -234,6 +235,7 @@ def ef_portfolio(stocks: List[str], port_type: str, other_args: List[str]):
|
||||
key: ns_parser.value * round(value, 5)
|
||||
for key, value in ef_min_vol.items()
|
||||
}
|
||||
val = None
|
||||
print("Weights that minimize volatility")
|
||||
|
||||
elif port_type == "eff_risk":
|
||||
@@ -243,7 +245,8 @@ def ef_portfolio(stocks: List[str], port_type: str, other_args: List[str]):
|
||||
key: ns_parser.value * round(value, 5)
|
||||
for key, value in ef_eff_risk.items()
|
||||
}
|
||||
print("Weights for maximizing returns at your risk level:")
|
||||
val = ns_parser.risk_level
|
||||
print(f"Weights for maximizing returns at risk = {100*val:.1f} %")
|
||||
|
||||
elif port_type == "eff_ret":
|
||||
|
||||
@@ -252,13 +255,14 @@ def ef_portfolio(stocks: List[str], port_type: str, other_args: List[str]):
|
||||
key: ns_parser.value * round(value, 5)
|
||||
for key, value in ef_eff_risk.items()
|
||||
}
|
||||
print("Weights for minimizing risk at your return:")
|
||||
val = ns_parser.target_return
|
||||
print(f"Weights for minimizing risk at target return = {100*val:.1f} %")
|
||||
|
||||
else:
|
||||
raise ValueError("EF Method not found")
|
||||
|
||||
if ns_parser.pie:
|
||||
pie_chart_weights(weights, port_type)
|
||||
pie_chart_weights(weights, port_type, val)
|
||||
|
||||
print("")
|
||||
ef.portfolio_performance(verbose=True)
|
||||
|
||||
@@ -10,7 +10,6 @@ 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 optimizer_view as po_api
|
||||
from gamestonk_terminal.portfolio_optimization.optimizer_helper import display_weights
|
||||
|
||||
|
||||
class PortfolioOptimization:
|
||||
@@ -179,6 +178,7 @@ def menu(tickers: List[str]):
|
||||
"""Portfolio Optimization Menu"""
|
||||
if tickers == [""]:
|
||||
tickers = []
|
||||
plt.close("all")
|
||||
po_controller = PortfolioOptimization(tickers)
|
||||
po_controller.call_help(tickers)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user