ベータ値(β)って結局何?「自分の製造業株、日経にどれだけ振り回されてる?」を計算してみた

基礎知識・戦略

日経平均が2%下げた日、自分の持ち株は4%下げていました。「今日は相場が悪かったから仕方ない」で片づけていたのですが、ふと疑問が湧きました。本当に相場のせいなのか、それとも自分の銘柄選びが偏っていたのか。

これを切り分けるのがベータ値(β)です。「市場が1%動いたとき、この銘柄は何%動くか」を1つの数字で表したもので、自分のポートフォリオが指数にどれだけ振り回される構造になっているかが分かります。

この記事では、PythonでベータをTOPIXや日経平均に対して計算し、製造業株を中心に実際の数値を比較します。さらに、ベータを使ってポートフォリオ全体の市場感応度を管理する方法まで扱います。

※本記事のコードや情報は執筆時点の仕様に基づいています。投資は自己責任であり、必ずデモ環境や少額資金でテストした上で運用してください。

ベータは「市場への連動度」を測る

ベータの定義は次のとおりです。

β = 銘柄と市場の共分散 ÷ 市場の分散

式だけ見ると難しそうですが、やっていることは「市場のリターンを説明変数、銘柄のリターンを目的変数とした回帰直線の傾き」です。散布図を描いて直線を当てはめたときの、その傾きがベータになります。

ベータ意味市場が−2%のとき典型的な業種
0未満市場と逆に動くプラスになることも金鉱株など(まれ)
0〜0.5ほとんど連動しない−1%未満ディフェンシブ株
0.5〜1.0市場より穏やか−1%〜−2%通信・食品・電力
1.0市場と同じ−2%指数そのもの
1.0以上市場より激しい−2%以上半導体・証券・不動産

冒頭の「日経が2%下げて自分は4%下げた」というのは、ベータ2.0相当の値動きということになります。これが1日だけの偶然なのか、常にそうなのか——それを確かめるのがこの記事の目的です。

ベータを知っておくと何が変わるのか、先にまとめておきます。

  • 損失の原因を切り分けられる: 相場全体のせいか、銘柄選びのせいか
  • 下落幅を事前に見積もれる: 「日経が5%下げたら自分は何円減るか」が計算できる
  • 現金比率の根拠になる: 目標ベータから逆算して、いくら現金化すべきかが決まる

特に1つめが大きいと感じています。相場が下げた日に落ち込むのは仕方ないとして、それが「避けられなかった下落」なのか「自分で選んだリスクの結果」なのかを区別できると、次に打つ手が変わります。

📘 外部参考Beta(Investopedia)資本資産価格モデル(Wikipedia)

Pythonで計算する(2つの方法)

pip install yfinance pandas numpy statsmodels matplotlib

まずは定義どおり、共分散と分散から求める方法です。

import yfinance as yf
import numpy as np
import pandas as pd

MARKET = "^N225"      # 日経平均。TOPIXなら "1306.T" などで代用する
TICKER = "7203.T"     # トヨタ自動車

data = yf.download([TICKER, MARKET], period="3y", auto_adjust=True,
                   progress=False)["Close"].dropna()
rets = data.pct_change().dropna()

cov = float(rets[TICKER].cov(rets[MARKET]))
var = float(rets[MARKET].var())
beta = cov / var

print(f"共分散 : {cov:.8f}")
print(f"市場分散: {var:.8f}")
print(f"ベータ : {beta:.3f}")

もう1つは回帰分析を使う方法です。ベータだけでなく、決定係数やアルファも一緒に得られるので、実務ではこちらをおすすめします。

import statsmodels.api as sm


def calc_beta(ticker, market="^N225", period="3y"):
    """回帰分析でベータ・アルファ・決定係数をまとめて求める"""
    data = yf.download([ticker, market], period=period,
                       auto_adjust=True, progress=False)["Close"].dropna()
    rets = data.pct_change().dropna()

    X = sm.add_constant(rets[market])
    model = sm.OLS(rets[ticker], X).fit()

    return {
        "beta": float(model.params.iloc[1]),
        "alpha_annual": float(model.params.iloc[0]) * 252,  # 年率換算
        "r_squared": float(model.rsquared),
        "p_value": float(model.pvalues.iloc[1]),
        "n": len(rets),
    }


