How to Quickly Backtest a Portfolio with Python and Empyrical
This walkthrough will demonstrate how to use Intrinio’s API and the PyPI package Empyrical to backtest and analyze a portfolio’s performance quickly.
Portfolio backtesting allows investors to simulate and analyze the performance of the investment strategies they design without putting dollars at risk.
Typically investors will use portfolio backtesting after constructing a portfolio based on a multi-factor model or conditional-based screen.
In either case, backtesting allows investors to see how different portfolio weightings of a selected group of aspects affect the risk and returns of the portfolio.
For example, say, an investor’s factor-based model only invested in equities with ticker symbols that start with the letter’ A.’
This investor could blindly test this theory by buying every ticker, starting with the letter’ A,’ waiting a year, and seeing how the portfolio turns out.
On the contrary, this investor could also backtest his theory by going back a year from today, ‘buying’ every ticker starting with the letter ‘A,’ and then use historical pricing data to see how it would perform — all without risking a dollar of their own money.
Furthermore, they could repeat this process repeatedly to see how this strategy would hold up and perform over varying market conditions.
While this is a relatively simple example, this is what investors building quantitative investment strategies do — they start with a theory and see how it holds up in application, rinse, wash, and repeat.
Therefore, using Python and Intrinio’s Historical prices, we will walk through exactly how to backtest a portfolio to test our theories quickly as well.
We will calculate and discuss the following portfolio statistics throughout this walkthrough:
- Max Drawdown
- Compound Annual Growth Rate
- Sharpe Ratio
- Sortino Ratio
Note: While the above list does not cover every statistic found throughout portfolio analysis, they provide a strong foundation for insight and analysis into your portfolio construction.
A portfolio’s Max Drawdown is the percentage decrease in portfolio value, calculated from its peak to its lowest trough. This drawdown period continues until a new peak (higher than the previous peak) has occurred, as demonstrated in the following example:
If a portfolio starts at $10,000, increases to a peak of $20,000, and decreases to $5,000, the portfolio’s max drawdown is -75%. Even if the portfolio increases to $10,000 in value, the maximum drawdown established for the current portfolio period will remain -75%. Only when the portfolio grows past $20,000 will the max drawdown reset.
Max drawdown is typically used by investors when they are comparing portfolio construction of multiple models. If the returns for each model are relatively similar, then the portfolio with the lowest drawdown would be preferable, particularly if the portfolio is being constructed by/for a risk-averse investor.
Compound Annual Growth Rate
The Compound Annual Growth Rate (CAGR) of a portfolio is the compounding mean annual growth rate of returns. In essence, this statistic informs investors of the annual return rate required to provide them with their overall desired return amount.
To highlight how CAGR helps investors understand their portfolio’s performance, we will look at the following example:
Portfolio Value Year 0 is $10,000
Portfolio Value Year 1 is $20,000
Portfolio Value Year 2 is $22,000
Portfolio Value Year 3 is $34,000
If an investor were to just average the returns across all four years in the above example, they would end up with the following percentages: (100%, 10%, 54.55%), and the average return across all three periods would be 54.85%. However, if an investor were to compound $10,000 over three years at 54.85%, they would end up with $37,130.74, not $34,000.
This discrepancy occurs because taking the mean annual return does not distinguish how the gains or losses of a portfolio from year to year contribute to the overall ending portfolio amount.
To appreciate the fluctuations between these periods, CAGR smooths out the return rates across the entire period. Applying this methodology to our example above leaves us with an Annualized Growth Rate (or CAGR) of 33.89%. Ultimately, CAGR allows us to easily compare growth rates across a wide variety of portfolios with vastly different rates of returns.
The Sharpe Ratio — named after its creator William F. Sharpe — was constructed to help investors calculate risk-adjusted returns and better understand their portfolio’s ROI compared to the risks of their holdings.
The Sharpe Ratio of a portfolio indicates how much total volatility is associated with a portfolio’s return compared to the risk-free rate available to an investor.
Generally, a high Sharpe Ratio is preferable when analyzing a portfolio because it indicates the portfolio has a higher risk-adjusted performance than its peers.
Similar to the Sharpe Ratio, the Sortino ratio helps investors analyze the volatility of their holdings. The main difference between the Sharpe and Sortino Ratio is that the Sortino Ratio only considers the volatility associated with the negative portfolio returns. Put simply, the Sortino Ratio only looks at the downside volatility of a portfolio instead of the total volatility.
Some investors prefer this as they feel that a portfolio shouldn’t be punished for having positive volatility. On the contrary, other investors feel that the Sharpe Ratio is still preferable as it gives a more holistic view of the portfolio holdings volatility.
In the end, it is likely best to look at the Sharpe and Sortino ratios when analyzing a portfolio’s performance, and just like the Sharpe Ratio, a higher Sortino ratio is preferred to a lower one.
The Beta of a portfolio is a measurement of the portfolio’s volatility of returns relative to the entire market or a benchmark, such as the S&P 500.
Examining the Beta Coefficient Closer:
Beta = 1 (portfolio returns are exactly as volatile as the market)
0 < Beta < 1 (portfolio returns are less correlated than the market)
Beta > 1 (portfolio returns are less correlated than the market)
Beta < 0 (portfolio returns are negatively correlated than the market)
Beta = 0 (portfolio returns are uncorrelated to the market)
To highlight these values, if your portfolio had a beta of 1.5, your portfolio returns would increase 150% if the market increased by 100% over a given period.
Additionally, if you had a negative beta, such as -1 your returns would be perfectly inverse to that of your benchmark. Negative Betas are often associated with Inverse ETFs as they (in theory) should be a complete inverse of the benchmark they are tracking.
Alpha is typically viewed as the excess return achieved by your portfolio over an index or benchmark.
For example, if an investor’s portfolio returns 17% in a year, and the benchmark they were comparing to returned 13% in that same year, their Alpha, or excess performance, would be 4%.
The ‘Alpha’ term is by far the most popular of them all, as many participants in the market are always looking to outperform and increase the Alpha associated with their portfolios.
For example, when investors are looking at money managers, they typically look for a consecutive history of outperformance compared to a benchmark, as the higher the Alpha generated the faster an investor can compound their starting equity.
Backtest a Portfolio with Python
The following code will allow us to:
- Ingest Historical Prices
- Calculate Daily Portfolio Returns
- Calculate Portfolio Tearsheet with Empyrical & PrettyTable
Ingest Historical Prices
The _historic_prices function utilizes Intrinio’s Stock Prices by Security API to return a Pandas DataFrame containing a single column containing the stock’s adjusted close end-of-day price history for the desired starting and ending backtest periods. The column name is adjusted to reflect the name of the ticker to ensure we can assign portfolio weights later on correctly.
The @retry decorator ensures that data is returned regardless of users hitting their API limitations.
The _folio_historic_prices_dataset function iterates through a list of tickers and returns a Pandas DataFrame where each column merged represents the associated stock price history returned from the _historic_prices function laid out above.
Calculate Daily Portfolio Returns
The _daily_returns_dataset function then ingests our Portfolio Dataset of historic adjusted close stock prices and returns a DataFrame of the daily percent changes in each ticker’s stock price.
We then pass in this Daily Returns Dataset to our _folio_daily_returns_dataset function along with our Ticker Weights Dataset. This function allows us to then utilize the Pandas .dot() method and apply our weights across our daily percent changes, leaving us with a Pandas DataFrame containing a single column of the total portfolio’s daily percent change — accounting for the weights associated with each ticker.
Calculate Portfolio Tearsheet with Empyrical & PrettyTable
Now that we have our folio_daily_returns_dataset (and have performed the same steps to calculate our benchmark_daily_returns_dataset, which is needed to calculate our Alpha and Beta metrics, in this case, we are using the SPY to represent the S&P 500) we can pass these datasets to the Empyrical Python package to calculate our portfolio tearsheet seamlessly.
Passing our daily returns datasets into the Empyrical methodologies allows us to calculate our TearSheet metrics quickly.
Once all of our desired metrics are calculated, we use the PrettyTable Python package to construct, well, a pretty table that we will ultimately print out and analyze for each of the portfolios we wish to backtest.
Where to access clean market data
At Intrinio, we’re recognized for being one of the top data providers that specializes in high-quality data, immediate customer service, and modern tools for a variety of fintech platforms and businesses. Our dedicated team wants to help you find the best data package that fits your unique needs. Reach out to one of our data specialists today to find out more about our equities market data packages. And to learn more about all our financial services and data please visit intrinio.com.