Продукты
Безлимитные прокси от $19/мес Ротационные прокси от $49/мес ISP прокси от $33/мес Персональные прокси от $3.50/мес UDP прокси от $5/мес Попробовать прокси
По целям
Парсинг и данные ИИ-сервисы Соцсети и мессенджеры Медиа и развлечения Все задачи →
Цены
Полная таблица цен Все 20 стран Гарантия возврата
Ресурсы
Блог MCP-сервер Инструкции по настройке Частые вопросы Для бизнеса О сервисе Партнёрская программа
Русский
English Русский
← Блог PapaProxy.net

Как выгрузить историю цен из CLOB Polymarket

Большинство пустых ответов эндпоинта истории цен Polymarket вызвано одной причиной: параметр с именем `market` не хочет ID рынка. Вторая по частоте — форма запроса, не подходящая к диапазону, который вы спрашиваете.

API и данные · Автоматизация 9 августа 2026 3 минуты Alex Young Alex Young Технический специалист
Ключевые выводыПараметр market у /prices-history ожидает ID актива (токена CLOB), а не ID рынка или condition ID.
  • Параметр market у /prices-history ожидает ID актива (токена CLOB), а не ID рынка или condition ID.
  • Различайте виды отказа: недопустимые фильтры отклоняются кодом 400, а корректно составленный запрос по токену, которым не торговали, возвращает {"history": []} с кодом 200.
  • У каждого рынка два токена и два отдельных ряда; берите их из поля clobTokenIds в Gamma, которое приходит строкой в JSON-кодировке и требует декодирования.
  • interval и пара startTs/endTs взаимоисключающи: для выгрузок задавайте явный диапазон и проверяйте покрытие, а не переходите на крупную детализацию, когда ответ пришёл скудным.
  • fidelity у /prices-history — целое число минут; отдельный GET /ohlc с перечислимой fidelity от 1m до 1w фигурирует в справочнике ошибок, но в основном индексе API ещё не представлен — проверьте его контракт, прежде чем полагаться.
  • Ценовой ряд состоит только из {t, p} — ни объёма, ни OHLCV, — поэтому свечи описывают наблюдаемый ряд, а не ленту сделок; данные уровня сделок берутся из /trades в Data API при 200 запросах за 10 секунд.
  • История глубины ненадёжна: унаследованный /orderbook-history лежит вне поддерживаемой поверхности API, и сообщается, что для окон после февраля 2026 года он ничего не возвращает, — записывайте вебсокет сами или берите у архивного поставщика.
  • Для массовых задач используйте POST /batch-prices-history — до 20 ID активов за вызов; у /prices-history квота 1 000 запросов за 10 секунд, у /markets в Gamma всего 300, поэтому группировать нужно на обоих концах.

Краткое содержание подготовлено с помощью ИИ.

Что принимает эндпоинт prices-history

Эндпоинт истории цен — GET https://clob.polymarket.com/prices-history, публичный и без аутентификации, как и вся читающая поверхность CLOB. Он принимает четыре параметра, и первый — то место, где ломается почти всё:

Параметр Тип Значение
market string, обязательный ID актива — токен CLOB, а не рынок
startTs number Unix-время, элементы после этой точки
endTs number Unix-время, элементы до этой точки
interval enum max, all, 1m, 1w, 1d, 6h, 1h
fidelity integer Разрешение в минутах

Первую строку стоит перечитать, потому что название действительно вводит в заблуждение, и документация эндпоинта говорит об этом прямо: параметр под именем market ожидает asset id. В модели данных Polymarket у рынка два токена исходов — Yes и No, — и у каждого свой CLOB token ID, а история цен принадлежит токену, а не рынку. Единого ценового ряда у рынка нет, потому что у рынка две стороны.

Ответ намеренно минимален:

JSON
{"history": [{"t": 1754400000, "p": 0.63}, {"t": 1754403600, "p": 0.65}]}

Unix-время и цена от 0 до 1, где цена читается как подразумеваемая вероятность. Обратите внимание, чего здесь нет: ни объёма, ни открытия, максимума и минимума, ни числа сделок. Это ряд наблюдений цены, а не OHLCV, — и это различие определяет весь следующий раздел.

Ещё две механики, которые стоит знать до написания цикла. interval и пара отметок времени — альтернативы, а не дополнения: либо interval для относительного окна, заканчивающегося сейчас, либо startTs/endTs для абсолютного диапазона. И fidelity задаётся в минутах, поэтому fidelity=60 означает часовые точки, а fidelity=1440 — суточные, а вовсе не «количество точек», как этот параметр подписывают некоторые сторонние обёртки.

Две причины, по которым ответ приходит пустым

