モメンタム戦略の落とし穴:気づくと全部同じセクターになる問題
モメンタム戦略はシンプルで強力です。「上がっている銘柄の上位10個を買う」——これだけで機能することが多くの研究で示されています。
しかし実際に運用すると、ある日ポートフォリオを見て気づきます。10銘柄すべてが同じ業種になっている、と。
これが モメンタム・クラウディング(集中) です。私自身、ペーパートレードで正面から食らったので、その実例と対策を共有します。
⚠️ 本記事は教育目的の情報共有です。投資は自己責任で行ってください。
目次
- 何が起きるのか
- 実例:10銘柄中9銘柄が同じ業種
- なぜ起きるのか(構造的な理由)
- 「分散しているつもり」を数字で暴く
- 対策1:セクター上限
- 対策2:相関ペナルティ
- トレードオフを正直に
- まとめ
1. 何が起きるのか
モメンタム戦略は「過去N ヶ月のリターン上位」を機械的に選びます。ここで見落としがちなのは、株価は業種単位でまとまって動くという事実です。
ある業種にブームが来ると、その業種の銘柄が揃ってランキング上位を占めます。結果、**「10銘柄に分散」したつもりが「1業種への集中投資」**になります。
2. 実例:10銘柄中9銘柄が同じ業種
私のペーパー運用(TSMOM・上位10銘柄・月次リバランス)で実際に起きたことです。
AI・データセンター需要の高まりで半導体関連が軒並み上昇 → モメンタム上位を独占 → 選定された10銘柄のうち9銘柄が半導体・ストレージ関連になりました。
そして調整局面が来たとき何が起きたか:
| 結果 | |
|---|---|
| 市場全体(S&P500) | ほぼ横ばい〜微増 |
| 私のポートフォリオ | -15%超 |
保有10銘柄のうち9銘柄が同時に下落しました。分散の効果がゼロだったということです。相場全体が悪かったのではなく、自分のポートフォリオだけが1つの賭けになっていたのが原因でした。
💡 これは戦略の欠陥ではなく、設計時に対策していなかった私の落ち度です。モメンタムの性質として事前に分かっていたはずのことでした。
3. なぜ起きるのか(構造的な理由)
- 業種内の相関が高い:同業種の銘柄は同じニュース・同じ需要動向で動く
- モメンタムはトレンドを追う:業種トレンドが最も強く出ている場所を必ず選ぶ
- ランキングは「相対」:市場の他が凪いでいるほど、勢いのある1業種が独占する
つまり クラウディングはモメンタム戦略の副作用ではなく、本質です。放置すれば必ず起きます。
4. 「分散しているつもり」を数字で暴く
保有銘柄が本当に分散しているかは、相関行列を見れば一発です。
import yfinance as yf
import numpy as np
holdings = ["NVDA", "AMD", "MU", "INTC", "WDC", "STX", "MRVL", "AAPL", "KO", "JNJ"]
px = yf.download(holdings, period="1y", progress=False)["Close"]
rets = px.pct_change().dropna()
corr = rets.corr()
# 平均相関(自分自身との相関=1を除く)
n = len(holdings)
avg_corr = (corr.values.sum() - n) / (n * n - n)
print(f"平均相関: {avg_corr:.2f}")
# 実効的な銘柄数(分散が効いている実質的な数)
eff_n = n / (1 + (n - 1) * avg_corr)
print(f"銘柄数: {n} → 実効的には {eff_n:.1f} 銘柄相当")
実効銘柄数は、相関を考慮したときに「実質何銘柄に分散できているか」を表す指標です。
- 平均相関 0.2 → 10銘柄が実効 3.6銘柄相当
- 平均相関 0.7 → 10銘柄が実効 1.3銘柄相当(=ほぼ1銘柄)
私のケースは後者に近い状態でした。この1行を毎月チェックするだけで、危険は事前に見えます。
5. 対策1:セクター上限
最も直接的で効果的な方法。「1セクターから最大N銘柄まで」と決めます。
def apply_sector_cap(ranked: list, sector_map: dict,
top_n: int = 10, max_per_sector: int = 3) -> list:
"""モメンタム順のリストから、セクター上限を守って選ぶ
ranked: [(symbol, momentum), ...] の降順リスト
sector_map: {symbol: sector}
"""
selected, counts = [], {}
for sym, mom in ranked:
sec = sector_map.get(sym, "unknown")
if counts.get(sec, 0) >= max_per_sector:
continue # そのセクターは満杯 → スキップして次点へ
selected.append(sym)
counts[sec] = counts.get(sec, 0) + 1
if len(selected) >= top_n:
break
return selected
セクター情報は yfinance からも取れます(銘柄数が多い場合はキャッシュ推奨)。
def get_sector(symbol: str) -> str:
try:
return yf.Ticker(symbol).info.get("sector", "unknown")
except Exception:
return "unknown"
上限は3〜4銘柄が一般的な目安です。厳しすぎるとモメンタムの強い部分を捨てることになります。
6. 対策2:相関ペナルティ
「すでに選んだ銘柄と相関が高いものは、順位を下げる」という方法。セクター分類に頼らないので、分類が曖昧な銘柄にも効きます。
def select_decorrelated(ranked: list, rets, top_n: int = 10,
max_corr: float = 0.8) -> list:
"""既選択銘柄と高相関なものを除外しながら選ぶ"""
selected = []
for sym, mom in ranked:
if sym not in rets.columns:
continue
if any(rets[sym].corr(rets[s]) > max_corr for s in selected):
continue # 既に似た値動きの銘柄を持っている
selected.append(sym)
if len(selected) >= top_n:
break
return selected
7. トレードオフを正直に
対策には必ず代償があります。
- セクター上限を入れると、最も勢いのある領域を取りこぼす
- ブームが本物だった場合、上限なしの方がリターンは高い
- つまり「上振れを削って、下振れも削る」トレードオフ
どちらが良いかはあなたが耐えられるドローダウン次第です。私の場合、-15%のドローダウンを実際に経験して「これは対策すべき」と判断しました。
⚠️ ここで重要なのは、痛い目に遭った直後に慌ててルールを変えないことです。検証中の戦略を感情で書き換えると、それは過剰最適化の入り口になります。私は「対策候補」として記録し、検証期間が終わる区切りで判断することにしました。
8. まとめ
- モメンタム戦略は構造的に同一セクターへ集中する(副作用ではなく本質)
- 「10銘柄に分散」は幻想になりうる。実効銘柄数を計算すれば実態が見える
- 対策はセクター上限(直接的)と相関ペナルティ(分類不要)
- 対策は上振れも削る。自分の耐えられるドローダウンから逆算して決める
- ルール変更は検証の区切りで。痛みの直後に変えない
分散は「銘柄数」ではなく「相関の低さ」で決まります。ポートフォリオを開いて、相関行列を1回計算してみてください。
関連記事 → ポジションサイジングとは? / レジームフィルタとは?
質問や感想は X (@QuantNobu) までどうぞ。
⚠️ 免責:本記事は教育目的の情報共有であり、投資助言ではありません。実際の投資判断はご自身の責任で行ってください。