😽

【関西TfLSテクニカルインフルエンサー】LLMで動く投資アドバイザーを作ってみた

に公開

無料APIを駆使したLLM基盤投資アドバイザー

はじめに

本記事は関西TfLSテクニカルインフルエンサー活動の一環として作成されました。
関西TfLSテクニカルインフルエンサーとは、関西オフィスTfLS所属の若手社員のITスキル強化を目的として設けられた活動であり、「Tech関連のテーマ1つを自由に選び、約半年間進めて何らかの成果物を生み出そう」という意図で企画されたものです。

筆者のテーマは「GenAIを個人的な投資に活用してみたい」で、自分の思い通りにLLMを動かして投資に役立てることができれば素晴らしいのではないか、という考えから始まりました。

では早速ですが、開発したシステムについて説明させて頂ければと思います。

システム概要

投資ニュース分析システムは、リアルタイムで投資関連ニュースを収集・分析し、AIによる感情分析やポートフォリオ提案を行うWebアプリケーションです。

主要機能

1. 多元的ニュース収集・分析

  • Google News RSS: 日本語のニュース検索
  • 信頼性の高いRSSフィード: NHK、日経、時事通信、ロイター、CNN、Bloomberg等の主要メディア
  • リアルタイム更新: 最新の投資関連情報を継続的に収集
  • 柔軟な検索方式: キーワード検索と自然言語質問の両方に対応
    • キーワード検索: テスラ半導体日銀
    • 質問文検索: テスラの株価はなぜ下がったの?今週の市場状況は?


ホーム画面。検索バーにキーワードや質問を入力できます。「NVIDIA」を入れて検索を回すと、


TriageのLLM(GPT-4o-mini)が動き出し、RSSフィードにて関連記事を探してくれます。
ここで2つのLLMが走ります。

  • Triage(分類) (gpt-4o-mini): トークン使用量を抑えた初期フィルタリング
    • 記事の関連性判定 (drop/keep)
    • ソース信頼性カテゴリ分類
    • 基本的なキーワード抽出

Triageフィルタリングプロンプト

// Triageプロンプトの核心部分
`
**CRITICAL: ユーザーの質問意図と記事の関連性を最優先で判定してください。**

厳格なルール:
1) 質問関連性判定(最重要):
   - 自然語質問の場合:質問の意図と記事内容の関連性で判定
   - 質問に答えるのに役立たない記事は必ず "drop": true にする

2) 記事ソースの信頼性カテゴリ:
   - economic_specialist (Bloomberg, WSJ, Nikkei等)
   - state_media (BBC, NHK等)
   - major_wire (Reuters, AP, AFP等)
   - other
`
  • Summary(要約) (gpt-5): 関連記事のみに高度な分析を適用
    • 詳細な市場分析と投資インサイト
    • センチメント分析
    • 関連銘柄の自動識別

これらを分けた理由ですが、大量の記事を処理する際のトークン使用量が懸念されたためです。
比較的シンプルな処理を要するトリアージ作業には gpt-4o-mini を使用しました(開発中に GPT-5-nano が登場しましたが、ロジック変更が必要だったため4o-miniを継続利用しました)。
一方で、記事の要約や「楽観/中立/悲観」といった記事の雰囲気把握など、より複雑な自然言語処理を要するタスクには GPT-5 を使用しました。

このようにして可能な限りトークンを節約し、高価なGPT-5の使用量を最小化するため、表示する記事数も最大5件に制限しました。

こうやって、半年の開発期間中$10分のトークンで問題なくシステムが作れました。


NVIDIAをキーワードとした記事の検索結果が表示されています。ここでGPT-5が返してくれる情報は以下の通り。

  • 詳細要約 (summary_ja): 200文字以内で投資家向け要約
    • 具体的な数値・データ含有
    • 価格変動の要因・背景解説
    • 短期・中期への影響予測
    • 関連業界・銘柄への波及効果
  • センチメント分析: 多層的な市場心理評価
    • 感情スコア (0-100): 1=極悲観 ↔ 100=極楽観
    • 感情タグ: ["恐怖", "警戒", "期待", "楽観", "FOMO"等]
    • 市場インパクト: High/Medium/Low 予測
    • 投資家心理キーワード: 最大5個の心理状態指標
  • 関連銘柄識別 (related_instruments): TradingView互換ティッカー
    • 米国株: NASDAQ:NVDA, NYSE:AAPL, NASDAQ:TSLA
    • 日本株: TSE:7203 (トヨタ), TSE:6758 (ソニー)
    • 指数: SP:SPX (S&P500), TVC:NI225 (日経平均)
    • 関係性分類: direct/peer/supplier/competitor/beneficiary
  • 可視化
    • TradingView統合: プロレベルのチャート表示
    • Lightweight Charts: 軽量で高速なチャート描画
    • ポートフォリオ円グラフ: 投資配分の視覚化

キーワードに関連する記事の要約やニュアンス、関連銘柄の最新動向を一目で把握できれば良いと考えており、ある程度は実現できたと思います。

2. 自然語処理

キーワードのみで検索すると少し汎用性が低い気がしちゃって、文末に「?」を付けるとユーザーの文章を「質問」として認識するようにしました。文中の各要素を分析してキーワードを抽出するようにトリアージ用のLLM(GPT-4o-mini)へ渡します。

ロジック&プロンプト
// 自然語質問の例
"テスラの株価はなぜ下がったの?"
"半導体業界への影響はどう?"
"今週の市場センチメントはどうですか?"
// GPT-4o-miniによる質問分析
async function analyzeQuery(query) {
  const prompt = `
次の質問から、ニュース検索に使える重要なキーワードを抽出してください。

質問: "${query}"

以下のJSONフォーマットで回答してください:
{
  "keywords": ["人名", "企業名", "重要単語"],
  "searchTerms": ["検索用語1", "検索用語2"],
  "topics": ["関連トピック1", "関連トピック2"],
  "focus": "political|economic|corporate|market"
}
  `.trim()

  const response = await openai.chat.completions.create({
    model: MODEL_TRIAGE, // gpt-4o-mini
    messages: [{ role: 'user', content: prompt }],
    response_format: { type: 'json_object' }
  })
  return JSON.parse(response.choices[0].message.content)
}
  • 質問意図分析: OpenAI GPTモデルによる自然言語理解
  • キーワード抽出: 質問から投資関連キーワードを自動抽出
  • 関連性判定: ニュース記事と質問の関連度を評価


    関連度高いキーワードを抽出してRSSフィードにて検索を行います。

3. AIポートフォリオアドバイザー

検索結果が表示された後に AI投資ポートフォリオアドバイザー の質問入力欄が表示されます。
ここに自分のポートフォリオ構成に関する質問などを入力すると、現在検索結果として表示されているニュース要約を基にコンテキストを把握します。

把握したコンテキストとユーザーの質問をインプットとして、LLMが「保守的ポートフォリオ」「バランス型ポートフォリオ」「成長重視ポートフォリオ」の3種類を提案します。
投資銘柄をどのように配分すれば良いかについて、予想収益率・リスク・機会を考慮するようプロンプトを構成しました。

// ポートフォリオ相談の例
"テスラに投資したいがリスクを分散させたい"
"日本株中心で安定した成長を目指したい"
"ESG投資に興味があるが何から始めるべき?"
ロジック&プロンプト
const PORTFOLIO_PROMPT = `
あなたは「AI投資ポートフォリオアドバイザー」です。

与えられた投資関連の質問に対して、リアルタイムニュース分析を基に
最適なポートフォリオを提案してください。

重要な指針:
1) 投資助言ではなく「教育的情報提供」として回答
2) リスク許容度別に3つのポートフォリオを提案
3) 各資産の投資根拠をニュース分析から導出
4) 具体的な金額や購入推奨は避ける

JSON出力フォーマット:
{
  "analysis_summary": "市場状況とポートフォリオ戦略の要約(200文字以内)",
  "portfolios": {
    "conservative": {
      "name": "保守的ポートフォリオ",
      "risk_level": "低リスク",
      "target_return": "3-5%",
      "allocation": [
        {
          "asset_type": "株式|債券|現金|コモディティ",
          "symbol": "具体的なティッカー",
          "percentage": 30,
          "rationale": "投資根拠(ニュースベース)"
        }
      ]
    }
    // moderate, aggressive も同様
  }
}
`.trim()

