Introduction
January effect in stocks market says that stocks perform especially well in the first month of the year. This seasonal effect might lead us to think that stocks performed well in the last year's January will perform well in this year's January. In this tutorial, we're going to implement a strategy based on this yearly seasonal effect.
Method
The investment universe consists of all stocks from AMEX and NYSE. Firstly, in CoarseSelectionFunction, we remove stocks which do not have fundamental data.
def coarse_selection_function(self, coarse):
if self.monthly_rebalance:
coarse = [x for x in coarse if (x.has_fundamental_data)
and (x.market == "usa")]
return [i.symbol for i in coarse]
else:
return []
Next, we are going to filter the top 30% of stocks based on their market cap.
In implementation, the market cap is calculated with the BasicAverageShares
, BasicEPS
and PERatio
.
Then we request the history price 12 months ago and 11 months ago to calculate the January return of the last year.
def fine_selection_function(self, fine):
if self.monthly_rebalance:
fine = [i for i in fine if ((i.security_reference.exchange_id == "NYS") or (i.security_reference.exchange_id == "ASE"))]
self.filtered_fine = []
for i in fine:
history_start = self.history([i.symbol], TimeSpan.from_days(365))
history_end = self.history([i.symbol],TimeSpan.from_days(335))
if not history_start.empty and not history_end.empty:
i.returns = float(history_end.iloc[0]["close"] - history_start.iloc[0]["close"])
self.filtered_fine.append(i)
size = int(len(fine)*.3)
self.filtered_fine = sorted(self.filtered_fine, key = lambda x: x.market_cap, reverse=True)[:size]
self.filtered_fine = sorted(self.filtered_fine, key = lambda x: x.returns, reverse=True)
self.filtered_fine = [i.symbol for i in self.filtered_fine]
return self.filtered_fine
else:
return []
Every month, stocks are grouped into ten portfolios with the equal number of stocks in each portfolio according to their performance in January one year ago. Investors go long in stocks from the winner decile and shorts stocks from loser decile. The portfolio is equally weighted and rebalanced every month.
def on_data(self, data):
if not (self.monthly_rebalance): return
if not (self.filtered_fine): return
self.monthly_rebalance = False
portfolio_size = int(len(self.filtered_fine)/10)
short_stocks = self.filtered_fine[-portfolio_size:]
long_stocks = self.filtered_fine[:portfolio_size]
stocks_invested = [x.key for x in self.portfolio]
for i in stocks_invested:
#liquidate the stocks not in the filtered balance sheet accrual list
if i not in self.filtered_fine:
self.liquidate(i)
#long the stocks in the list
elif i in long_stocks:
self.set_holdings(i, 1/(portfolio_size*2))
#short the stocks in the list
elif i in short_stocks:
self.set_holdings(i,-1/(portfolio_size*2))
Derek Melchin
See the attached backtest for an updated version of the algorithm with the following changes:
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.
Jing Wu
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!