آموزش Polymarket Bot · فصل ۷ از ۳۲

بررسی عمیق Polymarket Gamma API: endpointهای /events و /markets، pagination، tag IDها (864 Tennis، 745 NBA، و غیره)، فیلتر بر اساس volume 24h، rate limitها، و نمونه‌کدهای Python و Node.

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

Gamma API کاتالوگ Polymarket است - هر event، هر market، tag، image، و resolved outcome را که front-end نمایش می‌دهد فهرست می‌کند. CLOB API برای trade است؛ Gamma مشخص می‌کند چه چیزی قابل معامله است. بیشتر bugهای bot در لایه discovery از قاطی کردن این دو با هم، یا از نادیده گرفتن قرارداد pagination می‌آیند. این فصل مرجع میدانی endpointهای اصلی Gamma است، با رفتار دقیق parameterها که fetcherهای production ما به آن وابسته‌اند.

این فصل ۷ از سری ۳۲ بخشی ما درباره ساختن Polymarket trading bot است. ما این موضوع را در بخش‌های زیر به‌صورت عمیق پوشش می‌دهیم. محتوای بدنه برای هر بخش در حال نوشته شدن و انتشار مرحله‌به‌مرحله است؛ پاسخ‌های FAQ و referenceها از قبل کامل هستند و تجربه production از اجرای trader خودمان را منعکس می‌کنند.

  • Gamma در برابر CLOB: چه زمانی از کدام استفاده کنیم
  • آشنایی با endpoint /events
  • آشنایی با endpoint /markets
  • Tags و tag IDها (فهرست تأییدشده)
  • Filtering: active، closed، و مرتب‌سازی بر اساس volume24hr
  • Pagination و limitها
  • Rate limitها و caching
  • Code: دریافت marketهای برتر بر اساس volume 24h

Gamma در برابر CLOB: چه زمانی از کدام استفاده کنیم

دو service متفاوت برای دو کار متفاوت.

Gamma (gamma-api.polymarket.com): catalog. eventها، marketها، tagها، descriptionها، imageها، resolved outcomeها، volume 24 ساعته، total volume، و end dateها را فهرست می‌کند. Read-only HTTP. برای بیشتر readها authentication لازم نیست. به‌صورت پیوسته به‌روزرسانی می‌شود اما eventually consistent است - یک market که تازه بسته شده ممکن است برای چند ثانیه هنوز closed: false نشان داده شود.

CLOB (clob.polymarket.com): trading + order book. best bid/ask فعلی، top-N depth دفتر سفارش، و recent tradeها را فهرست می‌کند. برای write endpointها (ثبت order، لغو) authenticated است. کانال‌های WebSocket بلادرنگ برای به‌روزرسانی book موجود است.

قاعده سرانگشتی: برای پیدا کردن چیزی که می‌خواهید معامله کنید از Gamma استفاده کنید؛ برای معامله کردن از CLOB استفاده کنید. botی که price را از Gamma می‌خواند از data قدیمی استفاده می‌کند - فیلدهای price در Gamma کمتر از order book در CLOB به‌روزرسانی می‌شوند. botی که metadata بازار را از CLOB می‌خواند، درخواست‌های بیشتری از حد لازم می‌فرستد.

آشنایی با endpoint /events

GET /events data در سطح event را برمی‌گرداند. یک "event" یک صفحه Polymarket است؛ یک event واحد می‌تواند چندین market داشته باشد (مثلاً event انتخابات ریاست‌جمهوری 2024 برای هر نامزد یک market دارد).

فیلدهای کلیدی:

  • slug: شناسه URL-safe، ثابت در طول عمر event.
  • title، description: نمایش برای انسان.
  • endDate (ISO 8601): زمان بسته شدن event.
  • active، closed: boolean؛ برای eventهای زنده، در query با ?active=true&closed=false ترکیب شوند.
  • volume، volume24hr: مجموع USD.
  • tags: آرایه‌ای از objectهای tag (بخش tags را ببینید).
  • markets: آرایه‌ای از objectهای market فرزند (آشنایی با /markets را ببینید).

رایج‌ترین الگوی discovery: GET /events?active=true&closed=false&order=volume24hr&ascending=false&limit=100. این 100 event زنده با بالاترین volume را برمی‌گرداند.

آشنایی با endpoint /markets

GET /markets data در سطح market را برمی‌گرداند. یک market یک contract از نوع Y/N یا multi-outcome است؛ داخل یک event قرار دارد.

