Polymarket Bot Tutorial · 32章中の30章
Polymarket bot向けの本番運用レベルのリスク管理コード:ポジション上限、日次損失上限、停止センチネル、約定率ウォッチドッグ、再起動時の整合性確認、冪等なリトライ。実際の本番トレーダーから得たコードパターン。
この章で扱う内容
リスクコードは、本番トレーディング bot の大部分を占めます。戦略ロジックは簡単な部分で、その周囲にある上限、停止、ウォッチドッグ、再整合の仕組みこそが、bot が最初の悪い週を生き残れるかを左右します。この章では、本番運用レベルのリスクパターンを扱います。
- なぜリスクコードが実際の trading bot の大部分を占めるのか
- ポジション上限(市場別、戦略別、総量)
- 日次損失のキルスイッチ
- 停止センチネル(ファイルベースの緊急停止)
- 約定率ウォッチドッグ
- 再起動時に台帳とオンチェーンを整合させる
- コード:本番対応の停止認識ループ
なぜリスクコードが実際の trading bot の大部分を占めるのか
自社 bot のコードベースで測定したところ、LOC の60%がリスクコード(上限、停止、ウォッチドッグ、再整合)でした。30% が戦略、10% が接続用の glue コードです。
この比率は正しいです。戦略は簡単な部分で、いつ入っていつ出るかを記述するだけなら数十行で済みます。リスクコードはそれ以外すべてです。想定より速く価格が逆行したらどうするか、約定が止まったらどうするか、WebSocket が切れたらどうするか、戦略が実は収益性を持たないと分かったらどうするか。
多くの失敗談は同じ形をしています。戦略自体は動いていたのに、相場のレジーム転換時に停止が発動せず、そのまま取引を続けてしまったのです。戦略を書く前に、停止処理を書いてください。
ポジション上限(市場別、戦略別、総量)
3つの上限を、コードで強制します。
- 市場別上限: edge の確信度に関わらず、1つの市場あたり最大 $X。典型値:小規模 bot なら $25-100、本番運用なら $200-500。単一市場での誤判断による損害範囲を限定します。
- 戦略別上限: 複数戦略を運用する場合、各戦略に総資本の一部を割り当てます。典型値:戦略ごとに30-50%。ある戦略の不調が他の戦略の資本を食い潰すのを防ぎます。
- 総量上限: ウォレット残高の何%までを同時に投入するかの上限。典型値:50-70%。予期しない機会や、bot 自身の会計バグに備えて資本を残します。
3つの上限はすべて、戦略ロジックだけでなく、注文発行関数の内部で強制すべきです。戦略にはバグが入り得ます。注文発行ゲートが最後の防衛線です。
日次損失のキルスイッチ
最も重要な単体のリスク制御は、日次損失のキルスイッチです。
ルール:UTCの深夜以降の実現損益 + 評価損益が、当日開始時点の残高の -X% を下回ったら、bot は新規ポジションの建玉を停止し、必要に応じて既存ポジションをフラット化します。典型的な X は 5-10% です。
数値の考え方:期待勝率 60% の bot でも、10連敗する確率はおそらく 5% ほどあります。キルスイッチがなければ、この連敗は損失を増幅させます。$200 の損失 → bot は取引を継続 → さらに $200 の損失 → ウォレットは40%減。-10% でスイッチが発火すれば、悪い日の損失は $200 で止まり、翌日 bot は新たに開始できます。
このスイッチはサーバー側で強制します。停止ファイルを書き込むか、トレーディングループが毎回確認するデータベースフラグを設定してください。再起動は手動レビュー後のみ行います。
停止センチネル(ファイルベースの緊急停止)
最もシンプルな停止機構は、bot が毎ループでファイル(例:/opt/pmt/HALT)の有無を確認し、ファイルが存在すれば取引を停止する方法です。
def trading_loop():
while True:
if os.path.exists("/opt/pmt/HALT"):
log("HALT file detected, sleeping")
time.sleep(30)
continue
run_one_iteration()
time.sleep(5)
どこからでも即座に停止するには(SSH、Telegram bot、監視システムなど)、touch /opt/pmt/HALT を実行します。再開するには rm /opt/pmt/HALT です。
ファイルベースの方式をあえて低技術にしているのは、より高度な停止機構が失敗する状況でも機能するからです。bot が部分的にクラッシュしているとき、データベースに到達できないとき、API key にレート制限がかかっているときでも使えます。ファイルシステムへのアクセスは常に利用可能です。
約定率ウォッチドッグ
戦略は、FOK注文がある程度の割合で約定することを前提にしています(多くの場合60-80%)。その割合が大きく低下したら、何かが変わったということです。マーケットメイカーが引いた、戦略が識別された、API 障害が続いている、などです。理由が何であれ、戦略の PnL 計算を支えていた前提は壊れています。
ウォッチドッグのロジック:直近24時間の約定率を集計します。30%未満、または想定値の50%未満なら、アラートを出し、自動停止します。再開は手動レビュー後のみです。
ウォッチドッグは診断にも役立ちます。約定率の急落は通常、外部イベント(Polymarket のデプロイ、Polygon の混雑、自分の IP に対するレート制限など)と相関し、取引への影響に関係なく把握しておきたい事象です。
再起動時に台帳とオンチェーンを整合させる
bot は、自分が保有していると考えるポジションの台帳を保持します。チェーンは真実を保持します。両者は常に一致しているべきで、一致していない場合、bot は誤った認識のまま動作しており、誤った取引を行うことになります。
再整合ロジック:毎回の再起動時、および通常運用中は1時間に1回、bot が触れたすべての token についてオンチェーン残高を取得します。台帳と比較し、丸め誤差の許容範囲を超えていずれかの token の残高が異なる場合は、アラートを出して停止します。
乖離の最も一般的な原因は、bot の API 呼び出しが取り逃した成功注文です(タイムアウト、リトライが記録されなかった等)。チェーン上にはポジションがあるのに、bot はないと思っています。再整合がなければ、bot は利益確定の exit を出さず、ポジションはそのまま決済まで保有されます。
コード:本番対応の停止認識ループ
参考:すべてのリスク制御を組み込んだ本番トレーディングループです。
def production_loop():
while True:
# Halt checks
if os.path.exists("/opt/pmt/HALT"):
sleep_with_log(30); continue
if daily_pnl_below_threshold():
create_halt("daily PnL kill"); continue
# Reconcile every hour
if now() - last_reconcile > 3600:
ok = reconcile_diary_vs_chain()
last_reconcile = now()
if not ok: create_halt("reconciliation failed"); continue
# Fill-rate watchdog
if recent_fill_rate() < 0.30:
create_halt("fill rate collapse"); continue
# Strategy
try:
run_strategy_once()
except Exception as e:
log_exception(e)
if consecutive_exceptions >= 5:
create_halt(f"exceptions: {e}"); continue
time.sleep(5)
このパターンでは、すべての反復がゲートを通過します。構造上、戦略のバグが制御をすり抜けることはありません。





