LLMによるコード生成を活用した取引戦略の高速プロトタイピング

Python自動売買バックテスト

戦略概要

本記事では、LLM(Large Language Model)のコード生成能力を活用し、取引戦略のアイデアを高速にプロトタイプ・バックテストする手法を解説します。従来は数週間〜数ヶ月かかっていた戦略開発サイクルを、数時間〜数日に短縮できます。

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

従来の手法LLM活用後
戦略アイデア → コード化に数日自然言語で記述 → 数分でコード生成
バックテストコードの手動実装フレームワーク連携コードを自動生成
エラー修正に時間を要する対話的デバッグで即時修正
1つずつ手動でパラメータ調整パラメータ探索コードを自動生成
専門的なプログラミング知識が必須自然言語で戦略を表現可能

2026年現在、Claude Opus 4.6やGPT-4oを活用したトレーディングシステムでは、開発サイクルが従来比50%以上高速化され、一部事例ではROI 102%を記録しています。

プロンプトによる戦略仕様の記述方法

戦略をLLMに正しく伝えるためのプロンプト設計が重要です。

基本的なプロンプト構造

# 戦略仕様をプロンプトで記述する例
strategy_prompt = """
## 戦略名
RSIモメンタム戦略

## エントリー条件
- RSI(14)が30を下回った後、30を上抜けしたとき買いエントリー
- RSI(14)が70を上回った後、70を下抜けしたとき売りエントリー

## エグジット条件
- エントリーから5%の利益で利確
- エントリーから2%の損失でストップロス
- 最大保有期間: 10日

## 対象銘柄
- 日経225構成銘柄
- 1日の出来高が100万株以上

## バックテスト期間
- 2023年1月1日〜2025年12月31日

## 出力
- backtraderで動作するPythonコードを生成してください
- シャープレシオ、最大ドローダウン、勝率を計算してください
"""

効果的なプロンプトの要素

要素説明
明確な条件曖昧さを排除した数値条件「RSIが30を下回る」ではなく「RSI(14)が30未満になる」
時間軸の指定足の種類を明示「日足ベース」「1時間足」
リスク管理ストップロス・利確条件「エントリー価格から2%下落でストップ」
出力形式期待するコード形式「backtraderのStrategy形式」「vectorbtのシグナル配列」

バックテストフレームワークとの連携

backtraderでのコード自動生成

import anthropic
import backtrader as bt

def generate_backtrader_strategy(strategy_description: str) -> str:
    """LLMでbacktrader戦略コードを生成"""

    client = anthropic.Anthropic()

    prompt = f"""以下の戦略仕様に基づいて、backtraderのStrategyクラスを生成してください。

## 戦略仕様
{strategy_description}

## 出力要件
1. bt.Strategyを継承したクラス
2. __init__でインジケーターを定義
3. nextメソッドでエントリー・エグジットロジックを実装
4. 動作確認済みの完全なコード
5. 日本語コメント付き

## 注意
- self.dataで価格データにアクセス
- self.buy() / self.sell() / self.close()で注文
- sizer設定も含める
"""

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

    return response.content[0].text


# 使用例
strategy_spec = """
移動平均クロスオーバー戦略:
- 短期SMA(20)が長期SMA(50)を上抜けで買い
- 短期SMAが長期SMAを下抜けで売り
- ストップロス: エントリーから3%
"""

generated_code = generate_backtrader_strategy(strategy_spec)
print(generated_code)

生成されるコードの例

import backtrader as bt

class SMACrossStrategy(bt.Strategy):
    """移動平均クロスオーバー戦略"""

    params = (
        ("short_period", 20),
        ("long_period", 50),
        ("stop_loss", 0.03),
    )

    def __init__(self):
        # 移動平均インジケーター
        self.sma_short = bt.indicators.SMA(
            self.data.close, period=self.params.short_period
        )
        self.sma_long = bt.indicators.SMA(
            self.data.close, period=self.params.long_period
        )
        # クロスオーバー検出
        self.crossover = bt.indicators.CrossOver(
            self.sma_short, self.sma_long
        )
        # トラッキング用
        self.entry_price = None

    def next(self):
        if not self.position:
            # ポジションなし
            if self.crossover > 0:  # ゴールデンクロス
                self.buy()
                self.entry_price = self.data.close[0]
        else:
            # ポジションあり
            if self.crossover < 0:  # デッドクロス
                self.close()
            elif self.entry_price:
                # ストップロスチェック
                pnl_pct = (self.data.close[0] - self.entry_price) / self.entry_price
                if pnl_pct < -self.params.stop_loss:
                    self.close()

vectorbtでの高速バックテスト

vectorbtはNumba最適化により、従来ライブラリの100倍の速度でバックテストを実行できます。

import vectorbt as vbt
import numpy as np
import pandas as pd

def generate_vectorbt_signals(strategy_description: str, price_data: pd.Series) -> str:
    """LLMでvectorbt用シグナル生成コードを作成"""

    client = anthropic.Anthropic()

    prompt = f"""以下の戦略に基づいて、vectorbt用のエントリー・エグジットシグナル配列を生成するPythonコードを書いてください。

## 戦略仕様
{strategy_description}

## 入力データ
- price_data: pd.Series(終値の時系列データ)

## 出力形式
- entries: np.ndarray(Trueでロングエントリー)
- exits: np.ndarray(Trueでエグジット)

## 注意
- vectorbt.indicators を活用
- NumPy/Pandasでベクトル演算
- ループは使わない(パフォーマンスのため)
"""

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

    return response.content[0].text


# 生成されたコードの実行例
def run_vectorbt_backtest(price_data: pd.Series):
    """vectorbtでバックテスト実行"""

    # RSIベースのシグナル生成
    rsi = vbt.RSI.run(price_data, window=14)

    # シグナル配列
    entries = (rsi.rsi_crossed_below(30)).shift(1) & (rsi.rsi > 30)
    exits = (rsi.rsi_crossed_above(70)).shift(1) & (rsi.rsi < 70)

    # バックテスト実行
    portfolio = vbt.Portfolio.from_signals(
        price_data,
        entries=entries,
        exits=exits,
        init_cash=1000000,  # 100万円
        fees=0.001,  # 0.1%手数料
    )

    # 結果取得
    stats = portfolio.stats()
    return {
        "total_return": stats["Total Return [%]"],
        "sharpe_ratio": stats["Sharpe Ratio"],
        "max_drawdown": stats["Max Drawdown [%]"],
        "win_rate": stats["Win Rate [%]"],
    }

Claude Code / Cursorを使った対話的開発

Claude Codeでの戦略開発ワークフロー

# Claude Codeで戦略開発を開始
claude

# プロンプト例
> RSIとボリンジャーバンドを組み合わせた逆張り戦略を作成してください。
> RSIが30以下かつ価格がボリンジャーバンド下限を下回ったら買い、
> RSIが70以上かつ価格がボリンジャーバンド上限を上回ったら売りです。
> backtraderで実装し、日経225のデータでバックテストしてください。

対話的デバッグの例

ユーザー: このコードでバックテストを実行するとエラーが出ます
        "IndexError: array index out of range"

Claude: このエラーは、インジケーターの計算期間中にnextメソッドが
        呼ばれていることが原因です。以下の修正を提案します:

        ```python
        def next(self):
            # インジケーターの準備ができるまで待機
            if len(self.data) < self.params.long_period:
                return

            # 以降のロジック...
        ```

ユーザー: ありがとうございます。動作しました。
        シャープレシオを改善するためにパラメータを最適化できますか?

Claude: はい、optuna を使ったパラメータ最適化コードを生成します...

Cursorでの開発ポイント

機能活用方法
Tab補完戦略コードのボイラープレートを自動補完
Cmd+K選択したコードブロックの改善を依頼
Chatエラーメッセージを貼り付けてデバッグ
@codebaseプロジェクト全体を参照した修正提案

生成コードの品質検証

LLM生成コードは品質検証が必須です。

静的分析チェック

import ast
import subprocess
from dataclasses import dataclass

@dataclass
class CodeQualityReport:
    """コード品質レポート"""
    syntax_valid: bool
    type_check_passed: bool
    lint_issues: list[str]
    security_issues: list[str]

def validate_generated_code(code: str) -> CodeQualityReport:
    """生成コードの品質検証"""

    # 1. 構文チェック
    try:
        ast.parse(code)
        syntax_valid = True
    except SyntaxError as e:
        syntax_valid = False
        return CodeQualityReport(
            syntax_valid=False,
            type_check_passed=False,
            lint_issues=[str(e)],
            security_issues=[]
        )

    # 2. 一時ファイルに書き出してlint実行
    with open("temp_strategy.py", "w") as f:
        f.write(code)

    # ruffでlintチェック
    result = subprocess.run(
        ["ruff", "check", "temp_strategy.py"],
        capture_output=True,
        text=True
    )
    lint_issues = result.stdout.split("\n") if result.stdout else []

    # 3. セキュリティチェック(危険な関数の使用検出)
    dangerous_patterns = ["eval(", "exec(", "subprocess.call(", "__import__"]
    security_issues = [
        p for p in dangerous_patterns if p in code
    ]

    return CodeQualityReport(
        syntax_valid=syntax_valid,
        type_check_passed=len(lint_issues) == 0,
        lint_issues=lint_issues,
        security_issues=security_issues
    )

バックテスト結果の妥当性検証

def validate_backtest_results(
    stats: dict,
    price_data: pd.Series
) -> tuple[bool, list[str]]:
    """バックテスト結果の妥当性チェック"""

    warnings = []

    # 1. 非現実的なリターンの検出
    if stats.get("sharpe_ratio", 0) > 3.0:
        warnings.append(
            f"シャープレシオが異常に高い: {stats['sharpe_ratio']:.2f}。"
            "過学習の可能性があります。"
        )

    # 2. 取引回数のチェック
    trade_count = stats.get("total_trades", 0)
    if trade_count < 30:
        warnings.append(
            f"取引回数が少ない: {trade_count}回。"
            "統計的有意性が低い可能性があります。"
        )

    # 3. 期間に対するパフォーマンスの妥当性
    annual_return = stats.get("annual_return", 0)
    if annual_return > 100:
        warnings.append(
            f"年率リターンが異常: {annual_return:.1f}%。"
            "ルックアヘッドバイアスの可能性を確認してください。"
        )

    # 4. 最大ドローダウンのチェック
    max_dd = abs(stats.get("max_drawdown", 0))
    if max_dd > 50:
        warnings.append(
            f"最大ドローダウンが大きい: {max_dd:.1f}%。"
            "リスク管理の見直しを推奨します。"
        )

    is_valid = len(warnings) == 0
    return is_valid, warnings

