0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonプラグインアーキテクチャで投資分析の再現性を確保する【投資分析システム設計記 #1】

0
Last updated at Posted at 2026-04-17

Claude に「この銘柄どう思う?」と聞けば、それっぽい分析は返ってくる。

ただ、毎回プロンプトを投げるたびに微妙に違う判断が出てくるし、200銘柄をスクリーニングしようとすればトークン消費が膨大になる。再現性がなく、費用対効果も厳しい。

そこで考え方を変えた。定量的な分析は Python で確定的に回し、Claude には「複数の分析結果を統合して人間に伝える」部分だけを任せる。RakuScan はその設計思想で作った投資分析システムで、現在9本のプラグインが動いている。

この記事はシリーズ第1回として、プラグインアーキテクチャの設計思想と全体構成を書く。

RakuScan の全体像

RakuScan は、日本株のスクリーニングと銘柄分析を自動化するシステム。技術スタックは Python 3.13 + uv。

やっていることはシンプルで、毎日16時に以下の流れが自動で走る。

  1. 投資対象の銘柄群(ユニバース)を取得する
  2. 各銘柄に対して、複数の投資手法(プラグイン)を実行する
  3. 結果を Claude が統合分析して、Discord に通知する

ポイントは、2 の「投資手法」がすべて独立した Python スクリプトになっていること。ファイルを1個追加して設定に1行書けば、新しい手法がそのまま翌日のスクリーニングに組み込まれる。

なぜプラグイン型にしたのか

投資分析は手法の入れ替えが頻繁に発生する。

「モメンタムだけで見てたけど、やっぱり財務健全性も確認したい」「この手法は成績が悪いから一旦外したい」みたいなことが日常的に起きる。さらに、手法ごとに参照する学術論文も違えば、使うデータソースも異なる。

こういう性質のシステムを1枚岩で書くと、手法を追加するたびに既存コードに手を入れることになって、すぐに保守が厳しくなる。

だからこう決めた。

  • 1つの投資手法 = 1つの Python ファイル
  • すべてのプラグインは同じフォーマットで結果を返す
  • プラグイン同士は互いの存在を知らない

この設計にすることで、「手法を追加しても他の手法に影響しない」状態を維持している。

現在のプラグイン構成

9本のプラグインが動いていて、それぞれ学術論文ベースの投資手法を実装している。

プラグイン カテゴリ やっていること 根拠
regime 市場環境 TOPIX の200日移動平均で攻め/守りを判定 Faber (2007)
piotroski 財務 9項目の財務チェックで健全性をスコア化 Piotroski (2000)
factor_momentum ファクター 12ヶ月リターンでモメンタムを評価 Jegadeesh & Titman (1993)
factor_value ファクター PBR・PER・PCFRでバリューを評価 Fama & French (1992)
factor_quality ファクター ROE・ROA・FCFで収益の質を評価 Novy-Marx & Velikov (2016)
altman_z 財務 Z-Score で倒産リスクを判定 Altman (1968)
sepa テクニカル トレンドテンプレートの条件を判定 Minervini VCP
exit_rules リスク 5つの売却条件をチェック ATR ベース
factor_composite ファクター モメンタム×バリュー×クオリティの複合スコア Asness et al. (2013)

カテゴリは5種類ある。市場環境(regime)、ファクター(factor)、財務(fundamental)、テクニカル(technical)、リスク管理(risk)。カテゴリが違えば見ている観点がまったく異なるので、複数のプラグインが同時に BUY と言っているときほど信頼度が高くなる。

さらに7本の未実装プラグイン(CANSLIM、デュアルモメンタム、タートルブレイクアウトなど)が設計段階で控えている。実装したらファイルを置いて設定を1行足すだけで組み込める。

共通インターフェース:PluginResult

プラグインアーキテクチャの核は、すべてのプラグインが返す共通のデータ構造にある。

PluginResult という dataclass を定義していて、どのプラグインもこの形式で結果を返す約束になっている。

主なフィールドはこう。

フィールド 型 意味
signal str 5段階の判定(STRONG_BUY / BUY / NEUTRAL / SELL / STRONG_SELL)
score float 0.0〜1.0 に正規化されたスコア
confidence float 0.0〜1.0 の確信度
summary str 1行の日本語要約
details dict 手法固有の詳細データ
category str 5種類のカテゴリ

ここで重要なのは、score と confidence を分離していること。

たとえば Piotroski F-Score が 7/9 なら score は 0.78 だが、財務データに欠損があれば confidence は 0.6 に下がる。「高スコアだけどデータが不完全」という状態を表現できる。

バリデーションも入っていて、score や confidence が 0.0〜1.0 の範囲外だったり、signal が定義済みの5段階に合致しなかったりすると、インスタンス生成時にエラーになる。型で壊れたデータが後段に流れるのを防いでいる。

プラグインの規約

プラグインを書く側のルールは最小限にしている。

  • scripts/ ディレクトリに Python ファイルを置く
  • run() 関数を公開する
  • PluginResult を返す

これだけ。銘柄コードを受け取って分析するプラグインなら run(symbol: str) -> PluginResult、市場全体を見る regime のようなプラグインなら run() -> PluginResult になる。

各プラグインは if __name__ == "__main__" を付けてあるので、コマンドラインから単体実行もできる。開発中に個別の手法だけ試したいときに便利。

動的ロードの仕組み

プラグインの有効/無効は plugins.yaml で管理している。

plugins:
  - name: regime
    display_name: マーケットレジーム
    enabled: true
    category: regime

  - name: piotroski
    display_name: ピオトロスキー F スコア
    enabled: true
    category: fundamental

  # 成績が悪ければ enabled: false にするだけ
  # - name: canslim
  #   enabled: false