فیلدهای کلیدی:

  • slug: شناسه URL-safe.
  • question: عنوانی که در صفحه trading نمایش داده می‌شود (مثلاً "Will Trump be president on January 1, 2027?").
  • outcomes: رشته JSON از نام outcomeها، مثلاً '["Yes","No"]'. برای binary همیشه دو عنصر دارد؛ برای NegRisk بیشتر.
  • outcomePrices: رشته JSON از priceهای فعلی به‌صورت decimal، مثلاً '["0.62","0.38"]'. دو طرف تقریباً به 1.0 منهای spread جمع می‌شوند.
  • clobTokenIds: رشته JSON از ERC-1155 token IDها که با outcomeها هم‌راستا هستند. این‌ها tokenهایی هستند که واقعاً می‌خرید/می‌فروشید.
  • negRisk: boolean؛ برای marketهای multi-outcome که جمعشان 1 می‌شود true است. برای order placement مهم است (فصل 11).

فیلدهای outcomes / outcomePrices / clobTokenIds به‌صورت رشته JSON می‌آیند، نه آرایه‌های parse شده - قبل از استفاده JSON-decode کنید.

Tags و tag IDها (فهرست تأییدشده)

Tags برچسب‌های دسته‌بندی هستند (Sports، Crypto، Tennis، NBA، و غیره). tag IDهای production تأییدشده برای رایج‌ترین دسته‌ها:

TagIDTagID
Sports1NBA745
Crypto21NFL450
Politics2Tennis864
Bitcoin100196Esports702
Ethereum100383Soccer1059
Election3EPL739
Middle East1432UCL2186

با ?tag_id=745 برای یک tag خاص فیلتر کنید، یا با استفاده از slug از ?tag_slug=nba استفاده کنید. فیلتر کردن بر اساس slug در code خواناتر است اما کمی کندتر است؛ ID-based در production پیش‌فرض است.

Filtering: active، closed، مرتب‌سازی بر اساس volume24hr

چهار بُعد filtering که 95٪ مواقع از آن‌ها استفاده می‌کنید.

  • active=true|false: مقدار true marketهایی را که تیم Polymarket از UI مخفی کرده است حذف می‌کند.
  • closed=true|false: مقدار false marketهای resolved را حذف می‌کند. ترکیب active=true&closed=false رایج‌ترین فیلتر برای live marketها است.
  • order=volume24hr، order=volume، order=endDate: کلید مرتب‌سازی. مفیدترین گزینه volume24hr برای پیدا کردن marketهای فعال فعلی است.
  • ascending=true|false: در بیشتر endpointها پیش‌فرض true است؛ برای مرتب‌سازی بر اساس volume تقریباً همیشه false را می‌خواهید.

نکته: فیلتر کردن با tag_id همراه با order=volume24hr و ascending=false گاهی وقتی آن tag تعداد کمی live market دارد، یک صفحه خالی برمی‌گرداند. همیشه over-fetch کنید (بیشتر از چیزی که نمایش می‌دهید request بفرستید) و برای مدیریت این حالت، بعداً post-filter انجام دهید.

Pagination و limitها

پارامتر limit در هر call مقدار 1 تا 500 را می‌پذیرد. اگر مشخص نشود، مقدار پیش‌فرض 100 است. بالاتر از 500، server بی‌صدا cap می‌کند - شما 500 دریافت می‌کنید اما response هیچ نشانه‌ای از بیشتر بودن ندارد.

Pagination بر پایه offset است: ?limit=500&offset=500 برای صفحه دوم. pagination بر پایه cursor وجود ندارد، بنابراین درج هم‌زمان داده‌های جدید می‌تواند باعث جابه‌جایی مرز صفحات بین callها شود. برای بیشتر اهداف discovery در bot این قابل قبول است؛ برای archive scrapeها، بر اساس یک فیلد پایدار (endDate یا createdAt) مرتب کنید و هم‌پوشانی را با slug تشخیص دهید.

الگوی عملی برای "همه live marketها": limit=500&order=volume24hr&ascending=false را fetch کنید. این top 500 بر اساس volume را پوشش می‌دهد که عملاً هر market با فعالیت غیرtrivial را شامل می‌شود. رفتن فراتر از page 1 به‌ندرت مفید است - marketهای انتهای توزیع volume، به‌تعریف، جایی نیستند که action در آن رخ می‌دهد.

Rate limitها و caching

Gamma پشت Cloudflare قرار دارد و soft rate limitهایی به ازای هر IP دارد. آستانه‌های تجربی مشاهده‌شده در بار production:

  • تا حدود 30 req/sec از یک IP به‌صورت پایدار: مشکلی ندارد.
  • burstهای 100+ req/sec: گاهی 429 می‌گیرند، retry موفق می‌شود.
  • حدود 500 req/sec به‌صورت پایدار: rate-limit page یا block موقت (10 تا 60 ثانیه).

