モメンタム戦略の落とし穴:気づくと全部同じセクターになる問題


モメンタム戦略はシンプルで強力です。「上がっている銘柄の上位10個を買う」——これだけで機能することが多くの研究で示されています。

しかし実際に運用すると、ある日ポートフォリオを見て気づきます。10銘柄すべてが同じ業種になっている、と。

これが モメンタム・クラウディング(集中) です。私自身、ペーパートレードで正面から食らったので、その実例と対策を共有します。

⚠️ 本記事は教育目的の情報共有です。投資は自己責任で行ってください。

目次

  1. 何が起きるのか
  2. 実例:10銘柄中9銘柄が同じ業種
  3. なぜ起きるのか(構造的な理由)
  4. 「分散しているつもり」を数字で暴く
  5. 対策1:セクター上限
  6. 対策2:相関ペナルティ
  7. トレードオフを正直に
  8. まとめ

1. 何が起きるのか

モメンタム戦略は「過去N ヶ月のリターン上位」を機械的に選びます。ここで見落としがちなのは、株価は業種単位でまとまって動くという事実です。

ある業種にブームが来ると、その業種の銘柄が揃ってランキング上位を占めます。結果、**「10銘柄に分散」したつもりが「1業種への集中投資」**になります。

2. 実例:10銘柄中9銘柄が同じ業種

私のペーパー運用(TSMOM・上位10銘柄・月次リバランス)で実際に起きたことです。

AI・データセンター需要の高まりで半導体関連が軒並み上昇 → モメンタム上位を独占 → 選定された10銘柄のうち9銘柄が半導体・ストレージ関連になりました。

そして調整局面が来たとき何が起きたか:

結果
市場全体(S&P500)ほぼ横ばい〜微増
私のポートフォリオ-15%超

保有10銘柄のうち9銘柄が同時に下落しました。分散の効果がゼロだったということです。相場全体が悪かったのではなく、自分のポートフォリオだけが1つの賭けになっていたのが原因でした。

💡 これは戦略の欠陥ではなく、設計時に対策していなかった私の落ち度です。モメンタムの性質として事前に分かっていたはずのことでした。

3. なぜ起きるのか(構造的な理由)

  1. 業種内の相関が高い:同業種の銘柄は同じニュース・同じ需要動向で動く
  2. モメンタムはトレンドを追う:業種トレンドが最も強く出ている場所を必ず選ぶ
  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) までどうぞ。

⚠️ 免責:本記事は教育目的の情報共有であり、投資助言ではありません。実際の投資判断はご自身の責任で行ってください。