robertmartin8 / PyPortfolioOpt

Financial portfolio optimisation in python, including classical efficient frontier, Black-Litterman, Hierarchical Risk Parity
https://pyportfolioopt.readthedocs.io/
MIT License
4.43k stars 950 forks source link

max_sharpe() The objective function was changed after the initial optimization. #422

Closed enginance closed 2 years ago

enginance commented 2 years ago

Describe the bug plotting.plot_efficient_frontier(ef, ax=ax, show_assets=False)

The following plotting is causing an unexpected issue. It is the plot of only the boundary of efficient frontier on subplots pypfopt.exceptions.InstantiationError: The objective function was changed after the initial optimization. Please create a new instance instead.

Expected behavior The following plotting is causing an unexpected issue. It is the plot of only the boundary of efficient frontier on subplots image

Code sample #######################################################################################################

Plot efficient frontier with Monte Carlo sim

ef = EfficientFrontier(mu, S)

fig, ax = plt.subplots()

the following plotting is causing an unexpected issue. It is the plot of only the boundary of efficient frontier on subplots

plotting.plot_efficient_frontier(ef, ax=ax, show_assets=False)

Find and plot the tangency portfolio

ef.max_sharpe() ret_tangent, stdtangent, = ef.portfolio_performance() ax.scatter(std_tangent, ret_tangent, marker="*", s=100, c="r", label="Max Sharpe")

Plot random portfolios

ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r")

Format

ax.set_title("PyPortfolioOpt: Efficient Frontier with random portfolios") ax.legend() plt.tight_layout() plt.show()

Operating system, python version, PyPortfolioOpt version Windows 10, Python 3.8.8 (default, Apr 13 2021, 15:08:03) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32, PyPfOpt Library version: 1.5.1

Additional context Add any other context about the problem here.

phschiele commented 2 years ago

@enginance The problem is that you can only call a solve function on the frontier object once, as the problems are immutable.

Similar to what's happening within the plotting function, you can get around this by creating a deep copy of your object, like so:

import copy

fig, ax = plt.subplots()
ef_max_sharpe = copy.deepcopy(ef)
plotting.plot_efficient_frontier(ef, ax=ax, show_assets=False)

# Find the tangency portfolio
ef_max_sharpe.max_sharpe()
ret_tangent, std_tangent, _ = ef_max_sharpe.portfolio_performance()
ax.scatter(std_tangent, ret_tangent, marker="*", s=100, c="r", label="Max Sharpe")
enginance commented 2 years ago

Thanks @phschiele for the prompt reply. I've used the code above and it works, but I have doubts on the max_sharpe() function as what it is printed does not look correct. Here below the picture. Is it because I should plot the ef_max_sharpe instead of "ef" ?

image

phschiele commented 2 years ago

@enginance You're right, I forgot to rename a variable. I've updated the code in my comment above, can you try again?

enginance commented 2 years ago

@phschiele many thanks for your help! Much appreciated! It works perfectly :)