Живая цена это сердце криптовалютного продукта: виджет на сайте, калькулятор конвертации, терминал наблюдения, бот уведомлений, всё это стоит на потоке котировок, который биржи отдают публично и щедро. Техническая интеграция скромнее, чем кажется снаружи: пара эндпоинтов опроса, поток по сокету, нормализация символов и дисциплина соединений, и на этом фундаменте живут продукты любой сложности. Статья разбирает фундамент: публичные интерфейсы бирж, опросы и их лимиты, потоки в реальном времени, нормализацию форматов, восстановление соединений, ограничение темпа для интерфейса и гигиену приватного доступа.

Материал технический: цены рассматриваются как поток значений со своей механикой пересылки, без прогнозов и рекомендаций, потому что продукт на котировках обязан быть прежде всего честным конвейером цифр.

Что биржи отдают публично и что для этого не нужно ключей

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

Состав публичного набора устойчив между биржами: тикер с последней ценой и суточными изменениями, список сделок с временем и размером, стакан заявок с уровнями, свечи с периодами от минуты. Различия живут в форматах, путях и деталях полей, и потому первый шаг интеграции это сверка документации конкретной площадки с фиксацией различий в карте соответствий.

Выбор биржи под продукт диктуется глубиной рынка и качеством интерфейсов: глубокий рынок даёт честные цены без провалов на тонких инструментах, зрелый интерфейс даёт потоки без экзотики. Продукты, серьёзные к цифрам, читают несколько площадок и показывают консолидированную картину, потому что цена одного рынка это мнение одного рынка.

Ограничения публичного набора проговариваются честно: рыночные сведения без ключей идут с весовыми лимитами на источник, географические правила площадок соблюдаются на стороне продукта, и публичность сведений не отменяет уважения к условиям конкретного интерфейса. Дисциплина вежливости это часть интеграции, а не приложение к ней.

REST опросы тикеров лимиты весов и честная частота

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

Честная частота опроса выбирается по смыслу продукта: секундный опрос для живого виджета это уже расточительство, потому что для глаза человека цена и так меняется заметнее секунды, десятисекундный опрос закрывает калькулятор конвертации, минутный удовлетворяет исторический обзор. Каждое увеличение частоты умножает вес, и правило жанра: частота выбирается по потребности продукта, а не по максимальному разрешению лимита.

Опрос по списку символов требует отдельной дисциплины: эндпоинт одного символа дешевле эндпоинта всех, список из десяти символов собирается из кеша с одним общим опросом, и продукт без кеша на опросах горит бюджетом на ровном месте. Кеш на срок шага опроса снимает дублирование между пользователями, и сто пользователей видят одну цифру вместо ста запросов.

Обработка лимитов встраивается с первого дня: заголовки остатка бюджета читаются, превышение ловится статусом и уважается паузой, а не штурмом повторов. Биржа, заблокировавшая источник за настойчивость, разблокирует его по своему расписанию, и ни один продукт не выиграл гонку с лимитами.

WebSocket потоки подписка пульс и жизнь соединения

Поток по сокету снимает вопрос частоты целиком: соединение устанавливается один раз, подписка объявляет интересующие каналы, и обновления приезжают сами в момент события. Тикер обновляется по изменению, сделки сыплются потоком, свечи закрываются по расписанию рынка, и задержка пересылки превращается из произведения частоты опроса в свойство сети.

const ws = new WebSocket("wss://stream.example.net/ws");

ws.onopen = () => {
  ws.send(JSON.stringify({
    method: "SUBSCRIBE",
    params: ["btcusdt@ticker", "ethusdt@ticker"],
    id: 1
  }));
};

ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (msg.data) updateTicker(msg.data.s, msg.data.c);
};

Жизнь соединения обслуживается по протоколу площадки: пульс-сообщения подтверждают живучесть, молчание сверх порога закрывает соединение с нашей стороны, подписки на события системы чтения отдаются отдельным каналом. Протоколы различаются в деталях, и потому обвязка соединения пишется на абстракции конкретной площадки, а не на общем воображении.

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

Один продукт держит один поток на биржу с мультиплексированием символов: несколько соединений к одной площадке для одного приложения это штраф в лимитах соединений и усложнение наблюдения. Биржи ограничивают число соединений на источник, и единый поток с внутренним роутингом сообщений это архитектура по умолчанию.

Нормализация символов точность и деньги как строки

Символы это первое, что нормализуется: одна пара записывается по-разному на разных площадках, слитно, через дефис, через дробь, с нижним и верхним регистром, и слой нормализации переводит биржевые обозначения в единую внутреннюю форму. Карта соответствий заводится один раз на интеграцию и живёт со списком поддерживаемых площадок, а парсинг биржевого символа на лету остаётся источником тихих багов на границах.

