robertmartin8 / PyPortfolioOpt

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

Error while plotting EF #373

Closed zdlightman closed 2 years ago

zdlightman commented 2 years ago

Describe the bug While trying to plot the Efficient Frontier, the following error occurs:

TypeError: can't pickle osqp.OSQP_results objects

Expected behavior Expected to get a plot of the effecient frontier.

Code sample

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

# Find the tangency portfolio
ef.max_sharpe()
ret_tangent, std_tangent, _ = ef.portfolio_performance()
ax.scatter(std_tangent, ret_tangent, marker="*", s=100, c="r", label="Max Sharpe")

# Generate random portfolios
n_samples = 10000
w = np.random.dirichlet(np.ones(len(mu)), n_samples)
rets = w.dot(mu)
stds = np.sqrt(np.diag(w @ S @ w.T))
sharpes = rets / stds
ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r")

# Output
ax.set_title("Efficient Frontier with random portfolios")
ax.legend()
plt.tight_layout()
plt.savefig("ef_scatter.png", dpi=200)
plt.show()

Operating system, python version, PyPortfolioOpt version Binder environment

Additional context i'm facing the same issue like Error while plotting EF #332, Binder link https://mybinder.org/v2/gh/robertmartin8/pyportfolioopt/6158b1e22819d174bae8885fc74eed772890bb55?urlpath=lab%2Ftree%2Fcookbook%2Fbondef.ipynb I don't know whether this link works.

zdlightman commented 2 years ago

an interesting issue is i ran these code a week ago, and got a plot of the effecient frontier that time. So i suspect some automatic update issue.

robertmartin8 commented 2 years ago

Hi @zdlightman,

This seems to be similar to #332, which was caused by running the optimiser before plotting. For technical reasons, this doesn't work (though we are currently working on a fix). Let me know if that works!

PS – when putting a code sample on github, if you enclose your code within triple backticks it formats it nicely

zdlightman commented 2 years ago

Thank you very much for answering, and the tips of formating. i'm really new to Github and major in finance - not CS. After removing the optimiser from this block, i successfully generated both the random portfolio and the ef. i was reading your user guide https://pyportfolioopt.readthedocs.io/en/latest/Plotting.html and i have noticed your reminder _To produce a plot of the efficient frontier, you should instantiate your EfficientFrontier object and add constraints like you normally would, but before calling an optimization function (e.g with ef.maxsharpe()), i used your original sample code:

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

# Find the tangency portfolio
ef.max_sharpe()
ret_tangent, std_tangent, _ = ef.portfolio_performance()
ax.scatter(std_tangent, ret_tangent, marker="*", s=100, c="r", label="Max Sharpe")

# Generate random portfolios
n_samples = 10000
w = np.random.dirichlet(np.ones(len(mu)), n_samples)
rets = w.dot(mu)
stds = np.sqrt(np.diag(w @ S @ w.T))
sharpes = rets / stds
ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r")

# Output
ax.set_title("Efficient Frontier with random portfolios")
ax.legend()
plt.tight_layout()
plt.savefig("ef_scatter.png", dpi=200)
plt.show()

if the whole

# Find the tangency portfolio
ef.max_sharpe()
ret_tangent, std_tangent, _ = ef.portfolio_performance()
ax.scatter(std_tangent, ret_tangent, marker="*", s=100, c="r", label="Max Sharpe")

won't work, which is running the optimiser, how can I get the same picture as you on the website?(i.e. with Max Sharpe portfolio) and I do recall that I got the whole picture with Max Sharpe portfolio one time last week, with the same code.

Anyway, thank you again for your kindly answering, the question above is not important. Pypfopt is really a great project.

robertmartin8 commented 2 years ago

@zdlightman the second part should be ok, e.g the below script (this is also in the binder notebook at the bottom):

ef = EfficientFrontier(mu, S)
ef.add_constraint(...)
plotting.plot_efficient_frontier(ef, ax=ax, show_assets=False)

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

Just so you know, the reason why there are these issues is that any time you call max_sharpe, efficient_risk, efficient_return, constraints automatically get added to the object. Which is why you can't keep calling the optimisation function in a loop, unless you either 1) reinstantiate the object in each loop 2) deep copy the original (un-optimised) object in each loop.

Hope this helps!