Trend Following and Momentum Strategies › Cross-Sectional Momentum

Michael BrenndoerferDecember 28, 20258 min read
Quantitative FinanceAll chapters
Part 11 of 15Quantitative Trading Strategies
Chapter 61Trend Following and Momentum Strategies › Cross-Sectional Momentum
Reading section 3 of 9 · Cross-Sectional Momentum · 8 minSection 3 of 9 · 8 min

Book contents

Section 3 of 9 · 8 min read

Trend Following and Momentum Strategies › Cross-Sectional MomentumOne section · Sections in this chapter3 / 9 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

While time-series momentum compares an asset to its own history, cross-sectional momentum takes a fundamentally different approach: it ranks assets relative to their peers and takes long positions in recent winners while shorting recent losers. The distinction is subtle but important. Time-series momentum asks, "Has this asset been going up?" Cross-sectional momentum asks, "Has this asset been going up more than other similar assets?" This relative comparison exploits a different source of return persistence, one related to the dispersion in performance across assets rather than the absolute direction of any single asset.

This approach was documented in the seminal work by Jegadeesh and Titman (1993), who showed that buying stocks that performed well over the past 3 to 12 months and selling stocks that performed poorly generates significant excess returns. Their findings challenged the notion of market efficiency by demonstrating a simple, mechanical trading rule that produced consistent profits.

The Jegadeesh-Titman Framework

The classic cross-sectional momentum strategy involves three parameters that determine how the portfolio is constructed and managed:

  • Formation period (JJ): The lookback window for ranking stocks (typically 3, 6, or 12 months)
  • Holding period (KK): How long positions are held (typically 3, 6, or 12 months)
  • Skip period: Often 1 month is skipped between formation and holding to avoid short-term reversal effects

The formation period defines how we measure past performance, recognizing that momentum persistence varies across different time horizons. The holding period determines how long we maintain positions before rebalancing, balancing the tradeoff between capturing momentum profits and incurring transaction costs. The skip period addresses a well-documented empirical finding: stocks exhibit short-term reversal in the weeks immediately following extreme returns, so skipping a month between ranking and trading improves strategy performance.

At each rebalancing date, stocks are ranked by their formation period returns and sorted into decile portfolios. The momentum portfolio goes long the top decile (winners) and short the bottom decile (losers).

The momentum factor return captures the performance spread between recent winners and recent losers:

rMOM,t=rWinners,t−rLosers,tr_{\text{MOM},t} = r_{\text{Winners},t} - r_{\text{Losers},t}

where:

  • rMOM,tr_{\text{MOM},t}: return of the long-short momentum factor at time tt
  • rWinners,tr_{\text{Winners},t}: return of the top decile (winner) portfolio
  • rLosers,tr_{\text{Losers},t}: return of the bottom decile (loser) portfolio

This long-short construction creates a dollar-neutral portfolio with equal long and short exposure. The design isolates the performance spread, often interpreted as alpha, between winners and losers while hedging out the market's general direction, or beta. If the market rises 10%, both the winner and loser portfolios will likely rise, but the momentum return captures only the difference in how much each rose. This neutralization makes momentum returns relatively independent of market direction, though as we will see later, this independence breaks down during sharp market reversals.

Empirically, this spread has averaged 1% per month in US equities, though returns vary significantly over time and across markets. The magnitude and consistency of this premium attracted substantial academic attention and practical capital to momentum-based strategies.

Out[14]:
Visualization
Horizontal diverging bar chart ranking 20 synthetic stocks by past 12-month return, with teal winners, rose losers, and muted middle-ranked stocks.
Cross-sectional momentum ranks assets by past performance. The top five synthetic stocks form the winner basket, the bottom five form the loser basket, and the middle ten remain unselected.

Implementing Cross-Sectional Momentum

Let's implement a cross-sectional momentum strategy on a universe of liquid US stocks:

In[15]:
Code
# Download a universe of large-cap stocks
stock_universe = [
    "AAPL",
    "MSFT",
    "GOOGL",
    "AMZN",
    "META",
    "NVDA",
    "TSLA",
    "JPM",
    "V",
    "JNJ",
    "WMT",
    "PG",
    "MA",
    "HD",
    "DIS",
    "NFLX",
    "PYPL",
    "ADBE",
    "CRM",
    "INTC",
    "CSCO",
    "PFE",
    "ABT",
    "MRK",
    "KO",
]

end_date = datetime.now()
start_date = end_date - timedelta(days=365 * 6)

# stock_data = {}
# for ticker in stock_universe:
#     # ... download logic ...
#     pass
# stock_prices = pd.DataFrame(stock_data)

# Generate synthetic data for demonstration
np.random.seed(42)
dates = pd.date_range(
    end=pd.Timestamp.now().normalize(), periods=252 * 6, freq="B"
)
# Create correlated stock data
market_factor = np.random.normal(0.0005, 0.01, len(dates))
stock_data = {}
for ticker in stock_universe:
    noise = np.random.normal(0, 0.02, len(dates))
    ret = 0.5 * market_factor + 0.5 * noise
    stock_data[ticker] = 100 * (1 + ret).cumprod()
