まとめ- pandasのrolling + applyでローリングβを算出し、市場感応度の時間変化を追跡する。
- β=1を基準に、アグレッシブ(β>1)とディフェンシブ(β<1)の期間を可視化する。
- ウィンドウ長の感度分析で、ノイズと応答速度のトレードオフを理解する。
直感
#
β(ベータ)は銘柄の市場感応度を示す代表的な指標です。CAPMでは \( r_i = \alpha + \beta \cdot r_m + \epsilon \) として定義され、βが1より大きいと市場以上に変動し、1より小さいとディフェンシブな性質を持ちます。固定期間で推計するだけでなく、ローリングウィンドウで追跡すると市場環境の変化を捉えやすくなります。
詳細な解説
#
擬似データの生成
#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pathlib import Path
np.random.seed(7)
plt.style.use("scripts/k_dm.mplstyle")
dates = pd.date_range("2022-01-01", periods=520, freq="B")
market_returns = np.random.normal(0.0004, 0.01, size=len(dates))
signals = {}
for ticker, beta, vol in [
("AAA", 1.2, 0.012),
("BBB", 0.7, 0.009),
]:
residual = np.random.normal(0, vol, size=len(dates))
signals[ticker] = beta * market_returns + residual
returns = pd.DataFrame({"market": market_returns, **signals}, index=dates)
|
ローリングβを計算する関数
#
共分散と分散の比として \( \beta = \text{Cov}(r_i, r_m) / \text{Var}(r_m) \) を算出します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| def rolling_beta(asset: pd.Series, benchmark: pd.Series, window: int = 120) -> pd.Series:
def _calc(x: pd.Series) -> float:
ref = benchmark.loc[x.index]
cov = np.cov(x, ref)[0, 1]
var = np.var(ref)
return cov / var if var > 0 else np.nan
return asset.rolling(window).apply(_calc, raw=False)
betas = pd.DataFrame(
{
ticker: rolling_beta(returns[ticker], returns["market"])
for ticker in ["AAA", "BBB"]
}
)
|
βの推移を可視化
#
1
2
3
4
5
6
7
8
9
10
11
12
| fig, ax = plt.subplots(figsize=(9, 4.2))
betas.plot(ax=ax, linewidth=1.6)
ax.axhline(1.0, color="#ef4444", linestyle="--", linewidth=1, label="β=1")
ax.set_title("120営業日ローリングβ(サンプルデータ)")
ax.set_ylabel("β")
ax.legend()
ax.grid(alpha=0.3)
output = Path("static/images/finance/main/rolling_beta_trend.svg")
output.parent.mkdir(parents=True, exist_ok=True)
fig.tight_layout()
fig.savefig(output)
|

ウィンドウ長の感度分析
#
ウィンドウを短くするとβの変動がリアルタイムに反映されますが、ノイズも増えます。長くすると滑らかになりますが、構造変化の検出が遅れます。
1
2
3
4
5
6
7
8
9
10
11
12
13
| fig, ax = plt.subplots(figsize=(9, 4.2))
for w, color, ls in [(60, "#0ea5e9", "-"), (120, "#6366f1", "-"), (250, "#f97316", "--")]:
rb = rolling_beta(returns["AAA"], returns["market"], window=w)
ax.plot(rb.index, rb, linewidth=1.4, color=color, linestyle=ls, label=f"w={w}")
ax.axhline(1.0, color="#ef4444", linestyle="--", linewidth=0.8, alpha=0.5)
ax.set_title("AAA: ウィンドウ長によるローリングβの違い")
ax.set_ylabel("β")
ax.legend()
ax.grid(alpha=0.3)
fig.tight_layout()
plt.show()
|
βの要約統計量
#
期間全体のβの平均・標準偏差を確認し、真のβ(データ生成時のパラメーター)との乖離を検証します。
1
2
3
4
5
| summary = betas.dropna().describe().round(3)
print(summary)
print()
print("設定値: AAA=1.2, BBB=0.7")
print(f"推定平均: AAA={betas['AAA'].mean():.3f}, BBB={betas['BBB'].mean():.3f}")
|
実務でのポイント
#
- パラメーター(ウィンドウ長やリターンの頻度)は資産ごとに調整します。ボラティリティが高い資産は長めのウィンドウで平滑化すると安定します。
- ベンチマークには株価指数だけでなく、金利やコモディティなど複数のファクターを組み合わせることで、マルチファクターモデルに拡張できます。
- ローリングβをアラート化し、閾値を超えたらリバランスを検討する、といったルールベースの運用に組み込むのも有効です。
- βの急変動はイベントリスク(決算発表、地政学リスクなど)と対応していることが多く、イベントログと突き合わせると解釈が深まります。