Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Factor Analysis - Momentum Rank

Was wondering if anyone could explain this rank and the intuition behind it:
(can be found in lecture 38)

class MyFactor(CustomFactor):
""" Momentum factor """
inputs = [USEquityPricing.close,
Returns(window_length=126)]
window_length = 252

    def compute(self, today, assets, out, prices, returns):  
        out[:] = ((prices[-21] - prices[-252])/prices[-252] -  
                  (prices[-1] - prices[-21])/prices[-21]) / np.nanstd(returns, axis=0)

thank you!

8 responses

The classical momentum would be one year percentage price change:

(prices[-1] - prices[-252])/prices[-252] 

but then they discovered that the factor would be more predictive if the last month price change was removed from the computation:

(prices[-21] - prices[-252])/prices[-252]

They also discovered that the stocks are actually mean reverting in the last month, so they subtracted the last month price change from the annual momentum:

(prices[-21] - prices[-252])/prices[-252] -  (prices[-1] - prices[-21])/prices[-21]

They also found out that stocks that have lower volatility perform better than stocks with high volatility:

((prices[-21] - prices[-252])/prices[-252] -   (prices[-1] - prices[-21])/prices[-21]) / np.nanstd(returns, axis=0)

EDIT: I don't really know if this is the right explanation but this is how I read the factor

How is this different from simply using Returns?

Returns(window_length=252) - Returns(window_length=21)

How is using Prices different from using Returns?

Using 'prices' allows the returns to exclude the last month (prices[-21]). However, just using returns INCLUDES the last month.

Returns(window_length=252) - Returns(window_length=21)  
# is the same as  
((prices[-1] - prices[-252]) / prices[-252] ) -  ((prices[-1] - prices[-21]) / prices[-21])

However, the following cannot be done using the 'Returns' method alone.

((prices[-21] - prices[-252]) / prices[-252] ) -  ((prices[-1] - prices[-21]) / prices[-21])

Gotcha, thanks Dan!

I have been working hard to understand how to combine factors together over the last few weeks by using AdaBoost/decisionTree's and other Ensemble techniques. It would seem to me that this particular factor is not suitable for these kind of Ensemble machine learning combinations as this is really 3 factors packaged into 1 (-12 to -1 month slope (%), last month slope(%) and standard deviation of returns).

The whole aim of using these Ensemble techniques is to boost the overall signal by getting one extra dimension of NEW information. This would imply that each factor used in a custom factor in an Ensemble system should be stripped to its simplest form possible. In this case, we probably have 3 factors right from the get go...

It is counter intuitive that according to alphalens the -12m-1m slope has been a relatively poor predictor of returns over the last 2 years, whereas 1 month mean reversion and low vol have been pretty good predictors of returns. I know that tons of academic papers have been written over the 12-1 month slope factor more than 10yrs ago, and one wonders whether this factor doesn't work anymore just because everyone has incorporated it in their models. Looking at a longer timeframe, it appears that the first quantile out of 5 (the worse performers of the last year), tend to be the worse performers of the next few days. Beyond that, there's not much predictive value in other quantiles.

Note that the Q risk model has separate momentum and short-term reversal factors (see https://www.quantopian.com/papers/risk). On average, my understanding is that Q would like to see algos avoid these factors altogether (transient exposure is o.k., but consistent exposure, even within the constraint limits is frowned upon).

Can someone pls explain why the window_length for returns is 126 (I would've expected 252), and why np.nanstd(returns, axis=0) is not np.nanstd(returns[:-21], axis=0)?

I believe the window_length for returns should be 2, not 126, as we are calculating the volatility of daily returns.