r = calc_beta("7203.T")
print(f"ベータ        : {r['beta']:.3f}")
print(f"アルファ(年率): {r['alpha_annual'] * 100:>6.2f} %")
print(f"決定係数 R²   : {r['r_squared']:.3f}")
print(f"p値           : {r['p_value']:.2e}")

決定係数(R²)を必ず一緒に見る

ここが最も重要な注意点です。ベータの数値だけを見て判断してはいけません。R²は「その銘柄の値動きのうち、何割が市場全体で説明できるか」を表します。

  • R²が0.6以上: 市場との連動が強い。ベータは信頼できる
  • R²が0.3〜0.6: 個別要因も大きい。ベータは参考程度
  • R²が0.3未満: そもそも連動していない。ベータに意味がない

R²が0.1しかない銘柄で「ベータ1.5だから市場より1.5倍動く」と考えるのは誤りです。その銘柄は市場と無関係に、独自の理由で動いているだけです。ベータは連動している銘柄についてのみ意味を持つ指標だと覚えておいてください。

製造業株のベータを比較する

実際に複数銘柄で計算して並べてみます。業種による違いがはっきり出ます。

WATCHLIST = {
    "7203.T": "トヨタ自動車", "6501.T": "日立製作所", "6902.T": "デンソー",
    "6367.T": "ダイキン工業", "6301.T": "小松製作所", "7011.T": "三菱重工",
    "8035.T": "東京エレクトロン", "6758.T": "ソニーG",
    "9432.T": "NTT", "4502.T": "武田薬品",
}

rows = []
for code, name in WATCHLIST.items():
    try:
        r = calc_beta(code)
        rows.append({
            "銘柄": name,
            "ベータ": round(r["beta"], 2),
            "R²": round(r["r_squared"], 2),
            "α(年率%)": round(r["alpha_annual"] * 100, 1),
            "信頼性": "○" if r["r_squared"] >= 0.4 else "△",
        })
    except Exception:
        continue

df = pd.DataFrame(rows).sort_values("ベータ", ascending=False)
print(df.to_string(index=False))
print(f"\n平均ベータ: {df['ベータ'].mean():.2f}")

おおまかな傾向として、次のような結果になります。

  • 半導体製造装置・重工・証券: ベータが高い(1.2〜1.8)。景気敏感で振れが大きい
  • 自動車・電機: 1.0前後。市場と同程度に動く
  • 通信・食品・医薬: 0.5〜0.8。ディフェンシブで下げにくい

ここで気づくのは、製造業に偏ったポートフォリオは自然とベータが高くなるということです。「分散のつもりで10銘柄持っているが全部メーカー」という状態だと、指数が下がったときに全部まとめて、しかも指数以上に下がります。

この「分散したつもりで分散できていない」問題は、相関係数を計算して銘柄間の関係を数値化する方法でも別角度から確認できます。

ポートフォリオ全体のベータを求める

個別銘柄のベータが分かったら、次は全体です。ポートフォリオのベータは、各銘柄のベータの加重平均で求められます。この単純さがベータの便利なところです。

HOLDINGS = {          # 銘柄コード: 評価額(円)
    "7203.T": 1_200_000,
    "6501.T":   800_000,
    "8035.T":   600_000,
    "9432.T":   400_000,
}

total = sum(HOLDINGS.values())
rows, weighted_beta = [], 0.0

for code, value in HOLDINGS.items():
    r = calc_beta(code)
    w = value / total
    weighted_beta += r["beta"] * w
    rows.append({
        "銘柄": code, "評価額": f"{value:,}", "比率": f"{w:.1%}",
        "ベータ": round(r["beta"], 2),
        "寄与": round(r["beta"] * w, 3),
    })

print(pd.DataFrame(rows).to_string(index=False))
print(f"\n合計評価額         : {total:,} 円")
print(f"ポートフォリオβ    : {weighted_beta:.3f}")
print(f"日経が-5%のとき想定 : {-5 * weighted_beta:.2f} % "
      f"({total * -0.05 * weighted_beta:,.0f} 円)")

最後の行が実用的です。「日経が5%下げたら、自分はいくら失うのか」を事前に把握できます。この数字を見て「思ったより大きい」と感じたなら、ベータの低い銘柄を組み入れるか、比率を見直す判断ができます。

目標ベータに合わせて配分を調整する

