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.

  1. def coarse_selection_function(self, coarse):
  2. if self.monthly_rebalance:
  3. coarse = [x for x in coarse if (x.has_fundamental_data)
  4. and (x.market == "usa")]
  5. return [i.symbol for i in coarse]
  6. else:
  7. 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.

  1. def fine_selection_function(self, fine):
  2. if self.monthly_rebalance:
  3. fine = [i for i in fine if ((i.security_reference.exchange_id == "NYS") or (i.security_reference.exchange_id == "ASE"))]
  4. self.filtered_fine = []
  5. for i in fine:
  6. history_start = self.history([i.symbol], TimeSpan.from_days(365))
  7. history_end = self.history([i.symbol],TimeSpan.from_days(335))
  8. if not history_start.empty and not history_end.empty:
  9. i.returns = float(history_end.iloc[0]["close"] - history_start.iloc[0]["close"])
  10. self.filtered_fine.append(i)
  11. size = int(len(fine)*.3)
  12. self.filtered_fine = sorted(self.filtered_fine, key = lambda x: x.market_cap, reverse=True)[:size]
  13. self.filtered_fine = sorted(self.filtered_fine, key = lambda x: x.returns, reverse=True)
  14. self.filtered_fine = [i.symbol for i in self.filtered_fine]
  15. return self.filtered_fine
  16. else:
  17. return []
+ Expand

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.

  1. def on_data(self, data):
  2. if not (self.monthly_rebalance): return
  3. if not (self.filtered_fine): return
  4. self.monthly_rebalance = False
  5. portfolio_size = int(len(self.filtered_fine)/10)
  6. short_stocks = self.filtered_fine[-portfolio_size:]
  7. long_stocks = self.filtered_fine[:portfolio_size]
  8. stocks_invested = [x.key for x in self.portfolio]
  9. for i in stocks_invested:
  10. #liquidate the stocks not in the filtered balance sheet accrual list
  11. if i not in self.filtered_fine:
  12. self.liquidate(i)
  13. #long the stocks in the list
  14. elif i in long_stocks:
  15. self.set_holdings(i, 1/(portfolio_size*2))
  16. #short the stocks in the list
  17. elif i in short_stocks:
  18. self.set_holdings(i,-1/(portfolio_size*2))
+ Expand


Reference

  1. Quantpedia - 12 Month Cycle in Cross-Section of Stocks Returns

Author

Jing Wu

September 2018