آموزش Polymarket Bot · فصل 30 از 32

کد production-grade برای مدیریت ریسک در botهای Polymarket: position cap، daily loss limit، halt sentinel، fill-rate watchdog، reconcile-on-restart، retryهای idempotent. الگوهای کدنویسی از یک real production trader.

این فصل چه چیزهایی را پوشش می‌دهد

کد risk بیشترِ یک trading bot production است. منطق strategy بخش ساده‌تر ماجراست؛ capها، haltها، watchdogها و reconcilerها هستند که تعیین می‌کنند bot از اولین هفته بدش جان سالم به در می‌برد یا نه. این فصل الگوی risk در سطح production است.

  • چرا کد risk بخش اصلی یک real trading bot است
  • Position capها (per-market، per-strategy، total)
  • Daily loss kill switch
  • Halt sentinelها (emergency stop مبتنی بر فایل)
  • Fill-rate watchdog
  • Reconcile کردن diary با on-chain هنگام restart
  • Code: production-grade halt-aware loop

چرا کد risk بخش اصلی یک real trading bot است

یک اندازه‌گیری که روی codebase bot خودمان انجام دادیم: 60% از LOC مربوط به کد risk است (capها، haltها، watchdogها، reconciliation). 30% strategy است. 10% glue است.

این نسبت درست است. Strategy بخش ساده ماجراست - توضیح اینکه چه زمانی وارد شویم و چه زمانی خارج شویم، در چند ده خط جا می‌شود. کد risk همه چیزِ دیگر است: وقتی قیمت سریع‌تر از انتظار علیه شما حرکت می‌کند چه باید کرد، وقتی fillها دیگر انجام نمی‌شوند چه باید کرد، وقتی WebSocket قطع می‌شود چه باید کرد، وقتی معلوم می‌شود strategy سودآور نیست چه باید کرد.

بیشتر داستان‌های شکست builderها شکل مشابهی دارند: strategy کار می‌کرد، اما bot به‌خاطر نبودن halt در مواجهه با تغییر regime همچنان معامله می‌کرد. haltها را قبل از strategy بنویسید.

Position capها (per-market، per-strategy، total)

سه cap که در code enforce می‌شوند.

  • Per-market cap: حداکثر $X برای هر market، صرف‌نظر از confidence در edge. معمولاً: $25-100 برای botهای کوچک، $200-500 برای production. این cap دامنه آسیبِ یک تصمیم اشتباه در یک market را محدود می‌کند.
  • Per-strategy cap: اگر چند strategy را اجرا می‌کنید، هرکدام سهمی از total capital می‌گیرند. معمولاً: 30-50% برای هر strategy. اجازه نمی‌دهد روز بدِ یک strategy سرمایه بقیه را مصرف کند.
  • Total cap: حداکثر درصدی از wallet balance که هم‌زمان deploy شده است. معمولاً: 50-70%. سرمایه‌ای برای فرصت‌های غیرمنتظره یا برای گرفتن خطاهای bookkeeping خود bot باقی می‌گذارد.

هر سه cap باید داخل order-placement function enforce شوند، نه فقط در منطق strategy. strategy ممکن است bug داشته باشد؛ gate مربوط به order-placement آخرین خط دفاعی است.

Daily loss kill switch

مهم‌ترین کنترل ریسک منفرد: یک daily-loss kill switch.

قانون: اگر realized + unrealized PnL از نیمه‌شب UTC پایین‌تر از -X% از starting daily balance بیاید، bot دیگر position جدید باز نمی‌کند و (در صورت نیاز) positionهای موجود را flat می‌کند. X معمولاً 5-10% است.

ریاضی ماجرا: botی با win rate مورد انتظار 60% شاید 5% احتمال داشته باشد که 10 معامله پشت‌سرهم ببازد. بدون kill switch، این باخت‌ها روی هم جمع می‌شوند: $200 ضرر → bot به معامله ادامه می‌دهد → $200 ضرر دیگر → wallet 40% پایین می‌آید. با فعال شدن switch در -10%، روز بد در $200 محدود می‌شود و فردا bot با شرایط تازه شروع می‌کند.

این switch باید server-side enforce شود: یک halt file بنویسید یا database flag را تنظیم کنید که trading loop در هر iteration آن را چک کند. فقط بعد از manual review دوباره restart کنید.

Halt sentinelها (emergency stop مبتنی بر فایل)

ساده‌ترین مکانیزم halt ممکن: bot در هر iteration حلقه فایل مشخصی را بررسی می‌کند (مثلاً /opt/pmt/HALT) و اگر فایل وجود داشته باشد، trading را متوقف می‌کند.

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)

برای halt فوری از هرجا (SSH، Telegram bot، یا یک monitoring system): touch /opt/pmt/HALT. برای resume: rm /opt/pmt/HALT.

رویکرد مبتنی بر فایل عمداً low-tech است، چون در شرایطی که مکانیزم‌های پیچیده‌تر halt شکست می‌خورند کار می‌کند: وقتی bot نیمه-crash شده، وقتی database در دسترس نیست، وقتی API key rate-limited شده است. دسترسی به file-system همیشه در دسترس است.