スクリーニング実行時、ローダーがこの YAML を読んで enabled: true のプラグインだけを importlib.import_module() で動的にロードし、run() 関数を呼ぶ。

新しいプラグインを追加するときの手順はこう。

  1. scripts/new_strategy.py を作る
  2. run() 関数で PluginResult を返すように実装する
  3. plugins.yaml に1行追加する

既存のプラグインには一切手を入れない。この「追加だけで拡張できる」性質が、プラグインアーキテクチャの一番の恩恵だと感じている。

プラグインを束ねるスクリーナー

個別のプラグインを統合して動かすのが screener.py の役割。処理の流れはこう。

  1. plugins.yaml から有効なプラグインの一覧を取得する
  2. ユニバース(分析対象の銘柄群、150〜200銘柄)を取得する
  3. まず regime プラグインで市場環境を判定する。守りレジーム(DEFENSIVE)なら新規推奨は出さない
  4. 各銘柄に対して、有効な全プラグインを実行する
  5. 銘柄ごとに consensus(BUY/NEUTRAL/SELL の数)と composite_score(加重平均スコア)を計算する
  6. スコア上位の銘柄を抽出する

スクリーナー自体はプラグインの中身を知らない。PluginResult という共通形式を信頼して、signal と score と confidence だけを見て集計している。だから新しいプラグインが増えても、スクリーナーのコードは変わらない。

Claude の立ち位置:統合分析だけを任せる

ここまでの処理はすべて Python で確定的に動く。同じデータを入れれば同じ結果が出る。

Claude が登場するのはこの後。スクリーナーが出力した JSON(プラグイン名・シグナル・スコア・サマリー)を Claude Code CLI に渡して、統合レポートを生成させる。

つまり役割分担はこう。

役割 担当 理由
個別手法の定量判定 Python(各プラグイン) 再現性が必要。同じデータなら同じ結果
結果の統合・言語化 Claude 複数の矛盾する判定を読み解いて自然言語にまとめるのは LLM の得意領域

この設計にした背景には、最初にすべてを Claude にやらせようとして失敗した経験がある。

「TOPIX の200日移動平均を計算して、この銘柄の F-Score を出して、モメンタムも計算して…」と1つのプロンプトに詰め込むと、トークン消費が膨大になるし、計算の再現性もない。10回聞けば10回違う数字が返ってくることもあった。

今の設計では、Claude に渡すのはプラグインの出力 JSON だけ。各手法の生データや計算過程は渡さない。トークン消費を最小限に抑えつつ、Claude には「判断の統合」という最も得意な仕事だけをさせている。

最終的にこのレポートは Discord に通知される。スマホから毎日のスクリーニング結果を確認できる。この通知の仕組みと、Discord Bot による双方向のやりとりについてはシリーズ第3回で詳しく書く。

3つのインターフェースで同じエンジンを共有する

スクリーナーを動かす方法は3つある。

  • CLI: python -m scripts.daily_run で日次パイプラインを実行
  • Claude Code スキル: /scan でスクリーニング、/analyze 7203 で個別分析
  • Discord Bot: !scan で結果確認、!research で Claude を使った深掘り

どのインターフェースも内部では同じ screener.py を呼んでいる。入り口が違うだけで、走る分析は同じ。

平日16時の cron で daily_run.py が自動実行され、結果が Discord に飛んでくるのが基本の運用フロー。気になる銘柄があれば、Discord Bot に !analyze 7203 と打てばその場で全プラグインが走る。

段階的に育てられる設計

RakuScan は最初から9本のプラグインを揃えたわけではない。

最初に動いたのは3本だけ。regime(市場環境)、piotroski(財務健全性)、factor_momentum(モメンタム)。この3本でスクリーニングの基本形ができた段階で日次運用を開始し、そこから手法を1つずつ追加していった。

この「ミニマムで動き始めて、あとから足していく」ができるのは、プラグインアーキテクチャの構造的なメリットだと思っている。

現状でもまだ7本の未実装プラグイン(CANSLIM、デュアルモメンタム、タートルブレイクアウトなど)が設計段階で控えていて、優先度の高いものから順に実装を進めている。

まとめと次回予告

RakuScan のプラグインアーキテクチャをまとめるとこう。

  • 1ファイル = 1手法。プラグイン同士は互いを知らない
  • 共通インターフェース(PluginResult)で結果の形式を統一
  • YAML + 動的ロードでプラグインの追加・削除がコード変更なしで可能
  • Python で定量、Claude で統合。再現性とトークン効率を両立

シリーズ第2回では、3つの外部 API(yfinance / EDINET DB / J-Quants)のデータ層設計と、API 無料枠でやりくりするキャッシュ戦略について書く。

第3回では、プラグインの出力が Claude の手で統合レポートになり、Discord でスマホに届くまでの仕組みを書く。9本のプラグインが出したバラバラの判定を、Claude がどう1つのレポートにまとめるのか。このシリーズで一番面白い部分になると思う。

実際の Discord 通知はこんな感じで届く。

RakuScan の Discord 日次レポート
Claude が9本のプラグインの結果を統合して、スマホに毎日届く


この連載の内容をさらに深掘りした有料本を公開中。各プラグインの戦略設計の詳細、設計判断の裏側にある思想、失敗と修正の過程、実コードの解説など、連載では書ききれなかった内容を15章・約5万文字にまとめた。
https://zenn.dev/sktt_panda/books/rakuscan-design-philosophy


この記事は Zenn にも同じ内容を投稿しています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?