Strategy Library

Short-Term Reversal Strategy in Stocks


This strategy is called Short-Term Reversal Strategy which is discussed in detail in the paper written by Wilma de Groot, Joop Huij and Weili Zhou (2011) titled "Another look at trading costs and short-term reversal profits". The standard reversal strategy takes the whole universe of stocks into consideration, while this paper limits the stock universe only to large cap stocks so that trading costs could be significantly reduced.

One simple version of this strategy could be described like this: The investment universe consists of 100 biggest companies by market capitalization. We go long on the 10% stocks which have the lowest performances in the last month while going short on the 10% stocks with the highest ones. The portfolio is rebalanced weekly. In the paper, however, strategies with different investment universes and different rebalancing frequencies are all backtested. The results show that, the larger the size of the investment universe, the larger the trading costs caused by extensively trading in small cap stocks which are less liquid; and trading costs become substantially lower when the rebalancing frequency is decreased from daily to weekly, but so do gross returns. In this tutorial, we only use 100 stocks with weekly rebalancing for illustration.


The strategy code mainly consists of three parts: Initialization, Warm Up, and Weekly Rebalancing.

Step 1: Initialization

In the Initialize function, we set up look-back period, beginning cash balance, the size of the investment universe, the number of traded stocks, etc. We use self._numOfWeeks to count?the number of weeks that have passed since the start date, and self._LastDay to indicate whether it is a new week. self._ifWarmUp is true when the self._numOfWeeks is 3, which means as long as next week's data come, we can make our investment decisions.??self._stocks is a list containing all the symbols of the 100 stocks that are taken into consideration. self._values is a dictionary with keys the stock symbols and values the lists containing the prices of stock each week since 4 weeks ago.

    def Initialize(self):
        self.SetStartDate(2005, 1, 1)
        self.SetEndDate(2017, 5, 10)
        self.UniverseSettings.Resolution = Resolution.Daily
        self._numberOfSymbols = 100
        self._numberOfTradings = int(0.1 * self._numberOfSymbols)
        self._numOfWeeks = 0
        self._LastDay = -1
        self._ifWarmUp = False
        self._stocks = []
        self._values = {}

Also, we need to use?CoarseSelectionFunction to select 100 qualified stocks from the total stock universe. Here, we sort the total stock universe by each stock's DollarVolume in decreasing order. Then, we select the first 100 stocks that have the largest DollarVolume among all the stocks in the universe.

def CoarseSelectionFunction(self, coarse):
     sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
     top100 = sortedByDollarVolume[:self._numberOfSymbols]
     return [i.Symbol for i in top100]

Step 2:Warm Up

Before we are able to make our investment decisions, we must have at least 4 weeks' data to calculate the performance, i.e. the monthly return, of each stock. Hence, we need a warm up period as long as 3 weeks to accumulate price series, so that once the fourth week's data come we can calculate?the return of the whole month.

self._stocks = []
self.uni_symbol = None
symbols = self.UniverseManager.Keys
for i in symbols:
        if str(i.Value) == "QC-UNIVERSE-COARSE-USA":
                self.uni_symbol = i
        for i in self.UniverseManager[self.uni_symbol].Members:
                self._values[i.Value.Symbol] = [self.Securities[i.Value.Symbol].Price]

We get all the symbols of qualified stocks from UniverseManager and keep them in self._stocks which is a list. Then we create for each key in the dictionary self._values a list where its first week's price is stored. And every time new data come, we append the new price to the end of the list of each stock.

for stock in self._stocks:

Step 3:Weekly Rebalancing

After the warm-up period, we calculate monthly returns every week and based on the returns, we make our investment decisions.

returns = {}
for stock in self._stocks:
        newPrice = self.Securities[stock].Price
        oldPrice = self._values[stock].pop(0)
        returns[stock] = newPrice/oldPrice

Every week when new data come, we use them along with the data four weeks ago to calculate the monthly returns. At the same time, we remove the oldest data from our lists. This step is essential to prevent memory size exceeding the limit.

newArr = [(v,k) for k,v in returns.items()]
for ret, stock in newArr[self._numberOfTradings:-self._numberOfTradings]:
        self.SetHoldings(stock, 0)
for ret, stock in newArr[0:self._numberOfTradings]:
        self.SetHoldings(stock, 0.5/self._numberOfTradings)
for ret, stock in newArr[-self._numberOfTradings:]:
        self.SetHoldings(stock, -0.5/self._numberOfTradings)

Finally, we sort the returns in increasing order. For the stocks whose monthly returns fall into the first 10% (performed badly in last month), we long them; For those fall into the last 10% (performed well in last month), we short them. Others (between 10% and 90%) will be set to 0.


In the paper, the look-back period is from 1990 to 2009. However, we want to test whether the strategy is still profitable in the new time period. Hence we use different look-back periods instead. If we begin from 2005 and end in 2017, there will be a total return of 131.50%. Although to some extent the performance of this strategy is dependent on different market situations,?nevertheless, in either situation mentioned above, this strategy could significantly beat the S&P 500 benchmark. Further research and backtesting could be done on different look-back periods, rebalancing frequencies, investment universes, numbers of traded stocks, etc.



  1. Groot, Wilma (2011). Another look at trading costs and short-term reversal profit, page 1,? Online Copy

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

Did you find this page helpful?

Contribute to the tutorials: