どうも、コンです。
このブログでは時々、投資 × プログラミング系の記事も書いています。自分もまぁ今時の社会人っぽく、NISA 制度を活用してコツコツ資産形成をしている勢です。
普段の自分は「なるべく売買しない長期投資派」です。毎月インデックスに積み立てて、チャートはほぼ見ません。
でもある日ふと「楽して儲かる方法とかあるんじゃないか?」と思い立ち(最低だ)、Flask + Jinja2 + Claude Code で株のバックテスト可視化アプリを作って遊んでみた話です。実際のお金は使いません、過去データのシミュレーションだけ。
ソース一式は GitHub に公開しています:konPlantPG/vwap-web
作ったもの
証券コードを入力すると、3つの戦略で過去12週分の成績を比較してくれる Web アプリです。初期資金は銘柄ごとに100万円、全部シミュレーション。
戦略選びは正直何を入れればいいかわからなかったので、Claude Code に「投資初心者でも肌感がつかめる定番戦略をいくつか教えて」と聞いて、出てきた中からこの3つを採用しました。
- VWAP 戦略:期間累積の出来高加重平均価格(VWAP)を基準にして、終値が上抜けで買い・下抜けで売り
- 出来高加重価格戦略:出来高が直近5日平均の1.5倍を超えた日の値動き方向にベット(要は出来高ブレイクアウト)
- 移動平均クロス戦略:短期(5日) と長期(25日) のクロスで売買
用語だけちょっと補足しておくと、
- 上抜け/下抜け:終値が基準線(VWAPや移動平均)を下から上/上から下に突き抜けた瞬間。上向き/下向きの勢いが出てきたサインとして売買判断に使う
- ブレイクアウト:「いつもの状態を突き破って飛び出す」こと。ここでは出来高(売買量)が普段の1.5倍を超える日 = 何か特別なことが起きた日、と判定してその方向に乗っかる
- クロス:短期線と長期線が交差した瞬間。短期が長期を上に抜ければゴールデンクロス、下に抜ければデッドクロスと呼ぶらしい
実際の Web アプリのスクリーンショットがこちら。トップ画面はシンプルで、証券コードを入れて「バックテスト実行」を押すだけです。

実行するとヒートマップで「銘柄 × 戦略」の損益が一目で見えます。プラスは緑、マイナスは赤。セルをクリックすると詳細ページに飛びます。

詳細ページではローソク足にインジケータと約定シグナルを重ねて表示。「いつ買って、いつ売ったか」が色分けマーカーで見えます。

下にはトレード履歴テーブル。日付・売買・理由(シグナル/損切り/利確)・株数・損益・累計損益が並びます。

損切り/利確を入れたら「大きく負けない感」が出てきた
初期版は「シグナルで買う・反対シグナルで売る」だけだったのですが、それだと反対シグナルが出るまで損を抱え続けることになります。そこで後から損切り・利確オプションを追加しました。
- 損切り:エントリ価格から -3% 下がったら自動で手仕舞い(デフォルト値)
- 利確:エントリ価格から +5% 上がったら自動で手仕舞い(デフォルト)


