第 10 課:モデルからAgentへ

モデルは「明日は上昇する」と言うが、どれくらい? いくら買うべき? いつ売るべき? モデルはこれらの質問に答えない。しかしエージェントは答えなければならない。


モデルの限界

あるクオンツチームが高品質な予測モデルを学習させた:

  • IC = 0.05 (トップクラス)
  • 500銘柄のリターンランキングを毎日予測
  • バックテストで安定的で効果的であることを検証

彼らはモデルを取引システムに接続し、シンプルなルールを使った:

  • 毎日予測上位10銘柄を購入
  • 各銘柄に均等に資金を配分
  • 5日間保有後に売却

1ヶ月目: +3%のリターン、期待通り

2ヶ月目: -5%のリターン

何が起きたのか?

  1. 相関を考慮していなかった: 購入した10銘柄がすべてテック株; テックセクターが下落すると、全て一緒に損失
  2. ボラティリティを考慮していなかった: ある銘柄は日次ボラティリティ5%、他は1%のみだが、資金は均等配分
  3. 損切りメカニズムがなかった: ある銘柄が15%下落し続けたが、5日目まで保有
  4. 予期しないイベントを処理できなかった: ある銘柄が取引停止、資金がロック

モデルは「予測」のみ担当するが、エージェントは「判断」を担当しなければならない。 判断には以下が含まれる:

  • 何を買うか? (資産選択)
  • いくら買うか? (ポジションサイズ)
  • いつ買う/売るか? (タイミング)
  • 何か問題が起きたらどうするか? (例外処理)

このレッスンでは、「予測モデル」を「判断を下せるエージェント」にアップグレードする方法を教える。


10.1 予測 vs 判断

コアな違い

予測モデル取引エージェント
入力特徴量ベクトル特徴量 + 状態 + 制約
出力予測値/確率具体的な行動
評価予測誤差 (IC, MSE)リターン/リスク/コスト
時間ポイント予測継続的な意思決定
エラー処理なし必須

直感的な例

モデル出力:
  「AAPL 明日の期待リターン +0.5%, 信頼度60%

エージェントが答えなければならないこと:
  1. 買うべきか? -> 他の銘柄と比較し、現在のポジションを考慮する必要
  2. いくら買うか? -> 資本、リスク限度、相関を考慮する必要
  3. どの価格で? -> 成行注文か指値注文か? どのレベルで?
  4. 損切りはどこ? -> 予測が外れたら、いくら損失を出す前に撤退?
  5. いつ売るか? -> 目標価格、保有期間、それとも動的利確?

エージェントの判断パイプライン

Agent Decision Pipeline

10.2 エージェントのコアコンポーネント

状態

エージェントは「今どんな状況にいるか」を知る必要がある:

状態タイプ例示内容
市場状態現在価格、ボラティリティ、出来高、トレンド/レンジ
ポジション状態現在の保有、平均コスト、P&L、保有期間
アカウント状態利用可能キャッシュ、マージン、使用レバレッジ
システム状態APIは動作中? レイテンシは? アラートはあるか?

行動空間

エージェントが取れる行動:

基本行動:
  - BUY(symbol, quantity, order_type, price)
  - SELL(symbol, quantity, order_type, price)
  - HOLD()

複合行動:
  - REBALANCE(target_weights)
  - REDUCE_RISK(target_exposure)
  - CLOSE_ALL()

制約:
  - 単一取引は利用可能資本の10%以下
  - 総レバレッジは2x以下
  - 単一銘柄は総ポジションの20%以下

判断関数

状態を行動にマッピング:

action = decide(state, prediction, constraints)

:
  state = {
    cash: $100,000,
    positions: {AAPL: 100株, $180コスト},
    market_regime: "trend"
  }

  prediction = {
    AAPL: +0.5%,
    MSFT: +0.8%,
    GOOGL: +0.3%
  }

  constraints = {
    max_position: 20%,
    max_drawdown: 10%,
    stop_loss: 5%
  }

  action = decide(state, prediction, constraints)
  -> BUY(MSFT, 50株, LIMIT, $380)

