Возникла задача сделать уведомления в Телеграм о сделках на Binance: открытие и закрытие позиций, текущий профит, баланс и прочее. Это актуально если кто-то или что-то торгует на вашем счете и вы хотели бы быть в курсе. Задача выглядела несложной - из Binance API забираем последние сделки и шлём в Телеграм - работы на пару часов. Но на практике это превратилось в квест на пару дней в котором 90% времени ушло на изучение особенностей работы с Binance API, их довольно странную логику и жесткие лимиты.

В итоге, родился минималистичный скрипт на 40 строк кода и новый интересный проект о котором упомяну в конце статьи. Скрипт можно запустить на своем компьютере и уведомления о сделках на вашем счете типа BUY BTCUSDT volume: 0.01 и CLOSE BTCUSDT profit: 10$ полетят в Телеграм.

Подготовка

Для работы скрипта нужны:

  1. Ключи API созданные в личном кабинете на Binance. Заходим в "Управление API", жмём на кнопку "Создать API", выбираем "Сгенерированный системой" и копируем API key и Secret key. Убеждаемся, что права у ключей "только чтение".

  2. Токен Телеграм бота. Как сделать описывать не буду, инструкций много, смотрите здесь.

  3. 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. Как их удалось обойти - тема следующей статьи.

Комментарии (2)


  1. Hudrolax
    09.07.2023 18:46

    Лучше использовать websockets для получения данных о сделках с Binance и aiogram для асинхронной работы с ботами.


    1. evg_dc Автор
      09.07.2023 18:46

      Да, websockets правильнее и aiogram конечно использую, без него никак.