Эндпоинт исторических цен может отказать двумя разными способами, и различать их полезно. Некорректные или недопустимые фильтры отклоняются: справочник ошибок документирует ошибки валидации для market, startTs, endTs и fidelity, возвращаемые как 400 Bad Request. А вот запрос, синтаксически верный и просто называющий токен, которым никогда не торговали, — или окно, в котором ничего нет, — вернётся как {"history": []} с кодом 200. То есть пустой массив не является гарантированным признаком неверного идентификатора: это ответ на корректно сформулированный вопрос, за которым нет данных.

Причина первая: вы передали ID рынка вместо ID токена. Это ловушка имени из предыдущего раздела. Объект рынка в Gamma даёт вам оба варианта: id (короткая числовая строка вроде 15345), conditionId (хеш вида 0x…) и clobTokenIdsстроку в JSON-кодировке с двумя ID токенов. Это поле нужно декодировать перед использованием, а затем передать один из двух длинных числовых идентификаторов:

Python
import json, requests

GAMMA = "https://gamma-api.polymarket.com"
CLOB = "https://clob.polymarket.com"

def token_ids(slug):
    r = requests.get(f"{GAMMA}/markets", params={"slug": slug}, timeout=15)
    r.raise_for_status()
    market = r.json()[0]
    outcomes = json.loads(market["outcomes"])          # например, ["Yes", "No"]
    tokens = json.loads(market["clobTokenIds"])        # JSON-строка, а не список
    return dict(zip(outcomes, tokens)), market["closed"]

def price_history(token_id, start_ts=None, end_ts=None, fidelity=60, interval=None):
    params = {"market": token_id, "fidelity": fidelity}
    if interval:                                        # относительное окно, заканчивающееся сейчас
        params["interval"] = interval
    else:                                               # абсолютный диапазон — предпочтителен для бэкфилла
        params["startTs"], params["endTs"] = start_ts, end_ts
    r = requests.get(f"{CLOB}/prices-history", params=params, timeout=30)
    r.raise_for_status()                                # 400 при недопустимых фильтрах
    return r.json().get("history", [])

Причина вторая: форма запроса не подходит к диапазону. interval и пара отметок времени взаимоисключающи, и для исторических выгрузок брать следует именно абсолютный диапазон. В клиентском репозитории Polymarket описан случай, когда разрешившийся рынок при interval=max отдавал данные на крупной детализации и пустой массив на мелкой, — причём сам автор в продолжение сообщил, что перешёл на явные startTs/endTs. Считайте это уроком про форму запроса, а не правилом хранения: для закрытых рынков задавайте диапазон явно, вокруг периода, когда рынок действительно торговался, и затем проверяйте покрытие, сопоставив число полученных точек с длиной запрошенного отрезка. Не зашивайте крупную детализацию как универсальный запасной вариант — так вы выбросите детализацию, которая вполне может быть доступна.

Третья, более простая причина: у рынка, на котором никогда не торговали, истории нет. Прежде чем считать что-то сломанным, посмотрите volumeNum в объекте Gamma.

Как построить свечи из ценового ряда

Поскольку ответ несёт только отметки времени и цены, исторические ценовые данные здесь — это не OHLCV, и добросовестно превратить их в OHLCV нельзя. Свечи построить можно, но нужно понимать, что именно они означают.

Прежде чем строить своё, проверьте, нужно ли. Справочник ошибок Polymarket документирует эндпоинт GET /ohlc, принимающий asset_id, startTs, limit и перечислимую fidelity со значениями 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w — стиль параметров отличается от /prices-history, где fidelity задаётся целым числом минут. Оговорка в том, что в основном справочнике API он пока не представлен наравне с остальными эндпоинтами рыночных данных, поэтому считайте его контракт неподтверждённым: прощупайте, сравните одно окно с /prices-history и не стройте на нём продовый код, пока не проверите форму ответа сами.

Если же строите свечи из ценового ряда, понимайте, что именно они означают. Задав достаточно мелкую fidelity, вы можете сгруппировать последовательные наблюдения в корзины и взять первое, максимум, минимум и последнее внутри каждой. Это даст открытие, максимум, минимум и закрытие наблюдаемого ряда, а не ленты сделок: если между двумя наблюдениями произошёл всплеск, для ваших свечей его не существовало. Объёма в этих данных нет вовсе — всё, чему он нужен, придётся брать на уровне сделок, о чём дальше.

Два практических замечания. Цены здесь — вероятности в диапазоне 0–1, поэтому доходности и волатильность, посчитанные по ним, ведут себя не так, как по ценам активов: движение с 0,02 до 0,04 — это удвоение, а у границ ряд сжимается. И два токена рынка почти дополняют друг друга: ряды Yes и No должны примерно суммироваться в 1, а зазор между ними — артефакт спреда, а не сигнал. Выгрузить оба и проверить сумму — дешёвая проверка ваших идентификаторов: если она далека от единицы, вы, скорее всего, держите токены двух разных рынков.