stock_prices = pd.DataFrame(stock_data, index=dates)
stock_returns = stock_prices.pct_change()
In[16]:
Code
# Cross-sectional momentum parameters
formation_period = 252  # 12-month lookback
skip_period = 21  # Skip 1 month
n_long = 5  # Number of stocks to go long
n_short = 5  # Number of stocks to go short

# Calculate momentum scores (12-month returns)
momentum_scores = stock_prices.pct_change(formation_period)

# Rebalance monthly
rebalance_dates = stock_returns.resample("BME").last().index

portfolio_returns = []
holdings_history = []

for i, date in enumerate(rebalance_dates[:-1]):
    # Get momentum scores as of skip_period days before rebalance
    try:
        score_date = stock_returns.index[
            stock_returns.index.get_loc(date) - skip_period
        ]
    except (KeyError, IndexError):
        continue

    scores = momentum_scores.loc[score_date].dropna()
    if len(scores) < n_long + n_short:
        continue

    # Rank and select winners/losers
    ranked = scores.sort_values(ascending=False)
    winners = ranked.head(n_long).index.tolist()
    losers = ranked.tail(n_short).index.tolist()

    # Get returns for holding period (next month)
    next_date = rebalance_dates[i + 1]
    period_returns = stock_returns.loc[date:next_date]

    if len(period_returns) > 1:
        # Equal-weight long winners, short losers
        long_ret = period_returns[winners].mean(axis=1).sum()
        short_ret = period_returns[losers].mean(axis=1).sum()
        mom_return = long_ret - short_ret

        portfolio_returns.append(
            {
                "date": next_date,
                "return": mom_return,
                "long_return": long_ret,
                "short_return": short_ret,
                "winners": winners,
                "losers": losers,
            }
        )

momentum_df = pd.DataFrame(portfolio_returns)
momentum_df.set_index("date", inplace=True)
In[17]:
Code
# Calculate cumulative returns for plotting
cum_long = (1 + momentum_df["long_return"]).cumprod()
cum_short = (1 + momentum_df["short_return"]).cumprod()
cum_total = (1 + momentum_df["return"]).cumprod()

# Calculate performance metrics
mom_metrics = calculate_metrics(momentum_df["return"], periods_per_year=12)
avg_monthly_return = momentum_df["return"].mean()
win_rate = (momentum_df["return"] > 0).mean()
Out[18]:
Visualization
Line chart showing the winner and loser portfolios rising above 1.4 while the long-short momentum wealth index fluctuates near one and ends below 0.9.
Cumulative wealth of the synthetic winner portfolio, loser portfolio, and long-short spread. Both long-only baskets rise late in the sample, while the long-short strategy ends below its starting value; this simulation therefore does not provide evidence of a robust momentum premium.
Out[19]:
Console
Cross-Sectional Momentum Strategy Performance
==================================================
Annualized Return: -2.7%
Annualized Volatility: 9.8%
Sharpe Ratio: -0.27
Maximum Drawdown: -21.5%

Average Monthly Return: -0.19%
Win Rate: 50.9%

The performance metrics quantify the outcome of this particular synthetic strategy run. Here, the long-short wealth index finishes below its starting value, so the example does not confirm a momentum premium. That result is useful: a historically documented anomaly does not guarantee success in every sample, and formation rules, turnover, and market regime all affect realized performance.

Key Parameters

The key parameters for the Cross-Sectional Momentum strategy are:

  • Formation Period (JJ): The lookback window for ranking stocks (e.g., 12 months).
  • Holding Period (KK): The duration positions are held (e.g., 1 month).
  • Skip Period: The lag between formation and holding periods (e.g., 1 month) to avoid short-term reversals.
  • Portfolio Size: The number or percentage of stocks in the long and short legs (e.g., top/bottom decile).

Historical Evidence and the Momentum Premium

The momentum anomaly has been documented extensively across markets, time periods, and asset classes:

  • Equity markets: Momentum profits have been found in the US (Jegadeesh and Titman, 1993), international developed markets (Rouwenhorst, 1998), and emerging markets (Rouwenhorst, 1999)
  • Asset classes: Momentum works in currencies (Menkhoff et al., 2012), commodities (Miffre and Rallis, 2007), and government bonds (Moskowitz et al., 2012)
  • Historical depth: Geczy and Samonov (2016) documented momentum profits in US equities back to 1801

The persistence of momentum across such diverse settings suggests it reflects fundamental aspects of how information gets incorporated into prices rather than data mining or statistical artifacts.

The Momentum Factor (UMD)

In factor models discussed in Part IV, momentum is captured by the UMD (Up Minus Down) factor, representing the return of recent winners minus recent losers. This factor has historically earned a premium comparable to the value and size factors, making it a core component of multi-factor equity strategies.