10.3 ポジションサイズ: 予測からポジションへ

均等ウェイト (最もシンプル)

ルール: 上位N銘柄を購入、各銘柄に1/Nの資金を配分

:
  上位5銘柄、資本$100,000
  各銘柄に$20,000

問題: 予測強度、ボラティリティの違い、相関を考慮しない

予測強度による配分

ルール: 予測が強いほど、配分を大きく

予測リターン:
  AAPL: +1.0%
  MSFT: +0.5%
  GOOGL: +0.3%

ウェイト計算:
  AAPL: 1.0 / 1.8 = 56%
  MSFT: 0.5 / 1.8 = 28%
  GOOGL: 0.3 / 1.8 = 16%

問題: 高ボラティリティ銘柄が過大なウェイトを得る可能性

ボラティリティ調整 (リスクパリティ)

ルール: 各銘柄が等しいリスクを寄与させる

ボラティリティ:
  AAPL: 25%
  MSFT: 20%
  GOOGL: 15%

逆ボラティリティウェイト:
  AAPL: 1/0.25 = 4
  MSFT: 1/0.20 = 5
  GOOGL: 1/0.15 = 6.67

正規化:
  AAPL: 4/15.67 = 26%
  MSFT: 5/15.67 = 32%
  GOOGL: 6.67/15.67 = 42%

Kelly公式 (最適)

理論的に最適なポジションサイズ:

Kelly割合 = p/a - q/b

p = 勝率
q = 1 - p
a = 平均損失率
b = 平均勝率

:
  勝率55%, 勝敗比率1.5:1
  Kelly = 0.55/1 - 0.45/1.5 = 0.55 - 0.30 = 25%

実践では: Half-Kelly (12.5%) をより保守的なアプローチとして使用

Half-Kelly + Van Tharp ハイブリッドモデル (推奨)

なぜハイブリッドモデルを使うのか?

Kelly公式だけを使うと2つの問題がある:

  1. 無限の分割可能性を仮定: 実際には、株には最小取引単位がある
  2. ファットテールリスクを無視: 市場クラッシュ時、損失は過去平均をはるかに超える可能性

Van TharpのR-Multiple法はこのギャップに対処 - 損切りをポジション計算に強制し、単一取引の損失がアカウントの固定割合を超えないことを保証。

2つの手法の役割:

手法役割目的
Half-Kellyオフェンス - 上限を設定長期複利成長を最大化
Van Tharp R-Multipleディフェンス - 下限を設定サバイバル - 単一損失が致命的にならない

公式:

Half-Kelly (オフェンスの上限):
  f = (p × (b + 1) - 1) / b, 次に2で割る

  p = 勝率
  b = 報酬/リスク比率 (平均勝利 / 平均損失)

Van Tharp R-Multiple (ディフェンスの下限):
  position_size = (equity × risk_pct) / stop_loss_distance

  equity = アカウント資本
  risk_pct = 取引あたりリスク (通常1%)
  stop_loss_distance = エントリー価格 - 損切り価格

実装パターン:

def half_kelly(win_rate: float, reward_risk_ratio: float) -> float:
    """Half-Kellyポジション上限を計算"""
    full_kelly = (win_rate * (reward_risk_ratio + 1) - 1) / reward_risk_ratio
    return max(0, full_kelly / 2)

def van_tharp_limit(equity: float, risk_pct: float, stop_loss_dist: float, price: float) -> float:
    """Van Tharpポジション上限を計算 (ポジション比率を返す)"""
    max_loss = equity * risk_pct
    shares = max_loss / stop_loss_dist
    position_value = shares * price
    return position_value / equity

# 最終ポジション = 3つすべての最小値
strategy_cap = half_kelly(win_rate=0.55, reward_risk_ratio=1.5)  # : 0.10 (10%)
risk_cap = van_tharp_limit(equity=100000, risk_pct=0.01, stop_loss_dist=5, price=100)  # : 0.08 (8%)
max_notional_per_pair = 0.05  # ハードリミット: 取引あたり5%以下

final_position = min(strategy_cap, risk_cap, max_notional_per_pair)

