機械学習による仮想通貨価格予測 第2部:Prophet/LSTMによる価格予測モデル構築【実装コード付き】
機械学習による仮想通貨価格予測 第2部:Prophet/LSTMによる価格予測モデル構築【実装コード付き】
はじめに:なぜProphetとLSTMを選ぶのか
第1部では、仮想通貨価格予測の全体像とデータ分析の基礎について解説しました。本記事(第2部)では、いよいよ実際に動くモデルの構築へと進みます。
数ある時系列予測モデルの中で、ProphetとLSTMを選ぶ理由はシンプルです。実装の容易さと予測精度のバランスが、入門〜中級層にとって最適だからです。
2026年現在、研究の最前線ではTransformerベースのモデルやCNN-LSTMハイブリッドが注目を集めています。しかし、Transformerは計算コストが高く、実装ハードルも相応に高い。対してProphetとLSTMは、適切に設計すればR²=0.98〜0.99クラスの精度を実現しつつ、コードの見通しも良く保てます。
本記事で構築するものの全体像は以下の通りです。
[データ取得] → [前処理・特徴量エンジニアリング]
↓
[Prophet モデル] → [トレンド・季節性の予測]
[LSTM モデル] → [短期の非線形パターン予測]
↓
[評価・比較] → [精度改善アプローチの選択]
動作確認済み環境・主要ライブラリ:
Python 3.11.x
prophet==1.1.5
tensorflow==2.15.0 / keras==2.15.0
scikit-learn==1.4.x
ccxt==4.2.x / yfinance==0.2.x
ta==0.11.0
pandas==2.1.x / numpy==1.26.x
第1章:モデルを選ぶ前に知っておくべき基礎知識
1-1. ProphetとLSTMの特性比較
まず、両モデルの特性を整理します。「どちらが精度が高いか」という問いへの答えは**「用途による」**です。
| 観点 | Prophet | LSTM |
|---|---|---|
| 開発元 | Meta(旧Facebook) | 学術研究発祥 |
| 得意分野 | トレンド・季節性の把握 | 短期の非線形パターン |
| 解釈性 | 高い(分解可視化あり) | 低い(ブラックボックス) |
| 実装コスト | 低い | 中程度 |
| 短期予測(5〜7日) | △ | ◎ |
| 中長期予測(30日〜) | ◎ | △ |
| 外れ値への耐性 | 強い | 弱め |
判断の指針:解釈性を重視しトレンドを掴みたいならProphet、短期の価格変動パターンを学習させたいならLSTMを選択してください。
1-2. LSTMの仕組みをざっくり理解する
LSTMはRNN(再帰型ニューラルネットワーク)の一種です。通常のRNNが抱える勾配消失問題——長い系列を学習するほど過去の情報が薄れる問題——を、入力ゲート・忘却ゲート・出力ゲートの3つのゲート機構によって解決しています。
実装上で最初に戸惑うのが入力形式です。LSTMは3Dテンソル (batch_size, timesteps, features) を要求します。
batch_size:一度に処理するサンプル数(例:32)timesteps:過去何ステップ分のデータを見るか(例:90日)features:各時点の特徴量数(例:OHLCVの5列+テクニカル指標)
典型的な設計は「過去90日分のデータから翌日の終値を予測する」という構成です。この90日というルックバック期間は、仮想通貨の価格サイクルと計算コストのバランスを考慮した実用的な値です。
1-3. Prophetの仕組みをざっくり理解する
Prophetは時系列データを以下の3成分に加法分解するモデルです。
y(t) = trend(t) + seasonality(t) + holidays(t) + noise
- trend:長期的な上昇・下降トレンド
- seasonality:週次・年次などの周期的パターン
- holidays:特定イベントによる変動
仮想通貨データへ適用する際の注意点として、株式市場と異なり24時間365日取引が行われるため、「祝日効果」の設定は不要です。一方で、週末の流動性低下による価格パターンは「週次季節性」として捉えられます。
なお、MetaはProphetをさらに進化させたNeuralProphetを公開しています。LSTMコンポーネントを内包し、より高精度な予測が可能ですが、本記事では実装の見通しを優先してProphetを扱い、第6章で発展的トピックとして触れます。
第2章:データ取得と前処理【実装コード付き】
2-1. 仮想通貨データの取得方法
主要な取得手段を比較します。
| ライブラリ | 特徴 | 向いているケース |
|---|---|---|
yfinance |
インストールが簡単。Yahoo Finance経由 | すぐ試したい場合 |
ccxt |
Binanceなど100以上の取引所に対応 | 複数取引所のデータが必要な場合 |
| Binance API | 高頻度・ティックデータまで取得可能 | 本格的な高頻度分析 |
本記事では手軽さを優先して yfinance を使用します。
import yfinance as yf
import pandas as pd
def fetch_btc_data(start: str = "2020-01-01", end: str = "2026-01-01") -> pd.DataFrame:
"""
yfinanceでBTCの日次OHLCVデータを取得する
Args:
start: 取得開始日('YYYY-MM-DD'形式)
end: 取得終了日('YYYY-MM-DD'形式)
Returns:
OHLCVのDataFrame
"""
ticker = yf.Ticker("BTC-USD")
df = ticker.history(start=start, end=end, interval="1d")
# カラム名を小文字に統一
df.columns = [c.lower() for c in df.columns]
df = df[["open", "high", "low", "close", "volume"]]
# 欠損値の処理(前値埋め)
df.ffill(inplace=True)
df.dropna(inplace=True)
print(f"取得完了: {len(df)} 件 ({df.index[0].date()} 〜 {df.index[-1].date()})")
return df
df_raw = fetch_btc_data()2-2. 特徴量エンジニアリング
OHLCVデータだけでなく、テクニカル指標を追加することで予測精度が向上します。特にRSI・MACD・ボリンジャーバンドは仮想通貨分析で広く使われており、LSTMの入力特徴量として有効です。
import ta
from ta.momentum import RSIIndicator
from ta.trend import MACD
from ta.volatility import BollingerBands
def add_technical_indicators(df: pd.DataFrame) -> pd.DataFrame:
"""
OHLCVデータにテクニカル指標を追加する
Returns:
テクニカル指標追加後のDataFrame
"""
df = df.copy()
# RSI(14期間)
rsi = RSIIndicator(close=df["close"], window=14)
df["rsi"] = rsi.rsi()
# MACD
macd = MACD(close=df["close"])
df["macd"] = macd.macd()
df["macd_signal"] = macd.macd_signal()
df["macd_diff"] = macd.macd_diff()
# ボリンジャーバンド(20期間・2σ)
bb = BollingerBands(close=df["close"], window=20, window_dev=2)
df["bb_upper"] = bb.bollinger_hband()
df["bb_lower"] = bb.bollinger_lband()
df["bb_width"] = bb.bollinger_wband()
# 欠損値(指標計算のウォームアップ期間分)を除去
df.dropna(inplace=True)
return df
df = add_technical_indicators(df_raw)
print(f"特徴量数: {df.shape[1]} 列")2-3. データの前処理と正規化
正規化手法の選択:
- Min-Max正規化:値を[0, 1]に圧縮。価格のような非負値に適しています
- Z-score正規化:平均0・標準偏差1に変換。外れ値の影響を受けやすいですが分布の形状を保ちます
LSTMには一般的にMin-Max正規化が使われます。ただし、学習データのみでスケーラーを学習し、検証・テストデータにはそのスケーラーを適用することが重要です。
時系列データの分割:通常の機械学習と異なり、ランダム分割は厳禁です。未来のデータが学習データに混入する「データリーク」が発生し、評価が意味を持たなくなります。
from sklearn.preprocessing import MinMaxScaler
def split_and_scale(df: pd.DataFrame, target_col: str = "close",
train_ratio: float = 0.7, val_ratio: float = 0.15):
"""
時系列データを時間順に分割し、正規化する
Returns:
train, val, test の各DataFrame と scaler
"""
n = len(df)
train_end = int(n * train_ratio)
val_end = int(n * (train_ratio + val_ratio))
train = df.iloc[:train_end].copy()
val = df.iloc[train_end:val_end].copy()
test = df.iloc[val_end:].copy()
scaler = MinMaxScaler()
train[target_col] = scaler.fit_transform(train[[target_col]])
val[target_col] = scaler.transform(val[[target_col]])
test[target_col] = scaler.transform(test[[target_col]])
print(f"学習: {len(train)} 件 / 検証: {len(val)} 件 / テスト: {len(test)} 件")
return train, val, test, scaler
train_df, val_df, test_df, scaler = split_and_scale(df)第3章:Prophetによる価格予測モデルの構築【実装コード付き】
3-1. インストールと環境準備
pip install prophetProphetはインストール時にエラーが出やすいライブラリです。よくある原因は pystan のバージョン競合です。問題が発生した場合は conda install -c conda-forge prophet でのインストールを試してください。
3-2. データ整形:ds・y形式への変換
Prophetは ds(日付)と y(予測対象)の2列を持つDataFrameを要求します。
from prophet import Prophet
def prepare_prophet_df(df: pd.DataFrame, target_col: str = "close") -> pd.DataFrame:
"""
DataFrameをProphet用のds/y形式に変換する
"""
prophet_df = df[[target_col]].reset_index()
prophet_df.columns = ["ds", "y"]
# タイムゾーン情報を除去(Prophetはtimezone-naiveを要求)
prophet_df["ds"] = pd.to_datetime(prophet_df["ds"]).dt.tz_localize(None)
return prophet_df
prophet_df = prepare_prophet_df(df_raw) # 正規化前の実価格を使用
train_prophet = prophet_df.iloc[:int(len(prophet_df) * 0.85)]3-3. モデルの学習と基本予測
# モデルのインスタンス生成
model = Prophet(
changepoint_prior_scale=0.05, # トレンド変化点の柔軟性(大きいほど変化に敏感)
seasonality_prior_scale=10.0, # 季節性の強さ
daily_seasonality=False, # 日次季節性(日次データでは不要)
weekly_seasonality=True, # 週次季節性(仮想通貨に有効)
yearly_seasonality=True # 年次季節性
)
# 学習
model.fit(train_prophet)
# 予測期間の生成(30日先まで)
future = model.make_future_dataframe(periods=30)
# 予測の実行
forecast = model.predict(future)
# 予測結果の確認
print(forecast[["ds", "yhat", "yhat_lower", "yhat_upper"]].tail(10))3-4. 季節性のカスタマイズ(仮想通貨向け設定)
仮想通貨には週末の流動性低下など、特有の周期パターンが存在します。add_seasonality() で手動追加が可能です。
model_custom = Prophet(changepoint_prior_scale=0.05)
# 月次季節性を手動追加(仮想通貨のサイクルを考慮)
model_custom.add_seasonality(
name="monthly",
period=30.5,
fourier_order=5 # 大きいほど複雑な形状を学習(過学習に注意)
)
model_custom.fit(train_prophet)changepoint_prior_scale のチューニング:デフォルト値は0.05です。値を上げると急激なトレンド変化に追従しやすくなりますが、過学習のリスクも高まります。実務では0.01〜0.5の範囲で検証データに対してグリッドサーチすることをお勧めします。
3-5. 予測結果の可視化と解釈
import matplotlib.pyplot as plt
# 予測値と信頼区間の描画
fig1 = model.plot(forecast)
plt.title("BTC価格予測(Prophet)")
plt.xlabel("日付")
plt.ylabel("価格(USD)")
plt.tight_layout()
plt.show()
# トレンド・季節性の分解
fig2 = model.plot_components(forecast)
plt.tight_layout()
plt.show()plot_components() はProphetならではの強力な機能です。トレンド・週次・年次の各成分を分解して可視化することで、「なぜこの時期に価格が上昇しやすいのか」という解釈性の高いインサイトが得られます。
第4章:LSTMによる価格予測モデルの構築【実装コード付き】
4-1. ライブラリのインポートと乱数シードの固定
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# 再現性のためにシードを固定
SEED = 42
np.random.seed(SEED)
tf.random.set_seed(SEED)4-2. 時系列データをLSTM入力形式に変換する
スライディングウィンドウ関数で3Dテンソルを生成します。
def create_sequences(data: np.ndarray, timesteps: int = 90):
"""
スライディングウィンドウで (X, y) の時系列データセットを生成する
Args:
data: 正規化済みの1D配列(終値)
timesteps: ルックバック期間
Returns:
X: shape (samples, timesteps, 1)
y: shape (samples,)
"""
X, y = [], []
for i in range(len(data) - timesteps):
X.append(data[i : i + timesteps])
y.append(data[i + timesteps])
return np.array(X)[..., np.newaxis], np.array(y)
# 正規化済みの終値を使用
close_values = train_df["close"].values
X_train, y_train = create_sequences(close_values, timesteps=90)
close_val = val_df["close"].values
X_val, y_val = create_sequences(close_val, timesteps=90)
print(f"X_train shape: {X_train.shape}") # (samples, 90, 1)ルックバック期間90日の根拠:仮想通貨の価格サイクルにおける「3ヶ月周期」のパターンを捉えつつ、計算コストを現実的に保てる値として実務でよく使われます。短くするほど学習が速い反面、長期依存関係を見失います。
4-3. LSTMモデルのアーキテクチャ設計
def build_lstm_model(timesteps: int = 90, features: int = 1,
units: int = 64, dropout_rate: float = 0.2) -> keras.Model:
"""
2層LSTM + Denseの価格予測モデルを構築する
Args:
timesteps: ルックバック期間
features: 特徴量数
units: LSTMユニット数
dropout_rate: Dropoutの割合
Returns:
コンパイル済みのKerasモデル
"""
inputs = keras.Input(shape=(timesteps, features))
# 第1層LSTM:return_sequences=Trueで次のLSTM層へ系列を渡す
x = layers.LSTM(units, return_sequences=True,
kernel_regularizer=keras.regularizers.l2(1e-4))(inputs)
x = layers.Dropout(dropout_rate)(x)
# 第2層LSTM:return_sequences=Falseで最終隠れ状態のみを出力
x = layers.LSTM(units // 2, return_sequences=False,
kernel_regularizer=keras.regularizers.l2(1e-4))(x)
x = layers.Dropout(dropout_rate)(x)
# 出力層:価格予測なので線形活性化(デフォルト)
outputs = layers.Dense(1)(x)
model = keras.Model(inputs, outputs)
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=1e-3),
loss="mse",
metrics=["mae"]
)
return model
model_lstm = build_lstm_model()
model_lstm.summary()4-4. 過学習を防ぐトレーニング戦略
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
callbacks = [
EarlyStopping(
monitor="val_loss",
patience=15, # 15エポック改善しなければ停止
restore_best_weights=True
),
ModelCheckpoint(
filepath="best_lstm_model.keras",
monitor="val_loss",
save_best_only=True
)
]
history = model_lstm.fit(
X_train, y_train,
validation_data=(X_val, y_val),
epochs=200,
batch_size=32,
callbacks=callbacks,
verbose=1
)
# 学習曲線のプロット
plt.figure(figsize=(10, 4))
plt.plot(history.history["loss"], label="Train Loss")
plt.plot(history.history["val_loss"], label="Val Loss")
plt.xlabel("Epoch")
plt.ylabel("MSE Loss")
plt.title("学習曲線")
plt.legend()
plt.tight_layout()
plt.show()過学習対策のポイント:
- EarlyStopping:検証損失が改善しなくなった時点で学習を打ち切り、過学習前の最良モデルを復元します
- Dropout:各訓練ステップでランダムにニューロンを無効化し、汎化性能を高めます
- L2正則化:重みが過度に大きくなることを防ぎます
4-5. 予測と逆変換・可視化
close_test = test_df["close"].values
X_test, y_test = create_sequences(close_test, timesteps=90)
# 予測の実行
y_pred_scaled = model_lstm.predict(X_test)
# 元のスケールに逆変換
y_pred = scaler.inverse_transform(y_pred_scaled)
y_actual = scaler.inverse_transform(y_test.reshape(-1, 1))
# 実際の価格と予測価格の重ね合わせ
plt.figure(figsize=(14, 5))
plt.plot(y_actual, label="実際の価格", color="steelblue")
plt.plot(y_pred, label="予測価格(LSTM)", color="tomato", linestyle="--")
plt.xlabel("日数")
plt.ylabel("BTC価格(USD)")
plt.title("LSTMによるBTC価格予測 vs 実際の価格")
plt.legend()
plt.tight_layout()
plt.show()第5章:モデルの評価と比較
5-1. 評価指標の選び方
from sklearn.metrics import mean_absolute_error, mean_squared_error
import numpy as np
def evaluate_model(y_true: np.ndarray, y_pred: np.ndarray, model_name: str = "Model"):
"""
MAE・RMSE・MAPEを計算して出力する
"""
mae = mean_absolute_error(y_true, y_pred)
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100
print(f"--- {model_name} ---")
print(f"MAE : {mae:,.2f} USD")
print(f"RMSE : {rmse:,.2f} USD")
print(f"MAPE : {mape:.2f} %")
return {"mae": mae, "rmse": rmse, "mape": mape}各指標の意味:
- MAE(平均絶対誤差):予測の平均的なズレ幅。直感的に理解しやすい
- RMSE(二乗平均平方根誤差):大きなズレを強調する。外れ値の影響を受けやすい
- MAPE(平均絶対パーセント誤差):相対的な誤差率。10%以下が実用的な目安
注意点:R²=0.99 のような高い決定係数は、価格が「ほぼ一直線に上昇するトレンド」であれば自然に出る値です。単純に前日の価格を「今日の予測値」とするナイーブ予測でもR²が高くなる場合があります。RMSEやMAPEをベースに評価することをお勧めします。
5-2. ProphetとLSTMの予測精度を比較する
同一のテストデータで両モデルを評価した場合、一般的に以下のような傾向が見られます。
| 予測期間 | Prophet | LSTM |
|---|---|---|
| 短期(5〜7日) | △ | ◎ |
| 中期(14〜30日) | ○ | △ |
| 長期(60日〜) | ◎ | ✕ |
「何日先まで現実的に予測できるか?」という問いへの答えは、LSTMで5〜7日が実用的な精度範囲の上限です。それ以上になると誤差が急増し、予測の信頼性が著しく低下します。価格変動にはランダムウォーク成分が大きいため、これはモデルの問題ではなく予測問題の本質的な難しさです。
5-3. ウォークフォワード検証(より実戦的な評価方法)
単純なホールドアウト評価では「特定の期間にたまたま精度が高かった」という可能性を排除できません。より信頼性の高い評価としてウォークフォワード検証があります。
[学習期間1] → [予測・評価1]
[学習期間1 + 評価期間1] → [予測・評価2]
[学習期間2 + 評価期間2] → [予測・評価3]
...
このように学習データを時間方向に拡張しながら繰り返し評価することで、モデルの汎化性能をより実態に近い形で測定できます。実装コストは高くなりますが、本番投入前には必ず実施することを強くお勧めします。
第6章:精度をさらに上げるための発展的アプローチ
6-1. ハイブリッドモデルへの道:CNN-LSTMの概要
2026年時点での研究では、CNN+LSTMのハイブリッド構成がR²=0.99を達成したとの報告があります(MDPI, 2026)。
仕組みは以下の通りです:
入力 → [CNN層:局所的な価格パターンを特徴マップとして抽出]
↓
[LSTM層:抽出された特徴の時系列依存関係を学習]
↓
[Dense層:価格予測]
CNN層が「直近数日のローソク足パターン」を圧縮表現として抽出し、LSTM層がその変化の流れを学習する構成です。単純なLSTMより実装コストは上がりますが、次のステップとして挑戦する価値があります。
6-2. 特徴量にセンチメントスコアを追加する
RedditやX(旧Twitter)の投稿から感情スコアを算出し、LSTMの外部特徴量として加える手法が注目されています。実装には以下のライブラリが活用できます。
- PRAW:Reddit APIクライアント
- snscrape:X投稿の収集
- VADER / BERTopic:テキストのセンチメント分析
センチメントスコアを追加することで、特に急騰・急落局面での予測精度向上が報告されています。ただし、APIの利用制限やデータ収集のコストを考慮した上で導入を検討してください。
6-3. NeuralProphetで次のステップへ
ProphetをLSTMコンポーネントで強化したNeuralProphetは、標準のProphetと同様の直感的なAPIで高精度な予測を実現します。
pip install neuralprophetfrom neuralprophet import NeuralProphet
model_np = NeuralProphet(
n_forecasts=7, # 7日先まで予測
n_lags=90, # 過去90日を参照
learning_rate=1e-3
)
# 以降のAPIはProphetとほぼ同じ精度面ではLSTMが優位なケースも多いですが、Prophetの「解釈しやすさ」を保ちながら精度を上げたい場合の有力な選択肢です。
まとめ:ProphetとLSTMを使い分けるための判断フロー
本記事で実装したモデルと、用途別の選択指針を整理します。
予測の目的は?
├─ トレンドを把握したい・解釈性が必要
│ └─ → Prophet(またはNeuralProphet)
│
├─ 短期の価格変動を精度よく予測したい
│ └─ → LSTM(過去90日 → 翌5〜7日)
│
└─ さらなる精度が必要
├─ → CNN-LSTM ハイブリッド
└─ → Transformer系モデル(Informer / Temporal Fusion Transformer)
本記事で実装したコードの要点:
yfinanceでBTCの日次OHLCVデータを取得taライブラリでRSI・MACD・ボリンジャーバンドを追加- Prophet:
ds/y形式に変換 →fit()→predict()→plot_components()で解釈 - LSTM:スライディングウィンドウで3Dテンソルを生成 → 2層LSTM構築 → EarlyStoppingで学習 → 逆変換して評価
- 評価:MAE・RMSE・MAPEで比較し、ウォークフォワード検証で実戦的な精度を確認
第3部では、本記事で紹介したセンチメント分析の実装と、CNN-LSTMハイブリッドモデルの構築を詳しく解説する予定です。段階的に理解を深めていきましょう。
参考文献・関連リンク
- Prophet 公式ドキュメント
- NeuralProphet 公式ドキュメント
- TensorFlow/Keras 公式ドキュメント
- ccxt ライブラリ(GitHub)
- ta ライブラリ(GitHub)
- GitHub参考実装:
younghoonNa/bitcoin-prediction-LSTM-Prophet - 第1部:機械学習による仮想通貨価格予測 第1部(基礎編)
- 第3部:機械学習による仮想通貨価格予測 第3部:センチメント分析・ハイブリッドモデル編(近日公開)
関連記事
GRUで仮想通貨価格を予測する方法|初心者からわかる実装ガイド2026
GRU(Gated Recurrent Unit)を使った仮想通貨価格予測の実装ガイド。LSTMより速く・軽く・高精度なGRUの仕組みをゼロから解説し、BTC・ETH予測の実績データも紹介します。
機械学習による仮想通貨価格予測(第4部):強化学習で収益を最大化するトレードAIの実装ガイド
強化学習(PPO・DQN・SAC)を使った仮想通貨トレードAIの実装を解説。カスタムGym環境の構築からstable-baselines3による学習まで、Pythonコードで段階的に説明します。
機械学習による仮想通貨価格予測(第3部):予測モデルの検証と自動売買システムへの統合完全ガイド
バックテスト成功でも本番失敗する原因を解説。ウォークフォワード検証・CPCVなどプロの検証手法と、自動売買システムへの統合・運用まで実装コード付きで完全解説。