robertmartin8 / PyPortfolioOpt

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

sum(abs(weights[i])) == 1 #94

Closed dmicsa closed 4 years ago

dmicsa commented 4 years ago

I don't find a way to add a constrain that forces my total absolute weight to be 1. I've tried all range of lambda like this one, for example:

ef.add_constraint(lambda x : sum(abs(x)) == 1)

I want the total exposure to be 1 and to support negative weights too.

robertmartin8 commented 4 years ago

Hi @dmicsa,

Thanks for reaching out! Is this different from taking the weights and rescaling them as required?

ef = EfficientFrontier(mu, S, weight_bounds=(None, None))
ef.max_sharpe()

rescaled = ef.weights / np.abs(ef.weights).sum()
np.abs(rescaled).sum()  # = 1.000000

I've had trouble in the past adding explicit L1 norm constraints (not convex).

Best, Robert

dmicsa commented 4 years ago

It came to my mind to rescale them after but after what I saw something hugely hedged like this: Weights: {'tmf': 0.21, 'vnq': 0.07, 'qqq': 0.69, 'mdy': -0.68, 'vwo': -0.1, 'epp': -0.11, 'ilf': -0.24..} as the main holdings, I dont quite like 'qqq': 0.69, 'mdy': -0.68 part, I said better to ask. :)

btw your PyPortfolioOpt is an amazing library I tyvm for it!

Too bad is nearly impossible to install in Windows, I had to install Windows Subsystem for Linux to have it running and is darn clumsy to use it as a Windows user.

Your solution doesnt work quite so simple: ----> 4 rescaled = weights / np.abs(weights).sum() 5 np.abs(rescaled).sum() # = 1.000000 6 print('Weights:', rescaled)

TypeError: bad operand type for abs(): 'dict'

I had to write some ugly plumbing but is working.

def rescaleWeights(ws): s = 0 for key in ws: s += abs(ws[key])

print('s = ', s)

for key in ws:        
    ws[key] /= s
    ws[key] = int(ws[key] * 100) / 100    
robertmartin8 commented 4 years ago

Hi @dmicsa,

It came to my mind to rescale them after but after what I saw something hugely hedged like this

Unfortunately, this is an issue with mean-variance optimisation as a whole – it can lead to very unrealistic weights unless you put many constraints on the problem. If you want more stable portfolios, you may consider ef.min_volatility() with an L2 reg objective:

ef = EfficientFrontier(mu, S, weight_bounds=(None, None))
# experiment with different values of `gamma`
ef.add_objective(objective_functions.L2_reg, gamma=1)
ef.min_volatility()

# rescale
rescaled = ef.weights / np.abs(ef.weights).sum()
# to convert to a nice dict
weights = dict(zip(ef.tickers, rescaled))

Too bad is nearly impossible to install in Windows, I had to install Windows Subsystem for Linux to have it running and is darn clumsy to use it as a Windows user.

I wasn't aware about that! Do you have any more information about this? For example, does a simple pip install PyPortfolioOpt or poetry add PyPortfolioOpt not work?

Your solution doesnt work quite so simple:

In the snippet above I am using ef.weights, which is a numpy array, instead of weights, which is a dict, so my rescaling snippet should work fine.

Best, Robert

dmicsa commented 4 years ago

Thank you Robert for help.

I give it a try on some universes with: ef.add_objective(objective_functions.L2_reg, gamma=1)

and I always receive: OptimizationError: Please check your objectives/constraints or use a different solver.

Maybe I'm not that skilled to fix this.

I tried to install it on Windows again but I waisted over 10 hours trying around 1..2 months ago. I tried today too and again problems. Maybe I will investigate it when I will have some more time.

I received this error today:

  Running setup.py clean for scs
Failed to build scs
Installing collected packages: scs
    Running setup.py install for scs ... error

On Linux subsystem installs clean but it is annoying to don't have it natively on Windows.

I use it because many other user had the same problems installing it on Windows and they suggest this.

dmicsa commented 4 years ago

I managed to install it under windows it helped a lot:

conda install -c cvxgrp cvxpy

from: https://anaconda.org/cvxgrp/cvxpy

Now I have already on my system on Win10: pyportfolioopt-1.0.1-py3.7

So if I run: pip install PyPortfolioOpt will do nothing or will not update to the latest version. Is it possible to do it on windows like that and have the latest version?

Dan

dmicsa commented 4 years ago

TYVM for your time. I reinstall Conda and everything from scratch and managed to install it in windows after 10 hours of wrestling. :).

giusscr commented 8 months ago

Hi Robert, it has been a month since i'm stuck with the problem of setting the sum of absolute weights= 1, I think that I found a raw solution and I would like to give my contribution, I'm a beginner user of python and GitHub as I set this account up just for this matter.

Please let me know how can I contact you so we can discuss it together.

88d52bdba0366127fffca9dfa93895 commented 8 months ago

Hi @giusscr, you could post your suggestion here so anyone could discuss and review your work.

giusscr commented 8 months ago

Sure, I made a test that I think that it is harder to explain than seeing thats why I attached a colab file.

For a project I'm doing with university I need to create a long/short portfolio without leverege so that the abs sum of the weights is equal to 1.

After almost a month of trial and error ( I'm new to python) I tried to test to see what would happen if I made a simple optimization ( weights bounds [0,1]) by using as input:

1) the expected return all positive (even for the stocks i want to short)

2) a cov_matrix derived from a series of stock returns where i change the signs of the stocks i want to short( I call it in the file "stock_returns_inverted".

Obviously i would get all the weights positive, so i changed the sign of the weights that i wanted to short and then I used the base optimizer to see if i would get the same portfolio performance and volatility using as input this new set of weights, the original expected returns and original cov_matrix.

And indeed it works. I also made a comparison of performace with the rescaled solution and i get a better sharpe ratio.

In the file I used this set of stoks ["ADBE", "AMZN", "ENPH", "FTNT", "MCD", "PFE"] with this set of expectations er= [-0.15, 0.08, 0.30, 0.14, 0.12, -0.20].

https://colab.research.google.com/github/giusscr/test-long-short-portfolio/blob/main/test_to_send_github.ipynb

giusscr commented 8 months ago

@88d52bdba0366127fffca9dfa93895 @robertmartin8 what do you think? Is it financially sound or did I just got lucky with the numbers ?