Python: get all the possible weight combinations for a portfolio

I think this problem can be solved using either itertools or cartesian, but I am pretty new to Python and struggling to use them:

I have a portfolio of 5 stocks, where each stock can have a weight of -0.4, -0.2, 0, 0.2 or 0.4, with a weight summing up to 0. How to create a function that creates a list of all possible combinations of weights. for example [-0,4, 0,2, 0, 0,2, 0] ... etc.

Ideally, the function will work for n stocks, since in the end I want to do the same process for 50 stocks.

edit: To clarify, I search for all combinations of length n (in this case 5), adding up to 0. Values โ€‹โ€‹can be repeated : for example: [0.2, 0.2, -0.4, 0, 0], [0,4, 0, -0 , 2, -0,2, 0,4], [0,0,0,0,2,2, -0,2], [0, 0,4, -0,4, 0,2, -0,2 ] etc. 0,0,0,0,0] would be a possible combination. The fact that there are 5 possible weights and 5 reserves is a coincidence (which I should have avoided!). The same question could be with 5 possible weights and 3 reserves or 7 reserves. Thanks.

+6
source share
6 answers

Something like this, although it is not very effective.

from decimal import Decimal import itertools # possible optimization: use integers rather than Decimal weights = [Decimal("-0.4"), Decimal("-0.2"), Decimal(0), Decimal("0.2"), Decimal("0.4")] def possible_weightings(n = 5, target = 0): for all_bar_one in itertools.product(weights, repeat = n - 1): final = target - sum(all_bar_one) if final in weights: yield all_bar_one + (final,) 

I repeat from the comments, you cannot do this for n = 50 . The code gives the correct values, but there is no time in the Universe to sort through all possible scales.

This code is not brilliant. This does unnecessary work, considering cases where, for example, the sum of all but the first two is already greater than 0.8, and therefore there is no separate check of all the possibilities for the first of these two.

So, this makes n = 5 almost instantly, but there is some value of n , where this code becomes incredibly slow, and you can get a better code. You still won't get to 50. I'm too lazy to write this best code, but basically instead of all_bar_one you can make recursive calls to possible_weightings with successively lower n values โ€‹โ€‹and target value equal to the goal you gave, minus the amount you got . Then trim all the branches that you do not need to take, by saving in the early cases when the target too large (positive or negative) to be achieved using only the values โ€‹โ€‹of n .

+3
source

I understand that the values โ€‹โ€‹can be repeated, but all should be summed to zero, so the solution could be:

 >>> from itertools import permutations >>> weights = [-0.4, -0.2, 0, 0.2, 0.4] >>> result = (com for com in permutations(weights) if sum(com)==0) >>> for i in result: print(i) 

edit: you can use product as @Steve Jassop suggested.

 combi = (i for i in itertools.product(weights, repeat= len(weights)) if not sum(i)) for c in combi: print(c) 
+1
source

I like to use the filter function:

 from itertools import permutations w = [-0.4, -0.2, 0, 0.2, 0.4] def foo(w): perms = list(permutations(w)) sum0 = filter(lambda x: sum(x)==0, perms) return sum0 print foo(w) 
0
source

Different approach.

1 Find out all sequences of weights that are added to zero in order.

for example, these are some possibilities (using integers to input less):
[0, 0, 0, 0, 0]
[-4, 0, 0, +2, +2]
[-4, 0, 0, 0, +4]

[- 4, +4, 0, 0, 0] is incorrect because the weights are not selected in order.

2 Transfer what you got above, because permutations will also be added to zero.

Here you will get your [-4, 0, 0, 0, +4] and [-4, +4, 0, 0, 0]

OK, lazy. I am going to use pseudocode / comment code to solve my problem. Not so much with recursion, the material is too complicated for fast code, and I doubt that this type of solution scales to 50.

i.e. I do not think that I am right, but it can give someone else an idea.

 def find_solution(weights, length, last_pick, target_sum): # returns a list of solutions, in growing order, of weights adding up to the target_sum # weights are the sequence of possible weights - IN ORDER, NO REPEATS # length is how many weights we are adding up # last_pick - the weight picked by the caller # target_sum is what we are aiming for, which will always be >=0 solutions = [] if length > 1: #since we are picking in order, having picked 0 "disqualifies" -4 and -2. if last_pick > weights[0]: weights = [w for w in weights if w >= last_pick] #all remaining weights are possible for weight in weights: child_target_sum = target_sum + weight #basic idea, we are picking in growing order #if we start out picking +2 in a [-4,-2,0,+2,+4] list in order, then we are constrained to finding -2 #with just 2 and 4 as choices. won't work. if child_target_sum <= 0: break child_solutions = find_solution(weights, length=length-1, last_pick=weight, target_sum=child_target_sum) [solutions.append([weight] + child ) for child in child_solutions if child_solution] else: #only 1 item to pick left, so it has be the target_sum if target_sum in weights: return [[target_sum]] return solutions weights = list(set(weights)) weights.sort() #those are not permutated yet solutions = find_solutions(weights, len(solution), -999999999, 0) permutated = [] for solution in solutions: permutated.extend(itertools.permutations(solution)) 
0
source

If you only need a list of all combinations, use itertools.combinations :

 w = [-0.4, -0.2, 0, 0.2, 0.4] l = len(w) if __name__ == '__main__': for i in xrange(1, l+1): for p in itertools.combinations(w, i): print p 

If you want to calculate the different weights that can be created using these combinations, this is a little more complicated.

First you create permutations with elements 1, 2, 3, .... Then you take their sum. Then you add the amount to the set (you wonโ€™t do anything if the number is already present, very fast operation). Finally, you convert to a list and sort it.

 from itertools import combinations def round_it(n, p): """rounds n, to have maximum p figures to the right of the comma""" return int((10**p)*n)/float(10**p) w = [-0.4, -0.2, 0, 0.2, 0.4] l = len(w) res = set() if __name__ == '__main__': for i in xrange(1, l+1): for p in combinations(w, i): res.add(round_it(sum(p), 10)) # rounding necessary to avoid artifacts print sorted(list(res)) 
-one
source

This is what you are looking for: if L = [-0.4, 0.2, 0, 0.2, 0]

 AllCombi = itertools.permutations(L) for each in AllCombi: print each 
-3
source

All Articles