response headerهای منتشرشده شامل مقادیر Cache-Control برابر 30 تا 60 ثانیه برای بیشتر endpointها هستند. به آن‌ها احترام بگذارید - هیچ فایده‌ای ندارد که همان event را در یک دقیقه 10 بار دوباره fetch کنید. الگوی caching در production: LRU درون‌فرایندی + TTL با کلید full URL، و TTL برابر 30 ثانیه. این کار تعداد request را کم می‌کند و latency را کاهش می‌دهد.

برای استراتژی‌های high-frequency، داده‌های Gamma را به یک local store mirror کنید که توسط یک fetcher process به‌روزرسانی می‌شود؛ چندین consumer از همان store بخوانند. یک fetcher × چندین consumer > چندین fetcher × Gamma.

Code: دریافت marketهای برتر بر اساس volume 24h

Reference fetcher در سه زبان، که top 50 live market را بر اساس volume 24 ساعته برمی‌گرداند.

Python:

import requests
r = requests.get("https://gamma-api.polymarket.com/events",
                 params={"active":"true","closed":"false",
                         "order":"volume24hr","ascending":"false","limit":50},
                 timeout=10)
for ev in r.json()[:50]:
    print(ev["slug"], ev.get("volume24hr"))

Node:

const url = "https://gamma-api.polymarket.com/events?active=true&closed=false" +
            "&order=volume24hr&ascending=false&limit=50";
const events = await fetch(url).then(r => r.json());
for (const ev of events) console.log(ev.slug, ev.volume24hr);

Curl:

curl -s "https://gamma-api.polymarket.com/events?active=true&closed=false&order=volume24hr&ascending=false&limit=50" \
  | jq '.[].slug'

endpoint /events در Polymarket gamma از free-text search parameter پشتیبانی نمی‌کند - اضافه کردن ?q=foo یا ?search=foo بی‌صدا ترتیب پیش‌فرض را برمی‌گرداند. به‌جای آن بر اساس slug یا tag فیلتر کنید.

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

Polymarket Gamma API چیست؟
Gamma API متادیتا و discovery API پولی‌مارکت است. فهرست event/marketها، titleها، descriptionها، end dateها، tagها، و volume تجمیعی را ارائه می‌دهد. داده‌های order book بلادرنگ را ارائه نمی‌دهد - آن داده‌ها روی CLOB API هستند. برای بیشتر use caseهای discovery و analytics، از Gamma شروع می‌کنید.
تفاوت بین event و market در Polymarket چیست؟
یک event یک یا چند market را که یک موضوع مشترک دارند گروه‌بندی می‌کند. یک market یک outcome مشخص Yes/No با order book و token IDهای خودش است. یک event با عنوان "2026 NBA Champion" شامل 30 market است (هر تیم یک market). برای eventهای binary Yes/No، event یک underlying market دارد.
چطور Polymarket tag IDها را پیدا کنم؟
Tagها از طریق gamma /tags endpoint در دسترس هستند. IDهای تأییدشده‌ای که در production استفاده می‌کنیم: 864 Tennis، 745 NBA. tag slugها در URLها (?tag_slug=tennis) و query متن آزاد (?q=tennis) قابل اعتماد نیستند - فقط از numerical tag_id استفاده کنید. قبل از اتکا به یک slug، /tags را بررسی کنید.
چطور Polymarket eventها را بر اساس volume 24h مرتب کنم؟
/events?order=volume24hr&ascending=false - این همان چیزی است که بیشتر widgetهای "trending markets" را تغذیه می‌کند. آن را با active=true&closed=false ترکیب کنید تا marketهای منقضی یا متوقف‌شده حذف شوند. Limit به‌طور پیش‌فرض 25 است؛ حداکثر 500 در هر call است.
آیا Gamma محدودیت pagination دارد؟
بله. endpointهای /events و /markets پارامتر limit (حداکثر 500 در هر call) و offset را برای pagination می‌پذیرند. بیشتر libraryها به‌صورت خودکار pagination نمی‌کنند - اگر به کل universe نیاز دارید، با offset حلقه بزنید تا به pageی برسید که کمتر از `limit` نتیجه دارد.
آیا Gamma از WebSocket پشتیبانی می‌کند؟
خیر. Gamma فقط REST است. برای به‌روزرسانی‌های بلادرنگ (تغییر price، marketهای جدید، order book) از CLOB WebSocket استفاده کنید - که در فصل 8 این سری پوشش داده شده است.