Fill-rate watchdog

Strategy فرض می‌کند orderهای FOK با یک نرخ مشخص fill می‌شوند (اغلب 60-80%). وقتی این نرخ به‌طور محسوس افت می‌کند، یعنی چیزی عوض شده: market makerها کنار کشیده‌اند، strategy شما شناسایی شده، یا یک API outage در جریان است. هر دلیل که داشته باشد، فرضی که محاسبه PnL strategy را جلو می‌برده، دیگر درست نیست.

منطق watchdog: شمارش rolling 24-hour fill-rate. اگر < 30% باشد (یا 50% از مقدار expected)، alert + auto-halt. فقط بعد از manual review resume شود.

Watchdog همچنین برای diagnosis مفید است. افت ناگهانی fill-rate معمولاً با یک event بیرونی هم‌بستگی دارد (Polymarket deploy، congestion در Polygon، rate-limited شدن IP شما) که صرف‌نظر از اثرش روی trading، بهتر است از آن باخبر باشید.

Reconcile کردن diary با on-chain هنگام restart

Bot یک diary از positionهایی که فکر می‌کند دارد نگه می‌دارد. Chain حقیقت را نگه می‌دارد. این دو باید همیشه با هم منطبق باشند؛ وقتی نیستند، bot بر اساس یک باور اشتباه کار می‌کند و اشتباه trade خواهد کرد.

منطق reconciliation: در هر restart و هر یک ساعت در طول عملیات عادی، on-chain balances همه tokenهایی را که bot با آن‌ها درگیر بوده fetch کنید. آن‌ها را با diary مقایسه کنید؛ اگر balance هر token بیش از tolerance مربوط به rounding تفاوت داشت، alert + halt کنید.

رایج‌ترین علت divergence این است که یک order موفق شده اما API call bot آن را از دست داده است (timeout، یا retryیی که هیچ‌وقت ثبت نشده). chain position را دارد؛ bot فکر می‌کند ندارد. بدون reconciliation، bot خروج take-profit را ثبت نمی‌کند و position تا زمان resolution باقی می‌ماند.

Code: production-grade halt-aware loop

Reference: trading loop production با تمام کنترل‌های risk که به آن متصل شده‌اند.

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)

الگو این است: هر iteration از gate عبور می‌کند. با طراحی درست، bugهای strategy نمی‌توانند از کنترل‌ها عبور کنند.

سؤالات متداول

Halt sentinel چیست؟
یک فایل (مثلاً data/halt_autobuy) که bot قبل از هر order آن را بررسی می‌کند. اگر فایل وجود داشته باشد، bot حتی اگر strategy چیز دیگری بگوید، از ثبت order خودداری می‌کند. این امکان را می‌دهد که bot را وسط incident فقط با یک touch command متوقف کنید. ما همین الگو را بعد از یک wedged-fill incident در آوریل 2026 به production trader خودمان اضافه کردیم.
چه position capهایی باید تنظیم کنم؟
Per-market: 1-5% از bankroll. Per-strategy: 10-20%. Total open exposure: 50-70% از bankroll (یک cash buffer نگه دارید). یک SINGLE order را بدون توجه به strategy در 1-2% از bankroll cap کنید - یک order ناشی از fat-finger هرگز نباید در اندازه حساب باشد.
چطور daily loss kill switch پیاده‌سازی کنم؟
Realized + unrealized PnL را به ازای هر UTC day track کنید. اگر daily PnL به کمتر از -3 تا -5% از bankroll رسید، halt sentinel را set کنید و به خودتان notify بدهید. bot سفارش‌های جدید را متوقف می‌کند؛ positionهای موجود به‌صورت manual مدیریت می‌شوند. هر روز ساعت 00:00 UTC reset کنید.
بوت بعد از crash در restart چه کاری باید انجام دهد؟
سه مرحله: (1) open orderها را از طریق SDK با diary محلی‌تان reconcile کنید. (2) open positionها را روی chain با state محلی‌تان مقایسه کنید. (3) اگر هر چیزی diverge کرد، bot را halt کنید و manual review را الزامی کنید. هیچ‌وقت با state ناسازگار، auto-resume نکنید.
چطور جلوی این را بگیرم که یک bug کل حسابم را خالی کند؟
محدودیت‌های لایه‌لایه: position cap در سطح code، order-size cap در سطح code، halt sentinel در سطح فایل، محدودیت‌های ضمنی minimum/maximum در سطح exchange (Polymarket)، و monitoring alertهایی که در صورت غیرعادی بودن نرخ order شما را مطلع می‌کنند. هیچ لایه‌ای به‌تنهایی کافی نیست - این لایه‌ها اثر هم‌افزایی دارند.
اگر logging من fail کند، bot باید معامله کند؟
خیر. اگر bot نتواند در diary خودش بنویسد، در restart هم نمی‌تواند reconcile کند، و این یعنی یک crash به state ناسازگار منجر می‌شود. اگر logging fail کرد، bot را hard-fail کنید. production botهای سالم نسبت به observability خودشان وسواس دارند.