I'm working on a portfolio optimisation problem including cardinality and max weighting constraints, using a genetic algorithm to generate solutions. I want to create arrays for the initial population that meet a cardinality constraint of 5 (5 non zero elements total) and each of those numbers is less than 0.25, and those 5 elements sum to 1.
population_size = 100
cardinality_constraint = 5
# 50 is the length of the vector of assets that we can allocate to
initial_pop = np.zeros((population_size, 50)))
for i in range(population_size):
initial_pop[i,:cardinality_constraint] = np.random.rand(cardinality_constraint)
initial_pop[i] = initial_pop[i] / np.sum(initial_pop[i])
np.random.shuffle(initial_pop[i])
This creates an array that satisfies the cardinality constraint, putting values into the first 5 elements, normalising them, and then shuffling them.
I've been trying to think of a way to incorpate the max weighting constraint. I think all I need to do is find a way to randomly generate 5 numbers that are each less then 0.25, and together sum to 1. I'm strugging with that though, because I can easily create 5 random numbers in that range, but if I then normalise some of the values might then exceed the maximum.
Any ideas would be most appreciated thanks!
FOLLOW UP:
I think I have found a solution that I've written up which uses the numpy dirichlet function.
So as the example I originally described, where I want to have a cardinality constraint of 5 and a maximum individual asset weight of 0.25, or 25% of the portfolio:
initial_population_list = []
while len(initial_population_list) < 100:
x = np.random.dirichlet(np.ones(5), size = 1)
if len(x[x > 0.25]) < 1:
initial_population_list.append(x)
Seems to me to work well! Thanks for the suggestion to look at that function.