Closed joostbos123 closed 3 years ago
Hi @joostbos123,
great question! Indeed, you should be able to plot the inefficient part of the frontier in most cases. If you are in the completely unconstrained case (i.e. not even a short-sale constraint), you can use the closed-form solution by Merton (1972) discussed in #300.
If you are in a constrained case, consider the following: You want to minimize the return for a given volatility. Minimizing the portfolio returns is equivalent to maximizing the negative returns (in fact, the return maximization is even implemented as minimization of negative expected returns).
Hacking a bit into plot_efficient_frontier()
to return the list of sigmas
and mus
, you can do something like:
# with constraints
ef = setup_efficient_frontier()
ef.add_constraint(lambda x: x <= 0.15)
ef.add_constraint(lambda x: x[0] == 0.05)
_, sigmas1, mus1 = plotting.plot_efficient_frontier(ef)
mean_return, sample_cov_matrix = setup_efficient_frontier(data_only=True)
negative_returns = - mean_return
inefficient_frontier = EfficientFrontier(negative_returns, sample_cov_matrix)
inefficient_frontier.add_constraint(lambda x: x <= 0.15)
inefficient_frontier.add_constraint(lambda x: x[0] == 0.05)
_, sigmas2, negative_mus2 = plotting.plot_efficient_frontier(inefficient_frontier)
mus2 = [-mu for mu in negative_mus2]
plt.figure()
plt.plot(list(reversed(sigmas1))+sigmas2, list(reversed(mus1))+mus2)
plt.show()
This yields the following plot, I'll leave it to you to make the formatting pretty :)
Notice that in the constrained case, the frontier is not reflection symmetric, which can be seen when "flipping up" the inefficient part of the frontier:
plt.figure()
flipped_up_mus = [(max(mus2) - mu) + max(mus2) for mu in mus2]
plt.plot(list(reversed(sigmas1))+sigmas2, list(reversed(mus1))+flipped_up_mus)
plt.show()
This approach should work for cases where you do not have further concave objectives in the return maximization objective.
Hope this helps!
Edit: @robertmartin8 The icon of PyPortfolioOpt even shows the inefficient part of the frontier! Perhaps one could add it as a (non-default) "vanity" option to the plotting API :sparkles: But since it does not serve any purpose from the portfolio optimization perspective itself, that's probably debatable.
Thanks a lot for your quick reply and the suggested workaround the get the entire plot in the constrained case!
However, when I use this method on a set of assets of which some assets have negative expected returns (mu) then I recieve the error _"targetreturn should be a positive float" when trying to plot the lower part. This is caused by negative values of the target_return input argument of efficient_return function in effcient_frontier.py.
Do you have any suggestions to overcome this problem as well?
Kind regards, Joost
@joostbos123 Yes, you simply need to comment out this assertion.
This is a nice workaround, but unless there is a very simple implementation, I think there are other features that need to be prioritised
Hi!
I'm trying to use the package to plot the efficient frontier for a set of assets. Just to make the plot a bit nicer, I would like to plot the entire efficient frontier including the lower part (where the returns are lower then the retrun of the minimum risk portfolio). In the example I only see a plot of the upper part of the frontier. Is there a way to get this entire frontier using the package?
Kind regards, Joost