Две модели доставки
REST устроен как обычный веб: клиент отправляет HTTPS-запрос, сервер возвращает один ответ, обмен закончен. Каждый запрос самостоятелен, поэтому REST легко кэшировать и отлаживать стандартными инструментами. Он хорош для вопросов с определённым ответом: список инструментов, последние 300 часовых свечей, логотип.
WebSocket начинается как HTTPS-запрос и переходит в постоянное двустороннее соединение. Клиент подписывается один раз, а сервер присылает каждый новый тик сразу, без повторных запросов.
Разница не в скорости одного сообщения, а в том, кто решает, когда данные двигаются. В REST решает клиент и узнаёт об изменении цены только при следующем запросе. В WebSocket решает рынок, и клиент узнаёт сразу.
Когда поток, а когда запрос
Правило простое: если данные меняются сами и важно знать когда - нужен поток; если это свершившийся факт или они меняются редко - запрос.
- Поток (WebSocket): котировки в терминале, таблица котировок, бегущая строка, формирующаяся свеча на графике, ценовые алерты - всё, что должно реагировать на рынок быстрее секунды.
- Запрос (REST): история свечей для графика, каталог инструментов с символами и точностью, логотипы, разовый снимок для отчёта.
- Не опрашивайте REST ради живых цен: запрос последней цены каждую секунду по многим инструментам съедает лимиты, добавляет в среднем полпериода задержки и всё равно пропускает изменения между запросами.
Если сомневаетесь, задайте себе вопрос: изменятся ли эти данные через секунду без моего участия? Если да - это поток. Если нет - это запрос, и его результат можно спокойно кэшировать.
Как они работают вместе
Классический пример - график. При загрузке приложение запрашивает видимый диапазон свечей через REST, затем подписывается по WebSocket на тот же инструмент и обновляет последнюю свечу каждым тиком. Когда минута закрывается, свеча уходит в историю и начинается новая.
Фид FinaQuotes устроен так же: WebSocket передаёт поток тиков, REST отдаёт историю свечей в 9 таймфреймах, каталог инструментов и логотипы. Оба канала описаны в справочнике REST и в документации. Как формируются свечи - в статье о свечах из тиков.
| Параметр | WebSocket | REST |
|---|---|---|
| Модель | Сервер присылает по одному соединению | Запрос клиента, один ответ |
| Для чего | Тики, текущая свеча, алерты | История, каталог, логотипы, снимки |
| Свежесть | Доли секунды после получения цены | На момент запроса |
| Состояние | Подписки живут на соединении | Каждый запрос независим |
| Сбой | Разрыв: переподключение и дозагрузка | Упал запрос: повторить |
| Лимиты | Без тарификации за сообщение | Лимиты на IP, при превышении 429 |
Подключение и аутентификация
Сессия потока проходит одни и те же шаги: открыть соединение, пройти аутентификацию, подписаться на инструменты, обрабатывать сообщения до разрыва.
Аутентификация в WebSocket FinaQuotes построена на HMAC. Вы получаете идентификатор ключа и секрет. Ваш сервер вычисляет подпись короткого сообщения, обычно с меткой времени, с помощью секрета и хеш-функции вроде SHA-256 и отправляет идентификатор и подпись, но не сам секрет. Сервер повторяет вычисление своей копией секрета и принимает сессию, если подписи совпали. Метка времени в подписи не даёт повторно использовать старую подпись.
Отсюда два правила: секрет хранится только на сервере (переменная окружения или хранилище секретов), никогда в коде браузера или мобильного приложения; часы сервера синхронизированы, иначе подпись с сильно отличающимся временем могут отклонить. Формат сообщений и допуски - в документации.
Переподключение и дозагрузка пропуска
Любое долгое соединение рано или поздно рвётся. Хороший клиент считает разрыв штатным событием и восстанавливается сам.
- Заметить разрыв: событие закрытия плюс ping/pong на уровне протокола, чтобы поймать «мёртвое» соединение. Отсутствие тиков - не признак: тихий инструмент может молчать долго.
- Переподключиться с паузой, удваивая её после каждой неудачи до предела и добавляя случайную добавку, чтобы клиенты не ломились одновременно.
- Заново пройти аутентификацию и подписаться: подписки принадлежат соединению, новое начинается пустым.
- Закрыть пропуск: запомнить время последнего тика, после переподключения запросить через REST свечи с этого момента и слить их с графиком, заменив последнюю сохранённую свечу, а не добавив дубль.
// Иллюстрация. Типы сообщений и имена полей условные;
// реальный протокол описан в документации (/docs/).
import crypto from 'node:crypto';
const WS_URL = process.env.FEED_WS_URL;
const KEY_ID = process.env.FEED_KEY_ID;
const SECRET = process.env.FEED_SECRET; // не покидает ваш сервер
const sign = (ts) =>
crypto.createHmac('sha256', SECRET).update(String(ts)).digest('hex');
let lastTickTime = null; // время (UTC) последнего обработанного тика
let attempt = 0;
function connect() {
const ws = new WebSocket(WS_URL);
ws.onopen = async () => {
attempt = 0;
const ts = Date.now();
ws.send(JSON.stringify({ type: 'auth', key: KEY_ID, ts, sig: sign(ts) }));
ws.send(JSON.stringify({ type: 'subscribe', symbols: ['EURUSD', 'XAUUSD'] }));
// Закрываем дыру после разрыва свечами из REST.
if (lastTickTime) await backfillCandles(lastTickTime);
};
ws.onmessage = (event) => {
const tick = JSON.parse(event.data);
lastTickTime = tick.time;
applyTick(tick);
};
ws.onclose = () => {
// Экспоненциальная пауза со случайной добавкой, не больше 30 секунд.
const delay = Math.min(30000, 1000 * 2 ** attempt++) + Math.random() * 1000;
setTimeout(connect, delay);
};
}
connect(); Ниже - схема такого клиента на JavaScript. Реальных адресов и ключей в ней нет, формат сообщений условный.
Лимиты и ответ 429
REST-эндпоинты - общий ресурс, поэтому у них есть лимиты: в FinaQuotes это разумные лимиты на IP-адрес, по умолчанию 120 запросов в минуту. При превышении сервер отвечает статусом 429 Too Many Requests.
429 - сигнал притормозить, а не повод немедленно повторить. Подождите, учтите заголовок Retry-After, если он есть, и распределите запросы во времени. Обычно хватает двух привычек: кэшировать историю у себя (закрытые свечи не меняются) и загружать только тот диапазон, который видит пользователь.
Поток WebSocket не тарифицируется за сообщение, так что активный рынок не увеличивает счёт. Как устроена стоимость доступа - на странице тарифов.
Чек-лист интеграции
Перед запуском проверьте, что интеграция:
- берёт живые цены по WebSocket, а историю и каталог - через REST, без опроса REST ради котировок;
- хранит секрет HMAC только на сервере;
- переподключается автоматически с ограниченной экспоненциальной паузой;
- после каждого переподключения заново подписывается и дозагружает пропуск из REST;
- обрабатывает 429 паузой, кэширует историю и укладывается в лимиты;
- работает в UTC и переводит время в локальное только при отображении.
Частые вопросы
Можно ли получать живые цены только через REST?
Технически можно опрашивать, но это неверный инструмент: опрос добавляет задержку, пропускает изменения между запросами и упирается в лимиты. Для живых цен - поток WebSocket, для истории и справочных данных - REST.
Можно ли подключаться к WebSocket прямо из браузера?
Не с вашим секретом: всё, что отдаётся в браузер, становится публичным. Подключайтесь с сервера и передавайте цены во фронтенд, либо используйте готовые виджеты, если они подходят под задачу.
Что делать, если пришёл ответ 429?
Притормозить и повторить позже: выдержать паузу, учесть Retry-After, кэшировать историю и запрашивать только нужный диапазон. Лимит по умолчанию - 120 запросов в минуту на IP, подробнее - в справочнике REST.
Нужен фид, о котором эта статья?
Расскажите, что вы строите и какие классы активов нужны. Ответим в течение одного рабочего дня: условия доступа и стоимость.