Single-stock mean reversion
Mean-reversion strategies work on the assumption that there is an underlying stable trend in the price of an asset and prices fluctuate randomly around this trend . Therefore, values deviating far from the trend will tend to reverse direction and revert back to the trend. That is, if the value is unusually high, we expect it to go back down and if it is unusually low, go back up.
How do we identify the underlying trend? That’s the essence of this strategy! Consider the dummy example below:

We calculate the **90-day Moving Average(**90d MA) of the stock price and treat that as the underlying stable trend. We also calculate the **30-day Moving Average(**30d MA) and can see that it zig-zags around the 90d trend. Now we can build the following strategy:
- When the value of 30d MA falls below 90d MA we expect it to revert back to the 90d line. That is, the current price is too low and likely to increase. Hence this is a signal to buy
- Similarly, if the value of 30d MA rises above 90d MA we expect it to fall back to the 90d line. Hence the current price is too high and is a signal to sell
Mean reversion in the context of a stock price implies that periods of the price being far below the mean are followed by periods of the price going up, and vice versa. We can take advantage of this by buying the stock to go long when the price is lower than expected, and selling to go short when the price is higher than expected. Below we plot the price of a stock - 'PG' along with the mean of the prices up to each day to see whether the price reverts to the mean.
%matplotlib inlineimport numpy as npimport matplotlibmatplotlib.use('TkAgg')import matplotlib.pyplot as pltimport pandas as pdimport auquanToolbox.dataloader as dl
# Load the prices data for a stockstart = '2013-06-01'end = '2016-12-31'm= 'PG'data = dl.load_data_nologs('nasdaq', [m], start, end)prices = data['ADJ CLOSE']
# Compute the cumulative moving average of the priceprices['mu'] = [prices[m][:i].mean() for i in range(len(prices))]# Plot the price and the moving averageplt.figure(figsize=(15,7))plt.plot(prices[m])plt.plot(prices['mu']);plt.show()
# Compute the z-scores for each day using the historical data up to that dayzscores = [(prices[m][i] - prices['mu'][i]) / np.std(prices[m][:i]) for i in range(len(prices))]
# Start with no money and no positionsmoney = 0count = 0for i in range(len(prices)): # Sell short if the z-score is > 1 if zscores[i] > 1: money += prices[m][i] count -= 1 # Buy long if the z-score is < 1 elif zscores[i] < -1: money -= prices[m][i] count += 1 # Clear positions if the z-score between -.5 and .5 elif abs(zscores[i]) < 0.5: money += count*prices[m][i] count = 0print money
The danger of applying mean reversion to a single stock is that it exposes us to the movement of the market and the success or failure of the individual company, among other factors. If there is a persistent trend affecting the price of the security, we will find ourselves consitently undervaluing (if the price is moving steadily upward) or overvaluing (if the price is falling) the asset. Below we discuss two strategies that mitigate this risk.