Binance — python. Бот для крипто сигналов в Telegram, или как ошибка бывает удачной

Я новичок на Хабре, поэтому прошу не судить строго…

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

5-ти минутные свечи, Биткоин. Жёлтым маркером обозначено время сигнала от бота

5-ти минутные свечи, Биткоин. Жёлтым маркером обозначено время сигнала от бота

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

Начнём с установки библиотек, кому требуется:

Binance Python:

pip install python-binance

Telebot:

pip install pyTelegramBotAPI

Openpyxl — понадобится нам для выгрузки данных, т.к. многим проще проводить простейший анализ в эксель:

pip install openpyxl

Для начала мы сделаем бота, без сигналов в Telegram непосредственно для получения данных и их анализа. Особенно это будет необходимо тем, кто хочет посмотреть работу бота не только для Биткоина, но и для других криптовалют. В процессе этого исследования вы получите оптимальное значение ключевого параметра именно для вас.

import os
from binance.client import Client
from datetime import datetime
import time
from openpyxl import Workbook
import telebot

#Ключи для Бинанс
api_key = ('Ваш_api_key')
api_secret = ('Ваш_api_secret')
client = Client(api_key, api_secret)

#Создаем таблицу для сбора данных
wb = Workbook()
ws = wb.active
#Расписываем названия колонок для данных
ws['A1'] = 'Average'
ws['B1'] = 'Futures'
ws['C1'] = 'Percentage'
ws['D1'] = 'Time'

#Задаём торговую пару:
ASSET = 'BTCUSDT'

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

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

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

Вместо того, чтобы брать цену основного актива, я, как и все, наверное, из нелюбви читать документацию, забил слово «price» в поиск и взял подходящий, как мне казалось запрос «get_avg_price». Естественно уже потом получив результаты, я сподобился пошарить в документации и выяснил, что данная цена является средневзвешенной ценой за последние 5 минут. Как выяснилось это тоже очень интересная метрика (странно что ей трейдеры не пользуются). И именно её применение позволяет получать сигналы, подобно тем, что вы видели выше.

Создаём функции запросов:

def price(symbol):
    try:
        price = client.get_avg_price(symbol=symbol, requests_params={'timeout': 2})['price']
        return float(price)
    except Exception as e:
        print(e)

def priceF(symbol):
    try:
        priceF = client.futures_symbol_ticker(symbol=symbol, requests_params={'timeout': 2})['price']
        return float(priceF)
    except Exception as e:
        print(e)

Всем настоятельно рекомендую в запросах к Binance API, ставить жесткий timeout и не забывать про try/except. Как показывает практика, проблемы с запросами к Бинансу нередки (особенно в периоды волатильности). Таким образом вы можете получить не те данные, что вам нужны, если одна функция сработает нормально, а вторая даст результат с задержкой более 2-х секунд. Мы, в рамках этой статьи, конечно не собираемся осваивать квантовый трейдинг, однако любые расхождения в исходных данных могут периодически выдавать ложные сигналы.

Сразу настроим функцию для отправки сообщений в Telegram:

bot = telebot.TeleBot('ваш_ключ_бота_от_BotFather')

# Ваш ID или список ID получателей
ID = 111111111

#Функция отправки сообщений
def message(text):
    bot.send_message(ID, text)

def message_signal():
    message(f'Сигнал по торговой паре: {ASSET}')

Создаёте бота в BotFather, полученный ключ вставляете в код. Далее узнаете ваш Telegram ID и также вставляете сюда.

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

Также, мои друзья трейдеры, оценили дополнительную информацию о силе сигнала. Я сделал шкалу от 1 до 10, в зависимости от силы отклонения одной цены от другой. Сейчас мы не будем разбирать этот момент, уверен, в этом у вас не возникнет сложностей.

Далее я задаю время перерыва в запросах. Это реально необходимо, уверяю вас на количестве сигналов это не скажется. При запросах раз в секунду на всех местах, отмеченных жёлтым маркером на графике выше я получил от 5 до 50–80 сигналов.

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

