I would like to generate asset weights for more than two assets in a meshgrid fashion so that the sum is one. For example, I can use numpy's linespace with two assets, but not sure how to go about it with more than 2 assets:
import numpy as np
# get 3 weight configurations for 2 assets, that sum up to one
[np.array([w, 1-w]) for w in np.linspace(0, 1, 3)]
>[array([0., 1.]),
array([0.5, 0.5]),
array([1., 0.])]
# get 4 weight configurations for 2 assets, that sum up to one
[np.array([w, 1-w]) for w in np.linspace(0, 1, 4)]
>[array([0., 1.]),
array([0.33333333, 0.66666667]),
array([0.66666667, 0.33333333]),
array([1., 0.])]
# get 5 weight configurations for 2 assets, that sum up to one
[np.array([w, 1-w]) for w in np.linspace(0, 1, 5)]
> [array([0., 1.]),
array([0.25, 0.75]),
array([0.5, 0.5]),
array([0.75, 0.25]),
array([1., 0.])]
UPDATE: by meshgrid fashion I mean that the weights are not random but follow a mesh grid pattern, for example, for 3 assets these would be possible configurations. Of course, the granularity depends on the number of weight configurations requested in the num
argument to such function (equivalent to numpy#linspace):
[1, 0, 0]
[0, 1, 0]
[0, 0, 1]
[0.333333, 0.333333, 0.333333]
[0.333333, 0.666666, 0]
[0.666666, 0.333333, 0]
[0, 0.333333, 0.666666]
[0, 0.666666, 0.333333]
[0.333333, 0, 0.666666]
[0.666666, 0, 0.333333]
CodePudding user response:
Here is a solution that fits your examples.
Using integer partitioning!
from sympy.utilities.iterables import partitions
from more_itertools import distinct_permutations
# if you don't want to install more_itertools:
# set(itertools.permutations( ... ))
# is equivalent but less efficient than
# more_itertools.distinct_permutations( ... )
from collections import Counter # not really needed; just for .elements()
def weights(dim, grid):
for p in partitions(grid, m=dim):
p[0] = dim - sum(p.values())
yield from (distinct_permutations(x / grid for x in Counter(p).elements()))
print( list(weights(2, 2)) )
# [(0.0, 1.0), (1.0, 0.0), (0.5, 0.5)]
print( list(weights(2, 4)) )
# [(0.0, 1.0), (1.0, 0.0), (0.25, 0.75), (0.75, 0.25), (0.5, 0.5)]
print( list(weights(2, 3)) )
# [(0.0, 1.0), (1.0, 0.0), (0.333, 0.667), (0.667, 0.333)]
print( list(weights(3, 2)) )
# [(0.0, 0.0, 1.0), (0.0, 1.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.5, 0.5), (0.5, 0.0, 0.5), (0.5, 0.5, 0.0)]
for x,y,z in weights(3, 3):
print('{:0.2f},{:0.2f},{:0.2f}'.format(x,y,z))
# 0.00,0.00,1.00
# 0.00,1.00,0.00
# 1.00,0.00,0.00
# 0.00,0.33,0.67
# 0.00,0.67,0.33
# 0.33,0.00,0.67
# 0.33,0.67,0.00
# 0.67,0.00,0.33
# 0.67,0.33,0.00
# 0.33,0.33,0.33
Relevant documentation: