マルチモーダルLLM(チャート画像+テキスト統合)による売買判断戦略

機械学習テクニカル分析自然言語処理

戦略概要

本戦略は、マルチモーダルLLM(Large Language Model with Vision)を活用し、チャート画像とテキスト情報を単一モデルで統合分析して売買判断を行うアプローチです。従来は画像分析とテキスト分析を別々のモデルで処理し、結果を後から統合する必要がありましたが、GPT-4V、Claude Vision、Gemini Proなどの登場により、一体的な判断が可能になりました。

LLMでなければ実現できなかった理由

処理従来手法マルチモーダルLLM
チャート画像のパターン認識CNN + 手動ラベリング視覚的に「見て」パターンを理解
テキストとの統合別モデルで処理後に統合単一モデルで一体的に判断
ニュース文脈との照合ルールベース視覚情報と文脈を同時に理解
新しいチャートパターン再学習が必要ゼロショットで対応可能
説明可能性ブラックボックス判断理由を自然言語で出力

主要マルチモーダルLLMの比較(2026年3月時点)

モデル金融ベンチマーク入力コスト(/1M)出力コスト(/1M)特徴
OpenAI GPT-5.292/100$0.01$0.03高精度、画像+テキスト統合が最も安定
Claude Sonnet 4.689/100$0.02$0.04視覚推論強化、複雑なチャート分析向き
Gemini 3.1 Pro88/100$0.015$0.035多言語対応、リアルタイム統合
Meta LLaMA 485/100$0.005$0.01オープンソース、低コスト
FinVision (Molmoベース)87/100$0.01$0.02金融特化オープンソース

出典: FinanceAgent v1.1 Benchmark (2026年3月)

チャート画像のキャプチャと前処理

CHART-IMG APIの活用

TradingViewチャートのリアルタイムスクリーンショットを生成するREST APIを使用します。

import requests
import base64
from dataclasses import dataclass

@dataclass
class ChartCapture:
    """チャートキャプチャ設定"""
    symbol: str
    interval: str  # "1D", "4H", "1H", etc.
    studies: list[str] = None  # ["RSI", "MACD", "BB"]

def capture_chart_image(config: ChartCapture) -> str:
    """CHART-IMG APIでチャート画像を取得"""

    base_url = "https://api.chart-img.com/v2/tradingview/advanced-chart"

    params = {
        "symbol": config.symbol,
        "interval": config.interval,
        "theme": "dark",
        "width": 800,
        "height": 600,
    }

    if config.studies:
        params["studies"] = ",".join(config.studies)

    headers = {
        "Authorization": f"Bearer {CHART_IMG_API_KEY}"
    }

    response = requests.get(base_url, params=params, headers=headers)

    # Base64エンコードで返却
    return base64.b64encode(response.content).decode("utf-8")

画像前処理のベストプラクティス

処理目的レイテンシー
リサイズ (800x600推奨)APIコスト削減、精度維持0.05秒
コントラスト調整パターン認識向上0.1秒
Base64エンコードAPI送信形式0.02秒
合計約0.2秒

Claude Vision / GPT-4V でのチャート分析プロンプト設計

Claude Vision向けプロンプト(XML構造化)

import anthropic
import json
from dataclasses import dataclass

@dataclass
class ChartAnalysis:
    """チャート分析結果"""
    trend: str           # "bullish", "bearish", "neutral"
    patterns: list[str]  # 検出されたパターン
    support_levels: list[float]
    resistance_levels: list[float]
    technical_score: float  # -1.0 ~ 1.0
    key_observations: list[str]
    recommended_action: str

def analyze_chart_with_claude(
    chart_base64: str,
    symbol: str,
    client: anthropic.Anthropic
) -> ChartAnalysis:
    """Claude Visionでチャート分析"""

    prompt = f"""<chart_analysis>
<task>
このチャート画像を分析し、テクニカル指標に基づく売買判断材料を抽出してください。
銘柄: {symbol}
</task>

<analysis_requirements>
1. トレンド判定: bullish/bearish/neutral
2. チャートパターン: ヘッドアンドショルダー、ダブルトップ/ボトム、三角保ち合い等
3. サポート/レジスタンスライン: 主要な価格帯を3つまで
4. テクニカルスコア: -1.0(強い売り)〜 +1.0(強い買い)
5. 主要な観察点: 最大3つ
6. 推奨アクション: buy/sell/hold
</analysis_requirements>

<output_format>
JSON形式で出力してください。
</output_format>
</chart_analysis>"""

    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=2000,
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "image",
                        "source": {
                            "type": "base64",
                            "media_type": "image/png",
                            "data": chart_base64,
                        },
                    },
                    {
                        "type": "text",
                        "text": prompt,
                    },
                ],
            }
        ],
    )

    result = json.loads(response.content[0].text)

    return ChartAnalysis(
        trend=result["trend"],
        patterns=result["patterns"],
        support_levels=result["support_levels"],
        resistance_levels=result["resistance_levels"],
        technical_score=result["technical_score"],
        key_observations=result["key_observations"],
        recommended_action=result["recommended_action"]
    )

GPT-4V向けプロンプト

from openai import OpenAI

def analyze_chart_with_gpt4v(
    chart_base64: str,
    symbol: str,
    client: OpenAI
) -> ChartAnalysis:
    """GPT-4Vでチャート分析"""

    prompt = f"""このチャート画像から{symbol}の株価パターンを分析してください。

分析要件:
- トレンド方向(bullish/bearish/neutral)
- 検出されたチャートパターン
- サポート/レジスタンスレベル
- RSI/MACDがあれば、その状態
- テクニカルスコア(-1.0〜+1.0)
- 推奨アクション

JSON形式で回答してください。"""

    response = client.chat.completions.create(
        model="gpt-4-vision-preview",
        messages=[
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": prompt},
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/png;base64,{chart_base64}"
                        }
                    }
                ]
            }
        ],
        max_tokens=2000
    )

    result = json.loads(response.choices[0].message.content)
    return ChartAnalysis(**result)

テキスト情報との統合判断ロジック

チャート分析結果とニュース/決算情報を統合し、最終的な売買シグナルを生成します。

from dataclasses import dataclass
from enum import Enum

class SignalStrength(Enum):
    STRONG_BUY = 2
    BUY = 1
    HOLD = 0
    SELL = -1
    STRONG_SELL = -2

@dataclass
class IntegratedSignal:
    """統合売買シグナル"""
    symbol: str
    signal: SignalStrength
    chart_score: float      # テクニカルスコア
    text_score: float       # テキストセンチメント
    integrated_score: float # 統合スコア
    confidence: float
    reasoning: str
    sources: list[str]

def integrate_chart_and_text(
    chart_analysis: ChartAnalysis,
    text_sentiment: float,  # -1.0 ~ 1.0
    news_headlines: list[str],
    client: anthropic.Anthropic
) -> IntegratedSignal:
    """チャート分析とテキスト情報を統合"""

    # スコアの重み付け統合
    # テクニカル: 60%, センチメント: 40%
    integrated_score = (
        chart_analysis.technical_score * 0.6 +
        text_sentiment * 0.4
    )

    # 矛盾検出と解決
    contradiction = (
        (chart_analysis.technical_score > 0.3 and text_sentiment < -0.3) or
        (chart_analysis.technical_score < -0.3 and text_sentiment > 0.3)
    )

    if contradiction:
        # 矛盾がある場合はLLMで判断
        resolution_prompt = f"""
チャート分析とニュースセンチメントに矛盾があります。

## チャート分析
- トレンド: {chart_analysis.trend}
- テクニカルスコア: {chart_analysis.technical_score}
- パターン: {', '.join(chart_analysis.patterns)}
- 観察点: {', '.join(chart_analysis.key_observations)}

## ニュース情報
- センチメントスコア: {text_sentiment}
- 主要ヘッドライン:
{chr(10).join(f'- {h}' for h in news_headlines[:5])}

どちらの情報を優先すべきか、理由とともに判断してください。
最終的な売買推奨(strong_buy/buy/hold/sell/strong_sell)を提示してください。
"""

        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1000,
            messages=[{"role": "user", "content": resolution_prompt}]
        )

        # LLMの判断を解析(簡略化)
        llm_judgment = response.content[0].text
        confidence = 0.6  # 矛盾時は信頼度を下げる
    else:
        confidence = 0.8
        llm_judgment = ""

    # シグナル決定
    if integrated_score >= 0.5:
        signal = SignalStrength.STRONG_BUY
    elif integrated_score >= 0.2:
        signal = SignalStrength.BUY
    elif integrated_score <= -0.5:
        signal = SignalStrength.STRONG_SELL
    elif integrated_score <= -0.2:
        signal = SignalStrength.SELL
    else:
        signal = SignalStrength.HOLD

    return IntegratedSignal(
        symbol=chart_analysis.patterns[0] if chart_analysis.patterns else "",
        signal=signal,
        chart_score=chart_analysis.technical_score,
        text_score=text_sentiment,
        integrated_score=integrated_score,
        confidence=confidence,
        reasoning=f"チャート: {chart_analysis.trend}, テキスト: {'ポジティブ' if text_sentiment > 0 else 'ネガティブ' if text_sentiment < 0 else '中立'}",
        sources=news_headlines[:3]
    )

フルパイプライン実装

import asyncio
from datetime import datetime

class MultimodalTradingAgent:
    """マルチモーダル売買判断エージェント"""

    def __init__(
        self,
        anthropic_client: anthropic.Anthropic,
        chart_api_key: str
    ):
        self.llm_client = anthropic_client
        self.chart_api_key = chart_api_key

    async def analyze_symbol(
        self,
        symbol: str,
        news_text: str
    ) -> IntegratedSignal:
        """銘柄の統合分析"""

        # 1. チャート画像キャプチャ
        chart_config = ChartCapture(
            symbol=symbol,
            interval="1D",
            studies=["RSI", "MACD", "BB"]
        )
        chart_image = capture_chart_image(chart_config)

        # 2. 並行処理でチャート分析とテキスト分析
        chart_task = asyncio.create_task(
            self._analyze_chart_async(chart_image, symbol)
        )
        text_task = asyncio.create_task(
            self._analyze_text_async(news_text, symbol)
        )

        chart_analysis, text_sentiment = await asyncio.gather(
            chart_task, text_task
        )

        # 3. 統合判断
        signal = integrate_chart_and_text(
            chart_analysis,
            text_sentiment,
            news_text.split("\n")[:10],  # 先頭10行をヘッドラインとして使用
            self.llm_client
        )

        return signal

    async def _analyze_chart_async(
        self,
        chart_base64: str,
        symbol: str
    ) -> ChartAnalysis:
        """非同期チャート分析"""
        return analyze_chart_with_claude(
            chart_base64, symbol, self.llm_client
        )

    async def _analyze_text_async(
        self,
        text: str,
        symbol: str
    ) -> float:
        """非同期テキストセンチメント分析"""

        prompt = f"""以下のニュース/決算情報から{symbol}に関するセンチメントを分析してください。

{text}

センチメントスコアを-1.0(非常にネガティブ)〜+1.0(非常にポジティブ)で回答してください。
数値のみを回答してください。"""

        response = self.llm_client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=100,
            messages=[{"role": "user", "content": prompt}]
        )

        return float(response.content[0].text.strip())

バックテスト方法と精度評価

バックテスト設計

from dataclasses import dataclass
import pandas as pd

@dataclass
class BacktestResult:
    """バックテスト結果"""
    total_return: float
    sharpe_ratio: float
    max_drawdown: float
    win_rate: float
    total_trades: int
    avg_holding_days: float

def backtest_multimodal_strategy(
    historical_data: pd.DataFrame,
    signals: list[IntegratedSignal],
    initial_capital: float = 1_000_000
) -> BacktestResult:
    """マルチモーダル戦略のバックテスト"""

    capital = initial_capital
    positions = {}
    trades = []

    for signal in signals:
        date = signal.timestamp
        price = historical_data.loc[date, "close"]

        if signal.signal in [SignalStrength.BUY, SignalStrength.STRONG_BUY]:
            # ポジション構築
            position_size = capital * 0.1 * signal.confidence
            shares = position_size // price
            positions[signal.symbol] = {
                "shares": shares,
                "entry_price": price,
                "entry_date": date
            }
            capital -= shares * price

        elif signal.signal in [SignalStrength.SELL, SignalStrength.STRONG_SELL]:
            # ポジション解消
            if signal.symbol in positions:
                pos = positions[signal.symbol]
                pnl = (price - pos["entry_price"]) * pos["shares"]
                capital += pos["shares"] * price
                trades.append({
                    "symbol": signal.symbol,
                    "pnl": pnl,
                    "holding_days": (date - pos["entry_date"]).days
                })
                del positions[signal.symbol]

    # 結果計算
    total_pnl = sum(t["pnl"] for t in trades)
    wins = sum(1 for t in trades if t["pnl"] > 0)

    return BacktestResult(
        total_return=(capital - initial_capital) / initial_capital,
        sharpe_ratio=calculate_sharpe(trades),
        max_drawdown=calculate_max_drawdown(trades),
        win_rate=wins / len(trades) if trades else 0,
        total_trades=len(trades),
        avg_holding_days=sum(t["holding_days"] for t in trades) / len(trades) if trades else 0
    )

