Туторіал з бота Полімаркет · Розділ 30 із 32
Код управління ризиками production-рівня для ботів Полімаркет: ліміти позицій, ліміти денних збитків, сторожі зупинки, сторожі частоти виконання, звірка при перезапуску, ідемпотентні повтори. Патерни коду від реального production-трейдера.
Що охоплює цей розділ
Risk code - це більша частина production trading bot. Логіка стратегії - найлегша частина; навколо неї саме caps, halts, watchdogs і reconcilers визначають, чи переживе бот свій перший невдалий тиждень. Цей розділ - production-grade risk pattern.
- Чому код управління ризиками — більшість справжнього торгового бота
- Ліміти позицій (на ринок, на стратегію, загальний)
- Аварійний вимикач денних збитків
- Сторожі зупинки (аварійна зупинка через файл)
- Сторож частоти виконання
- Звірка журналу з on-chain при перезапуску
- Код: production-grade цикл з урахуванням halt
Чому код управління ризиками — більшість справжнього торгового бота
Один із показників, який ми виміряли у власній codebase бота: 60% LOC - це risk code (caps, halts, watchdogs, reconciliation). 30% - strategy. 10% - glue.
Це правильне співвідношення. Strategy - найлегша частина: опис того, коли входити і коли виходити, уміщується в кілька десятків рядків. Risk code - це все інше: що робити, коли ціна рухається проти вас швидше, ніж очікувалося, що робити, коли fills перестають проходити, що робити, коли WebSocket обривається, що робити, коли strategy виявляється збитковою.
У більшості історій провалу builder'ів спільний сюжет один: strategy працювала, але бот продовжував торгувати після regime change, тому що жоден halt не спрацював. Пишіть halts раніше, ніж strategy.
Ліміти позицій (на ринок, на стратегію, загальний)
Три caps, які мають бути enforced у code.
- Per-market cap: максимум $X на один market незалежно від confidence у edge. Типово: $25-100 для small bots, $200-500 для production. Обмежує blast radius однієї неправильної ставки на окремий market.
- Per-strategy cap: якщо ви запускаєте кілька strategies, кожна отримує свою частку total capital. Типово: 30-50% на одну strategy. Захищає capital інших strategies від поганого дня однієї з них.
- Total cap: максимальний % від wallet balance, який може бути deployed одночасно. Типово: 50-70%. Залишає capital для неочікуваних opportunity або щоб зловити власні bookkeeping bugs бота.
Усі три caps слід enforce всередині order-placement function, а не лише в strategy logic. У strategy може бути bug; order-placement gate - це остання лінія захисту.
Аварійний вимикач денних збитків
Найважливіший окремий risk control: daily-loss kill switch.
Правило: якщо realized + unrealized PnL з опівночі UTC падає нижче -X% від starting daily balance, бот припиняє відкривати нові positions і (за бажанням) закриває existing ones. Типове X: 5-10%.
Математика: bot із 60% expected win rate має, можливо, 5% шанс на losing streak із 10 trades. Без kill switch ця серія наростає: $200 loss → бот продовжує торгувати → ще $200 loss → wallet просідає на 40%. Якщо switch спрацьовує на -10%, поганий день обмежується $200, а завтра бот починає з нуля.
Switch enforce server-side: створіть halt file або встановіть database flag, який trading loop перевіряє на кожній ітерації. Перезапускати лише після manual review.
Сторожі зупинки (аварійна зупинка через файл)
Найпростіший можливий halt mechanism: бот перевіряє наявність file (наприклад, /opt/pmt/HALT) на кожній ітерації loop і зупиняє trading, якщо file існує.
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, monitoring system): touch /opt/pmt/HALT. Щоб відновити роботу: rm /opt/pmt/HALT.
File-based підхід навмисно low-tech, бо він працює в умовах, коли більш складні halt mechanisms fail: коли бот частково crashed, коли database недоступна, коли API key має rate limit. File system доступ завжди є.
Сторож частоти виконання
Strategy припускає, що FOK orders заповнюються з певною rate (часто 60-80%). Коли rate помітно падає, щось змінилося: market makers пішли, вашу strategy ідентифікували, триває API outage. Якою б не була причина, припущення, на якому будувалася PnL math strategy, зламане.
Watchdog logic: rolling 24-hour fill-rate count. Якщо < 30% (або 50% від expected), alert + auto-halt. Відновлення лише після manual review.
Watchdog також корисний як diagnostic. Раптове падіння fill-rate зазвичай корелює із зовнішньою подією (Полімаркет deploy, Polygon congestion, ваш IP отримав rate limit), про яку вам варто знати незалежно від trading impact.
Звірка журналу з on-chain при перезапуску
Bot веде diary positions, які, на його думку, він тримає. Chain зберігає truth. Вони мають завжди збігатися; коли це не так, бот працює на хибному припущенні й торгує неправильно.
Reconciliation logic: на кожному restart і раз на годину під час normal operation отримуйте on-chain balances для кожного token, якого бот торкався. Порівнюйте з diary; alert + halt, якщо баланс будь-якого token відрізняється від diary більше, ніж допускає rounding tolerance.
Найпоширеніша причина розбіжності - successful order, який bot's API call пропустив (timeout, retry never recorded). Chain має position; бот думає, що не має. Без reconciliation бот не виставить take-profit exit, і position дійде до resolution.
Код: production-grade цикл з урахуванням halt
Reference: production trading loop з усіма risk controls, підключеними належним чином.
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
# Сторож частоти виконання
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)
Pattern такий: кожна ітерація проходить через gate. Strategy bugs не можуть обійти controls за самою конструкцією.


