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))


Reference

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