In order to develop complex strategies for a successful asset allocation, portfolio managers need profound knowledge on the field. Apparently, this is the key to be able to consistently beat the market. In this post we will learn how to design investment strategy based in Modern Portfolio Theory in Python. Will we be able to outperform the market? Let’s find out!
Note that before diving into the content, for this post it is assumed a good understanding of the Markowitz model. In case someone feels not in that stage yet and needs to refresh some concepts, just go and check out my previous post: In the pursuit of the Perfect Portfolio.
To start with, it is actually interesting to make an example to understand how the calculation is done. In this post we use data from the main reference index in the US market, the S&P 500, which is generally easily available.
Calculating optimal portfolio
Our strategy will be based on the calculation of the optimal portfolio. To do so, we need to compute the expected return and the volatility (mean and standard deviation) from the historical prices of the companies in the S&P 500. Later, for a chosen optimization date, we need two sets of data;
- “Training set”: window of data previous to optimization date, used for the calculation of the optimal portfolio.
- “Testing set”: data right after the optimization date to analyze the performance of the computed portfolio.
However, before going straight into the portfolio calculation, we should take into account the S&P 500 constituents in that given date in order to avoid the survivorship bias. Otherwise, our strategy will most likely deceptively perform better due to the fact we are selecting among those who remain among the constituents of the index over time.
Once that we have the necessary data, the procedure of calculating the optimal portfolio works as follows:
- Find the asset with the highest expected return.
- Calculate the combination for the minimum variance portfolio (MVP).
- Construct the efficient frontier by minimizing risk for a given target return.
- Establish the risk-free rate. For simplicity, it is assumed to be zero.
- The optimal portfolio is the one with the maximum sharpe ratio.
For the sake of this small exercise, the chosen random date is 2015-03-03. The training data window will be 2 years (from 2013-03-02 to 2015-03-02) and the testing window 3 months ahead (from 2015-03-03 to 2015-06-03).
The result of the portfolio’s cumulative returns are shown in Figure 1. During these months, the performance of the optimal portfolio is slightly higher than the S&P 500.
Regarding the portfolio composition, as we can observe in Figure 2, out of 496 companies included in the optimization, a considerable amount of them have residual weight allocated. More precisely, only 3,63% of them have a weight greater than 1%.
The fact that our portfolio is constituted by 478 companies whose weight is smaller than 1% from a investment strategy perspective is not realistic nor efficient. Hence, we would like to see the change in the performance if we just include those companies with bigger allocation than a given threshold, for instance 1%.
The results shown in Figure 3 is illustrative, we can achieve a pretty accurate replication of the original optimal portfolio with just those 18 companies with weights above 1%. Note that the portfolio should be reweighted so that the weights still sum up to 1. The resulting portfolio is shown in Figure 4:
Rolling the strategy
The previous example was merely illustrative to understand the underlying process, it is not enough to see whether if the strategy is able to beat the benchmark. Hence, we have the necessary knowledge to implement this strategy as a rolling window for a longer period, 12 years. The results shown, have been obtained with quarterly rebalanced portfolio with efficient frontiers composed of 15 portfolios.
In Figure 5 we can see the cumulative returns of the strategy against the benchmark. Clearly our strategy is able to beat the S&P 500 in this regard. Besides, it is also interesting to complementary have a look to the yearly returns.The comparison among both is shown in Figure 6.
Therefore, we can observe that the strategy not only outperforms in cumulative returns but also during most of the years. In general, the returns are higher in magnitude for the strategy, but also when there are losses. In Figure 7, we have a more detailed view on the returns of the strategy.
As we can see in Figure 7, the highest and lowest returns where during the pandemic crisis of 2020. To end up with assessment of the strategy performance it is worth to compute some of the best known financial metrics:
- Sortino: is a modification of the Sharpe ratio that measures risk-adjusted return of an investment or portfolio using downside risk.
- Calmar: uses maximum drawdown as a measure of risk instead.
- Traynor: risk/return measure that allows investors to adjust a portfolio’s returns for systematic risk.
- Information: a measurement of portfolio returns beyond the returns of a benchmark.
In general, our strategy is more volatile and has greater potential loss in value if market conditions precipitate a decline. However, regarding the risk-adjusted metrics, the strategy outperforms the benchmark in all of them.
As just mentioned, our strategy is more volatile, which actually makes sense. This could be due to the fact that we imposed a threshold of 1%, so should be less diversified than the benchmark. To have more insight on this, it is interesting to look to the historical composition of the portfolio. In Figure 8 we can see the amount of holdings through time, which due to the imposed threshold, is bounded between 24 and 4 companies.
During this whole period, the company with higher frequency among the portfolio holdings is Netflix. Precisely, it has been selected 18 times out of 48 optimizations. Besides, to provide a more concrete view of the portfolio holdings, in Figure 9 we provide a detailed description from the last computed portfolio.
Wrapping up, we have been able to create a successful investment strategy based on the Modern Portfolio theory by only investing in risky assets contained on the S&P 500. The results obtained, although pretty satisfactory, are based in non frictional assumptions.
To improve the strategy, we could make a parametric sweep to test if for any number of efficient portfolios or rebalancing frequency would perform better. Besides, setting a fixed and arbitrary weight threshold is not very sensitive and could definitely be improved. Finally, since it has been implemented as daily rebalancing, to keep the weights stable until the next optimization date, it would be interesting to compare against the strategy where weights are allowed to evolve over time.
Do you have any other improvements in mind? In case the reader is interested in the actual implementation in Python, I will be working in making these resources available soon in my GitHub profile.
Stay tuned and thanks for reading!