Strategy Library

CAPM Alpha Ranking Strategy on Dow 30 Companies

Introduction

This tutorial performs a simple linear regression to build the Capital Asset Pricing Model(CAPM), a classical model developed by William F. Sharpe and Harry Markowitz. This model yields alpha and beta for each asset and is traded by going long on the stocks ranked with the highest alpha. This tutorial will demonstrate the following:

  • How to use historical data
  • Set an event handler
  • Conduct linear regression
  • Build your own functions in the QuantConnect Algorithm Lab

The implementation of the strategy demonstrates that stocks beat the market last month are likely to beat the market again in the subsequent month. This algorithm performs well when the market is smooth. However when the market volatility increases the model fails to capture alpha and it performs poorly. What we learn from this is that market fluctuations decrease the significance level of the linear regression coefficients, especially when we are using daily returns to fit the model.

CAPM Theory

The capital asset pricing model (CAPM) describes the relationship between systematic risk and expected return for assets, typically stocks. The formula for calculating the expected return of an asset given its risk is as follows:

\[r_a = r_f + \beta_a*(r_m - r_f) + \epsilon \]

where:

\[r_f = Risk Free Rate\] \[\beta = Beta of the security\] \[r_m = Expected market return\] \[\epsilon = Tracking error\]

This formula can be better understood if we refactor the formula as seen below:

\[(r_a - r_f ) = \beta_a*(r_m - r_f) + \epsilon \]

The left side of the equation gives us the difference between the asset return and risk free rate, the "excess return". If we regress the market excess return against the asset excess return the slope represents the "beta" of the asset. Therefore, beta can also be calculated by the equation:

\[\beta = \frac{Cov(r_a,r_b)}{var(r_b)}\]

So beta can be described as:

\[\beta = \rho _a,_b*\frac{\sigma _a}{\sigma_b}\]

The formula above indicates that beta can be explained as "correlated relative volatility". To make this simpler, beta can be calculated by doing a simple linear regression which can be viewed as a factor to explain the return, and the tracking error can represent alpha. To make this theory more convenient for our algorithm, we change the above formula into the following form:

\[r_a = \beta*r_m + r_f*(1-\beta) + \epsilon\]

r*(1-β) on the right hand side of the equation is a very small item, making it negligible in the context of the Dow 30 companies. If we regress the stocks return with the return of the benchmark, the slope and intercept will be beta and alpha.

Method

Our investment logic is simple and straightforward. We assume that stocks which beat the market last month will continue to beat the market. We rank stocks according to their alpha, and each month we "long" the top two stocks. For this strategy to work, we need to do the following at the start of each month:

  • Get the historical price of Dow 30 stocks in the past 21 trading days and calculate their daily rates of return.
  • Conduct simple linear regression on the return of each stock against a benchmark (S&P 500 index, SPY).
  • Rank the stocks by their intercepts.
  • Liquidate all our positions and purchase the first 2 stocks in our sorted list.

Dow Jones components change very infrequently, with the last change being on March 19th, 2015. To make the implementation easier we have simply listed the current Dow components in this algorithm. This means that the earliest start date of this algorithm is March 19th, 2015.

Step 1: Setup Event Handler

In the initialize method we define a Scheduled Event to trigger a monthly re-balancing of the portfolio. For more details about how to use Scheduled Events, you can read the Documentation or see the example ScheduledEventsAlgorithm.

def Initialize(self):
    self.Schedule.On(self.DateRules.MonthStart(self.benchmark), self.TimeRules.AfterMarketOpen(self.benchmark), Action(self.rebalance))
	

Step 2: Linear Regression Function

In order to conduct linear regression, we need to write a function to take the price data and output the regression results. The function takes a list of the "asset prices" (x) and a list of the "benchmark prices" (y). It then calculates the percentage change and conducts a linear regression. The output is a tuple which contains the intercept and slope.

def regression(self,x,y):
    x = np.array(x)
    x = np.diff(x)/x[:-1]
    y = np.array(y)
    y = np.diff(y)/y[:-1]
    A = np.vstack([x, np.ones(len(x))]).T
    result = np.linalg.lstsq(A, y)[0]
    beta = result[0]
    alpha = result[1]
    return(alpha,beta)

Step 3: History Function

Each month we get the historical prices of the DOW30 components using the History API. The data is returned from the API as complex Slice objects. To make this useful in the algorithm we extract the asset prices, and benchmark prices to a list.

def get_regression_data(self,symbol,history):
    symbol_price = []
    benchmark_price = []
    for i in history:
        bar = i[symbol]
        benchmark = i[self.benchmark]
        symbol_price.append(bar.Close)
        benchmark_price.append(benchmark.Close)

    result = self.regression(symbol_price,benchmark_price)
    return result

Step 4: Rebalance Function:

This function is where all the action happens, it will be executed on the first trading day of each month as a scheduled event. The second argument of SetHoldings is a decimal, setting this to "1" tells the algorithm to set the portfolio as "long 100%" with no leverage. More information on the function can be read on this link: SetHoldings.

def rebalance(self):
    # get historical stock symbols and prices, then put them in tuples
    history = self.History(self.regression_dates, Resolution.Daily)
    filter = []
    for i in self.symbols:
        filter.append((i,self.get_regression_data(i, history)[0]))
    # sort the filter by alpha
    filter.sort(key = lambda x : x[1],reverse = True)
    sorted_symbols = []
    for i in range(2):
    	sorted_symbols.append(filter[i][0])
    # get the symbols of our holding stocks
    holding_list = []
    for i in self.Portfolio:
    	if i.Value.Invested:
    		holding_list.append(i.Value.Symbol)
    # if we have holdings and we are not going to hold them anymore, sell them
    if holding_list:
    	for i in holding_list:
    		if i not in sorted_symbols:
    			self.Liquidate(i)
    # Long the 2 stock in our list.
    for i in sorted_symbols:
    	self.SetHoldings(i,1)
		

Summary

We have demonstrated that during a smooth market, the stocks that beat the market last month are likely to beat the market again in the subsequent month. When there is market fluctuation, the significance level of linear regression will reduce and the model performance will decrease. We can understand this by looking at the covariance of the asset(x) and the benchmark (y). As the covariance reduces to zero, the beta will decrease.

\[\hat{\beta} = \frac{Cov[x,y]}{\sum (x_i - \beta{x})^2}\]

As an experiment, we tested the algorithm on market data from 2015. This was a much more volatile period for the market with a fluctuation that returned a mean close to zero and dropped neaerly 10% from Aug 18th to Aug 25th of that year. The algorithm performed quite poorly in this year with a return rate of -11.58%. The risks associated with this strategy include a high drawdown, lack of hedging and not stop-loss. Since we are using leverage, the risk is increased and it has a margin call in January as a result. We can improve the performance by applying the following techniques:

  • Conduct optimizations: we can implement mean-variance analysis to determine the asset allocation each month and select more stocks to trade. This will lower our risk and manage the portfolio more scientifically.
  • Take beta into consideration: If we want to be more aggressive, we can select targets by a combination of alpha and beta. This means we choose stocks with a high alpha that are more volatile than the market. If we are conservative investors however, we can make the strategy market-neutral, which means the portfolio would not be affected by the market performance. For example, if we long two stocks with beta 1 and -1 respectively at the same position size, our portfolio becomes market-neutral.

Algorithm

Backtest using OptionChainProvider

You can also see our Documentation and Videos. You can also get in touch with us via Chat.

Did you find this page Helpful ?