逆に、「ポートフォリオのベータを0.8に抑えたい」という目標から配分を逆算することもできます。

TARGET_BETA = 0.8

current = weighted_beta
if current > TARGET_BETA:
    # 一部を現金(ベータ0)に置き換えて目標に近づける
    cash_ratio = 1 - TARGET_BETA / current
    print(f"現在のβ {current:.2f} → 目標 {TARGET_BETA}")
    print(f"→ 資産の {cash_ratio:.1%} "
          f"({total * cash_ratio:,.0f} 円) を現金化すると達成できます")
else:
    print(f"現在のβ {current:.2f} はすでに目標以下です")

現金はベータ0なので、現金比率を上げるだけで全体のベータは機械的に下がります。「なんとなく不安だから少し売る」ではなく、「ベータを0.8にするために25%を現金化する」と決められるのは大きな違いです。

ベータは固定値ではない

ここが実務での落とし穴です。ベータは時期によって大きく変わります。「トヨタのベータは1.05」と覚えても、来年には違う値になっています。

import matplotlib.pyplot as plt

data = yf.download(["7203.T", "^N225"], period="10y", auto_adjust=True,
                   progress=False)["Close"].dropna()
rets = data.pct_change().dropna()

WINDOW = 250   # 約1年
cov = rets["7203.T"].rolling(WINDOW).cov(rets["^N225"])
var = rets["^N225"].rolling(WINDOW).var()
rolling_beta = (cov / var).dropna()

fig, ax = plt.subplots(figsize=(11, 4.5))
ax.plot(rolling_beta.index, rolling_beta, linewidth=1.2)
ax.axhline(1.0, color="gray", linestyle="--", linewidth=1, label="β=1.0")
ax.set_ylabel("ローリング・ベータ(1年)")
ax.set_title("ベータの推移")
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("rolling_beta.png", dpi=120)

print(f"現在   : {float(rolling_beta.iloc[-1]):.3f}")
print(f"最小   : {float(rolling_beta.min()):.3f}")
print(f"最大   : {float(rolling_beta.max()):.3f}")
print(f"変動幅 : {float(rolling_beta.max() - rolling_beta.min()):.3f}")

実行すると、同じ銘柄でもベータが0.8から1.5まで動くといった結果が出ます。企業の事業構成が変わったり、相場全体の性格が変わったりすれば、市場との関係も変わるからです。

したがって、ベータは定期的に計算し直すものと考えてください。四半期に一度、保有銘柄のベータを再計算する運用がおすすめです。

暴落時にはベータが1に近づく

もう1つ知っておくべき性質があります。市場が急落する局面では、ほとんどの銘柄のベータが1に近づきます。平常時はディフェンシブだった銘柄も、パニック売りの局面では一緒に売られるためです。

# 市場が大きく下げた日だけを取り出してベータを計算する
def beta_in_crash(ticker, market="^N225", period="10y", threshold=-0.02):
    data = yf.download([ticker, market], period=period, auto_adjust=True,
                       progress=False)["Close"].dropna()
    rets = data.pct_change().dropna()

    crash = rets[rets[market] < threshold]
    normal = rets[rets[market] >= threshold]

    def b(df):
        return float(df[ticker].cov(df[market]) / df[market].var())

    return b(normal), b(crash), len(crash)


for code, name in [("9432.T", "NTT"), ("4502.T", "武田薬品"),
                   ("7203.T", "トヨタ")]:
    normal_b, crash_b, n = beta_in_crash(code)
    print(f"{name:<10s} 平常時 {normal_b:>5.2f} → 急落時 {crash_b:>5.2f} "
          f"(該当 {n} 日)")

ディフェンシブ銘柄ほど、この差が大きく出ます。「守りの銘柄を入れているから大丈夫」という安心は、いちばん必要な局面で裏切られるということです。分散効果が最も欲しいときに分散効果が消える——これは相関係数でも観測される現象で、市場の構造的な性質だと考えたほうがよいでしょう。

アルファ——市場では説明できない部分

回帰分析では、傾き(ベータ)と一緒に切片(アルファ)も得られます。これは「市場の動きでは説明できない超過リターン」を意味します。

rows = []
for code, name in WATCHLIST.items():
    try:
        r = calc_beta(code, period="5y")
        rows.append({
            "銘柄": name,
            "β": round(r["beta"], 2),
            "α(年率%)": round(r["alpha_annual"] * 100, 1),
            "R²": round(r["r_squared"], 2),
        })
    except Exception:
        continue

