第 10 課:モデルからAgentへ
モデルは「明日は上昇する」と言うが、どれくらい? いくら買うべき? いつ売るべき? モデルはこれらの質問に答えない。しかしエージェントは答えなければならない。
モデルの限界
あるクオンツチームが高品質な予測モデルを学習させた:
- IC = 0.05 (トップクラス)
- 500銘柄のリターンランキングを毎日予測
- バックテストで安定的で効果的であることを検証
彼らはモデルを取引システムに接続し、シンプルなルールを使った:
- 毎日予測上位10銘柄を購入
- 各銘柄に均等に資金を配分
- 5日間保有後に売却
1ヶ月目: +3%のリターン、期待通り
2ヶ月目: -5%のリターン
何が起きたのか?
- 相関を考慮していなかった: 購入した10銘柄がすべてテック株; テックセクターが下落すると、全て一緒に損失
- ボラティリティを考慮していなかった: ある銘柄は日次ボラティリティ5%、他は1%のみだが、資金は均等配分
- 損切りメカニズムがなかった: ある銘柄が15%下落し続けたが、5日目まで保有
- 予期しないイベントを処理できなかった: ある銘柄が取引停止、資金がロック
モデルは「予測」のみ担当するが、エージェントは「判断」を担当しなければならない。 判断には以下が含まれる:
- 何を買うか? (資産選択)
- いくら買うか? (ポジションサイズ)
- いつ買う/売るか? (タイミング)
- 何か問題が起きたらどうするか? (例外処理)
このレッスンでは、「予測モデル」を「判断を下せるエージェント」にアップグレードする方法を教える。
10.1 予測 vs 判断
コアな違い
| 予測モデル | 取引エージェント | |
|---|---|---|
| 入力 | 特徴量ベクトル | 特徴量 + 状態 + 制約 |
| 出力 | 予測値/確率 | 具体的な行動 |
| 評価 | 予測誤差 (IC, MSE) | リターン/リスク/コスト |
| 時間 | ポイント予測 | 継続的な意思決定 |
| エラー処理 | なし | 必須 |
直感的な例
モデル出力:
「AAPL 明日の期待リターン +0.5%, 信頼度60%」
エージェントが答えなければならないこと:
1. 買うべきか? -> 他の銘柄と比較し、現在のポジションを考慮する必要
2. いくら買うか? -> 資本、リスク限度、相関を考慮する必要
3. どの価格で? -> 成行注文か指値注文か? どのレベルで?
4. 損切りはどこ? -> 予測が外れたら、いくら損失を出す前に撤退?
5. いつ売るか? -> 目標価格、保有期間、それとも動的利確?
エージェントの判断パイプライン
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つの問題がある:
- 無限の分割可能性を仮定: 実際には、株には最小取引単位がある
- ファットテールリスクを無視: 市場クラッシュ時、損失は過去平均をはるかに超える可能性
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はサバイバルの下限を定義。
両方を組み合わせることで以下を保証:
- 機会が訪れたときに保守的すぎない (Kellyの数学的最適化)
- 判断が間違ったときに致命的にならない (Van Tharpのリスク管理下限)
- ハードリミットが過度な集中を防ぐ (モデルの信頼度に関係なく)
ポジション計算例
仮定:
- 資本$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タイムアウト | ネットワーク問題 | リトライメカニズム + サーキットブレーカー |
| データ欠損 | データソース障害 | バックアップデータソース使用か一時停止 |
例外処理設計原則
- Fail Fast: 不確実な場合、実行を続けるのではなく停止
- Graceful Degradation: 主機能が失敗したときのバックアッププラン
- 人間の介入: 深刻な例外はアラートをトリガー、人間の判断を待つ
- 事後監査: すべての例外をログ記録してレビュー
10.6 エージェントのライフサイクル
日次プロセス
状態機械の視点
10.7 マルチエージェントの視点
Signal Agentのポジション
Signal Agentの責任:
- 予測モデルを実行
- 生シグナルを生成 (予測ランキング/リターン)
- 初期ポジション計算
- シグナル信頼度評価
Signal Agentがやらないこと:
- 最終注文判断 (Risk Agent)
- 注文実行 (Execution Agent)
- ポジションモニタリング (Position 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レッスンの成果物
このレッスン完了後、以下が得られます:
- 予測から判断への変換マインドセット - モデル出力が具体的な取引行動になる方法を理解
- エージェントアーキテクチャ設計 - 状態、行動空間、判断関数の設計方法
- ポジションサイジングスキル - 均等ウェイト、予測ウェイト、リスクパリティの計算方法
- 例外処理意識 - どの例外を処理しなければならないかを知る
受入基準
| チェック項目 | 受入基準 | 自己テスト方法 |
|---|---|---|
| 予測 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のポジションを理解
さらなる読書
- Active Portfolio Management by Grinold & Kahn - ポジション管理の古典的著作
- Lesson 15: Risk Control and Money Management - より詳細なリスク管理コンテンツ
Part 3まとめ
機械学習ステージ完了おめでとうございます!
| レッスン | 重要なポイント |
|---|---|
| Lesson 09 | クオンツ取引における教師あり学習の役割、特徴量エンジニアリング、モデル選択、IC評価 |
| Lesson 10 | 予測から判断への変換、エージェントアーキテクチャ、ポジションサイジング |
次のステージプレビュー:
Part 4ではマルチエージェントシステムに深く入る:
- Lesson 11: なぜマルチエージェントが必要か
- Lesson 12: 市場レジーム検出
- Lesson 13: レジーム誤判定とシステミック崩壊パターン
- Lesson 14: クオンツ取引におけるLLMアプリケーション
- Lesson 15: リスク管理と資本管理
- Lesson 16: ポートフォリオ構築とリスクエクスポージャー管理
- Lesson 17: オンライン学習と戦略進化
分析から実行までの完全なワークフローを完成させるために、異なる専門エージェントが協調して動く完全なマルチエージェント取引システムの構築方法を学びます。