研究ノート: HPフィルタ+ハイブリッド推定による株価モンテカルロシミュレーション
1. 研究目的
株価の将来予測においては、ノイズを含む生データをそのまま使うのではなく、トレンド成分とサイクル成分を分離して解析することで、より滑らかな推定が可能となる。本研究では、HPフィルタ (Hodrick–Prescott Filter) を用いて株価の分解を行い、そのうえでハイブリッド推定によるパラメータ (μ, σ) を算出し、モンテカルロシミュレーションにより将来の価格分布を推定する。
2. データ処理
2.1 株価データの取得
Yahoo Finance から任意の銘柄 (例: JMIA) の株価終値を取得する。
ticker = "JMIA"
start = "2024-01-01"
end = "2025-09-01"
data = yf.download(ticker, start=start, end=end)
prices_ = data["Close"]
2.2 HPフィルタによる分解
HPフィルタを適用し、株価をトレンド (prices) と循環成分 (cycle) に分解する。
これにより、短期的なノイズを除去し、長期的な動きをより明確に捉えることができる。
cycle, prices = sm.tsa.filters.hpfilter(prices_, 10)
2.3 リターン計算
対数リターンを計算する。
returns: HPフィルタ後の価格に基づくリターン
returns_c: サイクル成分に基づくリターン
returns = np.log(prices / prices.shift(1))
# returns_c = np.log(cycle / cycle.shift(1))
3. ハイブリッド推定 (μ, σ)
株価シミュレーションにはリターンの平均と分散 (μ, σ) が必要である。
本研究では、異なる期間を用いて平均とボラティリティを推定する「ハイブリッド推定」を採用する。
def hybrid_params(returns, vol_window=21, mean_window=126):
vol = returns[-vol_window:].std() # 短期ボラティリティ
mu = returns[-mean_window:].mean() # 中期リターン
return mu, vol
vol_window (例: 21日) → 直近のボラティリティ (1か月相当)
mean_window (例: 63日) → より長期の平均リターン (四半期相当)
4. モンテカルロシミュレーション
モンテカルロ法を用いて、株価の将来分布を生成する。
予測期間 horizon を変えることで、翌日〜数か月先の分布を評価可能。
def monte_carlo(prices, mu, vol, horizon=1, n_sims=10000):
S0 = prices.iloc[-1]
sims = np.zeros(n_sims)
for i in range(n_sims):
rand_returns = np.random.normal(mu, vol, horizon)
sims[i] = S0 * np.prod(1 + rand_returns)
return sims
入力: 現在価格、推定された μ と σ
出力: 将来の株価シミュレーション結果 (多数のサンプル)
5. 実行と結果
5.1 シナリオ設定
以下の複数の予測期間を設定する。
scenarios = {
"1d": 1,
"1w": 5,
"1m": 21,
"2m": 42,
"3m": 63,
"4m": 84,
}
5.2 結果の描画
ヒストグラム → 価格分布を可視化
赤線 → 現在価格
青線 → 各シナリオの中央値
plt.axvline(prices_.iloc[-1].squeeze(), color="red", linestyle="--", label="現在価格")
plt.legend()
plt.xlim(0,50)
plt.title(f"{ticker} Monte Carloシミュレーション")
5.3 出力例
現在価格 : 15.20
--- 1d --- 15.25
--- 1w --- 15.60
--- 1m --- 16.80
--- 3m --- 20.50
6. 考察
本コードは「HPフィルタによるトレンド抽出 × ハイブリッド推定 × モンテカルロ法」を組み合わせた実験的アプローチです。
投資家にとっては、複数シナリオ下での将来株価分布を確率的に理解するためのツールとして活用できます。
HPフィルタを導入した意義
短期的なノイズを抑えつつ、トレンドに基づいた安定的な推定が可能。
ハイブリッド推定の効果
ボラティリティは直近の変動を反映、平均リターンはより長期の傾向を反映 → 市場環境の変化を早期に捕捉できる。
モンテカルロの結果
将来の株価は確率分布として提示され、投資判断の不確実性を定量的に把握できる。
7. 今後の展開
μ, σ の時系列推移をリアルタイムで可視化
サイクル成分に基づくリスク調整モデルの導入
シナリオ分析 (金利上昇局面、ボラティリティ急増局面)
具体例
$JMIA 大
JMIA
現在価格 : 8.60
--- 1d --- 8.74
--- 1w --- 9.31
--- 1m --- 12.04
--- 2m --- 16.77
--- 3m --- 23.43
--- 4m --- 32.85
$SOFI 大
SOFI
現在価格 : 25.54
--- 1d --- 25.81
--- 1w --- 26.91
--- 1m --- 31.80
--- 2m --- 39.59
--- 3m --- 49.28
--- 4m --- 61.36
$HIMS 低
HIMS
現在価格 : 42.35
--- 1d --- 42.20
--- 1w --- 41.59
--- 1m --- 39.26
--- 2m --- 36.44
--- 3m --- 33.70
--- 4m --- 31.18
$APP 小
APP
現在価格 : 478.59
--- 1d --- 480.23
--- 1w --- 486.46
--- 1m --- 512.86
--- 2m --- 548.80
--- 3m --- 586.80
--- 4m --- 628.81
$OPEN 大
OPEN
現在価格 : 4.45
--- 1d --- 4.59
--- 1w --- 5.17
--- 1m --- 8.35
--- 2m --- 15.72
--- 3m --- 29.40
--- 4m --- 55.51
$IREN 大
IREN
現在価格 : 26.48
--- 1d --- 26.92
--- 1w --- 28.77
--- 1m --- 37.53
--- 2m --- 53.21
--- 3m --- 75.64
--- 4m --- 106.99
$RKLB 大
RKLB
現在価格 : 48.60
--- 1d --- 49.05
--- 1w --- 50.88
--- 1m --- 58.93
--- 2m --- 71.43
--- 3m --- 86.47
--- 4m --- 105.13
$AFRM 大
AFRM
現在価格 : 88.46
--- 1d --- 89.14
--- 1w --- 91.90
--- 1m --- 103.83
--- 2m --- 121.83
--- 3m --- 142.79
--- 4m --- 168.01
$OKLO 小
OKLO
現在価格 : 73.64
--- 1d --- 74.08
--- 1w --- 75.87
--- 1m --- 83.45
--- 2m --- 94.52
--- 3m --- 106.87
--- 4m --- 121.55
$NBIS 小
NBIS
現在価格 : 68.32
--- 1d --- 68.99
--- 1w --- 71.73
--- 1m --- 83.80
--- 2m --- 102.71
--- 3m --- 125.61
--- 4m --- 154.80
$TEM 小
TEM
現在価格 : 75.86
--- 1d --- 76.15
--- 1w --- 77.36
--- 1m --- 82.33
--- 2m --- 89.30
--- 3m --- 96.68
--- 4m --- 105.35
コード全体
サンプルコード
import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import japanize_matplotlib # 日本語表示用
import statsmodels.api as sm
# ==============================
# 設定
# ==============================
ticker = "IREN" # 銘柄コード JMIA SOFI OPEN IREN HIMS APP
start = "2024-01-01" # データ取得開始日
end = "2025-09-01" # データ取得終了日
n_sims = 10000 # シミュレーション回数
# データ取得
data = yf.download(ticker, start=start, end=end)
prices_ = data["Close"]
cycle, prices = sm.tsa.filters.hpfilter(prices_, 10)
# 日次リターン
#returns = prices.pct_change().dropna()
returns = np.log(prices / prices.shift(1))
returns_c = np.log(cycle / cycle.shift(1))
print(cycle)
# ==============================
# ハイブリッド推定関数
# ==============================
def hybrid_params(returns, vol_window=21, mean_window=126):
"""
vol_window : ボラティリティを計算する日数 (例: 21営業日 ≒ 1か月)
mean_window: 平均リターンを計算する日数 (例: 252営業日 ≒ 1年)
"""
#vol = returns_c[-vol_window:].std()
vol = returns[-vol_window:].std()
mu = returns[-mean_window:].mean()
# mu = returns.mean() * 252 # 年率換算 (252営業日)
# vol = returns.std() * np.sqrt(252) # 年率換算
return mu, vol
# ==============================
# Monte Carloシミュレーション関数
# ==============================
def monte_carlo(prices, mu, vol, horizon=1, n_sims=10000):
"""
horizon : 予測する営業日数
1 = 翌日
5 = 翌週
21= 翌月
"""
S0 = prices.iloc[-1] #[-1]
sims = np.zeros(n_sims)
for i in range(n_sims):
rand_returns = np.random.normal(mu, vol, horizon)
sims[i] = S0 * np.prod(1 + rand_returns)
return sims
# ==============================
# 実行
# ==============================
mu, vol = hybrid_params(returns, vol_window=21, mean_window=63) #18 21 #252 126 504 63
scenarios = {
"1d": 1,
"1w": 5,
"1m": 21,
"2m": 42,
"3m": 63,
"4m": 84,
}
results = {}
for label, horizon in scenarios.items():
sims = monte_carlo(prices_, mu, vol, horizon, n_sims=n_sims)
results[label] = sims
# 分布表示
plt.hist(sims, bins=100, alpha=0.6, label=label)
plt.axvline(prices_.iloc[-1].squeeze(), color="red", linestyle="--", label="現在価格")
plt.legend()
#plt.xlim(0,75)
plt.title(f"{ticker} Monte Carloシミュレーション")
#plt.show()
# 結果例: 平均・中央値・下位5%信頼区間
print(f"現在価格 : {prices_.iloc[-1].squeeze():.2f}")
for label, sims in results.items():
plt.axvline(np.median(sims), color="blue", linestyle="--", label="中央値")
print(f"\n--- {label} --- {np.mean(sims):.2f}")
#print(f"平均価格 : {np.mean(sims):.2f}")
#print(f"中央値 : {np.median(sims):.2f}")
#print(f"下位25%水準 : {np.percentile(sims, 25):.2f}")
#print(f"上位75%水準 : {np.percentile(sims, 75):.2f}")
plt.show()