実例:

アカウント: $100,000
戦略履歴: 55%勝率、1.5:1報酬/リスク比率
ターゲット: AAPL $200、損切り$190 (距離$10)

ステップ1: Half-Kelly上限
  f = (0.55 × 2.5 - 1) / 1.5 / 2 = 0.375 / 2 = 18.75%
  -> 最大投資$18,750

ステップ2: Van Tharp下限
  取引あたり最大損失 = $100,000 × 1% = $1,000
  許可株数 = $1,000 / $10 = 100株
  ポジション価値 = 100 × $200 = $20,000
  -> リスク管理により、最大投資$20,000

ステップ3: ハードリミット
  取引あたり上限 = $100,000 × 5% = $5,000

最終ポジション = min($18,750, $20,000, $5,000) = $5,000
-> AAPL 25株を購入

コア原則:

Half-Kellyはオフェンスの上限を定義; Van Tharp R-Multipleはサバイバルの下限を定義。

両方を組み合わせることで以下を保証:

  1. 機会が訪れたときに保守的すぎない (Kellyの数学的最適化)
  2. 判断が間違ったときに致命的にならない (Van Tharpのリスク管理下限)
  3. ハードリミットが過度な集中を防ぐ (モデルの信頼度に関係なく)

ポジション計算例

仮定:

  • 資本$100,000
  • 最大単一ポジション20% ($20,000)
  • 最大総ポジション80% ($80,000)

モデル予測上位3:

銘柄予測リターンボラティリティ生ウェイトリスク調整後ウェイト最終ポジション
AAPL+1.0%25%40%30%$24,000 -> $20,000 (上限適用)
MSFT+0.8%20%32%35%$28,000 -> $20,000 (上限適用)
GOOGL+0.7%15%28%35%$28,000 -> $20,000 (上限適用)

最終: AAPL/MSFT/GOOGLそれぞれ$20,000、総ポジション60%


10.4 リスク管理統合

エージェントの組み込みリスクルール

ルールタイプトリガーされるアクション
損切り単一ポジション損失 > 5%ポジション閉鎖
利確単一ポジション利益 > 10%ポジションを50%削減
総ドローダウンアカウントドローダウン > 15%新規ポジション開始停止
集中度単一銘柄 > 25%追加購入禁止
時間保有 > 20日強制ポジション閉鎖

Risk Agentとの協調

Signal Agentが提案:
  「AAPL $30,000購入」

エージェント内部チェック:
  OK - 単一取引 < 最大ポジション
  OK - 十分なキャッシュ利用可能
  X  - 購入すると、テックセクターが60%超過

内部処理オプション:
  A) 注文を$15,000に削減
  B) 同時に他のテック株を売却
  C) この取引を放棄

-> Aを選択、$15,000注文をRisk Agentに審査のため提出

Risk Agent審査:
  OK - アカウントレベルのリスク管理に適合
  OK - 異常なし
  -> 実行承認

10.5 例外処理

必須の例外処理

例外タイプシナリオ処理方法
注文拒否資金不足、銘柄取引停止ログ記録、計画調整
部分約定流動性不足追い注文するか判断
価格ギャップオーバーナイト大幅変動損切り価格を再評価
APIタイムアウトネットワーク問題リトライメカニズム + サーキットブレーカー
データ欠損データソース障害バックアップデータソース使用か一時停止

例外処理設計原則

  1. Fail Fast: 不確実な場合、実行を続けるのではなく停止
  2. Graceful Degradation: 主機能が失敗したときのバックアッププラン
  3. 人間の介入: 深刻な例外はアラートをトリガー、人間の判断を待つ
  4. 事後監査: すべての例外をログ記録してレビュー

10.6 エージェントのライフサイクル

日次プロセス

Trading Agent Daily Lifecycle

状態機械の視点

Agent State Machine

10.7 マルチエージェントの視点

Signal Agentのポジション

Signal Agentの責任:
  - 予測モデルを実行
  - 生シグナルを生成 (予測ランキング/リターン)
  - 初期ポジション計算
  - シグナル信頼度評価

