Возникла задача сделать уведомления в Телеграм о сделках на Binance: открытие и закрытие позиций, текущий профит, баланс и прочее. Это актуально если кто-то или что-то торгует на вашем счете и вы хотели бы быть в курсе. Задача выглядела несложной - из Binance API забираем последние сделки и шлём в Телеграм - работы на пару часов. Но на практике это превратилось в квест на пару дней в котором 90% времени ушло на изучение особенностей работы с Binance API, их довольно странную логику и жесткие лимиты.
В итоге, родился минималистичный скрипт на 40 строк кода и новый интересный проект о котором упомяну в конце статьи. Скрипт можно запустить на своем компьютере и уведомления о сделках на вашем счете типа BUY BTCUSDT volume: 0.01 и CLOSE BTCUSDT profit: 10$ полетят в Телеграм.
Подготовка
Для работы скрипта нужны:
Ключи API созданные в личном кабинете на Binance. Заходим в "Управление API", жмём на кнопку "Создать API", выбираем "Сгенерированный системой" и копируем API key и Secret key. Убеждаемся, что права у ключей "только чтение".
Токен Телеграм бота. Как сделать описывать не буду, инструкций много, смотрите здесь.
Telegram id вашего аккаунта в Телеграм. Что бы его узнать, просто отправьте любое сообщение этому боту или аналогичному, таких сервисов много.
Скрипт
# pip install binance-futures-connector, pyTelegramBotAPI
import telebot
from binance.um_futures import UMFutures
binance_api_key = "..."
binance_api_secret = "..."
bot_token = "..."
my_telegram_id = "..."
bot = telebot.TeleBot(bot_token)
prev_symbols = []
prev_data = []
while True:
new_symbols = []
new_data = []
client = UMFutures(key=binance_api_key, secret=binance_api_secret)
try:
info = client.account(recvWindow=6000)
except Exception as e:
print(e)
break
for p in info['positions']:
if float(p['positionAmt']) != 0:
new_symbols.append(p['symbol'])
new_data.append([p['positionAmt'], p['unrealizedProfit']])
if new_symbols != prev_symbols:
symbol = list(set(new_symbols) ^ set(prev_symbols))[0]
if len(new_symbols) > len(prev_symbols):
amount = float(new_data[new_symbols.index(symbol)][0])
text = symbol + " amount: " + str(abs(amount))
if amount > 0:
text = "BUY " + text
else:
text = "SELL " + text
else:
profit = round(float(prev_data[prev_symbols.index(symbol)][1]), 2)
text = "CLOSE " + symbol + " profit, $: " + str(profit)
prev_symbols = new_symbols
prev_data = new_data
print(text)
bot.send_message(my_telegram_id, text)
Как он работает
У биржи Binance своё представление о "сделке". Для нас сделка - это основной юнит торговли. У сделки есть точка входа, точка выхода и, как итог, профит или убыток. У Binance такой сущности нет, у них есть "позиция" и "трейд" (речь идет о фьючерсах). Позиция - это открытая в данный момент сделка. После закрытия позиции информация о ней исчезает бесследно и достать ее из истории одним запросом невозможно. А трейд, в свою очередь, - вход или выход из позиции. Т.е. случился трейд - позиция открылась, случился следующий - позиция закрылась. Это все просто для отслеживания, пока вы не увеличили или не уменьшили объем открытой позиции. Тогда трейдов на одну позицию может быть неограниченное количество и начинается путаница.
Кроме этого, просто запросить все трейды нельзя, их можно запрашивать только с указанием конкретной торговой пары. Учитывая, что торговых пар более 200, опросить их все невозможно. По этой же причине нельзя отслеживать и ордера, запрос тоже требует торговую пару.
В итоге, для мониторинга событий остается только две точки входа - Account Information и Get Income History. В первом случае получим всю информацию о счете включая текущие открытые позиции, во втором - все начисления и списания. Get Income History содержит в себе указание на трейд результатом которого стало движение средств. По этому указателю можем достать нужный трейд и получить то, что надо - торговую пару, объем и направление сделки. Формально это самый правильный путь, но нам не подойдет. Он в 6 раз дороже (по ограничениям API) чем запрос Account Information и требует выстраивания цепочки из минимум двух запросов.
Поэтому оставляем первый вариант - Account Information. Всего один запрос и прилетает много информации включая открытые позиции. По наличию или отсутствию открытых позиций можно сделать вывод о случившемся трейде. Да, это выглядит немного криво, но работает и очень экономно.
По такой схеме в приведенном скрипте делаем несложную операцию - проверяем текущие открытые позиции и сравниваем с результатом предыдущей проверки. Если видим разницу то делаем вывод о случившемся событии и шлем уведомление.
Более продвинутый вариант
Все это нормально работает пока ботом пользуются не более 10 человек. А что если их не 10, а 1000? Я задался этим вопросом и попытался найти ответ. Одно за другим рождались технические решения, в итоге всё свелось в проект Binance Connector.
Бот справляется с любым количеством пользователей и уведомляет о событии с задержкой всего 3-4 секунды. Понимает по какому типу ордера произошел трейд, видит изменение объема позиции, сообщает о текущем нереализованном профите и многое другое...
Здесь главной проблемой были лимиты Binance API. Как их удалось обойти - тема следующей статьи.
Hudrolax
Лучше использовать websockets для получения данных о сделках с Binance и aiogram для асинхронной работы с ботами.
evg_dc Автор
Да, websockets правильнее и aiogram конечно использую, без него никак.