過去4年・977営業日の相場を、AIと一緒に1日ずつやり直しました。かかった時間は1時間46分、出したピックは1万3,542件⏪
……なのに、本番の評価の数字は1ミリも動きませんでした😇 しかも、それで正解でした。
日本株のAI銘柄スクリーニング&継続学習端末「ALPHA FORGE(アルファフォージ)」を、Claude Code と二人三脚で個人開発しています。今回は【事件簿】回。過去に戻って学び直す「リプレイ学習」を作った話と、「数が増えても本番は動かない」ように作った話です🛠️
【要約:この記事で伝えたいこと】
- 「いちばん古いデータから1日ずつやり直して学習したい」と頼んだら、AIは作る前に「LLMは結末を知っている」「全期間だと約290ドル」と2つの待ったをかけた
- 第1段階はLLMなし・本番と分けた専用台帳で、LLMありの第2段階は予算15ドルまで。枠を決めたのは人間
- 持ち帰り:未来のデータは「気をつける」ではなく、読もうとした瞬間に例外で止まる門番で防ぐ。やり直しの結果は本番と分け、届いていないことを画面で確かめる
※本記事はnote連載[ALPHA FORGE 開発秘話 #20] の技術的な部分を、エンジニア向けに再構成したものです。
⏪ 過去に戻って、1日ずつやり直したい
ALPHA FORGE は、毎朝の予測とその後の実際の値動きを突き合わせて、モデルを学び直しています。ただ、予測は1日1回。答え合わせの済んだ記録は、カタツムリのペースでしか増えません🐌
そこで9月23日、私はAI(Claude Code)にこう頼みました。
取得できるもっとも古いデータからAIピックを行い、その次の日のデータで答え合わせすることで学習を進めたい
過去の各日に「その日の朝に戻ったつもり」で予測を出し、その後の値動きで答え合わせする。何年分も繰り返せば、学習の材料が一気に増える……はず! 気分はタイムマシンです🚀
⚠️ 作る前に出てきた、2つの「待った」

過去に戻ったつもりでも、AIは結末を知っているかもしれない。そして、やり直しの結果を本番の成績に混ぜてはいけない
AIは、すぐには作り始めませんでした。「判断が必要な点が2つあります」と整理して返してきたんです。
🔮 1つ目:LLMは、その後の相場を知っている
ALPHA FORGE の判断には、LLMが入っています。ところがLLMは、学習した時点までの世の中の出来事を知っています。過去のある日に戻って「この銘柄はこの先どうなる?」と聞いても、LLMはその後の展開を覚えているかもしれない。それは予測じゃなくて、ただの思い出しです。
しかもお金がかかります。AIは実際の利用記録から1回あたり約0.015ドルという単価を出し、全期間(約950営業日、1日最大20回)を回すと約290ドルになる、と試算してきました。
LLMを過去データの検証に使うと、「学習時点より前の期間」では予測ではなく記憶の再生になりえます。バックテストにLLMを入れるときは、対象期間をモデルの学習時点より後に絞るのが安全です。
🧼 2つ目:本番の成績が汚れる
やり直しの結果を本番の台帳に入れると、画面に出る実測の勝率や、モデルの昇格判定に使う成績が「実際の運用ではない数字」で汚れます。第18話の試し打ちモードと同じ問題です。
🧭 人間が決めた3つのこと
この2つを受けて、私は3つを決めました。
- 段階に分ける。第1段階はLLMを使わず、候補選び・株価から計算できる分析・機械学習・価格の目安までの定量部分だけで全期間を再現する
- 結果は専用の台帳に分ける。機械学習の学び直しには使うけれど、本番の勝率や昇格の判定には混ぜない
- LLMを含める第2段階の予算は15ドルまで。LLMが学習した時点より後の期間に絞り、上限に達したら止まる仕組みを入れる
290ドルという数字があったから、15ドルという枠を即決できました。数字で渡された材料は、数字で決められるんです💡
🔒 未来を読もうとしたら、例外で止める
第10話では、未来のカンニングを「その日以前の記録しか結び付けない」やり方で防ぎました。リプレイでは、もう一段強くしています。データの読み出し窓口(リプレイ日時点のビュー)そのものに、日付の門番を置きました。以下は実際のコードからの抜粋です。
class FutureDataAccessError(RuntimeError):
"""リプレイ日より後のデータへアクセスしようとした(未来リーク)."""
class AsOfView:
...
def assert_visible(self, date: str) -> None:
if date > self.as_of:
raise FutureDataAccessError(f"{self.as_of} 時点で {date} のデータは参照できません")
リプレイの日付より後のデータを読もうとした瞬間に、例外でドーン💥 未来のデータが静かに混ざるのではなく、うるさく失敗する。「気をつける」のではなく「間違えられない」形です。
この門番は、テストで「未来の日付を読んだら例外になる」ことを固定しています。
with pytest.raises(ps.FutureDataAccessError):
view.bars_on("2021-10-05")
🧪 開発者的に面白かった小技
- 生存者バイアス対策: 日付ごとに「その日に上場していた全銘柄」を取り直す。のちに上場廃止になった銘柄も含まれるので、生き残った銘柄だけで検証する偏りを避けられる
- 一括処理で約2.8倍速: テクニカル分析の計算を一括処理に書き換え、1銘柄あたり約123ミリ秒→約44ミリ秒。旧実装と結果が同じことはテストで固定
- 本番を占有しない別プロセス: 生存確認はプロセスIDではなく、定期的な生存報告(ハートビート)で行う
- 採用は人間の承認だけ: 学び直したモデルは「挑戦者」として登録されるだけ。本番への採用は、人間の承認でしか起きない
Windows の Python では、POSIX でプロセスの存在確認によく使う os.kill(pid, 0) が、実は CTRL_C_EVENT(Ctrl+C)の送信になってしまいます😱 長時間ジョブの生存確認は、ハートビートのように「ジョブ側から定期的に生きていると書き込む」方式が安全です。
📉 4年ぶん走らせても、本番の数字は動かない(それでいい)
当日はまず3か月分で試運転(14時23分〜14時48分)。終わったところで、私はモデルラボの画面を開いてAIに聞きました。
ソースアブレーション評価はかわりませんか
(情報源を1つずつ外して効き目を比べる評価のことです)。続けて「銘柄別モデルの品質分布も変化なし」。
AIの答えは「変わりません」。しかも理由つきでした。リプレイは専用の台帳にしか書かないし、登録した学び直しモデルは挑戦者1本だけで、品質分布の集計の対象(銘柄ごとの現役モデル)には入っていない。混ぜないと決めたものは、ちゃんと混ざっていなかったんです✅
その裏で、4年ぶんの本番リプレイが走っていました。リプレイ台帳に残っている実数はこうです。

リプレイ台帳に残った実数。数は増えた、でも本番の評価は動かず
| 項目 | 値 |
|---|---|
| 期間 | 2022-09-22〜2026-09-22(977営業日) |
| 出したピック | 13,542件 |
| 答え合わせ | 39,626件 |
| 途中の学び直し | 49回 |
| 所要時間 | 14:51 開始 → 16:37 完了(1時間46分) |
数はドカンと増えました。でも、本番の評価の数字は動かない。これは失敗ではなく設計どおりです。やり直しの効き目が本番に届くのは、挑戦者が昇格の評価を通り、人間が承認したときだけ。タイムマシンで集めた経験は、まだ「控え室」にいます🪑
第2段階(LLMあり・15ドル)は、まだ手を付けていません。「数が増えた=賢くなった」ではない。それを画面で確かめられたのが、この日いちばんの収穫でした。
やったこと/やらなかったこと
- やったこと: LLMなしの第1段階を全期間で実行。結果は専用台帳と挑戦者登録まで
- やらなかったこと: 最初からLLM込みで全期間を回す案。約290ドルかかるうえ、LLMが結末を覚えている期間では「予測」の検証にならないから
- やらなかったこと: リプレイの成績を本番の勝率に混ぜる案。画面の数字が実運用でなくなるから
- 決めたのは人間: 段階分け・台帳分け・予算15ドル。AIは試算と設計で判断材料をそろえました
🧰 今回のプロンプト術:予算を数字で渡す
今回いちばん効いたのは、予算を数字で渡したことでした。「なるべく安く」ではなく「15ドルまで」💰
数字があると、AIの計画はその枠の中で組まれます。対象期間を絞る、まとめて安く処理する方式を使う、上限で止まる仕組みを入れる。どれも、枠があって初めて出てくる工夫です。
- 費用のかかる作業は、まず実績の単価から試算させる
- 試算を見て、人間が上限を数字で決める
- 上限に達したら止まる仕組みを、計画に入れさせる
🤝 まとめ:今回の役割分担
- 言い出したのは人間💭: 過去に戻って1日ずつやり直したいという発想
- 待ったをかけて作ったのはAI🛠️: LLMの未来リークと約290ドルの試算、本番の成績が汚れる問題の指摘。専用台帳・日付の門番・別プロセス実行の設計と実装
- 決めて疑ったのは人間🧐: 段階分け・台帳分け・予算15ドル。試運転のあと「本当に変わってない?」を画面で確かめた
保存用チェックリスト
- 過去データの検証は、未来を読んだら例外で止まる窓口を通す
- LLMを過去の検証に使うときは、学習時点より前の期間を避ける
- お金のかかる処理は、実績の単価から試算させてから上限を決める
- やり直しの結果は本番の台帳と分ける
- 本番に混ざっていないことを、画面の数字で確かめる
次回は【事件簿】【ハーネス】回。「ドキュメントは嘘をつく ― 実装とズレたMDを大掃除して765行消した日」。AIは古いドキュメントを読むと、正しく間違えます📚
☕ note.comで開発秘話と毎朝の検証ログを連載中
note.comでは、「ALPHA FORGE」の開発秘話と、毎朝のアルゴリズム検証ログを公開しています。
👉 元記事(note連載第20話):
https://note.com/featured_stocks/n/nd1c4bfa293f2
免責事項
本記事は個人開発およびAIエージェントを活用したシステム開発の技術記録です。特定の銘柄の売買推奨や投資助言を行うものではありません。本ツールは証券会社等への発注機能を持ちません。記事中の費用の試算・処理時間・件数は、開発時の記録とリプレイ台帳にもとづく値です。過去データでの再現の結果は、将来の成果を示すものではありません。紹介したコードは抜粋であり、実際のファイルそのものではありません。
⚠️ 【システム検証記録に関する免責事項・注意事項】
- 本記事は、独自開発アルゴリズム「ALPHA FORGE」の動作検証およびデータ分析過程を公開する技術・運用の個人的な記録ログです。
- 掲載されているすべてのデータ(各スコア、基準観測値、統計的変動上限、シナリオ無効化水準など)は、過去の市場データに基づき数式(ATR等)により機械的に算出されたバックテスト・シミュレーション用のパラメータであり、特定の有価証券の売買勧誘、取引の推奨、投資助言・代理行為を目的としたものではありません。
- また、将来の株価変動や運用成果を保証するものではありません。実際の投資判断および最終決定は、必ずご自身の責任と判断において行っていただけますようお願いいたします。





