Mímisbrunnr知恵の泉

← 時系列分析 一覧

🎓 レベル:標準 | 重要度:A(必須)

📎 前提:定常性と自己相関(ACF・分散) | 対比:ARMA・ARIMAモデル(条件付き平均) | 数理:確率過程(マルコフ連鎖・ポアソン過程)(統計)

要点(BLUF)

1. 条件付き平均から条件付き分散へ

第2〜5章のモデルはすべて、過去から**水準(平均)**を予測するものでした。ARIMA は

yt=E[ytFt1]過去で動く条件付き平均+εt,Var[εt]=σ2 (一定)y_t = \underbrace{\mathbb E[y_t\mid \mathcal F_{t-1}]}_{\text{過去で動く条件付き平均}} + \varepsilon_t,\qquad \mathrm{Var}[\varepsilon_t]=\sigma^2\ (\text{一定})

と書け、誤差の分散 σ2\sigma^2 は時間によらず一定と仮定していました(定常性と自己相関 の弱定常)。ところが金融リターンを見ると、平均はほぼ0で予測不能なのに、変動の大きさ(分散)は時期で大きく変わる——荒れる時期と凪の時期がある。これを捉えるのが本章で、平均ではなく分散そのものを過去の関数にするのが発想の転換です。

yt=μ+εt,εt=σtzt,zti.i.d.(0,1),σt2=Var[εtFt1]y_t = \mu + \varepsilon_t,\qquad \varepsilon_t = \sigma_t\, z_t,\quad z_t\sim \text{i.i.d.}\,(0,1),\qquad \sigma_t^2 = \mathrm{Var}[\varepsilon_t\mid \mathcal F_{t-1}]

ztz_t は標準化されたノイズ(標準正規など)、σt\sigma_t条件付き標準偏差=ボラティリティσt\sigma_t を過去のショック εt1,\varepsilon_{t-1},\dots や過去の分散 σt12,\sigma_{t-1}^2,\dots で決める漸化式がモデルの本体です。平均方程式は定数 μ\mu(リターンはほぼ無相関なので μ=0\mu=0 でもよい)で十分なことが多い——主役は分散の方です。

2. ARCH(qq):過去のショックの2乗で分散を決める

Engle (1982) の ARCH(qq)(自己回帰条件付き分散不均一, AutoRegressive Conditional Heteroskedasticity) は、条件付き分散を過去 qq 期のショックの2乗の線形和で表します。

σt2=ω+α1εt12+α2εt22++αqεtq2,ω>0, αi0\sigma_t^2 = \omega + \alpha_1\varepsilon_{t-1}^2 + \alpha_2\varepsilon_{t-2}^2 + \cdots + \alpha_q\varepsilon_{t-q}^2,\qquad \omega>0,\ \alpha_i\ge 0

直観は「大きなショック εt12\varepsilon_{t-1}^2 が来た翌期は分散が大きくなる」。εt1\varepsilon_{t-1} の符号は2乗で消えるので、上げでも下げでも「大きく動いた」こと自体が次の分散を押し上げます。これがボラティリティクラスタリング(ボラティリティクラスタリングと予測)の最小の数式表現です。非負制約は分散が負にならないため。難点は、変動の持続を表すのに**qq を大きく取る必要がある**こと——AR を MA で近似するような冗長さが出ます。

3. GARCH(p,qp,q):過去の分散も足す(倹約版)

Bollerslev (1986) の GARCH(p,qp,q)(一般化ARCH) は、ARCH に過去の条件付き分散 σtj2\sigma_{t-j}^2 を足します。

σt2=ω+i=1qαiεti2+j=1pβjσtj2\sigma_t^2 = \omega + \sum_{i=1}^{q}\alpha_i\,\varepsilon_{t-i}^2 + \sum_{j=1}^{p}\beta_j\,\sigma_{t-j}^2

過去の分散を再帰的に取り込むことで、少ない項で長い記憶を表せます——ARMA が AR を倹約したのと同じ構図(ARMA・ARIMAモデル)。実務の定番は GARCH(1,1)

σt2=ω+αεt12+βσt12\sigma_t^2 = \omega + \alpha\,\varepsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2

ここで重要な2つの量があります。

σ2=ω1αβ\sigma_\infty^2 = \frac{\omega}{1-\alpha-\beta}

α+β1\alpha+\beta\to1 で無条件分散が発散することからも、α+β<1\alpha+\beta<1 が定常性の境界だと分かります。

コード①:真の (ω,α,β)(\omega,\alpha,\beta) を GARCH(1,1) で復元

真の ω=0.2, α=0.10, β=0.85\omega=0.2,\ \alpha=0.10,\ \beta=0.85(持続性 0.950.95、無条件分散 4.04.0)を仕込んだゼロ平均 GARCH(1,1) のリターンを arch で生成し、同じ archarch_model3つのパラメータを復元します。arch は 8.0.0 を使用します。

import numpy as np
from arch.univariate import ZeroMean, GARCH, Normal
from arch import arch_model

# 真の GARCH(1,1): sigma_t^2 = omega + alpha*eps_{t-1}^2 + beta*sigma_{t-1}^2
omega_true, alpha_true, beta_true = 0.2, 0.10, 0.85

# --- 真のパラメータでゼロ平均GARCH(1,1)のリターンを生成 ---
sim_model = ZeroMean()
sim_model.volatility = GARCH(p=1, q=1)
sim_model.distribution = Normal(seed=np.random.default_rng(42))  # 再現用
sim = sim_model.simulate([omega_true, alpha_true, beta_true], nobs=4000)
returns = sim["data"].values

# 定常条件 alpha+beta<1 と無条件分散 omega/(1-alpha-beta)
persistence = alpha_true + beta_true
uncond_var = omega_true / (1 - persistence)
print(f"alpha+beta = {persistence:.3f}  (stationary if < 1)")
print(f"unconditional var = omega/(1-alpha-beta) = {uncond_var:.3f}")
print(f"sample var of returns    = {returns.var():.3f}")

# --- arch でパラメータを復元 ---
fit = arch_model(returns, mean="Zero", vol="GARCH", p=1, q=1, dist="normal").fit(disp="off")
om = fit.params["omega"]; al = fit.params["alpha[1]"]; be = fit.params["beta[1]"]
print(f"{'param':>8}{'true':>10}{'est':>10}")
print(f"{'omega':>8}{omega_true:>10.3f}{om:>10.3f}")
print(f"{'alpha':>8}{alpha_true:>10.3f}{al:>10.3f}")
print(f"{'beta':>8}{beta_true:>10.3f}{be:>10.3f}")
print(f"est alpha+beta = {al+be:.3f}   est uncond var = {om/(1-al-be):.3f}")

出力:

alpha+beta = 0.950  (stationary if < 1)
unconditional var = omega/(1-alpha-beta) = 4.000
sample var of returns    = 3.845
   param      true       est
   omega     0.200     0.243
   alpha     0.100     0.086
    beta     0.850     0.851
est alpha+beta = 0.937   est uncond var = 3.863

出力の意味:最尤推定が ω^=0.243, α^=0.086, β^=0.851\hat\omega=0.243,\ \hat\alpha=0.086,\ \hat\beta=0.851 と、仕込んだ 0.2, 0.10, 0.850.2,\ 0.10,\ 0.85 を概ね復元しました(ω\omegaα\alpha は相関が強く分離がやや難しいのが GARCH の癖です)。推定された持続性 α^+β^=0.937\hat\alpha+\hat\beta=0.937 は真値 0.950.95 に近く、推定無条件分散 3.8633.863 は標本分散 3.8453.845 とよく一致——「分散が時間変化していても、構造を仮定すればパラメータを当てられる」ことが確認できます。なお res.summary() は記号を含むので、ここでは fit.params['omega'] のように個別の数値だけを取り出しています。

4. 推定したボラティリティを可視化する

推定後、各時点の条件付きボラティリティ σ^t\hat\sigma_tfit.conditional_volatility で取り出せます。これを系列に重ねると、分散が時期によって膨らむ様子(ボラティリティクラスタリング)が目で見えます。

コード②:条件付きボラティリティ σ^t\hat\sigma_t を重ねる

同じ擬似 GARCH 系列を推定し、リターンの直下に σ^t\hat\sigma_t を描きます。あわせて、ボラティリティが低い時期(下位25%)と高い時期(上位25%)でリターンの大きさ rt|r_t| がどれだけ違うかを数値で対比します。

import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib  # 日本語ラベル用
from arch.univariate import ZeroMean, GARCH, Normal
from arch import arch_model

# 真の GARCH(1,1) を生成(コード(1)と同じ構造)
sim_model = ZeroMean()
sim_model.volatility = GARCH(p=1, q=1)
sim_model.distribution = Normal(seed=np.random.default_rng(42))
returns = sim_model.simulate([0.2, 0.10, 0.85], nobs=4000)["data"].values

# GARCH(1,1) を推定し、条件付きボラティリティ sigma_hat_t を取り出す
fit = arch_model(returns, mean="Zero", vol="GARCH", p=1, q=1, dist="normal").fit(disp="off")
vol = fit.conditional_volatility       # 各時点の sigma_hat_t(標準偏差)
uncond_sd = np.sqrt(0.2 / (1 - 0.10 - 0.85))   # 無条件SD = sqrt(4)=2

# 高ボラ・低ボラ期間を数値で対比
print(f"sigma_hat: min={vol.min():.2f}  median={np.median(vol):.2f}  max={vol.max():.2f}")
print(f"unconditional SD = {uncond_sd:.2f}")
calm = returns[vol < np.percentile(vol, 25)]
turb = returns[vol > np.percentile(vol, 75)]
print(f"|return| mean  calm(low-vol)={np.abs(calm).mean():.2f}  turbulent(high-vol)={np.abs(turb).mean():.2f}")

fig, ax = plt.subplots(2, 1, figsize=(10, 6), sharex=True)
ax[0].plot(returns, color="gray", lw=0.6)
ax[0].set_ylabel("リターン r_t"); ax[0].set_title("擬似リターン(変動が時期で膨らむ=ボラティリティクラスタリング)")
ax[1].plot(vol, color="C3", lw=1.0, label="推定ボラティリティ sigma_hat_t")
ax[1].axhline(uncond_sd, ls="--", color="k", lw=1, label="無条件SD")
ax[1].set_ylabel("sigma_hat_t"); ax[1].set_xlabel("時点 t"); ax[1].legend()
plt.tight_layout(); plt.show()

出力:

sigma_hat: min=1.25  median=1.87  max=4.06
unconditional SD = 2.00
|return| mean  calm(low-vol)=1.29  turbulent(high-vol)=1.95

出力の意味:推定ボラティリティ σ^t\hat\sigma_t1.251.25 から 4.064.06 まで動き、無条件SD 2.02.0 の周りを揺れます。ボラが低い時期のリターンの大きさは平均 1.291.29、高い時期は 1.951.95——荒れる時期は実際に大きく動いている。図では下段の σ^t\hat\sigma_t が膨らむ位置で、上段のリターンの振れ幅も大きくなります。これが GARCH が捉えている「分散の時間変化」で、点予測(平均≈0)だけでは表せない情報です。次ノートでは、この性質を検定で確かめ、予測へつなげます(ボラティリティクラスタリングと予測)。

5. 数式の直観

⚠️ よくある誤解・落とし穴

関連ノート