Signal Agentがやらないこと:
  - 最終注文判断 (Risk Agent)
  - 注文実行 (Execution Agent)
  - ポジションモニタリング (Position Agent)

シングルエージェントからマルチエージェントへ

From Single Agent to Multi-Agent

このレッスンでは「単一エージェントがすべてを行う」簡略版をカバーした。Lesson 11から、異なる責任を専門エージェントに分割する。


コード実装 (オプション)

コード例を展開
from dataclasses import dataclass
from typing import Dict, List, Optional
from enum import Enum

class OrderType(Enum):
    MARKET = "market"
    LIMIT = "limit"

@dataclass
class Order:
    symbol: str
    quantity: int
    side: str  # "buy" または "sell"
    order_type: OrderType
    price: Optional[float] = None

@dataclass
class Position:
    symbol: str
    quantity: int
    avg_cost: float
    current_price: float

    @property
    def pnl_pct(self) -> float:
        return (self.current_price - self.avg_cost) / self.avg_cost

class TradingAgent:
    def __init__(
        self,
        capital: float,
        max_position_pct: float = 0.2,
        max_total_exposure: float = 0.8,
        stop_loss_pct: float = 0.05
    ):
        self.capital = capital
        self.max_position_pct = max_position_pct
        self.max_total_exposure = max_total_exposure
        self.stop_loss_pct = stop_loss_pct
        self.positions: Dict[str, Position] = {}

    def get_current_exposure(self) -> float:
        """現在の総エクスポージャーを資本の割合として計算"""
        total_value = sum(
            pos.quantity * pos.current_price
            for pos in self.positions.values()
        )
        return total_value / self.capital

    def calculate_position_size(
        self,
        symbol: str,
        prediction: float,
        volatility: float,
        current_price: float
    ) -> int:
        """ポジションサイズを計算"""
        # 総エクスポージャー限度をチェック
        current_exposure = self.get_current_exposure()
        remaining_exposure = self.max_total_exposure - current_exposure
        if remaining_exposure <= 0:
            return 0  # すでに最大総エクスポージャー

        # 予測強度に基づく生ウェイト
        raw_weight = abs(prediction)

        # ボラティリティ調整
        vol_adjusted_weight = raw_weight / volatility

        # 単一ポジション上限、残りエクスポージャー余地も尊重
        capped_weight = min(vol_adjusted_weight, self.max_position_pct, remaining_exposure)

        # 株数を計算
        position_value = self.capital * capped_weight
        shares = int(position_value / current_price)

        return shares

    def check_stop_loss(self) -> List[Order]:
        """損切りトリガーをチェック"""
        orders = []
        for symbol, pos in self.positions.items():
            if pos.pnl_pct < -self.stop_loss_pct:
                orders.append(Order(
                    symbol=symbol,
                    quantity=pos.quantity,
                    side="sell",
                    order_type=OrderType.MARKET
                ))
        return orders

    def generate_orders(
        self,
        predictions: Dict[str, float],
        volatilities: Dict[str, float],
        prices: Dict[str, float]
    ) -> List[Order]:
        """注文を生成"""
        orders = []

        # 1. 最初に損切りをチェック
        stop_loss_orders = self.check_stop_loss()
        orders.extend(stop_loss_orders)

        # 2. ターゲットポジションを計算
        for symbol, pred in predictions.items():
            if pred > 0.001:  # 正の予測のみ処理
                target_shares = self.calculate_position_size(
                    symbol, pred,
                    volatilities.get(symbol, 0.2),
                    prices[symbol]
                )

                current_shares = self.positions.get(symbol, Position(symbol, 0, 0, 0)).quantity
                diff = target_shares - current_shares

                if diff > 0:
                    orders.append(Order(
                        symbol=symbol,
                        quantity=diff,
                        side="buy",
                        order_type=OrderType.LIMIT,
                        price=prices[symbol] * 0.999  # 現在価格より若干下
                    ))
                elif diff < 0:
                    orders.append(Order(
                        symbol=symbol,
                        quantity=-diff,
                        side="sell",
                        order_type=OrderType.LIMIT,
                        price=prices[symbol] * 1.001  # 現在価格より若干上
                    ))

        return orders

