Введение
Большинство современных криптобирж отдают информацию через REST-запросы – клиент должен запросить через HTTP страницу с определенным адресом, отправить нужные параметры и получить некоторый ответ.
Сама по себе технология REST довольно интересна – незаметно для получателя запрос может путешествовать от одного сервера к другому, можно балансировать нагрузку, можно снизить кол-во отдаваемого трафика, но этого мало (с)
На крупных биржах счет пользователей идет на миллионы, а кол-во запросов к API намного выше, т.к. и команды пользователей идут через эти же интерфейсы, через них работают работы, через них другие сайты получают информацию для виджетов на свои сайты, брокеры выводят информацию своим клиентам, тысячи телеграм-каналов что-то там транслируют, анализируют, и многое-многое другое. А ведь каждый лишний байт трафика, помноженный на десятки миллионов запросов выливается в большие долларовые потери на оплату трафика, содержание серверов, сетевого оборудования, зарплаты техподдержки – поэтому REST становится накладным.
Для экономии трафика используется технология WebSocket - с ней некоторое кол-во трафика тратится один раз на установление соединения, после чего клиент и сервер обмениваются непосредственно данными. Это как связаться с кем-то по телефону – вы позвонили, трубку снял автоответчик, теперь вы можете бесконечно висеть на трубке и слушать, что там происходит. Вообще вебсокеты подразумевают двустороннюю связь – вы можете что-то говорить в трубку, ваш абонент может, но в случае Binance (и большинства других криптобирж) есть ограничения.
Так, например, на бинансе нельзя слушать сокет непрерывно свыше 24 часов. Так же связь односторонняя – сервер время от времени (один раз в секунду, как правило) выдаёт один и тот же набор данных всем, кто его слушает. Это как прийти на вокзал и слушать объявления – «Поезд на Бердичев прибыл на второй путь», «Отправление поезда Саратов-Омск» в толпе таких же людей.
Работа с сокетами Binance
Для работы вам потребуется установленный питон версии 3.6 и выше. Скачать его можно с официального сайта, при установке установив все флаги.
Технически, нет ничего сложного, чтобы реализовать протокол самому с нуля, для этого у вас всё есть, но не будем изобретать велосипеды и возьмем чужой готовый модуль питона websocket-client и посмотрим, что можно получить с binance. Для установки модуля выполните в командной строке
pip install websocket-client
Теперь давайте напишем простой скрипт для проверки работы, а потом пройдемся детальнее. Вот он:
import websocket
def on_message(ws, message):
print(message)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
print("### connected ###")
if __name__ == "__main__":
#ws = websocket.WebSocketApp("wss://stream.binance.com:9443/stream?streams=ltcbtc@aggTrade/ethbtc@aggTrade",
ws = websocket.WebSocketApp("wss://stream.binance.com:9443/ws/ltcbtc@aggTrade/ethbtc@aggTrade",
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()
После его запуска вы увидите нечто похожее:
Фактически, вы подключились к сокет-серверу и подписались на обновления сделок по парам LTCBTC и ETHBTC. Фактически синхронно с веб-интерфейсом у вас обновляются данные по последним сделкам. Теперь давайте рассмотрим подробнее, что и как можно и нужно делать. Начнем с режимов.
Режимы raw и combined
В моем скрипте одна строка закомментирована – вы можете её раскомментировать и удалить строку под ней – тогда вам будет выдаваться чуть больше информации, вот так:
import websocket
def on_message(ws, message):
print(message)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
print("### connected ###")
if __name__ == "__main__":
ws = websocket.WebSocketApp("wss://stream.binance.com:9443/stream?streams=ltcbtc@aggTrade/ethbtc@aggTrade",
#ws = websocket.WebSocketApp("wss://stream.binance.com:9443/ws/ltcbtc@aggTrade/ethbtc@aggTrade",
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()
Первый режим – это то, что Binance называет raw режимом – каждый набор данных отдается как есть, независимо от другого. Т.е. если в один момент времени произошли сделки И LTCBTC И ETHBTC, то вы получите две записи.
В случае combined режима, вы, скорее всего, получите одну запись, которая логически будет структурирована, и внутри которой будут сделки по каждой паре, каждая в своем разделе. Смотрите сами, как вам удобнее, мне удобнее raw.
Требования к подключениям
Каждое подключение должно идти на адрес
wss://stream.binance.com:9443
В конце адреса нужно подставить параметр, что бы указать, какая информация вам нужна. В случае raw укажите /ws/…, в случае combined - /stream?streams=, как у меня в примере.
Все пары маленькими буквами, и не забывайте пересоздавать коннект раз в 24 часа.
Непосредственно стримы
Стримы – streams (потоки) – это то, что «льется» из биржи – например, информация по свечам EOSBTC – это один стрим, по свечам ONTBTC – другой, тикер по всем парам – третий, и т.п.
Информация по сделкам (агрегированная)
То, что мы смотрели в примере выше – сделки по паре (или парам), объединенные в рамках одного ордера. Иными словами, если по одной цене одним человеком по одной паре в течении последних милисекунд было совершено несколько сделок, то вы увидите одну строку.
Пример запроса: ethbtc@aggTrade
wss://stream.binance.com:9443/ws/ethbtc@aggTrade
wss://stream.binance.com:9443/stream?streams=ethbtc@aggTrade
Структура ответа:{
"e": "aggTrade", // Тип события
"E": 123456789, // Время отправки события
"s": "BNBBTC", // Пара
"a": 12345, // ID возвращаемой записи
"p": "0.001", // Цена
"q": "100", // Кол-во
"f": 100, // ID первой сделки
"l": 105, // ID последней сделки
"T": 123456785, // Время сделки
"m": true, // Покупатель мейкер?
"M": true // Не актуально.
}
Информация по сделкам (подробная)
Выдается информация по сделкам, как есть.
Пример запроса: ethbtc@trade
wss://stream.binance.com:9443/ws/ethbtc@trade
wss://stream.binance.com:9443/stream?streams=ethbtc@trade
Структура ответа:
{
"e": "trade", // Тип события
"E": 123456789, // Время отправки
"s": "BNBBTC", // Пара
"t": 12345, // ID события
"p": "0.001", // Цена
"q": "100", // Объем
"b": 88, // Id заказа покупателя
"a": 50, // Id заказа продавца
"T": 123456785, // Время сделки
"m": true, // Покупатель мейкер?
"M": true // Не актуально.
}
Информация по свечам
Отдается каждую секунду. Нужно указать период, за который интересует информация.
Формируется так: параkline_период
Например
wss://stream.binance.com:9443/ws/ethbtc@kline_5m
wss://stream.binance.com:9443/stream?streams=ethbtc@kline_5m
Единицы измерения:
m -> минуты; h -> часы; d -> дни; w -> недели; M -> месяца
Возможные варианты:
- 1m
- 3m
- 5m
- 15m
- 30m
- 1h
- 2h
- 4h
- 6h
- 8h
- 12h
- 1d
- 3d
- 1w
- 1M
Структура ответа:
{
"e": "kline", // Тип события
"E": 123456789, // Время события
"s": "BNBBTC", // пара
"k": {
"t": 123400000, // Время открытия свечи (UNIXTIME)
"T": 123460000, // Время закрытия свечи
"s": "BNBBTC", // Пара
"i": "1m", // Период
"f": 100, // ID первой сделки периода
"L": 200, // ID последней сделки периода
"o": "0.0010", // Цена открытия
"c": "0.0020", // Цена закрытия
"h": "0.0025", // High
"l": "0.0015", // Low
"v": "1000", // Объем базовой валюты
"n": 100, // Кол-во сделок
"x": false, // Закрыта ли свеча?
"q": "1.0000", // Объем квотируемой валюты
"V": "500", // Сколько базовой валюты куплено тейкерами
"Q": "0.500", // Сколько квотируемой валюты куплено тейкерами
"B": "123456" // Не актуально
}
}
Статистика по паре за 24 часа
Отправляется с сервера раз в секунду.
Пример запроса: ethbtc@ticker
wss://stream.binance.com:9443/ws/ethbtc@ticker
wss://stream.binance.com:9443/stream?streams=ethbtc@ticker
Структура ответа:
{
"e": "24hrTicker", // Тип события
"E": 123456789, // Время события
"s": "BNBBTC", // Пара
"p": "0.0015", // Изменение цены
"P": "250.00", // Изменение цены в процентах
"w": "0.0018", // Средневзвешенная цена
"x": "0.0009", // Цена закрытия предыдущих суток
"c": "0.0025", // Цена закрытия текущих суток
"Q": "10", // Объем закрытия
"b": "0.0024", // Цена лучшего Bid (спроса/покупки)
"B": "10", // Объем лучшего Bid
"a": "0.0026", // Цена лучшего ask (преложение/продажа)
"A": "100", // Объем лучшего ask
"o": "0.0010", // Цена открытия
"h": "0.0025", // High
"l": "0.0010", // Low
"v": "10000", // Общий объем торгов в базовой валюте
"q": "18", // Общий объем торгов в квотируемой валюте
"O": 0, // Время начала сбора статистики
"C": 86400000, // Время окончания сбора статистики
"F": 0, // ID первой сделки
"L": 18150, // Id последней сделки
"n": 18151 // Общее кол-во сделок
}
Статистика по всем парам за 24 часа
Отправляется раз в секунду.
Пример запроса: /!ticker@arr
wss://stream.binance.com:9443/ws/!ticker@arr
wss://stream.binance.com:9443/stream?streams=!ticker@arr
Возвращает то же, что и статистика за 24 часа, но по всем парам сразу, вот в такой структуре:
[
{
// Отдельно взятая пара
}
]
Часть книги ордеров
Лучшие предложения покупки и продажи. Допускается получать по 5, 10 или 20 предложений из каждого стакана. Обновляется раз в секунду
Пример запроса: ethbtc@depth5
wss://stream.binance.com:9443/ws/ethbtc@depth5
wss://stream.binance.com:9443/stream?streams=ethbtc@depth5
Структура ответа:
{
"lastUpdateId": 160, // ID события
"bids": [ // Стакан покупок
[
"0.0024", // Цена
"10", // Объем
[] // Не актуально
]
],
"asks": [ // Стакан продаж
[
"0.0026", // Цена
"100", // Объем
[] // Не актуально
]
]
}
Обновление книги ордеров
Обновляется раз в секунду. Метод нужен тем, кто локально собирает и анализирует копии стаканов.
Пример запроса: ethbtc@depth
wss://stream.binance.com:9443/ws/ethbtc@depth
wss://stream.binance.com:9443/stream?streams=ethbtc@depth
Структура ответа:
{
"e": "depthUpdate", // Тип события
"E": 123456789, // Время события
"s": "BNBBTC", // Пара
"U": 157, // Первое ID события
"u": 160, // Последнее ID события
"b": [ // Покупки
[
"0.0024", // Цена
"10", // Объем
[] // Не актуально
]
],
"a": [ // Продажи
[
"0.0026", // Цена
"100", // Объем
[] // Не актуально
]
]
}
Как правильно собирать стаканы на своей стороне:
- Откройте подключение к Open a stream to wss://stream.binance.com:9443/ws/bnbbtc@depth
- Собирайте в буфер информацию, которую получите оттуда
- Получите текущую информацию (слепок) по стаканам с адреса https://www.binance.com/api/v1/depth?symbol=BNBBTC&limit=1000
- У себя в буфере удалите (и не принимайте) все события, у которых u <= lastUpdateId слепка и u > lastUpdateId+1
- Каждое новое событие стрима должно быть выше предыдущего u на единицу
- Данные в каждом событии – абсолютные значения текущей цены и объема
- Если объем 0, удалите у себя цену
- Получение данных с 0, которых нет у вас в книге, допускается, и не является какой-либо ошибкой.