df = pd.DataFrame(rows).sort_values("α(年率%)", ascending=False)
print(df.to_string(index=False))
print("\n※αは「市場に乗っただけでは得られなかった分」を表します")

アルファがプラスなら、その銘柄は市場平均を上回る何かを持っていたことになります。ただし過去のアルファが将来も続く保証はまったくありません。むしろアルファは消えやすいことが知られており、過去5年のアルファ上位銘柄を買っても再現しないのが普通です。

実用的な使い方としては、自作戦略の評価が挙げられます。戦略の日次損益を目的変数にしてベータとアルファを計算すれば、「単に相場に乗っていただけか、独自の優位性があったか」を切り分けられます。ベータが1に近くアルファがゼロなら、その戦略は指数を買うのと変わりません。

戦略評価の他の指標についてはシャープレシオを使って投資戦略を評価する方法もあわせてご覧ください。

計算するときの注意点

どの指数を市場とするか

日経平均は225銘柄の株価平均型、TOPIXは時価総額加重型で、性格が異なります。日経平均は値がさ株の影響が大きいため、指数の選び方でベータが変わります。

# 日経平均基準とTOPIX連動ETF基準で比べてみる
for market, label in [("^N225", "日経平均"), ("1306.T", "TOPIX連動ETF")]:
    try:
        r = calc_beta("7203.T", market=market)
        print(f"{label:<14s} β={r['beta']:.3f}  R²={r['r_squared']:.3f}")
    except Exception as e:
        print(f"{label}: 取得失敗 {e}")

日本株全体との連動を見るならTOPIXのほうが素直です。どちらを使うかは目的次第ですが、比較するときは必ず揃えてください。

期間と頻度の選択

一般的には日次で2〜3年、または週次で5年が使われます。日次はデータ量が多い反面ノイズも多く、週次は安定するもののデータ数が減ります。

設定データ数特徴
日次・1年約250直近を反映。ぶれやすい
日次・3年約750実務での標準
週次・5年約260安定するが反応が遅い
月次・5年60データ不足。推奨しない

新規上場銘柄では計算できない

上場から日が浅い銘柄は、そもそも十分なデータがありません。最低250営業日は確保できることを確認してから計算してください。また、上場直後は値動きが特殊なため、落ち着くまでは参考値と考えるべきです。

# データ不足を検出してから計算する
def safe_beta(ticker, market="^N225", period="3y", min_days=250):
    data = yf.download([ticker, market], period=period, auto_adjust=True,
                       progress=False)["Close"].dropna()
    if len(data) < min_days:
        return None, f"データ不足({len(data)}日 < {min_days}日)"
    r = calc_beta(ticker, market, period)
    if r["r_squared"] < 0.3:
        return r, "R²が低く、ベータの信頼性は低いです"
    return r, "OK"

まとめ

ベータ値で「自分の持ち株がどれだけ市場に振り回されているか」を測る方法を見てきました。要点を整理します。

  • ベータは市場が1%動いたときに何%動くか。回帰直線の傾きそのもの
  • R²を必ず一緒に見る。0.3未満ならベータに意味はない
  • 製造業や半導体はベータが高く、通信・食品・医薬は低い
  • ポートフォリオのベータは加重平均で計算できる。現金はベータ0
  • ベータは固定値ではない。四半期ごとに計算し直す
  • 暴落時にはベータが1に近づく。ディフェンシブ銘柄も一緒に売られる
  • アルファは市場で説明できない部分。自作戦略の評価に使うと有用

冒頭の疑問——「相場が悪かったのか、自分の銘柄選びが偏っていたのか」への答えは、ポートフォリオのベータを計算すれば出ます。ベータが1.4なら、それは相場のせいではなく、そういう構成にした自分の選択の結果です。

もっとも、ベータが高いこと自体は悪ではありません。上昇局面では 市場より大きく取れるということでもあります。問題なのは、自分がベータ1.4を取っていると知らないまま持っていることです。知ったうえで選ぶなら、それは戦略です。

まずは保有銘柄の評価額を入力して、全体のベータを計算してみてください。「日経が5%下げたら自分はいくら減るか」が数字で見えるだけで、ポジションの取り方が変わってきます。

タイトルとURLをコピーしました