ニュースコンテキスト統合処理

// /api/portfolio-advisor エンドポイント
app.post('/api/portfolio-advisor', async (req, res) => {
  const { query, newsContext } = req.body || {}

  // ニュースコンテキストを要約
  const contextSummary = newsContext && newsContext.length > 0
    ? newsContext.slice(0, 5).map((article, i) =>
        `${i+1}. ${article.title}
   要約: ${article.summary_ja || article.description}
   センチメント: ${article.sentiment?.sentiment_score || 'N/A'}`
      ).join('\n')
    : '最新のニュース分析データなし'

  const prompt = `
投資クエリ: "${query}"

現在の市場状況(最新ニュース分析):
${contextSummary}

上記の市場分析を基に、このクエリに対する最適なポートフォリオを
3パターン(保守的/バランス型/成長重視)でJSON形式で提案してください。
  `.trim()

  const response = await openai.chat.completions.create({
    model: MODEL_TRIAGE, // gpt-4o-mini使用でコスト削減
    messages: [
      { role: 'system', content: PORTFOLIO_PROMPT },
      { role: 'user', content: prompt }
    ],
    max_completion_tokens: 2000
  })
})
  • 投資提案: 最新ニュース分析に基づくポートフォリオ提案
  • リスク評価: 分散投資戦略の提案
  • 市場分析: 現在の市場状況を考慮した投資判断


「マイクロソフト」をキーワードとし検索結果が表示されている状態で、AI投資ポートフォリオアドバイザーに「マイクロソフトに投資しつつ、リスクを分散させたい。」と質問します。

記事のContextに基づいた市場分析の概要と総合センチメント、信頼度を判定し、ポートフォリオを提案してくれます。

バランス型ポートフォリオ

成長重視ポートフォリオ

5. リアルタイムAIトレーディングシグナル

上で紹介した AIポートフォリオアドバイザー はポートフォリオをどのように構成すべきかを提案しますが、リアルタイムAIトレーディングシグナル は、本記事で取り上げた具体的な個別銘柄について、どのようなトレーディング戦略を立てるのが良いかを提案します。

基本的にはAIポートフォリオアドバイザーと同様の仕組みで動作し、記事の内容や総合セグメントをコンテキストとしてインプットし、個別銘柄の市場状況や取引タイミングを判断します。

  • 自動銘柄発掘: ニュース記事から related_instruments 抽出および重複除去
  • 多次元シグナル: BUY/SELL/HOLD/WATCH + 信頼度 + 時間軸 + リスクレベル
  • 根拠ベース分析: センチメントスコアとニュース要約を総合した売買判断
ロジック&プロンプト

トレーディングシグナル生成プロンプト

const TRADING_SIGNAL_PROMPT = `
あなたは「AI市場分析・トレーディングシグナル」の専門アシスタントです。

最新のニュース分析、センチメント、市場データを総合して、
具体的なトレーディングシグナルを生成してください。

重要な指針:
1) Buy/Sell/Hold/Watchの明確なシグナル生成
2) 信頼度スコア (0-100) 付与
3) 短期/中期/長期の時間軸別分析
4) リスクレベル評価
5) 根拠となるニュース・データ提示

JSON出力フォーマット:
{
  "signals": [
    {
      "symbol": "NASDAQ:NVDA",
      "name": "NVIDIA Corporation",
      "signal": "BUY|SELL|HOLD|WATCH",
      "confidence": 85,
      "timeframe": "短期|中期|長期",
      "rationale": "AI需要拡大とデータセンター投資増加により...",
      "risk_level": "低|中|高",
      "key_factors": ["要因1", "要因2", "要因3"]
    }
  ],
  "market_overview": {
    "overall_sentiment": "Risk-On|Risk-Off|Mixed",
    "volatility_level": "低|中|高",
    "key_themes": ["テーマ1", "テーマ2"]
  }
}

シグナル基準:
- BUY: 明確な上昇要因、高い確信度
- SELL: 明確な下落要因、リスク増大
- HOLD: 現状維持、様子見推奨
- WATCH: 注目銘柄、変化待ち
`.trim()