レッスンの成果物

このレッスン完了後、以下が得られます:

  1. 予測から判断への変換マインドセット - モデル出力が具体的な取引行動になる方法を理解
  2. エージェントアーキテクチャ設計 - 状態、行動空間、判断関数の設計方法
  3. ポジションサイジングスキル - 均等ウェイト、予測ウェイト、リスクパリティの計算方法
  4. 例外処理意識 - どの例外を処理しなければならないかを知る

受入基準

チェック項目受入基準自己テスト方法
予測 vs 判断3つのコアな違いを述べられるメモなしで違いをリスト
ポジション計算リスクパリティウェイトを手動計算できる3銘柄のボラティリティが与えられたらウェイトを計算
判断パイプライン予測から注文までの完全なフローを描ける白紙に描く
例外処理処理しなければならない5種類の例外をリストできる例外処理テーブルを設計

シナリオ演習:

モデル予測: AAPL +1.2%, TSLA +0.8%, MSFT +0.5% ボラティリティ: AAPL 25%, TSLA 50%, MSFT 20% 資本: $100,000, 最大単一ポジション20%

質問: リスクパリティ計算を使用して、各銘柄にいくらの資本を配分すべきか?

答えを見るにはクリック

逆ボラティリティ:

  • AAPL: 1/0.25 = 4
  • TSLA: 1/0.50 = 2
  • MSFT: 1/0.20 = 5
  • 合計: 11

正規化ウェイト:

  • AAPL: 4/11 = 36.4%
  • TSLA: 2/11 = 18.2%
  • MSFT: 5/11 = 45.4%

資本配分 (20%単一ポジション上限を考慮):

  • AAPL: $36,400 -> $20,000 (上限適用)
  • TSLA: $18,200 -> $18,200
  • MSFT: $45,400 -> $20,000 (上限適用)

最終: AAPL $20k, TSLA $18.2k, MSFT $20k, 総ポジション58.2%


レッスンまとめ

  • 予測モデルと取引エージェントのコアな違いを理解
  • エージェントのコアコンポーネントをマスター: 状態、行動空間、判断関数
  • 複数のポジションサイジング手法を学ぶ: 均等ウェイト、予測ウェイト、リスクパリティ、Kelly
  • 必須の例外タイプと処理原則を認識
  • マルチエージェントシステムにおけるSignal Agentのポジションを理解

さらなる読書


Part 3まとめ

機械学習ステージ完了おめでとうございます!

レッスン重要なポイント
Lesson 09クオンツ取引における教師あり学習の役割、特徴量エンジニアリング、モデル選択、IC評価
Lesson 10予測から判断への変換、エージェントアーキテクチャ、ポジションサイジング

次のステージプレビュー:

Part 4ではマルチエージェントシステムに深く入る:

  • Lesson 11: なぜマルチエージェントが必要か
  • Lesson 12: 市場レジーム検出
  • Lesson 13: レジーム誤判定とシステミック崩壊パターン
  • Lesson 14: クオンツ取引におけるLLMアプリケーション
  • Lesson 15: リスク管理と資本管理
  • Lesson 16: ポートフォリオ構築とリスクエクスポージャー管理
  • Lesson 17: オンライン学習と戦略進化

分析から実行までの完全なワークフローを完成させるために、異なる専門エージェントが協調して動く完全なマルチエージェント取引システムの構築方法を学びます。

この章を引用する
Zhang, Wayland (2026). 第 10 課:モデルからエージェントへ. In AIクオンツ取引:ゼロからイチへ. https://waylandz.com/quant-book-ja/Lesson-10-From-Models-to-Agents
@incollection{zhang2026quant_Lesson_10_From_Models_to_Agents,
  author = {Zhang, Wayland},
  title = {第 10 課:モデルからエージェントへ},
  booktitle = {AIクオンツ取引:ゼロからイチへ},
  year = {2026},
  url = {https://waylandz.com/quant-book-ja/Lesson-10-From-Models-to-Agents}
}