前回はバックテスト最適化を解説しました。今回は 実際に資金を守るためのリスク管理レイヤーの実装です。
リスク管理の全体像
自動売買で最も重要なのは「いくら稼ぐか」より「いくら失わないか」です。Hermes Agent には複数の防衛レイヤーがあります。
1. ポジションサイジング — 1トレードのリスクを資産の2%以内に
2. ATRボラティリティガード — 異常なボラに自動でサイズ半減
3. 最大ポジション数制限 — 同時8ポジションまで
4. サーキットブレーカー — 連続損失N回でEXECUTE停止
5. ドローダウン停止 — 総資産がピークから X% 下落で停止
6. セクター集中制限 — 同セクターは最大N枚
7. VIXレジームフィルター — 恐怖指数が高い局面は見送り
8. 経済指標ブロック — CPI/FOMC前60分はStage 1でHOLD
ポジションサイジング
「1トレードで資産の2%しか失わない」を実現します。
// position-sizer.ts
const riskTarget = equity * (riskPct / 100); // 例: 10000 × 2% = 200ドル
const quantity = Math.floor(riskTarget / Math.abs(entry - stopLoss));
// 例: 200 / 5ドル(SL幅) = 40株
// 重要:equity(含み益込み)ではなく buying_power(使える現金)で上限
const capital = buyingPowerCap ?? equity;
const maxByCapital = Math.floor(capital / entry);
quantity = Math.min(quantity, maxByCapital);
このコードに至るまで「Alpaca 403 insufficient buying power」エラーで詰まりました。equity は含み益で膨らむため、既存ポジションがある状態では実際の buying power を大きく超える注文が生成されてしまいます。buying_power(実際の買付余力)でキャップすることで解決しました。
アセット別の計算
| アセット | 単位 | equity参照元 | 最小単位 |
|---|---|---|---|
| 株 | 株数(整数) | Alpaca equity | 1株 |
| 暗号資産 | 小数可 | Alpaca equity | 0.0001 |
| FX | units(整数) | OANDA NAV | 1,000 unit |
サーキットブレーカー
連続損失が閾値(デフォルト3回)に達すると EXECUTE をブロックします。
// memory.ts
getConsecutiveLosses(): number {
const closed = this.entries
.filter(e => e.type === "real" && e.outcome)
.sort((a, b) => b.closedAt.localeCompare(a.closedAt));
let count = 0;
for (const e of closed) {
if (e.outcome === "LOSS") count++;
else break; // WINがあったらリセット
}
// 最後の損失から24時間以上経過していたら自動リセット
const timeoutH = parseFloat(process.env.CIRCUIT_BREAKER_TIMEOUT_H ?? "24");
if (count > 0 && ageH >= timeoutH) return 0;
return count;
}
重要な設計判断:タイムアウト機能。ブレーカーが発動したまま次の WIN を記録できないデッドロック(「ブレーカー発動中は EXECUTE 禁止 → EXECUTE しないと WIN が記録されない」)を防ぐため、24時間後に自動解除します。
ドローダウン停止
口座残高がピーク比 X% 以上下落したら全トレードを停止します。
// hermes.ts
if (drawdownPct >= ddHaltPct) {
logInfo(`[DD HALT] Peak $${peak} → current $${current} = ${drawdownPct.toFixed(1)}% DD. EXECUTE blocked.`);
return { blocked: true, reason: "DD halt" };
}
口座残高は毎サイクル Alpaca/OANDA API から取得して logs/equity.jsonl に記録しています。
経済指標ブロック
Finnhub の経済指標カレンダーを取得し、CRITICAL イベント(NFP、FOMC、CPI、GDP)の60分前はすべての EXECUTE をブロックします。
function imminentEventBlock(events: EconomicEvent[]): string | null {
for (const e of events) {
if (e.tier === "critical" && e.minutesUntil < 60) {
return `[CRITICAL] "${e.event}" in ${e.minutesUntil}min`;
}
if (e.impact === "high" && e.minutesUntil < 30) {
return `[HIGH] "${e.event}" in ${e.minutesUntil}min`;
}
}
return null;
}
このチェックは Stage 1 で行うため、Claude を呼ぶ前にブロックできます(APIコスト節約)。
ファントムトレードとキャリブレーション
Claudeが WAIT を選んだとき、「もしエントリーしていたら?」を仮想トレードとして記録します。
// hermes.ts(WAIT時)
if (isMarketTradeable(symbol)) {
this.memory.phantomRecord({
symbol, direction, entryPrice: indicators.price,
stopLoss: decision.stop_loss ?? preliminary.stopLoss,
takeProfit: decision.take_profit ?? preliminary.takeProfit,
reasoning: decision.reasoning_brief,
});
}
phantom の WIN/LOSS が蓄積されると、Claude へのコンテキストに警告が入ります:
Calibration: You have been too conservative — your WAITs are
missing more wins than losses.
これにより「Claudeが慎重になりすぎて勝てるトレードを見逃している」状態を自動検知・補正できます。
リスクパラメータの設定例(.env)
RISK_PER_TRADE_PCT=2 # 1トレード最大リスク(資産の2%)
MAX_POSITIONS=8 # 同時最大ポジション数
CIRCUIT_BREAKER_LOSSES=3 # 連続損失でブレーカー発動
DD_HALT_PCT=15 # ドローダウン15%で全停止
MAX_ATR_PCT=3.0 # ATR%がこれ以上ならサイズ半減
まとめ
実際にお金を動かすシステムでは、「どうやって稼ぐか」よりも「どうやって守るか」の設計のほうが重要です。Hermes Agent は複数の防衛レイヤーが独立して機能しており、一つが突破されても他が守る構造になっています。
次回(第5回・最終回)は 本番運用とバグ修正の実録。実際に発生したバグとその原因分析を、コード差分と共に詳しく紹介します。