By Kevin Vecmanis, P.Eng
Portfolio optimization is a mathematically intensive process that can be accomplished with a variety of optimization functions that are freely available in Python.
In Part 1 of this series, we're going to accomplish the following:
The theory behind the capital market line and efficient frontier is outside the scope of this post, but plenty of material is available with a quick google search on the topic. Explanations of concepts will be provided throughout this post as required. I assume here that the reader has a basic familiarity with modern portfolio theory (MPT).
The first function we define pulls assets from Quandl based on a list of ticker names that we provide in the variable 'assets'
We're going to complete this post by optimizing portfolio weights for a basket of five assets.
Here we strip out only the adjusted closes of each asset by creating a mask.
By plotting the normalized adjusted closes we can see the relative performance of each asset. The ideal portfolio will benefit from assets that tend to covary in opposing ways.
We can calculate the average daily returns for each asset accordingly:
To get the average annualized returns we multiple by 252 trading days
We can generate the covariance matrix for these assets, a critical component of the optimization algorithm, using the following.
The annualized covariance matrix for these five assets...
The following single line of code generates a random array of weights that sum to one. In the portfolio, one of the assumptions that all funds will deployed to the assets in the portfolio according to some weighting.
From these weights, we can calculate the expected weighted return of the portfolio of assets using these random weights.
Calculate the portfolio variance by way of the following:
The previous lines of code generated the portfolio mean return and portfolio volatility for one set of randomly selected weights. In order to find an optimal solution, we need to repeat this process iteratively many thousands of times to determine what the optimal asset weights might be.
While we're at it, we might as wrap all of this up into a function.
Here we'll pass our list of assets to the Portfolio_Simulation function and have it randomly generate 3000 portfolios and plot them by their volatility and return.
The colorbar shows us the sharp ratio. Note that the sharp ratio calculation here assumes the risk-free rate is 0
Next, if we want to optimize based on the sharpe ratio we need to define a function that returns only the sharpe ratio. Since our optimization functions naturally seek to minimize, we can minimize one of two quantities: The negative of the sharpe ratio, (or 1/(1+Sharpe Ratio). Accordingly, if the sharpe ratio increases both of these quantities will decrease. We'll choose the negative of sharpe.
The next thing we need to do is define the constraints, bounds, and seed the initial search parameters of the optimization function.
From this result, our optimal portfolio results are identified by the dicionary key "x", so we can pull this directly using the following code.
Now, by calling our Portfolio_Statistics function we can get the performance using these weights.
So what have we done here? We've run the optimization function by maximizing the Sharpe Ratio (minimizing the negative of the Sharpe Ratio). Accordingly, the portfolio weights that are spit out will provide us with a portfolio optimized for Sharpe.
This tells us that a portfolio of 45.69 TLT, 15.07 GLD, and 39.24 QQQ will give us the best risk adjusted returns.
We can pull out the individual performance parameters of this portfolio accordingly.
Building an Efficient Frontier:
The efficient frontier is defined as all the portfolios that maximize the return for a given level of volatility. There can only be one of these for each level of volatility, and when plotted forms a curve around the cluster of portfolio values.
What we do is we iterate through a series of target returns, and for each target return we find the portfolio with the minimal level of volatility. To do this, we'll need minimize volatility instead of the negative of the sharpe ratio.
In the code above we had the optimization algorithm optimize the portfolio such that it has the least amount of risk. The output shows the asset weighting required to minimize risk with this set of assets. We're going to plot both of these below along with our efficient frontier.
In the above chart we can see the efficient frontier denoted by 'x's'. The big red star is the portfolio optimized for Sharpe Ratio, and the Yellow star is the portfolio is optimized to minimize variance (risk).
Now what we need to do is calculate the capital market line. We can accomplish this by calculating the line that intercepts the efficient frontier tangentially.
In order to do this, we need to make a better approximation of the efficient frontier and then calculate its first derivative along the approximated curve.
Note that solving for the capital market line equation can be finicky and you may have to play with it to get it right. Ultimately you're looking for the capital market line to be tangential to the efficient fronter.
From experience, I find setting the first parameter equal to the risk free rate, the second paramter to half the max portfolio volatility, and the last parameter to half the max portfolio return seems to work.
Check to see the optimization function reduces all three equations to 0
Now we'll plot the capital market line, along with our spline approximation of the frontier along with all of the simulated portfolios.
Now we can arrive at the weights of the markowitz optimal portfolio by running the optimization function again using the output from func as our constraint.
By zipping together out asset list and our list of optimal weights we get a clear picture of how the optimal portfolio should be constructed.
In part two of this series we'll tie everything together into a unified class function that allows us to analyze a portfolio of any number of assets we choose.
I hope you enjoyed this post!