Точность вторая обязанность нормализации: количество знаков у цен и объёмов различается между инструментами, шаг цены не позволяет произвольных дробей, и округление обязано знать шаг, а не резать все цены к двум знакам. Числа с плавающей точкой для денег запрещены культурой жанра: двоичная запись искажает десятичные дроби, и цена в плавающем формате однажды превращается в артефакт на десятом знаке, который виден в интерфейсе.

Строки и десятичные объекты держат значения от приёма до показа: парсинг в десятичную форму после приёма, арифметика в десятичной, форматирование по шагу инструмента на выходе. Сравнение и сортировка считаются в десятичной системе, и весь конвейер от сокета до экрана переживает без единого плавающего угла.

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

Разрывы и пропуски последовательности heartbeat и честные метки времени

Соединение рвётся по расписанию сети, а не по расписанию продукта, и обвязка восстановления обязана жить с первого дня: закрытие ловится, повторные попытки идут с растущей паузой, потолок ожидания ограничивает раздражение, и успех сбрасывает паузу к старту. Подписки восстанавливаются автоматически, потому что соединение без подписок это тишина, которую продукт примет за пустой рынок.

let delay = 1000;

function connect() {
  const ws = new WebSocket(STREAM_URL);
  ws.onopen = () => {
    delay = 1000;
  };
  ws.onclose = () => {
    delay = Math.min(delay * 2, 30000);
    setTimeout(connect, delay);
  };
}
connect();

Пропуски между разрывом и восстановлением закрываются сверкой: после переподключения продукт запрашивает снимок состояния по запросу и сравнивает с последним полученным событием, и расхождение лечится свежим снимком, а не доверчивым продолжением потока. Некоторые протоколы дают номера последовательностей, и пропуск номера это официальный сигнал разрыва логики, а не повод для надежды.

Метки времени читаются с уважением к источнику: время события от площадки это время площадки, локальное время приёма это время задержки, и обе метки хранятся рядом. Сверка часов между машиной и площадкой прогоняется по запросу времени, и скос, превышающий секунды, корректируется, потому что сортировка событий со сдвинутыми часами это сортировка фантазий.

Наблюдение за потоком завершает гигиену: возраст последнего события по символу, частота обновлений, доля пропусков, и возраст, растущий сверх нормы, подсвечивает символ, который замолчал. Тихая смерть подписки без наблюдения обнаруживается пользователем, смотрящим на замершую цену, и этот сценарий дороже любой строки мониторинга.

Ограничение темпа для интерфейса батчинг обновлений и спящие вкладки

Поток опережает интерфейс: события приходят десятками в секунду, а экрану достаточно кадров отрисовки, и пропускать каждое событие в интерфейс напрямую значит рисовать вхолостую. Батчинг собирает последние значения по символам и отдаёт их отрисовке по кадру: одно чтение состояния на кадр, и цена на экране свежая без затрат на промежуточные рендеры.

Троттлинг по символам применяется к тяжёлым цепочкам: конвертация, пересчёт портфеля, тяжёлые компоненты пересчитываются по накоплению изменений, а не по каждому событию. Порог накопления настраивается по тяжести, и живой виджет с лёгким обновлением остаётся мгновенным, а витрина с вычислениями экономит кадры без потери смысла.

Спящая вкладка обязана засыпать честно: невидимая страница прекращает отрисовку, поток можно приостановить с переходом на опрос по расписанию или отключить с восстановлением по видимости, и сниженная нагрузка окупает чуть меньшую свежесть невидимого окна. Продукты с фоновыми уведомлениями держат лёгкий канал, остальные засыпают целиком, и выбор сценария пишется словами в документации.

Гигиена интеграции ключи подписи и приватные эндпоинты

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

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

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

Порядок сборки интеграции котировок свёрнут в короткую лестницу:

  1. зафиксировать список площадок и карту символов в единой внутренней нотации;
  2. поднять опрос тикеров с кешем и уважением к весам лимитов;
  3. открыть поток по сокету с подпиской по видимой сцене;
  4. нормализовать события в единую модель с десятичными значениями;
  5. встроить восстановление с паузами, снимками и сверкой пропусков;
  6. батчить поток в кадры отрисовки и усыплять невидимые вкладки;
  7. наблюдать возраст событий и частоты обновлений по каждому символу.

Живая цена это продукт внутри продукта: маленький конвейер с нормализацией, восстановлением и уважением к темпу человеческого глаза. Интеграция, собранная по этой лестнице, показывает цифры, которым можно верить, а за доверие к цифрам платят именно такой скучной дисциплиной, и в котировальном жанре скучность остаётся высшим комплиментом.