過学習検出の自動化

from sklearn.model_selection import TimeSeriesSplit

def detect_overfitting(
    strategy_func,
    price_data: pd.DataFrame,
    n_splits: int = 5
) -> dict:
    """時系列交差検証で過学習を検出"""

    tscv = TimeSeriesSplit(n_splits=n_splits)

    train_sharpes = []
    test_sharpes = []

    for train_idx, test_idx in tscv.split(price_data):
        train_data = price_data.iloc[train_idx]
        test_data = price_data.iloc[test_idx]

        # 訓練期間でバックテスト
        train_stats = strategy_func(train_data)
        train_sharpes.append(train_stats["sharpe_ratio"])

        # テスト期間でバックテスト
        test_stats = strategy_func(test_data)
        test_sharpes.append(test_stats["sharpe_ratio"])

    avg_train_sharpe = np.mean(train_sharpes)
    avg_test_sharpe = np.mean(test_sharpes)

    # 過学習度の計算(訓練とテストの乖離)
    overfit_ratio = (avg_train_sharpe - avg_test_sharpe) / avg_train_sharpe

    return {
        "avg_train_sharpe": avg_train_sharpe,
        "avg_test_sharpe": avg_test_sharpe,
        "overfit_ratio": overfit_ratio,
        "is_overfitting": overfit_ratio > 0.3,  # 30%以上の乖離で過学習判定
        "recommendation": (
            "過学習の兆候あり。パラメータ数を減らすか、戦略を簡素化してください。"
            if overfit_ratio > 0.3 else "過学習の兆候なし。"
        )
    }

過学習検出の基準値

指標健全要注意過学習の疑い
訓練/テスト シャープ比< 1.21.2-1.5> 1.5
パラメータ数 / 取引回数< 0.010.01-0.05> 0.05
In-sample/Out-sample乖離< 20%20-30%> 30%

実運用までのワークフロー

graph TD
    A[戦略アイデア] --> B[LLMでコード生成]
    B --> C[静的分析・lint]
    C --> D[単体テスト]
    D --> E[バックテスト]
    E --> F{シャープ比 > 1.5?}
    F -->|Yes| G[過学習検証]
    F -->|No| B
    G --> H{過学習なし?}
    H -->|Yes| I[ペーパートレード]
    H -->|No| B
    I --> J[1ヶ月検証]
    J --> K{実績一致?}
    K -->|Yes| L[小ロット実運用]
    K -->|No| B
    L --> M[段階的スケール]

各フェーズのチェックリスト

フェーズチェック項目基準
コード生成構文エラーなし100%
静的分析セキュリティ警告なし0件
バックテストシャープレシオ> 1.5
バックテスト最大ドローダウン< 20%
バックテスト取引回数> 50回
過学習検証訓練/テスト乖離< 30%
ペーパートレード実績との乖離< 20%

リスクと注意事項

LLMコード生成のリスク

リスク影響対策
誤ったロジック生成意図しない取引実行コードレビュー必須
ルックアヘッドバイアスバックテスト結果の過大評価時系列検証の実施
セキュリティ脆弱性システム侵害静的分析ツールで検出
ハルシネーション存在しないAPIの呼び出し実行前テスト
モデルドリフト生成品質の変化バージョン固定

推奨されるベストプラクティス

  1. 段階的検証: コード生成 → 静的分析 → バックテスト → ペーパートレード → 小ロット実運用
  2. 人間によるレビュー: 特にリスク管理ロジックは必ず人間が確認
  3. バージョン管理: 生成に使用したLLMモデルとプロンプトを記録
  4. 継続的モニタリング: 実運用後もパフォーマンスを監視し、乖離があれば停止

まとめ

LLMによるコード生成は、取引戦略開発のスピードを劇的に向上させます。従来数週間かかっていたプロトタイピングが数時間で完了し、より多くの戦略アイデアを検証できるようになります。

ただし、生成されたコードは必ず検証が必要です。過学習リスク、セキュリティ脆弱性、ハルシネーションへの対策を怠ると、実運用で大きな損失につながる可能性があります。

推奨導入ステップ:

  1. まず簡単な戦略(移動平均クロス等)でワークフローを習得
  2. 静的分析・過学習検証のパイプラインを整備
  3. ペーパートレードで実際の市場と比較
  4. 小ロットから実運用を開始し、段階的にスケール

免責事項: 本記事は情報提供を目的としており、特定の投資行動を推奨するものではありません。投資判断はご自身の責任で行ってください。LLMが生成したコードは必ず検証し、十分なテストを経てから実運用してください。