関連銘柄自動抽出とシグナル生成

// /api/trading-signals エンドポイント
app.post('/api/trading-signals', async (req, res) => {
  const { newsContext, focusSymbols } = req.body || {}

  // ニュースコンテキストから重要銘柄を抽出
  const relevantInstruments = []
  if (newsContext && newsContext.length > 0) {
    newsContext.forEach(article => {
      if (article.related_instruments && article.related_instruments.length > 0) {
        relevantInstruments.push(...article.related_instruments)
      }
    })
  }

  // 重複除去および上位5個選別
  const uniqueInstruments = relevantInstruments
    .filter(inst => inst.symbol_or_ticker)
    .reduce((acc, inst) => {
      const key = inst.symbol_or_ticker
      if (!acc[key]) acc[key] = inst
      return acc
    }, {})

  const topInstruments = Object.values(uniqueInstruments).slice(0, 5)

  // コンテキスト要約生成
  const contextSummary = newsContext && newsContext.length > 0
    ? newsContext.slice(0, 8).map((article, i) =>
        `${i+1}. [${article.sentiment?.sentiment_score || 'N/A'}점] ${article.title}
   要約: ${article.summary_ja || article.description}
   関連銘柄: ${article.related_instruments?.map(ri => ri.symbol_or_ticker).join(', ') || 'N/A'}`
      ).join('\n')
    : '最新ニュースデータなし'

  const response = await openai.chat.completions.create({
    model: MODEL_TRIAGE, // gpt-4o-mini使用
    messages: [
      { role: 'system', content: TRADING_SIGNAL_PROMPT },
      { role: 'user', content: `
現在の市場状況 (最新ニュース分析):
${contextSummary}

上記市場分析を基にリアルタイムトレーディングシグナルを生成してください。
      `.trim() }
    ],
    max_completion_tokens: 1800
  })
})

アーキテクチャ設計

簡単に紹介させてください。

全体構成

Frontend (Vue.js)     ←→     Backend (Express.js)     ←→     External APIs
                             │                              │
┌─────────────────┐          ├─ News Collection           ├─ Google News RSS
│ Vue 3 + Vite    │          ├─ AI Analysis              ├─ OpenAI API
│ Chart.js        │          ├─ Sentiment Processing     ├─ Yahoo Finance
│ TradingView      │          ├─ Portfolio Advisory      └─ Various RSS Feeds
│ Responsive UI   │          └─ Real-time Data Serving
└─────────────────┘

バックエンド設計

1. モジュラー構成

// server.js - ESM形式の採用
import express from 'express'
import OpenAI from 'openai'
import Parser from 'rss-parser'
import yahooFinance from 'yahoo-finance2'

// 設定可能なパラメータ
const MODEL_TRIAGE = process.env.MODEL_TRIAGE || 'gpt-4o-mini'
const MODEL_SUMMARY = process.env.MODEL_SUMMARY || 'gpt-5'
const RSS_CONCURRENCY = Number(process.env.RSS_CONCURRENCY || 3)

2. 並行処理最適化

// バッチ処理による効率化
async function runInBatches(items, batchSize, worker) {
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize)
    await Promise.all(batch.map(worker))
  }
}

3. キャッシュ戦略

  • TTL設定: 90秒のキャッシュで API呼び出し最適化
  • 重複排除: 同一記事の除去アルゴリム
  • 並行制御: p-limitライブラリによるAPI呼び出し制限

フロントエンド設計

1. Vue 3 Composition API

<script setup>
import { ref, reactive, onMounted } from 'vue'

const news = ref([])
const loading = ref(false)
const overallSummary = reactive({})

// リアクティブな状態管理
const searchNews = async () => {
  loading.value = true
  // ニュース検索ロジック
}
</script>

2. コンポーネント設計

  • KeywordMarquee: 動的キーワード表示
  • PortfolioAdvisor: AI投資アドバイス
  • TradingSignals: リアルタイム取引シグナル
  • ChartWidget: 多様なチャート表示オプション

技術スタック詳細

