Equity indices exhibit mean reversion in daily returns, which means that if a significant price movement occurs during one day period, it is likely that the price will move in the opposite direction the following day. There are a couple of factors that might contribute to this phenomenon.
For example, a decline in stock price makes a stock more attractive to investors with everything else being equal because the stock becomes "cheaper." Consequently, increased demand for the stock might cause the stock price to mean-revert. Similarly, an upward movement of a stock price might cause investors to sell the stock as valuation metrics do not look as appealing as before.
Capturing the Mean-Reversion effect
There are a couple of methods that can be used to anticipate mean-reversion, one of which is using the indicator Internal-Bar Strength (IBS). The IBS indicator measures the relative position of the close price within the high-low price range.
IBS = (close-low)/(high-low)
It's possible to use IBS to determine which securities are likely to mean-revert in price the following day.
Constructing the Alpha Model
We apply this mean reversion logic described above to a set of global equity Exchange Traded Funds (ETFs). In the method Initialize() we select and construct our modules.
First, we define our security universe using the ManualUniverseSelectionModel():
## In Initialize()
# Global Equity ETF tickers
tickers = ["ECH","EEM","EFA","EPHE","EPP","EWA","EWC","EWG",
"EWH","EWI","EWJ","EWL","EWM","EWM","EWO","EWP",
"EWQ","EWS","EWT","EWU","EWY","EWZ","EZA","FXI",
"GXG","IDX","ILF","EWM","QQQ","RSX","SPY","THD"]
# Create symbol objects
symbols = [Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in tickers]
# Manually curated universe
self.UniverseSettings.Resolution = Resolution.Daily
self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))
Then, we create the Alpha model, MeanReversionIBSAlphaModel(), where we rank global equity ETFs on their IBS value and emit insights. The Alpha model contains two methods, __init__() and Update(): class MeanReversionIBSAlphaModel(AlphaModel):
'''Uses ranking of Internal Bar Strength (IBS) to create direction prediction for insights'''
def __init__():
# Set parameters
def Update():
# Rank ETFs on their IBS value and emit insights.
We define our parameters used in the algorithm in the method __init__(), such as the number of stocks to emit insights for, the lookback period and the prediction interval:def __init__(self):
# Set the lookback period
lookback = 1
# Set the prediction interval for insights
self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(Resolution.Daily), lookback)
# Set the number of stocks emitting insights
self.numberOfStocks = 2
In the method Update(), we implement the ranking mechanism and emit insights for the two ETFs exhibiting the highest and the lowest IBS value.
We calculate the IBS value for each ETF and store the indicator value in the symbolsIBS dictionary. Similarly, we calculate the intraday return for each ETF and store the value in the dictionary returns:
def Update(self, algorithm, data):
# Inititalize list to store insights
insights = []
# Inititalize dictionaries to store IBS and intraday return values
symbolsIBS = dict()
returns = dict()
for security in algorithm.ActiveSecurities.Values:
if security.HasData:
# Retrieve the High and Low price
high = security.High
low = security.Low
# Calculate the difference between High and Low price
hilo = high - low
# Do not consider symbol with zero open and avoid division by zero
if security.Open * hilo != 0:
# Store Internal Bar Strength (IBS) value in dictionary
symbolsIBS[security.Symbol] = (security.Close - low)/hilo
# Store intraday return value in dictionary
returns[security.Symbol] = security.Close/security.Open-1
We rank all ETFs on their IBS value and select two ETFs, the two ETFs that have the highest IBS values and the two that have the lowest IBS values:
## In Update()
# Rank securities with the highest IBS value
ordered = sorted(symbolsIBS.items(), key=lambda kv: (round(kv[1], 6), kv[0]), reverse=True)
highIBS = dict(ordered[0:number_of_stocks]) # Get highest IBS
lowIBS = dict(ordered[-number_of_stocks:]) # Get lowest IBS
Then, we emit insights for the four ETFs using intraday returns as a magnitude prediction:
## In Update()
# Emit "down" insight for the securities with the highest IBS value
for key,value in highIBS.items():
insights.append(Insight.Price(key, self.predictionInterval, InsightDirection.Down, abs(returns[key]), None))
# Emit "up" insight for the securities with the lowest IBS value
for key,value in lowIBS.items():
insights.append(Insight.Price(key, self.predictionInterval, InsightDirection.Up, abs(returns[key]), None))
return insights
Halldor Andersen
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!