Спуститься ниже цен — к отдельным сделкам

Когда агрегированных цен недостаточно, исторические сделки доступны — но с другого хоста. Эндпоинт /trades в Data API возвращает исполненные сделки и принимает фильтры, включая condition ID рынка, поэтому естественный путь такой: Gamma за condition ID, затем Data за исполнениями. Бюджет у него жёстче, чем у CLOB, — 200 запросов за 10 секунд, — поэтому бэкфилл на уровне сделок остаётся самой медленной частью любого конвейера, и место ему в фоновой задаче. Второй маршрут — слой сабграфов: orderbook-сабграф индексирует ончейн-события исполнения, что лучше подходит для агрегатного анализа, чем для запросов по отдельным рынкам. Одна оговорка: публичный манифест сабграфа по-прежнему перечисляет исходные контракты CTF Exchange, тогда как в 2026 году Polymarket перешла на CLOB V2 с новыми контрактами биржи, — поэтому старое развёртывание может не содержать полной свежей истории исполнений. Проверьте, какие контракты индексирует развёртывание, прежде чем считать его авторитетным.

Историческая глубина стакана — самый мутный угол. На CLOB существует унаследованный эндпоинт GET /orderbook-history: он фигурирует в справочнике ошибок Polymarket и принимает asset_id, startTs, endTs, limit и offset, — но он не входит в поддерживаемую основную поверхность API, а в баг-репорте от февраля 2026 года против одного из торговых фреймворков задокументировано, что для любого окна после примерно 20 февраля 2026 года он возвращает {"count": 0, "data": []}, тогда как более старая история приходит нормально. То есть для архивной глубины он может пригодиться, а вот полагаться на него в свежих данных нельзя. Для надёжной новой истории глубины варианты прежние: записывать вебсокет-поток самостоятельно начиная с сегодня либо покупать у поставщика, который записывал. Узнать об этом после того, как бэктест построен в расчёте на историческую глубину, — дорогой урок; знать на первой неделе — обычное проектное ограничение.

Массовые выгрузки по многим рынкам

Исторические данные для бэктестинга — это тысячи рынков, и здесь однотокенный эндпоинт становится неподходящим инструментом. У CLOB документирован пакетный вариант: POST https://clob.polymarket.com/batch-prices-history с JSON-телом, где markets — список ID активов, максимум 20, плюс start_ts, end_ts и fidelity. Обратите внимание на snake_case против camelCase у одиночного эндпоинта и на то, что fidelity по умолчанию равна 1 минуте.

Python
import requests

def batch_history(token_ids, start_ts, end_ts, fidelity=1440):
    assert len(token_ids) <= 20                    # документированный потолок
    r = requests.post(f"{CLOB}/batch-prices-history",
                      json={"markets": token_ids, "start_ts": start_ts,
                            "end_ts": end_ts, "fidelity": fidelity},
                      timeout=60)
    r.raise_for_status()
    return r.json()

Это двадцатикратное сокращение числа запросов за те же данные и самое действенное, что можно сделать для массовой выгрузки истории. Обход 5 000 рынков — то есть 10 000 токенов, поскольку у каждого рынка их два, — падает с 10 000 запросов до 500.

Как рассчитать остальное: у /prices-history в таблице лимитов Polymarket своя строка — 1 000 запросов за 10 секунд, а не общая квота хоста CLOB в 9 000, — и сами лимиты применяются по IP с троттлингом Cloudflare, а не немедленным отказом. При этом ID токенов приходят из Gamma, где ещё строже: у /markets — 300 запросов за 10 секунд. То есть ограничены оба конца конвейера, и группировка важна на обоих. И отдельно, под частый поисковый запрос: истории цен на Gamma как эндпоинта не существует. Gamma несёт текущие outcomePrices и поля изменения за горизонт прямо в объекте рынка, но сам ряд живёт только в CLOB.

Отсюда практический порядок действий: один раз пройти Gamma курсорными keyset-эндпоинтами и собрать локальную таблицу рынков с их ID токенов, закешировать её (эти идентификаторы не меняются), а затем идти по пакетному эндпоинту истории по 20 токенов за раз с той детализацией, которая исследованию действительно нужна: суточные свечи для широкого обзора и мельче — только там, где это важно. Там, где оставшимся ограничением становится объём запросов по большому массиву рынков, бюджеты считаются по IP-адресу, и наши прокси для криптопроектов — это выделенные IPv4-адреса, статичные на весь срок плана, к каждому из которых применяется своя документированная квота. Масштабирование проверяйте на своей нагрузке, а не считайте линейным по умолчанию.