Введение
Большинство современных криптобирж отдают информацию через 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, которых нет у вас в книге, допускается, и не является какой-либо ошибкой.
Это значит, что сравниваете два каких-то значения, из которых одно является числом с плавающей точкой, а другое неопределено.
Больше ничего сказать нельзя, не видя кода.
Проверьте, где у вас используется конструкция вида
переменная1 > переменная2
Я скопировал скрипт со страницы, запустил, ошибок не вижу - может быть, у вас скопировались какие-то html-теги или вроде того?
Решил данную проблему откатом библиотеки websocket-client
версии выше 0.45.0 не работали откатился к версии 0.21.0 всё заработало
Для того, что бы отслеживать конкретного человека, нужно знать его API ключи, ну или хотя бы публичный.
Получать информацию по стаканам можно без авторизации, но там не будет видно, кто именно какие ставит, только суммарные данные, такие же, как вы видите на сайте в стакане.
А это все стримы? Если нет, то где можно посмотреть список всех?
Конкретно интересует "recent market activity", как на сайте, может есть такой.
Нужно запустить, и дать поработать какое-то время, постепенно кол-во пар дойдет до максимального, вроде бы так.
"status":"TRADING"
Такая информация позволит самостоятельно строить свечи любой размерности, включая секундные, и так же даст некоторую дополнительную информацию для анализа. Разумеется, объем данных будет больше и потребуется больше времени и ресурсов для обработки и хранения.
Как вариант можно завести глобальный словарь, где ключом будет пара, и перед созданием ордера проверять, что значение не выставлено, а после создания проставлять.
я получаю одним WebSocket все пары @trade но когда сравниваю с сайтом не все данные приходят?
Может есть ограничения? или надо отдельно под каждую торговую пару получать данные?