Туторіал з бота Полімаркет · Розділ 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 за самою конструкцією.

Поширені запитання

Що таке halt sentinel (сторож зупинки)?
Це файл (наприклад, data/halt_autobuy), який бот перевіряє перед кожним ордером. Якщо файл існує, бот відмовляється розміщувати ордери, навіть якщо strategy цього вимагає. Дає змогу зупинити бота посеред інциденту однією командою touch. Ми додали саме цей патерн до нашого production-трейдера після інциденту із застряглим fill у квітні 2026 року.
Які ліміти позицій варто встановити?
На ринок: 1-5% від банкролу. На стратегію: 10-20%. Загальна відкрита експозиція: 50-70% від банкролу (тримайте грошовий буфер). Обмежуйте ОДИН ордер до 1-2% від банкролу незалежно від стратегії - помилковий ордер «товстим пальцем» ніколи не має дорівнювати розміру всього account.
Як реалізувати аварійний вимикач денних збитків?
Відстежуйте реалізований + нереалізований PnL за добу UTC. Якщо денний PnL падає нижче -3 до -5% від банкролу, увімкніть halt sentinel і сповістіть себе. Бот зупиняє нові ордери; наявними позиціями керують вручну. Скидання щодня о 00:00 UTC.
Що бот має робити при перезапуску після збою?
Три кроки: (1) Звірте відкриті ордери через SDK з вашим локальним журналом. (2) Перевірте відкриті позиції on-chain проти вашого локального стану. (3) Якщо щось розходиться, зупиніть бота і вимагайте ручної перевірки. Ніколи не відновлюйтеся автоматично в неузгодженому стані.
Як запобігти спустошенню account через один баг?
Багатошарові ліміти: ліміт позиції на рівні коду, ліміт розміру ордера на рівні коду, halt sentinel на рівні файлу, неявні мінімум/максимум на рівні біржі (Полімаркет), сповіщення моніторингу, що пейджать вас при незвичній частоті ордерів. Жоден окремий шар не є достатнім - вони множаться.
Чи має бот торгувати, якщо мій logging відмовив?
Ні. Якщо бот не може записувати у свій журнал, він не може звіритися при перезапуску, а отже збій призведе до неузгодженого стану. Зробіть hard-fail бота, якщо logging відмовляє. Здорові production-боти параноїдально ставляться до власної спостережності.