はじめに
毎朝7時にGitHub Actionsが起動し、Claude APIが日本株を分析して
Discordに結果を送ってくる仕組みを作りました。
実装期間3週間・月額コスト$2以下で動いています。
対象読者は、AIを使ったシステム構築に興味があるエンジニアです。
1. きっかけ
20歳からNISAで積み立てて5年。
2025年から個別株に興味を持ち、銘柄選定にAIを使った結果、実現損益+40万円を達成できました。
相場環境が良かっただけの可能性はあります。
ただ「判断ロジックを言語化してAIに渡す」アプローチに手応えがあった。
ならば分析プロセスそのものを自動化できるはず。
エンジニアとしてデータ分析基盤を作ってきた経験を、そのまま投資分析に適用してみようと考えました。
2. 作ったものの全体像
毎朝7:00 GitHub Actions起動
↓
collect.py 株価・決算・ニュース収集
↓
analyze.py Claude APIで銘柄分析
↓
output.py Discordに通知
↓
📱 スマホで確認(7:20)
技術スタック
- Python 3.11
- Claude API(claude-sonnet-4-5)
- GitHub Actions(無料枠運用)
- yfinance / EDINET API
- Discord Webhook
月のAPIコスト:$1.5〜2.0
3. Phase 1:毎朝Discordに分析が届く仕組み
3-1. データ収集(collect.py)
3つのソースから情報を収集します。
- yfinance:watchlist銘柄の株価・出来高・TOPIX
- EDINET API:決算短信(公式・無料)
- GoogleアラートRSS:銘柄関連ニュース
設計上の最重要点は「止まらないこと」です。
1ソース障害でパイプライン全体が落ちると朝の確認が不安定になるため、
取得失敗は例外にせずerrorフィールドに格納して処理を継続します。
# 銘柄ごとにエラーを格納して継続
for stock in watchlist["stocks"]:
try:
data = yf.download(f"{stock['code']}.T", period="2d")
except Exception as e:
results[stock["code"]] = {"error": str(e)}
continue
3-2. Claude APIで銘柄分析(analyze.py)
分析プロンプトはStep1〜6の構造化フォーマットです。
分析プロンプトの設計思想
プロンプトは6ステップの構造化フォーマットにしています。
LLMに「何を考えるか」ではなく「どの順序で考えるか」を指定するのがポイントです。
Step 1. 注目テーマ抽出
└ 織り込み不足の根拠を①〜④のいずれかで明示する
Step 2. 銘柄スクリーニング
└ 4軸スコアリング(成長性・割安度・競合優位・リスク耐性)
Step 3. 仮説構築
└ ブル/ベース/ベアの3シナリオ+仮説間の相関リスク
Step 4. 株価倍増シナリオ
└ EPS×PERの組み合わせで2倍達成条件を事前定義
Step 5. リスク・リワード評価
└ ダウンサイドを定量で明示
Step 6. 最終結論
└ 最有力銘柄1社+推奨スタンス
全文プロンプトは非公開ですが、この構造自体が設計の肝です。
「何を出力させるか」より「どういう思考順序で考えさせるか」を意識すると、LLMの出力品質が安定します。
コスト削減のため、曜日で分析深度を切り替えています。
ANALYSIS_DEPTH = {
"Monday": "full", # Step1〜6フル分析
"Tuesday": "medium", # Step1〜3のみ
"Wednesday": "medium",
"Thursday": "full",
"Friday": "medium",
"Saturday": "light", # 株価更新のみ
"Sunday": "light"
}
月額コスト試算:フル×8回+中×12回+軽×10回 ≒ $1.6〜2.0/月
3-3. Discord通知(output.py)
# 成功・失敗どちらでも必ず通知
try:
result = run_analysis()
notify_discord(result, channel="daily-report")
except Exception as e:
notify_discord(
f"🚨 分析が止まっています\nエラー:{str(e)}",
channel="error-log"
)
Discordのチャンネル構成はこうしています。
#daily-report ← 分析レポート全文
#watchlist-alert ← 株価±5%以上の変動アラート
#error-log ← エラー通知(メンション付き)
#cost-log ← 毎日のAPIコスト
3-4. 実際の通知イメージ
4. Phase 2:銘柄の自動発掘とwatchlist管理
4-1. watchlistが空のときの動作
watchlistに銘柄がない状態からスタートするため、
「監視モード」と「発掘モード」を自動で切り替える設計にしました。
if not watchlist["stocks"]:
prompt = load("analysis_discovery.md") # 新規発掘
else:
prompt = load("analysis_full.md") # 既存監視
4-2. 上限10銘柄・卒業ロジック
無制限に増え続けないよう、卒業ルールを設けています。
卒業条件(優先順)
1. stop_lossに到達した銘柄
2. 登録から90日以上 かつ TOPIX比-10%以上アンダーパフォーム
3. 登録から180日以上(強制卒業)
4. 上限10銘柄超過時は登録日が古い順に削除
4-3. 同一銘柄の連続選定を3層で防止
実際に特定銘柄が毎日選ばれ続ける問題が発生しました。
3層のガードで解消しています。
Layer 1:データ層
直近7日のdraft_logから除外コードを収集し、日次JSONに出力。
Layer 2:LLM指示層
プロンプトに除外コードを明示し、選定禁止を指示。
Layer 3:機械検証層
analyze.pyで結果を機械的に検証し、違反時はリトライ。
MAX_RETRY = 3
# 3回すべて除外対象なら停止してDiscordに通知
if retry_count >= MAX_RETRY:
notify_discord("🚨 候補選定失敗:除外対象以外の銘柄が選定されませんでした")
raise RuntimeError("discovery failed")
5. 詰まったポイントと解決策
| 問題 | 原因 | 解決策 |
|---|---|---|
| TOPIXが取得できない |
^TOPXティッカー廃止 |
1306.Tへフォールバック |
extract_failed |
dry-runにStep6がない | 本番実行で解消・仕様として整理 |
| 同一銘柄が毎日選ばれる | 過去データがLLM文脈に混入 | 3層ガードで解決 |
monthly_total_usdが0 |
列ずれでパース失敗 | 集計ロジックを堅牢化 |
6. やらなかったこと・やらない理由
自動売買はしない
中長期保有スタイルのため、売買タイミングの自動化は不要というのが理由です。
加えて、短期の成績(例:3週間で4%)はサンプル数が少なすぎて、ロジックの有効性の証明にならないと考えています。
LLMの判断だけに依存しない
LLMへの指示だけで品質を担保しようとすると、出力がブレたときに検知が遅れます。
LLMからのアウトプットを必ず機械的な検証レイヤーに重ねる設計にしています。
7. コストと工数の実績
Claude API :月$1.5〜2.0(従量課金)
※ $3チャージで約1.5か月分
GitHub Actions :無料枠内(月2000分のうち約30分使用)
EDINET API :無料
Discord :無料
実装期間 :約3週間(通常業務と並行)
1日の手動確認時間:約20分
8. まとめ
起点は「判断ロジックの言語化」です。
AIは道具であり、最終判断は人間が持つ設計が重要だと考えています。
分析の自動化を実現でき、スマホから気軽に見れるようになったのが今は嬉しいです。
GitHubリポジトリ:https://github.com/YoseiIkegami/stock-analysis-automation
免責事項
本記事は技術的な実装記録であり、特定の金融商品の売買を推奨するものではありません。
投資判断はご自身の責任で行ってください。

