AI株式投資ツールに、「過去の上昇前パターン類似度チェック」という機能を追加してみました。
やりたかったのは、買おうとしている銘柄が、過去に大きく上昇した銘柄の「上昇する前の状態」とどれくらい近いのかを見られるようにすることです。過去に上がった銘柄の上昇後の姿ではなく、上がる前に見えていた特徴量と比べます。
ただし、これは買いシグナルを増やすための機能ではありません。距離が近いから上がる、という話にはしません。あくまで、購入候補銘柄を見たときに「過去の上昇前パターンに近いのか」「どの特徴量が似ていて、どこが違うのか」を確認する補助材料として使いたいと思っています。
今回はまだ売買を十分に重ねていないため、自分の成功・失敗パターンをためたデータベースはありません。その状態で試したので、最初はパターンDBが未構築でエラーも出ました。むしろ今回は、そのエラーも含めて「類似度を株式投資ツールのどこで使えるか」を探るトライアルとして記録します。
ユークリッド距離そのものの計算方法は、以前のPythonでユークリッド距離を計算する記事で整理しています。この記事では、そこから一歩進めて、距離計算を投資ツールの確認機能にどう持ち込んだかを書きます。
類似度を買いシグナルではなく、候補を見直す補助にしたかった
最初に決めたのは、この機能を「買い判断の自動化」にしないことでした。過去の上昇前パターンに近いから買う、という使い方にしてしまうと、距離計算に期待しすぎてしまいます。株価は似た特徴量を持っていても、同じように動くとは限りません。
私が見たかったのは、候補銘柄を見たときの確認材料です。過去に上がった銘柄の上昇前の状態と似ているのか。似ているなら、どの特徴量が近いのか。逆に、距離が遠いなら、何が違うのか。そこを見えるようにしたいと考えました。
画面では、過去の上昇前パターンとの距離、類似度ラベル、近い過去銘柄TOP5、近い特徴量、違う特徴量を出す想定にしました。ラベルは「近い」「普通」「遠い」の3段階です。見た目としてはわかりやすくしつつ、最後には必ず「距離が近いことは上昇を保証しない」という注意文を入れるようにしました。
類似度は答えではなく、候補銘柄をもう一度見直すための問いとして扱う。この前提を置いたことで、機能の位置づけはかなり整理しやすくなりました。
比較対象は、上昇した後ではなく上昇する前の特徴量にした
Claude Codeに最初に強く伝えたのは、未来情報を使わないことです。過去に上昇した銘柄を比べるとき、上昇後の特徴量を使ってしまうと、実際の投資判断では使えない情報になります。見た目はそれらしい結果が出ても、買う前には見えていなかった情報を混ぜていることになるからです。
そこで、比較対象は「最近上昇した銘柄の現在の特徴」ではなく、「過去に上昇した銘柄の、上昇する前の時点で見えていた特徴量」にしました。基準日以前のデータだけを使って特徴量を作り、その後60営業日以内に15%以上上昇したかどうかを見る形です。
設定値は、まず仮でrise_window_days: 60、rise_threshold: 0.15にしました。この条件を満たした基準日を、上昇前パターンとして保存します。後から変更できるようにしておくことで、検証しながら調整できる余地も残しました。
投資ツールでは、ここを曖昧にすると一気に危なくなります。上がった後の情報で似ているかを見るのではなく、買う前に見えていた情報だけで似ているかを見る。今回の実装では、この制約を最初に置けたのがよかった点でした。
実装では、取れるデータに合わせて価格ベースの6特徴量に絞った
最初は、PER、PBR、ROE、売上成長率、営業利益率なども特徴量候補に入れていました。ファンダメンタル情報も含めて似ているかを見られたら、かなり面白そうだと思ったからです。
ただ、コードベースを確認していくと、今回使っているyfinanceでは過去時点のPER、PBR、ROEなどを安定して取得できず、現在の情報しか使えないことがわかりました。現在のファンダメンタル情報を、過去の上昇前パターンに混ぜてしまうと、比較としてはずれてしまいます。
そのため、今回は無理にファンダメンタル情報を使わず、価格ベースの特徴量だけに絞りました。最終的に使ったのは、20日リターン、60日リターン、出来高比率、25日移動平均線からの乖離率、75日移動平均線からの乖離率、20日ボラティリティの6つです。75日移動平均線を使うため、最低でも76本のローソク足が必要になります。
距離計算では、単位の違う特徴量をそのまま比べると数値の大きい項目に引っ張られます。そのため、z-scoreで標準化してからユークリッド距離を計算するようにしました。欠損している特徴量は無理に埋めず、距離計算から除外する方針にしています。
取れないデータを無理に使わず、今のデータで未来情報を混ぜずに扱える範囲へ絞る。地味ですが、今回の機能ではこの判断がかなり大事だったと思います。
Claude Codeには、domainから画面まで小さく分けて実装してもらった
実装では、Claude Codeにまず計画を作ってもらいました。方針としては、domain層に純粋な類似度計算ロジックを作り、data層でパターンDBを構築し、app_api層にAPIを追加し、web層に類似度パネルを置く流れです。
中心になったのは、packages/domain/src/trading_domain/similarity.pyです。ここには、FeatureVector、PreRisePattern、PatternMatch、SimilarityResult、compute_features()、find_similar_patterns()などを置きました。外部APIやファイル読み書きには依存させず、純粋な計算ロジックとして扱えるようにしています。
パターンDBの構築は、packages/data/src/trading_data/pattern_builder.pyで行います。TOPIX500の銘柄リストを取得し、キャッシュまたはyfinanceから過去データを読み、条件を満たした上昇前パターンをpackages/app_api/data/patterns/pattern_db.jsonへ保存する設計です。
API側には、POST /api/v1/similarity/build、GET /api/v1/similarity/status、GET /api/v1/similarity/{code}を追加しました。フロント側では、SimilarityPanelを作り、候補銘柄の行に「類似度」ボタンを追加しました。
検証では、最初にsimilarity.pyの単体テスト20件がすべて通り、その後の全体テストも最終的に177件すべてパスしました。lintは最初にruff: No such file or directoryで止まりましたが、uv run --with ruffで実行し直し、try-exceptの書き方、import順、未使用importを直して通しました。
最初に出たエラーで、パターンDBがないと類似度は始まらないとわかった
実装後、画面で「類似度」ボタンを押すと、最初にエラーが出ました。内容は、パターンDBが未構築なので、先にPOST /api/v1/similarity/buildを実行してください、というものです。
APIとしては正しいエラーです。類似度を計算するには、比較元になる過去の上昇前パターンDBが必要です。DBがなければ、距離を測る相手がいません。今回のように、まだ売買履歴も十分になく、自分の成功・失敗パターンが育っていない段階では、なおさら「比較元をどう作るか」が重要になります。
ただ、画面を使う側から見ると、このエラーは少し不親切でした。APIのメッセージとしては意味が通っていても、ユーザーは「では、画面上で何をすればいいのか」がわかりません。実際にボタンを押して初めて、DBがないことに気づく状態でした。
ここで感じたのは、ロジックが正しくても、画面の導線が足りないと機能としては使いにくいということです。類似度計算の実装そのものより、最初に詰まったのは「計算前に必要な準備をどう案内するか」でした。
DB未構築でも押せるボタンは、機能ではなく迷いを増やした
最初のエラーを見て、CandidatePageの上部に「過去パターンDB」の状態を表示するバーを追加しました。未構築、構築中、構築済み、エラーの状態を表示し、未構築の場合は「構築する」ボタンを出す形です。
構築ボタンを押すと、POST /api/v1/similarity/buildを呼び出し、バックグラウンドでパターンDBの構築を開始します。ステータス確認には、GET /api/v1/similarity/statusを使います。ここまでで、ユーザーが何をすればよいかはかなり見えやすくなりました。
ただ、それでもまだ問題が残りました。DBが未構築の状態でも、各候補銘柄の「類似度」ボタンが押せてしまったのです。つまり、上部に構築ボタンはあるのに、下の行ではまだ類似度計算を実行できるように見えてしまう状態でした。
最終的には、dbStatus?.db_exists === trueのときだけdbReadyをtrueにし、DB未構築時は類似度ボタンを無効化しました。表示も「類似度(DB未構築)」に変え、マウスを乗せたときに「上部の『構築する』ボタンでパターンDBを構築してください」と案内するようにしました。
APIで正しいエラーを返すだけではなく、ユーザーがその前に迷わない画面にする。今回の追加修正で、そこまで含めて初めて「使える機能」に近づくと感じました。
売買が増えてから、類似度チェックの意味を育てていきたい
今回の機能は、完成された投資判断ロジックというより、類似度という考え方を株式投資ツールの中で試してみたトライアルです。LLMや検索でも類似度の考え方に触れる機会が増えていて、株価や特徴量の判定でも使える場所があるのではないか、という興味から実装してみました。
今の段階では、まだ自分の売買履歴が十分にありません。過去に自分が買ってよかった銘柄、失敗した銘柄、買わなかったけれど上がった銘柄をためて比較できるほどのデータは、これから育てていく必要があります。その意味では、今回の類似度チェックは、現時点で強く信じる機能ではなく、将来の確認材料を増やすための土台です。
それでも、候補銘柄を見るときに「過去の上昇前パターンと近いのか」「価格の動きや出来高の特徴は似ているのか」「逆に何が違うのか」が見えるだけでも、判断を少し冷静にできます。チャートの印象だけで買い急ぎそうなときに、一度立ち止まる材料になります。
最終的に実装できたのは、過去の上昇前パターンとの距離計算、類似パターンTOP5、近い特徴量と違う特徴量の表示、z-score標準化、未来情報を使わない設計、注意文、DB未構築時の構築ボタン、DB未構築時の類似度ボタン無効化までです。全177件のテストも通り、lintも通せました。
今回は、買いシグナルを増やしたというより、候補銘柄を判断するときの確認材料を一つ増やした実験でした。これから実際の売買が増えてきたとき、この類似度チェックがどのくらい役に立つのか、使いながら見直していきたいと思っています。

