CASSIA TRADING
INVESTING MASTERY COURSE
Idiosyncratic Momentum per Blitz, Huij, and Martens (2011)
Blitz, D. C., Huij, J., & Martens, M. (2011). Residual Momentum. Journal of Empirical Finance, 18(3), 506-521.
| Aspect | Total Momentum | Residual Momentum |
|---|---|---|
| Signal Source | All returns (α + β) | Only residuals (ε) |
| Factor Exposure | High | Neutral |
| Beta to Market | ~1.2 | ~0.1 |
| Sharpe Ratio | 0.45 | 0.85 |
| Max Drawdown | -22% | -8% |
| Turnover | Medium | Similar |
| Implementation | Simple | Complex |
Use Fama-French 3-factor or Carhart 4-factor model as baseline
For each stock, regress 36 months of returns against factor returns
OLS(stock_returns ~ market + SMB + HML)
Save regression residuals (ε) for each month—this is the idiosyncratic return
Sum residuals over past 12 months (skip most recent month to avoid reversal)
Re-run regressions and update rankings monthly with rolling windows
By removing factor exposures, we isolate pure firm-specific momentum—the part of returns driven by company fundamentals, earnings surprises, and investor attention, not broad market trends.
Step-by-step workflow with US markets example
Skip month t-1 to avoid short-term reversal
Residual vs Total Momentum vs Market (1990-2010)
Breaking down return sources
Residual momentum generates +9.2% alpha vs total momentum's +2.1%
Near-zero exposure to market (0.09), size (-0.02), value (0.01)
Volatility drops from 18.2% to 10.8%
Complete code example with pandas and statsmodels
import pandas as pd
import numpy as np
from statsmodels.api import OLS, add_constant
def calculate_residual_momentum(stock_returns, factors,
lookback=36, signal_period=11):
data = pd.concat([stock_returns, factors], axis=1).dropna()
residuals = []
for i in range(lookback, len(data)):
window = data.iloc[i-lookback:i]
X = add_constant(window[['MKT', 'SMB', 'HML']])
y = window['stock_return']
model = OLS(y, X).fit()
current_resid = data.iloc[i]['stock_return'] - model.predict(
add_constant(data.iloc[i][['MKT','SMB','HML']])
)[0]
residuals.append(current_resid)
if len(residuals) >= signal_period:
resid_momentum = np.sum(residuals[-signal_period:-1])
else:
resid_momentum = np.nan
return resid_momentum
print("Residual Momentum Ready")
Use Kenneth French data sources for factor construction.
Practical worksheet approach with formulas
=Intercept + (Beta_MKT*C37) + (Beta_SMB*D37) + (Beta_HML*E37)
=B37 - F37
=SUM(G37:G47)
=LINEST(B13:B48, C13:E48, TRUE, TRUE)
Long top 20 stocks by residual momentum (e.g., Apple, Microsoft, JPMorgan), short bottom 20 (laggards), rebalance monthly. Target 10-15% annual alpha.
You now understand how to construct factor-neutral momentum strategies that capture pure firm-specific alpha. Apply this to your US trading to identify genuine outperformers.