バックテスト結果の目安

2023-2025年のS&P500構成銘柄でのバックテスト結果:

戦略予測精度Sharpe比率年率リターン最大DD
LSTM+Transformer統合95%1.5-2.015-25%-12%
Transformer単体93%1.2-1.512-18%-15%
Bidirectional LSTM91%1.0-1.310-15%-18%
マルチモーダルLLM (本戦略)88-92%1.2-1.812-20%-15%

出典: Trading-R1モデルバックテスト結果, arxiv.org (2026年2月)

コストとレイテンシー

API利用コスト試算

項目1リクエスト100銘柄/日月間 (20営業日)
チャート画像取得 (CHART-IMG)$0.002$0.20$4.00
Claude Vision分析$0.03$3.00$60.00
テキスト分析$0.01$1.00$20.00
合計$0.042$4.20$84.00

レイテンシー内訳

処理平均時間最大時間
チャートキャプチャ0.5秒1.0秒
画像前処理0.2秒0.3秒
LLM推論(チャート)1.5秒3.0秒
LLM推論(テキスト)0.8秒1.5秒
統合判断0.3秒0.5秒
合計(並列処理時)2.0秒3.5秒

注意: 高頻度取引(HFT)には不向き。スイングトレード〜ポジショントレード向け。

リスク要因

リスク影響対策
データバイアス過去データへの過剰適合、地政学リスクで誤判断多様なデータソース、人間の監督
レイテンシーリアルタイム機会喪失(HFTには致命的)スイングトレード以上の時間軸で運用
APIコスト大規模運用で月間$1000超バッチ処理、重要銘柄に絞る
ハルシネーション存在しないパターンを「検出」複数モデル検証、閾値設定
モデルドリフトLLM更新で分析傾向変化定期的な精度検証、A/Bテスト

日本市場への適用

2026年現在、日本のAIインフラ市場は5.5億ドルを超え、株式取引の自動化が進んでいます。本戦略の日本株適用における考慮点:

  • CHART-IMG: 東証銘柄コード(例: TYO:7203)に対応
  • 決算情報: TDnet/適時開示情報のPDF解析が必要
  • 時間帯: 東証の取引時間(9:00-15:30 JST)に合わせたスケジューリング
  • 言語: Claude/GPT-4Vは日本語テキストにも高精度で対応

まとめ

マルチモーダルLLMによるチャート画像+テキスト統合分析は、従来は別々のシステムで処理していた視覚情報とテキスト情報を単一モデルで一体的に判断できる革新的なアプローチです。

推奨される導入ステップ:

  1. 検証フェーズ (1-2週間): 10-20銘柄で精度検証、人間の判断との一致率測定
  2. ペーパートレード (1ヶ月): 実際のシグナルに基づく仮想売買で執行タイミング検証
  3. 小ロット実運用 (3ヶ月): 総資産の5-10%で実運用開始
  4. スケールアップ: 精度・収益性を確認後、段階的に拡大

重要: レイテンシーの制約からデイトレードや高頻度取引には不向きです。スイングトレード(数日〜数週間)以上の時間軸での運用を推奨します。


免責事項: 本記事は情報提供を目的としており、特定の金融商品の売買を推奨するものではありません。投資判断は自己責任で行ってください。バックテスト結果は過去のデータに基づくものであり、将来の収益を保証するものではありません。LLMの出力にはハルシネーション(事実と異なる情報の生成)のリスクがあり、必ず人間による検証を行ってください。