Курсы и алерты в реальном времени

Стриминг котировок, живые курсы и price alerts

Предложения приходят по SSE по мере ответа каждого обменника, а не после самого медленного; фид курсов пересобирается каждые несколько секунд; ордера опрашиваются до финального статуса. Этого достаточно для сервиса алертов и живого табло.

  • Источникистрим · фид · опрос
  • СтримSSE, первое предложение за 200–400 мс
  • Фид курсовпересобирается каждые несколько секунд
  • Статус ордераопрос раз в 5–10 с; вебхуков пока нет

Собрано на/v1/partner/quotes-sse/v1/export/rates.xml/v1/partner/orders/{id}/v1/partner/orders/{id}/refresh

Два источника действительно реального времени, а третий — нет, и это стоит знать до того, как проектировать вокруг них. Котировки приходят потоком по server-sent events по мере ответа провайдеров. Кросс-провайдерский фид курсов пересобирается каждые несколько секунд. Статус ордера опрашивается: вебхуков пока нет, поэтому push-слой, который видят ваши пользователи, поднимаете вы.

  • /v1/partner/quotes-sse — первое предложение за 200–400 мс, поток закрывается сам.
  • /v1/export/rates.xml — лучший исполнимый курс по направлению, без ключа, всегда свежий.
  • Ордера: опрос раз в 5–10 секунд или один синхронный refresh.
  • Price alerts строятся на фиде курсов, статусные — на цикле опроса.

Три источника, три ритма

ИсточникРитмКлючДля чего
GET /v1/partner/quotes-ssePush в рамках запроса. События: offer, private_route, provider_error, ping, done. Закрывается по timeout_ms — по умолчанию и максимум 5000.даЖивой калькулятор, чекаут — всё, где ждёт пользователь.
GET /v1/export/rates.xmlПересобирается каждые несколько секунд и кэшируется.нетТабло курсов, мониторинги, price alerts, виджеты «1 BTC = …».
GET /v1/partner/orders/{id}Опрос раз в 5–10 с; наш поллер обновляет каждый открытый ордер раз в 10 с.даТаймлайны статусов, уведомления, сверка.
POST /v1/partner/orders/{id}/refreshСинхронно, не чаще раза в 5 с.да«Я отправил» — один немедленный ответ, а не более частый цикл.

Фид отдаёт исполнимый курс, а не индекс. Каждый <item> — лучший кросс-провайдерский курс по направлению с minamount, maxamount и признаком floating/fixed. Алерт на его основе срабатывает по числу, по которому пользователь действительно может обменять, — чего не даёт биржевой тикер.

Чтение потока

const res = await fetch(sseURL, {
  headers: { "X-API-Key": key, Accept: "text/event-stream" },
});
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();

let buf = "";
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += value;

  let i;
  while ((i = buf.indexOf("\n\n")) !== -1) {          // один SSE-фрейм
    const frame = buf.slice(0, i); buf = buf.slice(i + 2);
    const event = /^event:\s*(.+)$/m.exec(frame)?.[1];
    const data = JSON.parse(/^data:\s*(.+)$/m.exec(frame)?.[1] ?? "null");

    if (event === "offer") render(data);             // рисуйте сразу по приходу
    if (event === "provider_error") log(data);       // в лог, но не на экран
    if (event === "done") rerank(data.deviations);   // финальный рейтинг нарисованного
  }
}

Как собрать price alerts

  1. Опрашивайте фид, а не эндпоинт котировок

    /v1/export/rates.xml отдаётся без ключа, кэшируется и покрывает все направления сразу — одного запроса хватает на все ваши алерты. Отдельная котировка на пару каждого пользователя — верный способ попасть под троттлинг провайдеров.

  2. Сравнивайте по направлению вместе с лимитами

    Алерт на BTC → USDTTRC20 имеет смысл только внутри minamount и maxamount. Храните оба значения рядом с порогом, чтобы не уведомлять человека о курсе, которым он не сможет воспользоваться.

  3. Гасите дребезг до уведомления

    Курсы колеблются. Требуйте, чтобы порог держался два чтения подряд, и перевзводите алерт только после отхода курса на запас — иначе спокойный рынок пришлёт сотню уведомлений.

  4. Превращайте алерт в предложение

    Как только пользователь решил действовать, считайте нормальную котировку через /quotes или /quotes-sse: фид говорит о том, что было доступно несколько секунд назад, а исполнимо только предложение — и то пять минут.

