はじめに
「AIで株価を予測できるのか?」
この問いに対して、実際にプロダクトを作り、6万件以上のバックテストで検証した結果を全て公開します。
結論から言うと、日経225構成銘柄の3ヶ月後の値動き方向を67.3%の精度で的中させるモデルが完成しました。 ただし、ここに至るまでに6回の改善チャレンジで全敗し、「特徴量を増やすほど精度が下がる」という反直感的な壁にぶつかりました。
この記事では、モデル構築の全過程 — 成功も失敗も含めて — を技術的な詳細とともに公開します。
完成したサービス: Kabu Prediction(全機能無料)
実際のダッシュボード: kabu.microforge.works/dashboard
目次
- プロジェクト概要
- 技術スタック
- モデルアーキテクチャ
- 特徴量エンジニアリング
- 精度改善の6回の失敗
- SHAP分析で見えたモデルの本質
- 大型株 vs 小型株で精度が全く違った話
- バックテスト結果
- 法的リスクとの戦い
- まとめと教訓
プロジェクト概要
何を作ったか
日経225構成銘柄(約200銘柄)を対象に、4つのタイムフレーム(3営業日・1週間・1ヶ月・3ヶ月)で株価の方向を予測するAI分析ツールです。
| 項目 | 内容 |
|---|---|
| 対象 | 日経225構成銘柄(約200銘柄) |
| 予測ホライズン | 3営業日 / 1週間 / 1ヶ月 / 3ヶ月 |
| 方向的中率 | 3d: 56% / 1w: 70% / 1m: 58% / 3m: 56% |
| バックテスト件数 | 66,215件 |
| 料金 | 全機能無料 |
なぜ作ったか
機関投資家はクオンツチームを擁してデータドリブンな投資判断をしていますが、個人投資家にはそのようなツールがありません。「同等の定量分析を個人でも使えるようにしたい」というモチベーションで開発を始めました。
技術スタック
┌─────────────────────────────────────────────────┐
│ フロントエンド │
│ Next.js 14 (App Router) + Tailwind CSS │
│ Vercel にデプロイ │
├─────────────────────────────────────────────────┤
│ バックエンド │
│ Supabase (PostgreSQL + Auth + RLS) │
├─────────────────────────────────────────────────┤
│ 分析エンジン │
│ Python 3.11 + LightGBM + XGBoost + Ridge │
│ BigQuery (株価データ基盤) │
├─────────────────────────────────────────────────┤
│ 自動化 │
│ GCP Cloud Functions + Cloud Scheduler │
│ 週次パイプライン自動実行 │
└─────────────────────────────────────────────────┘
アーキテクチャ詳細: 分析手法ページ
モデルアーキテクチャ
4モデルのスタッキングアンサンブル
単一モデルではなく、4つの異なるモデルの予測値をRidgeメタラーナーで統合しています。
# Model 1: LightGBM Primary(Optuna最適化対象)
model1 = lgb.LGBMRegressor(n_estimators=300, max_depth=6, learning_rate=0.02, ...)
# Model 2: LightGBM Diversity(異なるハイパラで多様性確保)
model2 = lgb.LGBMRegressor(n_estimators=200, max_depth=4, learning_rate=0.05, ...)
# Model 3: Ridge Regression(線形モデルでアンカー)
model3 = Ridge(alpha=1.0)
# Model 4: XGBoost(異なるブースティング実装)
model4 = xgb.XGBRegressor(n_estimators=200, max_depth=5, learning_rate=0.03, ...)
# メタラーナー: 非負制約付きRidgeで最適重み学習
meta_model = Ridge(alpha=1.0, positive=True)
meta_model.fit(np.column_stack([m.predict(X) for m in [model1, model2, model3, model4]]), y)
なぜ positive=True が重要か: 制約なしだとModel2に大きな負の重みがつき、「Model2の予測を逆に使う」という不安定な学習になりました。非負制約により、Model1とModel4が自然に選ばれる安定した重みが得られます。
ターゲット変数: 市場調整リターン(Excess Return)
素の株価リターンではなく、市場平均からの超過リターンを予測ターゲットにしています。
# Raw return
fwd_return = (price_t+N / price_t - 1) × 100
# Market average
market_avg = mean(fwd_return across all stocks on same date)
# Excess return(これを予測する)
fwd_excess = fwd_return - market_avg
なぜ必要だったか: 市場全体が下落トレンドの時期に学習すると、全銘柄が「下がる」と予測してしまう問題がありました。Excess returnにすることで、「市場平均と比べてどうか」を学習させ、この問題を解消しました。
ウィンソライズ(外れ値処理)
小型株時代の名残ですが、大型株でも決算発表後のストップ高/安は起こります。ターゲット変数の1%/99%パーセンタイルでクリッピングしています。
lo = train_df[target_col].quantile(0.01)
hi = train_df[target_col].quantile(0.99)
train_df[target_col] = train_df[target_col].clip(lo, hi)
ホライズン別サンプル重み
短期予測(3日)は最新データを重視、長期予測(3ヶ月)はデータ全体から均等に学習するのが最適でした。
horizon_decay = {
"3d": 0.5, # 最新を1.65倍重視
"1w": 0.4,
"1m": 0.2,
"3m": 0.1, # ほぼ均等
}
weights = np.exp(np.linspace(0, 1, n_samples) * decay)
特徴量エンジニアリング
最終的に使用している特徴量は79個。大きく分けて7カテゴリです。
特徴量カテゴリ別の構成
| カテゴリ | 特徴量数 | 代表例 |
|---|---|---|
| テクニカル | 18 | ATR, MACD, RSI, ボリンジャーバンド, 出来高比率 |
| ファンダメンタル | 7 | PER, PBR, ROE, EPS, 配当利回り |
| センチメント | 10 | ニュース感情スコア7日平均, ソーシャルバズ |
| クロスセクション | 14 | リターンZスコア, 市場ブレッドス, セクターモメンタム |
| マクロ | 10 | 日経, S&P500, VIX, ドル円 |
| ランク | 7 | RSIランク, モメンタムランク, ボラティリティランク |
| その他 | 13 | セクターコード, 決算接近度, ペア特徴量 |
クロスセクションランク特徴量(v0.5.0で追加、効果あり)
生の指標値ではなく、当日の全銘柄中の相対順位を特徴量にしたものです。これにより市場レジーム(上昇相場/下落相場)が変わっても安定した入力が得られます。
rank_targets = {
"rsi_14": "rsi_rank",
"momentum_10d": "momentum_10d_rank",
"momentum_20d": "momentum_20d_rank",
"volume_ratio": "volume_ratio_rank",
"volatility_20d": "volatility_20d_rank",
"return_5d": "return_5d_rank",
"return_20d": "return_20d_rank",
}
for src_col, rank_col in rank_targets.items():
df[rank_col] = df.groupby("trade_date")[src_col].rank(pct=True)
精度改善の6回の失敗
ここが最も学びの大きかった部分です。特徴量の追加・削減・ターゲット変更・データ期間延長、すべて中長期の精度を悪化させました。
失敗の一覧
| 試行 | 内容 | 3d | 1w | 1m | 3m |
|---|---|---|---|---|---|
| Baseline (v0.5.0) | 79特徴量 | 51.6% | 56.5% | 59.9% | 67.3% |
| v0.6.0 | +フラクショナル微分 +日本市場特徴量 +レジデュアルモメンタム | +0.3 | +0.2 | -2.7 | -5.2 |
| ニュース拡充 | +業績トレンド +センチメント強化 | ±0 | +0.5 | -3.4 | -5.9 |
| v0.7.0 | +クロスアセット連動 +サプライチェーン +FF因子残差 (計+20特徴量) | -2.5 | -4.8 | -11.8 | -16.2 |
| SHAP削減 | 79→35に絞り込み | +1.5 | -0.4 | -8.0 | -16.7 |
| 10年データ | 5年→10年に期間延長 | +2.5 | -3.3 | -8.4 | -20.6 |
なぜ失敗するのか
200銘柄×5年(約20万行)というデータ量に対して、79特徴量が過学習の限界点でした。
- 特徴量を増やす → ノイズが増えて過学習が悪化
- 特徴量を減らす → 中長期に必要な情報が失われる
- データ期間を延ばす → コロナ前後で市場構造が異なりノイズに
この「増やしても減らしても悪化する」状態は、現在の特徴量セットが局所最適解であることを意味しています。
SHAP分析で見えたモデルの本質
SHAP(SHapley Additive exPlanations)で各特徴量の寄与度を分析した結果、モデルの予測はたった3つの柱で構成されていることが分かりました。
特徴量重要度トップ10
| Rank | 特徴量 | SHAP寄与 | カテゴリ |
|---|---|---|---|
| 1 | atr_14 | 56.1%(3m) | ボラティリティ |
| 2 | vix_level | 24.9%(1w) | マクロ |
| 3 | macd_hist | 13.6%(3d) | トレンド |
| 4 | sector_code | 6.6% | セクター |
| 5 | volatility_60d | 5.2% | ボラティリティ |
| 6 | volatility_20d_zscore | 5.1% | ボラティリティ |
| 7 | market_return_20d | 4.1% | マクロ |
| 8 | price_to_vwap | 3.6% | テクニカル |
| 9 | bb_width | 2.8% | ボラティリティ |
| 10 | rsi_14 | 2.5% | トレンド |
モデルの3つの柱
- ボラティリティ(40-60%): ATR, VIX, 60日/20日ボラティリティ → 「市場の不安定さ」が最大の予測因子
- トレンド(15-25%): MACD, RSI, モメンタム → 「現在の勢い」が次の方向を示唆
- マクロ環境(10-20%): セクター、市場リターン → 「どのセクターが強いか」
衝撃的だったのは、ファンダメンタル(PER, PBR, ROE)やニュースセンチメントがほぼゼロ寄与だったこと。 79特徴量のうち下位20個はSHAP≒0でした。
大型株 vs 小型株で精度が全く違った話
当初はグロース市場の小型株498銘柄を対象にしていましたが、日経225に切り替えたところ精度が劇的に向上しました。
| Horizon | Growth 498銘柄 | Prime 200銘柄 | 差分 |
|---|---|---|---|
| 3d | 51.7% | 51.6% | ±0 |
| 1w | 51.6% | 56.5% | +4.9% |
| 1m | 51.5% | 59.9% | +8.4% |
| 3m | 54.3% | 67.3% | +13.0% |
なぜ大型株の方が予測しやすいのか
- データが豊富: 出来高が大きく、ニュースも毎日出る。センチメント特徴量が機能する
- ノイズが少ない: 小型株は出来高が少なく、少額の売買で株価が大きく動く
- セクター連動が安定: 大型株はマクロ指標との連動が明確(ドル円↑→自動車株↑ etc.)
バックテスト結果
66,215件のバックテスト結果を全て公開しています。
バックテスト全データ: kabu.microforge.works/track-record
方向的中率の推移
各テスト期間での的中率にはバラつきがありますが、全体として50%を安定的に上回っています。
| テスト日 | 3d | 1w | 1m | 3m |
|---|---|---|---|---|
| 2025-10 | 44% | 53% | 45% | 47% |
| 2025-11 | 59% | 66% | 57% | 55% |
| 2025-12 | 55% | 50% | 57% | 50% |
| 2026-01 | 47% | 57% | 52% | - |
| 2026-02 | 60% | 55% | 48% | - |
| 平均 | 53% | 56% | 52% | 51% |
上記のベンチマーク(5テスト日の平均)とバックフィル全体(66,215件)の数値は異なります。これはテスト日のサンプリングによるバイアスです。バックフィル全体での精度は答え合わせページで確認できます。
まとめと教訓
技術的な教訓
- 特徴量は増やせばいいというものではない — 200銘柄×5年のデータ量では79特徴量が限界
- モデルの本質はSHAPで見える — 79特徴量のうち実質的に予測に使われているのは上位10個
- 大型株は予測しやすい — データ量・ノイズの少なさ・セクター連動の安定性
- 古いデータは毒になる — コロナ前後で市場構造が変わるため、5年が最適
-
スタッキングの非負制約が重要 —
positive=Trueがモデルの安定性を大幅に向上
ビジネス面の教訓
- 金融系サービスは法的リスクが大きい — 弁護士相談が必須
- 「投資助言」に該当しない建て付けが重要 — 全機能無料+登録不要
- 用語1つで法的リスクが変わる — "Buy" → "ポジティブ" に変えるだけで安全に
コード
本プロジェクトのサービスは https://kabu.microforge.works で全機能無料で公開しています。
免責事項: 本記事および本サービスは統計的な計算結果の提示を目的としており、特定の金融商品の売買を推奨するものではありません。投資判断はご自身の責任で行ってください。