バックエンド

  • Express.js: RESTful API サーバー
  • OpenAI API: GPT-4o-mini/GPT-5による自然言語処理
  • RSS Parser: 多様なニュースフィード対応
  • Yahoo Finance API: リアルタイム株価データ
  • p-limit: 並行処理制御
  • CORS: クロスオリジン対応

フロントエンド

  • Vue 3: Composition APIによる現代的な開発
  • Vite: 高速ビルドツール
  • Chart.js: データ可視化
  • Axios: HTTP通信
  • TradingView Widget: プロ仕様チャート

開発過程での課題と解決策

1. コスト問題への対応

課題

開発当初は以下のような有料APIの利用を検討していました:

  • NewsAPI: $449/月(プロプラン)
  • Yahoo Finance Premium: $34.99/月
  • OpenAI API: 使用量に応じた従量課金

個人開発プロジェクトにとって、これらの費用は非常に大きな負担でした。

解決策

無料代替案の活用

// Google News RSS (無料)
const url = `https://news.google.com/rss/search?q=${q}&hl=ja&gl=JP&ceid=JP:ja`

// 信頼性の高い無料RSS
const FEEDS = [
  { name: 'NHK 経済', url: 'https://www3.nhk.or.jp/rss/news/cat5.xml' },
  { name: '日経 トップ', url: 'https://www.nikkei.com/rss/category/top.xml' },
  { name: 'Reuters', url: 'https://feeds.reuters.com/reuters/businessNews' }
]

コスト効率化戦略
- OpenAI APIは最小限のモデル(gpt-4o-mini)を使用
- キャッシュ機能でAPI呼び出し回数を削減
- バッチ処理で効率的なリクエスト管理

2. AI投資アドバイスの実装難易度

課題

ChatGPTに投資アドバイスをさせることは想像以上に困難でした:

  • 規制への配慮: 金融アドバイスに関する法的責任
  • バイアス回避: 中立的で客観的な分析の確保
  • 免責事項: 適切な注意書きの挿入

解決策

プロンプトエンジニアリング

const INVESTMENT_PROMPT = `
あなたは投資情報分析アシスタントです。

重要な注意事項:
1. 投資アドバイスではなく、情報分析と教育目的のみ
2. 投資判断は個人の責任であることを明記
3. リスクについて必ず言及
4. 過去のデータは将来を保証しないことを強調

以下のニュース分析に基づいて、教育目的での情報提供を行ってください...
`

段階的なアプローチとして、
ニュース分析を通じて必要な情報を収集 → その内容をもとに市場の動向を分かりやすく要約 → 投資に伴うリスクについて具体的に示すのフローを取っています。
ユーザーに取ってリスクを十分理解できるように頑張りました。本システム特にリリースする予定はないのですがDisclaimerとして投資判断はあくまで自己責任で行うべきであることを明記し、利用者自身の主体的な判断を促す構成としています。

まとめ

週に1~2時間程度、約半年にわたって開発を続けてきましたが、想定通りに進まない部分も多く、当初計画していたよりも機能は縮小する結果となりました(この点についてはテーマの特性にも起因する部分があると考えています)。
もっとも、出発点が自分の私的な関心・動機であったため、開発の過程自体は非常に楽しむことができ、試行錯誤を通じて多くの学びを得ることができました。

また、1~2か月程度は実際自分の投資に活用してみましたが、直近数か月市場全体が好調で、どの銘柄を購入しても儲かるような状況だったため、残念ながらツールとしての有効性を検証する上で大きな意味を見出すことはできませんでした。とはいえ、実運用を通じてユーザー体験を観察し、プロンプト設計やモデル選定における課題を明確にできた点は有意義だったかと。

さらに、LLMをメインに据えた開発は今回が初めてでしたが、従来型のシステム開発とは異なり、モデルの挙動や出力のばらつきを前提に設計を進める必要があることを実感しました。プロンプトの工夫やタスク分割の仕方によって成果が大きく変わる点は難しさであると同時に面白さでもあり、生成AIを活用した開発の特徴を体感する良い機会となりました。今後は今回得られた知見を基に、より汎用性の高いアプリケーションや実務に直結するユースケースに展開していきたいと考えています。

Accenture Japan (有志)

Discussion