Статусные алерты устроены зеркально: один цикл опроса на открытый ордер и ваша собственная рассылка туда, где слушают пользователи, — websocket, ваш собственный вебхук или сообщение в Telegram.

Что не в реальном времени

Чего нетЧто делать вместо этого
Вебхуков по статусу ордераОпрашивать раз в 5–10 секунд до финального статуса и рассылать пользователям самим. Если вебхуки меняют вашу интеграцию — скажите нам: их приоритет определяется спросом.
Постоянного websocketСтриминговые эндпоинты — это SSE в рамках одного запроса котировки; они закрываются сами в пределах пяти секунд.
Истории курсов и свечейФид отдаёт текущий лучший курс. Историю для графиков храните у себя.
Price alerts как готового сервисаПороги, хранение и уведомления — ваши; API даёт число, с которым они сравниваются.
Постоянных потоков курса по каждому провайдеруПредложения приходят по провайдерам внутри потока котировок, но отдельного постоянного фида на провайдера нет.

Кто на этом работает

  • Мониторинги и агрегаторы, читающие rates.xml в формате, который они и так разбирают.
  • Боты-алерты — Telegram, Discord, почта — срабатывающие по исполнимому курсу, а не по биржевому тикеру.
  • B2B-партнёры с дашбордами, раздающие статусы ордеров своих клиентов из одного цикла опроса.
  • Кошельки и калькуляторы, где число на экране должно двигаться, пока пользователь думает.

Вопросы

Присылает ли SwapZilla вебхуки при смене статуса ордера?

Пока нет. Статус читается опросом GET /v1/partner/orders/{id} раз в 5–10 секунд до финального, а наш собственный поллер и так обновляет каждый открытый ордер раз в десять секунд. Приоритет вебхуков определяется спросом партнёров, поэтому об интеграции, которой они нужны, стоит сказать.

Стриминговый эндпоинт — это websocket?

Нет, это server-sent events поверх обычного HTTP-запроса, привязанные к одной котировке: предложения приходят по мере ответа провайдеров, ping держит прокси живыми, а поток закрывается сам по timeout_ms — 5000 мс по умолчанию и по максимуму.

Как часто меняется фид курсов?

/v1/export/rates.xml пересобирается каждые несколько секунд и кэшируется, поэтому опрашивать его чаще бессмысленно. Он публикует лучший кросс-провайдерский курс по направлению с минимальной и максимальной суммой и типом курса в формате мониторингов.

Можно собрать price alerts без API-ключа?

Сам фид публичный: /v1/export/rates.xml ключа не требует, как и /v1/assets с /v1/validate-address. Ключ нужен в тот момент, когда алерт превращается в настоящую котировку или ордер.

Почему не котировать пару каждого пользователя по таймеру?

Потому что провайдеры ограничат повторные котировки задолго до того, как это сделает SwapZilla. Фид и существует ради того, чтобы наблюдение за ценой стоило одного запроса на всех, а котировки оставались на момент, когда пользователь готов обменивать.

Насколько свеж только что прочитанный статус?

Не старше примерно десяти секунд: фоновый поллер обновляет каждый открытый ордер с этим интервалом. Если пользователь только что сказал «я отправил», POST /v1/partner/orders/{id}/refresh синхронно сходит к провайдеру и вернёт обновлённый ордер — вызывайте его один раз, а не в цикле.

Что ещё собирают на этом же ключе

Как получить ключ

Зарегистрируйтесь в партнёрском кабинете — партнёрский id и api_key для заголовка X-API-Key выпускаются сразу. Ключ становится рабочим после активации: напишите в @swapzilla_support_bot и коротко опишите интеграцию. Все эндпоинты, поля и статусы — в документации partner API.