ふくふくHukuhuku Inc.
EP.24Data Fetch 9分公開: 2026-09-02

指数の構成銘柄を取る ── 「今の一覧」では過去を再現できない

現在の構成銘柄で過去にさかのぼって集計すると、途中で除外された銘柄が消えます。生き残った銘柄だけで計算した結果は、実際より良く出ます。

#株価指数#Python#落とし穴
執筆 / 監修
松尾 亮合同会社ふくふく 代表社員

データ基盤・データパイプライン構築 / BI / 生成 AI 活用支援を専門とするエンジニア (28 年)。 本記事は AI 利用ポリシーに基づき、生成 AI の補助で執筆 → 人間が監修・編集して公開しています。

プロフィール詳細
シェア

のような指数について調べるとき、構成銘柄の一覧が要ります。多くの提供元が配信しているのは現在の構成銘柄で、を叩けば今の中身は分かります。

問題が出るのは、この一覧をそのまま使って過去にさかのぼったときです。指数の構成銘柄は、定期的に見直されて入れ替わります。業績が悪化して除外された銘柄、上場廃止になった銘柄、新しく採用された銘柄。現在の一覧には、除外された銘柄が入っていません

現在の構成銘柄だけで過去10年の平均収益率を計算すると、10年間生き残った銘柄だけの成績になります。途中で脱落したものが除かれているので、実際の指数より良い数字が出ます。これは分析の誤りであって、データの誤りではありません。

時点ごとの構成が要る

この現象には名前が付いていて、生き残ったものだけを見てしまう偏りとして知られています。正しく扱うには、各時点での構成銘柄が必要です。2020年の分析には2020年時点の一覧、2015年には2015年時点の一覧。これを持っている提供元は多くありません。無料で手に入ることはさらに稀です。

手に入らない場合、選択肢は3つです。自分で取り続けて貯める入替の履歴から復元するその分析をしないと決める。3つ目は消極的に見えますが、正しくない結果を出すよりはよほど良い判断です。

構成銘柄の入手方法
方法得られるもの難点
現在の一覧を取る今の構成過去の再現ができない
毎月取って貯める貯め始めて以降の構成過去にさかのぼれない
入替の履歴から復元理論上は全期間履歴の入手が難しい
有償の提供元時点ごとの構成費用がかかる
貯め始めるなら今日から

過去はさかのぼれませんが、今日から貯め始めれば、来年には1年分の履歴になります。月に1回、構成銘柄の一覧を保存するだけです。量も小さいので、迷うなら始めておくほうが得です。

貯め方は差分ではなく全件で

構成銘柄の一覧は、毎回全件をそのまま保存してください。差分だけを記録すると、ある時点の構成を知るために全部の差分を順に適用する必要があり、途中で1つ欠けると以降が全部狂います。

銘柄を表す値はであることが多く、文字列として保存するのが安全です。件数は多くても数千件なので、毎月全件保存しても量はたかが知れています。EP.05 の考え方をそのまま当てはめて、取得した日付で分けて置けば十分です。

その時点の一覧をそのまま残し、必要な時点の構成を取り出す
Python
import jsonfrom datetime import datefrom pathlib import Path
BASE = Path("data/raw/index_members")

def save_members(index_name: str, members: list[str], as_of: date) -> Path:    """差分ではなく全件を残す。復元の手間がかからない。"""    d = BASE / index_name    d.mkdir(parents=True, exist_ok=True)    path = d / f"{as_of.isoformat()}.json"    path.write_text(json.dumps(sorted(members), ensure_ascii=False), encoding="utf-8")    return path

def members_as_of(index_name: str, target: date) -> list[str]:    """指定した日付以前で最も新しい一覧を返す。無ければ例外。"""    files = sorted((BASE / index_name).glob("*.json"))    usable = [p for p in files if date.fromisoformat(p.stem) <= target]    if not usable:        raise FileNotFoundError(f"{target} 以前の構成銘柄が保存されていない")    return json.loads(usable[-1].read_text(encoding="utf-8"))

上のコードは、指定した日付以前で最も新しい一覧を返します。その日ちょうどの一覧が無ければ例外にするという作り方もありますが、月1回しか取っていないなら現実的ではありません。どちらにせよ、無いときに黙って現在の一覧を返さないことが重要です。

重みも一緒に残す

構成銘柄の一覧だけでは足りない場合があります。指数は銘柄を等しく扱っているわけではなく、時価総額や株価に応じた重みが付いています。一覧だけを保存して、あとで自分の計算に使おうとすると、重みが無いことに気づきます。

重みが配信されているなら、一覧と一緒に残してください。配信されていない場合、時価総額から自分で計算することになりますが、指数側の算出規則と一致する保証はありません。上限を設けている指数もあり、単純な計算では再現できません。

保存の形としては、銘柄・重み・時点を1行ずつ並べた縦長が扱いやすいです。銘柄数が変わっても形が変わらないので、EP.12 で扱ったとも噛み合います。

銘柄と重みを縦長で残す。重みが無ければ空にして、後で区別できるように
Python
import pandas as pd

def normalize_members(raw: list[dict], as_of: str, index_name: str) -> pd.DataFrame:    """提供元の形を、銘柄 x 重み x 時点の縦長に揃える。"""    rows = []    for item in raw:        rows.append(            {                "index_name": index_name,                "as_of": as_of,                "code": str(item["code"]),          # 数値にしない。先頭ゼロが落ちる                "weight": item.get("weight"),        # 無ければ None のまま残す            }        )    df = pd.DataFrame(rows)    if df["code"].duplicated().any():        raise ValueError("同じ銘柄が複数行ある。取得の重複を疑う")    return df

指数そのものの値と、構成銘柄から自分で計算した値は一致しません。指数には算出方法の細かい規則があり、外から完全には再現できないためです。指数の値が欲しいなら指数を取る、というのが結局いちばん確実です。次回からは、公的なデータに話を移します。

構成銘柄は、地味なわりに後から取り返せない種類のデータです。価格は過去にさかのぼって取れますが、ある時点の構成は、その時点で保存しておかないと手に入りません。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp