Mímisbrunnr知恵の泉

← 時系列分析 一覧

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

📎 前提:ARCHとGARCHモデル | 関連:定常性と自己相関(ACF)・モデル選択と残差診断(残差のARCH効果)

要点(BLUF)

1. ボラティリティクラスタリングとは

金融リターンを眺めると、荒れる時期と凪の時期が固まって現れます。暴落の直後は連日大きく動き、落ち着けばしばらく穏やかが続く。これを ボラティリティクラスタリングと呼びます。GARCH(1,1) の漸化式

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

がまさにこれを生みます——大きなショック εt12\varepsilon_{t-1}^2 が来ると σt2\sigma_t^2 が上がり、βσt12\beta\sigma_{t-1}^2 を通じて高ボラ状態が次々に持続する(ARCHとGARCHモデル)。重要なのは、この依存は「水準(平均)」ではなく「変動の大きさ(分散)」に現れること。だから普通の ACF(定常性と自己相関)をリターンに当てても見えず、リターンの2乗に当てて初めて指紋が浮かびます。

2. 指紋:リターンの ACF ≈ 0 vs リターン2乗の ACF は有意

リターン rt=σtztr_t=\sigma_t z_t は、ztz_t が i.i.d. なのでそれ自体は無相関E[rtrtk]=0\mathbb E[r_t r_{t-k}]=0)。ところが2乗 rt2=σt2zt2r_t^2=\sigma_t^2 z_t^2σt2\sigma_t^2 が過去に依存するぶん自己相関を持ちます。これが ARCH 効果を見つける鍵です。

コード①:リターン vs リターン2乗の ACF を数値で対比

真の GARCH(1,1)(ω=0.2,α=0.10,β=0.85\omega=0.2,\alpha=0.10,\beta=0.85)の擬似リターンを作り、リターンの ACFリターン2乗の ACF を並べ、Ljung-BoxARCH-LM で ARCH 効果を判定します。

import numpy as np
from arch.univariate import ZeroMean, GARCH, Normal
from statsmodels.tsa.stattools import acf
from statsmodels.stats.diagnostic import acorr_ljungbox, het_arch

# 真の GARCH(1,1) リターンを生成(リターン自体は無相関だが分散が連動)
sim_model = ZeroMean()
sim_model.volatility = GARCH(p=1, q=1)
sim_model.distribution = Normal(seed=np.random.default_rng(7))
returns = sim_model.simulate([0.2, 0.10, 0.85], nobs=4000)["data"].values

n = len(returns)
band = 1.96 / np.sqrt(n)   # ACFの95%有意帯(白色なら±この範囲)
acf_r = acf(returns, nlags=5, fft=True)
acf_r2 = acf(returns**2, nlags=5, fft=True)
print(f"95% band = +/-{band:.3f}")
print(f"{'lag':>4}{'ACF(r)':>10}{'ACF(r^2)':>12}")
for k in range(1, 6):
    print(f"{k:>4}{acf_r[k]:>10.3f}{acf_r2[k]:>12.3f}")

# Ljung-Box(帰無=自己相関なし): リターン vs リターン2乗
lb_r = acorr_ljungbox(returns,    lags=[10], return_df=True)["lb_pvalue"].iloc[0]
lb_r2 = acorr_ljungbox(returns**2, lags=[10], return_df=True)["lb_pvalue"].iloc[0]
print(f"Ljung-Box p  returns   = {lb_r:.3f}  (large -> no autocorr)")
print(f"Ljung-Box p  returns^2 = {lb_r2:.3e}  (small -> autocorr in variance)")

# ARCH-LM 検定(帰無=ARCH効果なし)
lm_stat, lm_p, f_stat, f_p = het_arch(returns, nlags=10)
print(f"ARCH-LM stat = {lm_stat:.1f}  p = {lm_p:.3e}  -> ARCH effect present")

出力:

95% band = +/-0.031
 lag    ACF(r)    ACF(r^2)
   1     0.005       0.197
   2     0.006       0.157
   3     0.009       0.163
   4    -0.015       0.144
   5    -0.018       0.156
Ljung-Box p  returns   = 0.922  (large -> no autocorr)
Ljung-Box p  returns^2 = 2.554e-197  (small -> autocorr in variance)
ARCH-LM stat = 410.0  p = 7.003e-82  -> ARCH effect present

出力の意味:リターンの ACF は全ラグで ±0.031\pm0.031 帯の内側(0.005,0.006,0.005,0.006,\dots)=ほぼ無相関。一方リターン2乗の ACF は 0.197,0.157,0.197,0.157,\dots と帯を大きく超えて有意に正——分散に自己相関がある証拠です。Ljung-Box も、リターンでは p=0.922p=0.922(自己相関なしを棄却しない)に対し、2乗では p=2.6×10197p=2.6\times10^{-197}(強く棄却)と対照的。ARCH-LM 検定も p=7.0×1082p=7.0\times10^{-82} で「ARCH 効果あり」。**「平均は予測できないが分散は予測できる」**という GARCH の前提が、検定でも裏づきます。これは ARIMA の残差診断(モデル選択と残差診断)で「残差は白色なのに残差2乗に相関が残る」ときに GARCH を足す合図でもあります。

3. ボラティリティ予測と時変の予測区間

GARCH の予測対象は水準ではなく分散です。hh 期先の条件付き分散は、GARCH(1,1) なら漸化式を前向きに回して

σ^t+h2=σ2+(α+β)h1(σ^t+12σ2),σ2=ω1αβ\hat\sigma_{t+h}^2 = \sigma_\infty^2 + (\alpha+\beta)^{\,h-1}\big(\hat\sigma_{t+1}^2-\sigma_\infty^2\big),\qquad \sigma_\infty^2=\frac{\omega}{1-\alpha-\beta}

と書けます(解析的)。持続性 α+β<1\alpha+\beta<1 なので、多期先では無条件分散 σ2\sigma_\infty^2 へ指数的に収束します。点予測(平均)は ≈0 のままでも、予測区間 ±1.96σt+h\pm1.96\,\sigma_{t+h} は時期と先読みで伸縮する——ここが ARIMA の「区間は単調に広がる」(ARMA・ARIMAモデル)と決定的に違う点です。

flowchart LR
    A["リターン r_t(平均≈0)"] --> B["GARCH(1,1) を推定"]
    B --> C["条件付きボラ sigma_t を更新"]
    C --> D["多期先の分散予測 sigma_{t+h}^2"]
    D --> E["時変の予測区間 ±1.96 sigma_{t+h}"]
    D --> F["h を増やすと無条件分散へ収束"]

コード②:時変区間とボラ予測(無条件分散へ収束)

GARCH(1,1) を推定し、(a) 時変の95%帯 ±1.96σt\pm1.96\,\sigma_t がリターンを約95%覆うか(点予測=0だけでは不十分)、(b) 多期先の分散予測が無条件分散へ収束するか、を確かめます。図では帯が時期で伸縮します。

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) リターンを生成
sim_model = ZeroMean()
sim_model.volatility = GARCH(p=1, q=1)
sim_model.distribution = Normal(seed=np.random.default_rng(7))
returns = sim_model.simulate([0.2, 0.10, 0.85], nobs=2000)["data"].values

# GARCH(1,1) を推定
fit = arch_model(returns, mean="Zero", vol="GARCH", p=1, q=1, dist="normal").fit(disp="off")
sigma = fit.conditional_volatility          # 1期先ボラ sigma_t(時変)
uncond_var = fit.params["omega"] / (1 - fit.params["alpha[1]"] - fit.params["beta[1]"])

# (a) 時変の95%帯 ±1.96 sigma_t がリターンを約95%覆うか(点予測=0だけでは不十分)
lo, hi = -1.96 * sigma, 1.96 * sigma
cover = np.mean((returns > lo) & (returns < hi))
print(f"time-varying band width: min={2*1.96*sigma.min():.2f}  max={2*1.96*sigma.max():.2f}")
print(f"95% band coverage of returns = {cover*100:.1f}%")

# (b) 多期先の分散予測(最終時点から): 無条件分散へ収束
fc = fit.forecast(horizon=100)   # analytic(GARCHは多期先も解析的)
var_path = fc.variance.iloc[-1].values       # h.1 ... h.100
for h in [1, 5, 20, 50, 100]:
    print(f"h={h:>3}  var forecast={var_path[h-1]:.3f}  sigma={np.sqrt(var_path[h-1]):.3f}")
print(f"unconditional var (limit) = {uncond_var:.3f}")

# 図:直近400点のリターンに時変95%帯を重ねる(帯が時期で伸縮)
seg = slice(1600, 2000)
t = np.arange(1600, 2000)
plt.figure(figsize=(10, 4.5))
plt.plot(t, returns[seg], color="gray", lw=0.7, label="リターン r_t")
plt.fill_between(t, lo[seg], hi[seg], color="C0", alpha=0.25, label="時変95%帯 ±1.96σ_t")
plt.axhline(0, ls=":", color="k", lw=1, label="点予測 ≈ 0")
plt.xlabel("時点 t"); plt.ylabel("r_t"); plt.legend(loc="upper right")
plt.title("時変予測区間:点予測は0でも帯が時期で伸縮する")
plt.tight_layout(); plt.show()

出力:

time-varying band width: min=5.62  max=19.87
95% band coverage of returns = 94.9%
h=  1  var forecast=5.715  sigma=2.391
h=  5  var forecast=5.385  sigma=2.321
h= 20  var forecast=4.680  sigma=2.163
h= 50  var forecast=4.299  sigma=2.073
h=100  var forecast=4.234  sigma=2.058
unconditional var (limit) = 4.231

出力の意味:(a) 時変の95%帯の幅は最小 5.625.62・最大 19.8719.87時期で3倍以上に伸縮し、リターンの 94.9%94.9\% を覆いました——名目95%とよく一致(較正済み)。点予測(橙の0線)だけでは「いま危険か穏やかか」が一切表せないのに対し、±1.96σt\pm1.96\sigma_t の帯はそれを定量化します。(b) 多期先の分散予測は h=1h=15.7155.715(推定時点が高ボラ局面)から始まり、h=100h=1004.2344.234——無条件分散 4.2314.231 へ収束します。これは前ノートの「σ2=ω/(1αβ)\sigma_\infty^2=\omega/(1-\alpha-\beta) が均衡点」の数値確認です(ARCHとGARCHモデル)。先読みが長いほど「いまの局面情報」が薄れ、長期平均の分散へ戻る——遠い将来のボラは結局「平均的な荒さ」に落ち着く、と読みます。

補足(VaR):金融実務ではこの σt\sigma_tVaR(Value at Risk) に使います。正規を仮定すれば1日先の99% VaR は 2.33σt+1\approx 2.33\,\sigma_{t+1}(保有額に対する損失の下側分位点)。GARCH で σt+1\sigma_{t+1} が時変なら、VaR も日々更新されます。深入りはしませんが、「時変ボラ → 時変リスク指標」という応用先がある、と押さえておけば十分です。

4. 数式の直観

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

関連ノート