TIME = 1

# Процент выше которого мы начинаем получать сигналы
GROWTH_PERCENT = 0.053

while True:
    #Сравниваем цену фьючерсов со средней ценой
    FIRST_PRICE = price(ASSET)
    PRICEF = priceF(ASSET)
    PERCENT = ((PRICEF - FIRST_PRICE) / FIRST_PRICE) * 100

    if PERCENT >= GROWTH_PERCENT:

        #Нет необходимости получать результат в консоль, но я их обычно вывожу
        print(ASSET)
        print("LOOK")
        print(PERCENT)
        times = datetime.now().strftime("%H:%M:%S")
        print(times)
        ws.append([FIRST_PRICE, PRICEF, PERCENT, times])
        wb.save("data.xlsx")
    time.sleep(TIME)

Можете ограничить длительность цикла и сохранять файл после него, при желании, вынеся «wb.save («data.xlsx»)» за пределы цикла.

В процессе анализа данных вам нужно получить значение «GROWTH_PERCENT» оптимальное для ваших целей и/или торговой пары. Сразу скажу, по тем альткоинам, что я исследовал это значение на порядок выше, чем для Биткоина (в данный момент, в зависимости от пары я использую от 0,6 до 0,87). В эксель это делается легко, однако не задавайте сразу слишком большое значение, иначе, возможно, пропустите более слабые, но не менее нужные сигналы.

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

import os
from binance.client import Client
from datetime import datetime
import time
from openpyxl import Workbook
import telebot

#Ключи для Бинанс
api_key = ('Ваш_api_key')
api_secret = ('Ваш_api_secret')
client = Client(api_key, api_secret)

#Создаем таблицу для сбора данных
wb = Workbook()
ws = wb.active
#Расписываем названия колонок для данных
ws['A1'] = 'Average'
ws['B1'] = 'Futures'
ws['C1'] = 'Percentage'
ws['D1'] = 'Time'

#Задаём торговую пару:
ASSET = 'BTCUSDT'

def price(symbol):
    try:
        price = client.get_avg_price(symbol=symbol, requests_params={'timeout': 2})['price']
        return float(price)
    except Exception as e:
        print(e)

def priceF(symbol):
    try:
        priceF = client.futures_symbol_ticker(symbol=symbol, requests_params={'timeout': 2})['price']
        return float(priceF)
    except Exception as e:
        print(e)


bot = telebot.TeleBot('ваш_ключ_бота_от_BotFather')

# Ваш ID или список ID получателей
ID = 111111111

#Функция отправки сообщений
def message(text):
    bot.send_message(ID, text)

def message_signal():
    message(f'Сигнал по торговой паре: {ASSET}')

TIME = 1

# Процент выше которого мы начинаем получать сигналы
GROWTH_PERCENT = 0.053

while True:
    #Сравниваем цену фьючерсов со средней ценой
    FIRST_PRICE = price(ASSET)
    PRICEF = priceF(ASSET)
    PERCENT = ((PRICEF - FIRST_PRICE) / FIRST_PRICE) * 100

    if PERCENT >= GROWTH_PERCENT:
        
        #Нет необходимости получать результат в консоль, но я их обычно вывожу
        message_signal()
        print(ASSET)
        print("LOOK")
        print(PERCENT)
        times = datetime.now().strftime("%H:%M:%S")
        print(times)
        ws.append([FIRST_PRICE, PRICEF, PERCENT, times])
        wb.save("data.xlsx")
        
    time.sleep(TIME)

Вы можете справедливо заметить, что вместо цены фьючерса можно использовать цену базового актива. И это безусловно верно и приведет к практически тем же результатам.

Однако у фьючерсов есть замечательное свойство. В некоторые моменты из-за значительного колебания цены многие фьючерсные позиции принудительно ликвидируются. Что иногда приводит к гораздо большим скачкам цены в моменте. И естественно сигнал о таких моментах тоже ценится трейдерами.

Спасибо, что дочитали до конца! Буду рад конструктивной и не очень критике в комментариях.

© Habrahabr.ru