地味なオプションですが、有効にするとトレードの挙動がガラッと変わります。「抱え続けてじわじわ負ける」のが減って、大きく負けない感が出てくるんですよね。利益は伸びきらない代わりに最悪値がマシになる、というリスク管理の効き方が、グラフ上でもトレード履歴上でもはっきり見えました。損切りって重要なんですね。
詰まったポイント:J-Quants API の 5 req/分 制限
株価データには J-Quants(JPX公式の金融データ API)の Free プランを使いました。個人向けで無料なのはありがたいのですが、遊び始めてすぐぶつかったのが 5リクエスト/分 というレート制限です。これを超えると約5分のブロック(出禁の儀)に入ります。10銘柄を並べて一気に叩こうものなら、最初の5発で即座にブロック。体感として、遊んでいるうちに一番よくハマるのがこれです。
対策として、3段構えで実装しました。
- 同じ銘柄の日足データは プロセス内に1時間キャッシュ(2回目以降は API を叩かない)
- 銘柄ごとの呼び出しの間に 2秒のウェイト(1分あたり最大でも30回に収まる保険)
- 429(Too Many Requests)を食らったら 指数バックオフで最大3回リトライ。レスポンスの Retry-After ヘッダも見る
外部 API を叩くアプリは、遅かれ早かれレート制限と向き合うことになるので、この辺の作法は投資アプリ以外にも効く気がします。(逆に言うと、この辺を雑に作ると Free プランで遊ぶのも割と無理です。)
UI は Flask + Jinja2 だけ
フロントエンドは Flask + Jinja2 でサーバー側レンダリングだけで済ませました。チャートは Plotly の JS ライブラリを CDN から読み込んでいます。「とりあえず動くもの」を最短で作るなら、やっぱり Flask + Jinja2 は強い。
一つだけコードを載せるとすれば、POST/Redirect/GET(PRG)パターンのところです。バックテストをフォーム送信で受けて結果ページを表示する構成だと、そのまま書くとユーザーがブラウザの戻るボタンを押したときに「フォーム再送信の確認」ダイアログが出ます。あれを防ぐ定番の手口がこれ。
@app.route("/run", methods=["POST"])
def run_backtest():
codes = request.form.getlist("codes")
results = execute_backtest(codes)
# 結果をプロセス内キャッシュに保存し、トークンだけセッションに渡す
token = _store_result(results)
session["result_token"] = token
# 302 リダイレクトで GET /result に飛ばす(これが PRG)
return redirect(url_for("show_result"))
@app.route("/result", methods=["GET"])
def show_result():
token = session.get("result_token")
cached = _load_result(token)
if cached is None:
return redirect(url_for("index"))
return render_template("result.html", **cached)POST を受けた直後にリダイレクトで GET ページに飛ばすことで、ブラウザの履歴には GET だけが残ります。戻ってきても再送信ダイアログが出ず、普通に結果ページが再表示されます。地味ですが、「フォームで重い処理を走らせる Web アプリ」だと毎回必要になる作法です。
Claude とどう作ったか(軽く)
実装には Claude Code を使いました。ざっくり 6時間ほどでひと通り動くところまで到達しています。
やったことは単純で、作りたいアプリの仕様を1枚のテキストファイル(prompt.txt)にひたすら書き出して、Claude Code に「これで作って」と渡しただけ。画面構成、戦略ロジック、API の叩き方、エラー表示、Docker 構成まで事前に言語化しておくと、あとはほぼ Claude が手を動かしてくれます。
途中で「損切り/利確を後から足したい」「スマホで見ると崩れる」「フォーム再送信ダイアログを消したい」みたいな追加要望が出てきて、そのたびに prompt.txt を更新 → Claude に追従させる、という流れで進めました。個人的には、コードを書く時間より仕様を言語化する時間のほうが本体だった感があります。
ローカルで試してみたい方へ
ソースコードは GitHub に公開してあるので、自分の手元で動かすことができます。
リポジトリ: https://github.com/konPlantPG/vwap-web
ただ、個人的におすすめしたい遊び方はコードをそのまま clone するのではなく、リポジトリの prompt.txt をそのまま Claude Code に渡して、自分の手元で同じアプリを再現してもらうやり方です。理由は単純で、Claude と対話しながらアプリが組み上がる過程そのものが一番の学びになるからです。私自身、仕様を言語化して Claude に渡す体験で得たものがかなり大きかったので。
手順はざっくり3ステップ。
- GitHub リポジトリを clone して
prompt.txtを取り出す - 空の作業ディレクトリで Claude Code を起動し、
prompt.txtを渡して「これで作って」と依頼する - J-Quants のダッシュボードで API キーを発行し、
.envにJQUANTS_API_KEYを設定する(API キーは各自で取得してください)
あとは docker compose up -d でブラウザから触れるはずです。
ちなみに、このアプリをインターネット上に公開サービスとして置くのは推奨しません。J-Quants の利用規約では取得データの第三者提供や、自分の API キーで他人にデータを使わせる行為が禁止されているので、あくまで「自分のローカル」で遊ぶ形をおすすめします。コード自体を共有するのは問題ないので、この記事のような形になりました。
まとめ
「楽して儲けたい」から始まった遊びでしたが、作ってみて得たものはだいたい以下の3つでした。
- 損切り/利確を入れるだけで「大きく負けない感」が出てくるというリスク管理の手応え
- レート制限のある API でも、キャッシュ・ウェイト・指数バックオフの3段構えで意外と遊べるという作法
- 仕様を
prompt.txtに書き切ってしまえば、Flask + Jinja2 + Claude Code だけで半日〜1日で遊べるアプリが組める感
結論、相変わらず長期積立派のままでいきます(笑)。「投資はわからないけど Python は触れる」タイプの方が、ちょっと遊んでみる取っかかりになれば良い感じにハッピーです。
ここまで読んでいただきありがとうございました。それでは、また。
