In a previous post, we saw how to use genetic algorithms to make implicit optimizations. We used that technique to construct a portfolio, but in a very simple manner: we were just limited to 5 stocks, and we were looking for a constant weight for each of them, assuming daily rebalancing. Today, we will focus on another, somehow related, problem: how to aggregate rankings using these genetic algorithms.
Reviewing the algorithm
Let us review, in big strokes, how the algorithm works:
Create initial population
Measure fitness of agents
Stop condition = false
While stop condition = false
Select best agents
Create new agents by crossover
Apply mutation to new agents
Measure fitness of agents
Check stop condition
Each agent of the population represents what we are trying to optimize. In our previous example, it represented the weight assigned to each of the five stocks. The fitness is the measure we are optimizing for. In the previous post, it was the Sharpe ratio.
The problem we are going to optimize today is that of ranking aggregation. Let us say we have several factors, which give us corresponding rankings of assets, which we know give us some alpha. We can aggregate them naively, just doing a simple average of the ranking positions in each of the rankings. Or we can do a weighted average.
To assign the weights (for the weighted average), we can start with random weights, shift them slowly, and take all the results and compare them. But that’s exactly what genetic algorithms do! If we have a clear measure we want to optimize for, this algorithm is perfectly suited for us.
For this example, we have used the return, volatility and Sharpe ratio in several windows (1, 3 and 6 months and 1 year). We have allowed our weights to oscillate between -1 and 1 to account for the fact that, while in some factors —like volatility— lower is probably better, in others —like Sharpe ratio— higher is probably better.
We will choose the best 10% of stocks according to our weighted-average final ranking. For simplicity’s sake, we will do a daily selection, and ignore transaction fees and other slippage.
We have used the components of the S&P 500® index as our investment universe, from January 1, 2010, to April 30, 2023. The cutoff date between our train and test periods is January 1, 2020. Having a test period is very important. We know that during our train period, the Sharpe ratio is going to improve. After all, it is what we are explicitly optimizing for! But we have to check if this is also the case outside that period, with data the algorithm hasn’t been trained on, so that it has been able to generalize.
We can see we have had some amount of success. The test period Sharpe ratio goes up, just like the train period does. It still lags behind overall, but some amount of overfitting is expected. Some questions are left for the reader:
- Can you implement this simple example?
- Can you think of other, more complicated, factors to aggregate?
- Can you think of a different way to aggregate factors, which is not a weighted average? Maybe acting on the raw factors instead of the ranking? Maybe something else?
Thank you for reading, and see you next time!