Back to Community
The Next Quantopian-Based Paper on Uncovering Momentum

This post features the next Quantopian-based research presented in the paper “Uncovering Momentum” ( that extends the previous “Momentum with Volatility Timing” study with a top-bottom analysis of the strategy. In summary, below are the four outcomes outlined in the Conclusion section:

  1. The heterogeneous momentum behavior across market states and deciles portfolios suggests that each case can be associated with different processes and hence requires independent evaluation. The winners performance during the bull market state represents the most unblemished case for explaining the momentum effect.
  2. Detaching the volatility scaling component from the time series momentum portfolio identifies the momentum decile as a basic common block across conventional, time-series, and dual momentum strategies.
  3. Combining the in- and out-of-sample momentum decile analysis highlights the difference in performance levels between the two intervals and prompts a more asymmetrical version of the momentum definition like "achievement award for lucky winners". The small price of this award does not represent a challenge for the Efficient Market Hypothesis and defines a criterion for assessing the underlying momentum theories and models.
  4. The paper proposes to extend the scope of cross-section return studies with the analysis of temporal patterns by applying time series clustering.

This notebook is related to Section IV focusing on time series clustering of the in- and out-of-sample dataset. The figure below shows the means and standard deviations of in/out-of-sample monthly stock time series clustered within the ranking period.

Loading notebook preview...
9 responses

Yulia, very nice paper. A lot of references in the paper made me fit the advances in momentum in somewhat of a 3d space and see the progression relative to each other. I have been working with momentum in one form or another over the years and especially the TSFM moskowitz 2012 implementation that I tried to recreate last year. Breezed through the paper yesterday and will go over it in great detail after the holidays. Very well done. Congrats.


Glad to hear you liked reading it. Yes, the paper aims to connect momentum studies across four topics
and hence there are quite a few citations. Best regards, Yulia


I’ve only read the abstract so far but I look forward to reading the paper and going through the notebook. Thank you for sharing your work here!

Yulia will be hosting a live webinar based on her "Uncovering Momentum" paper this Thursday, January 30th at 11 am EST. You'll be able to view the webinar and participate in the live chat at this link, or watch just the webinar right here:


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 Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian 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.

Just a reminder that our live webinar for Yulia's second Quantopian-based paper, "Uncovering Momentum", is tomorrow, Thursday, January 30th at 11 am EST! It can be watched at this link, or in the player embedded in the comment above.

Hows the performance look like under Q?

Thank you everyone for joining the webinar.

Leo, Optimal. As you can see from the attached notebook, the pipeline execution time for the QTradableStocksUS universe from 07/01/04 to 09/30/19 takes around 39 seconds.

Best regards, Yulia

Good morning,

Continuing on uncovering the momentum effect via Fundamentals, intermediate results highlight the on-going broken behavior of the value factor (e.g., documented here and here). While explanation of this phenomenon is a separate topic, this post shares the significant backtest performance of growth stocks that is even much better than the quality factors investigated in the post “New Strategy - Presenting the Quality Companies in an Uptrend Model”.

As the value factor gets hot, eager to see its behavior in the next bull market.

Best regards, Yulia

Clone Algorithm
Total Returns
Max Drawdown
Benchmark Returns
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# import the base algo class. Not entirely needed but a good practice
import quantopian.algorithm as algo

# import things need to run pipeline
from quantopian.pipeline import Pipeline

# import any built-in factors and filters being used
from quantopian.pipeline.filters import QTradableStocksUS
from quantopian.pipeline.factors import SimpleMovingAverage as SMA
from quantopian.pipeline.factors import CustomFactor, Returns

# import any needed datasets
from import USEquityPricing
from import Fundamentals as ms

# import optimize for trade execution
import quantopian.optimize as opt
import numpy as np 
import pandas as pd

# Set to False for the Smart Beta Factors challenge
mom_on = True

mom_t1 = 126
mom_t2 = 2

class Momentum(CustomFactor):    
    inputs = [USEquityPricing.close]  
    window_length = mom_t1
    def compute(self, today, assets, out, prices):  
        out[:] = (prices[-mom_t2] - prices[-mom_t1])/prices[-mom_t1] 

def initialize(context):
    # Set target number of securities to hold    
    if mom_on is True:
        context.target_stocks = 20
        context.target_stocks = 250
        context.new_stocks = 5

    # Normally a contest algo uses the default commission and slippage
    # This is unique and only required for this 'mini-contest'
    set_commission(commission.PerShare(cost=0.000, min_trade_cost=0))
    set_slippage(slippage.FixedSlippage(spread = 0.0))
    # Create and attach pipeline for fetching all data
    algo.attach_pipeline(make_pipeline(context), 'pipeline')    
    # Schedule functions
def make_pipeline(context):
    universe = QTradableStocksUS()
    if mom_on is True:
        mom = Momentum(mask = universe)
        w10 = mom.percentile_between(90, 100, mask=universe)
        pb =, mask=w10) 
        pb = ms.pb_ratio.latest 
    # Creating a pipe
    pipe = Pipeline(columns={'pb': pb,}, screen=universe)
    return pipe

def rebalance(context, data):

    # Get pipeline output and select stocks
    df = algo.pipeline_output('pipeline')
    if mom_on is True:
        stocks_to_hold = df[df.pb].index
        stocks_to_hold = select_top_stocks(context)
    # Define equally-weighted portfolio
    stock_weight = 1.0 / context.target_stocks
    stock_weights = pd.Series(index=stocks_to_hold, data=stock_weight)
    # Create a TargetWeights objective
    target_weights = opt.TargetWeights(stock_weights) 

    # Execute the order_optimal_portfolio method
        objective = target_weights,
        constraints = []

def select_top_stocks(context):
    # Get pipeline output
    df = algo.pipeline_output('pipeline')
    all_stocks = df['pb']
    current_holdings = list(context.portfolio.positions.keys())
    if len(current_holdings) == 0:
        top_stocks = all_stocks.nlargest(context.target_stocks)
        return top_stocks.index
    common_stocks = all_stocks[all_stocks.index.isin(current_holdings)]
    extra_stocks = all_stocks[~all_stocks.index.isin(current_holdings)]
    n_new_stocks = context.new_stocks
    n_core_stocks = context.target_stocks - n_new_stocks
    common_top_stocks = common_stocks.nlargest(n_core_stocks)
    extra_top_stocks = extra_stocks.nlargest(n_new_stocks)
    top_stocks = common_top_stocks.add(extra_top_stocks)
    return top_stocks.index
There was a runtime error.

… furthermore, the equally weighted portfolio with 250 growth stocks seems like an appropriate solution for the recent “Build Smart Beta Factors” challenge.

Loading notebook preview...