1. Введение: что делает бот

Продолжая проект из первой части статьи, бот стал полноценной системой:

  • принимает сигналы четырёх типов: 5% long / 5% short / 12% long / 12% short

  • фильтрует их по дисбалансу ордеров в стакане, капитализации (MCAP), cooldown'у

  • рассчитывает стопы, тейки, трейлинг при необходимости

  • открывает сделки на BingX demo через свой API-клиент

  • управляет пользователями через SQLite и Telegram меню

  • работает асинхронно и обрабатывает десятки сигналов одновременно

Теперь поехали по ключевой функциональности.

Я решил ввести два типа сделок, зависящих от 5% и 12% сигналов. При 12% движении мы открываем позиции в контр-тренд, при 5% движении открываем позиции в продолжение тренда. Такие проценты были выведены путём довольно долгой ручной торговле. На монетках средней и маленькой капитализации это действительно рабочая стратегия. Вероятно, она основана на принципах работы ММ.

Всю эту стратегию я реализовал в телеграмм бота - с ручными настройками каждой из четырех стратегий, с фильтрами. Бот сейчас находится в тестнете и является на 100% бесплатным. Так что вы можете тестировать его не опасаясь за свои деньги - все сделки будут происходить на демо-счёте. ссылка на бота

База данных: как устроено хранение пользователей

Когда проект вырос из простого парсера сигналов в полноценного торгового бота, появилась необходимость хранить индивидуальные настройки каждого пользователя:

  • API ключи BingX

  • плечо / маржу

  • включённые стратегии (5long, 5short, 12long, 12short)

  • стопы, тейк-профиты

  • мкап фильтр

  • фильтр дисбаланса

  • трейлинг стопы

  • коэффициенты трейлинга

  • cooldown на повторные сделки

Именно поэтому был создан SQLite хранилище, ведь оно является отличным решением как по скорости, так и по функциональности.

Создание базы данных

При каждом запуске бот проверяет наличие таблицы users, а если её нет — создаёт.

Структура:

def setup_db():
    conn = sqlite3.connect("users.db")
    c = conn.cursor()

    c.execute("""
    CREATE TABLE IF NOT EXISTS users (
        user_id INTEGER PRIMARY KEY,
        api_key TEXT,
        api_secret TEXT,

        leverage REAL DEFAULT 20,
        margin REAL DEFAULT 10,

        # Включение стратегий
        enable_5long INTEGER DEFAULT 1,
        enable_5short INTEGER DEFAULT 1,
        enable_12long INTEGER DEFAULT 1,
        enable_12short INTEGER DEFAULT 1,

        # Стопы и тейки
        stop_5long REAL DEFAULT 5,
        stop_5short REAL DEFAULT 5,
        stop_12long REAL DEFAULT 12,
        stop_12short REAL DEFAULT 12,

        tp_5long TEXT DEFAULT "5,10,15",
        tp_5short TEXT DEFAULT "5,10,15",
        tp_12long TEXT DEFAULT "12,18,25",
        tp_12short TEXT DEFAULT "12,18,25",

        # Фильтры
        mcap_filter INTEGER DEFAULT 0,
        imbalance_filter INTEGER DEFAULT 0,

        # Трейлинг стоп
        trailing INTEGER DEFAULT 0,
        trail_rate_5long REAL DEFAULT 0.005,
        trail_rate_5short REAL DEFAULT 0.005,
        trail_rate_12long REAL DEFAULT 0.01,
        trail_rate_12short REAL DEFAULT 0.01,

        cooldown INTEGER DEFAULT 0
    );
    """)

    conn.commit()
    conn.close()

Фильтр дисбаланса (Orderbook imbalance)

Это очень важная часть проекта, так как она позволяет опираться при открытии сделок на объективные рыночные данные.

Что делает фильтр:

  1. Получает стаканы сразу с bingX

  2. Анализирует объёмы покупателей и продавцов на глубине 1000 тиков.

  3. Вычисляет формулу дисбаланса:

imbalance = (bid_volume-ask_volume)/(bid_volume+ask_volume)

? За что отвечает imbalance?

  • показывает давление покупателей / продавцов

  • отсеивает ложные сигналы

  • помогает торговать по тренду для 5% сигналов

  • помогает торговать контртренд для 12% сигналов

def get_bingx_orderbook(symbol: str = "BTC-USDT", limit: int = 100):
    url = "https://open-api.bingx.com/openApi/swap/v2/quote/depth"
    
    params = {
        "symbol": symbol.replace("/", "-"),  # на всякий случай
        "limit": int(limit)
    }
    # Сортируем параметры и добавляем timestamp
    sorted_keys = sorted(params.keys())
    params_str = "&".join([f"{k}={params[k]}" for k in sorted_keys])
    timestamp = str(int(time.time() * 1000))
    query_string = f"{params_str}&timestamp={timestamp}" if params_str else f"timestamp={timestamp}"
    # Подпись (даже для публичного эндпоинта — BingX требует подпись)
    signature = hmac.new(
        SECRETKEY.encode("utf-8"),
        query_string.encode("utf-8"),
        hashlib.sha256
    ).hexdigest()
    # Финальный URL
    final_url = f"{url}?{query_string}&signature={signature}"

    headers = {
        "X-BX-APIKEY": APIKEY,  # можно пустой
    }

    response = requests.get(final_url, headers=headers, timeout=10)
    data = response.json()

    if data.get("code") != 0:
        print(f"[BingX Depth] Ошибка API: {data}")
        return None

    bids = [(float(x[0]), float(x[1])) for x in data["data"]["bids"]]
    asks = [(float(x[0]), float(x[1])) for x in data["data"]["asks"]]
    return {"bids": bids, "asks": asks, "timestamp": data["data"].get("timestamp")}

Фильтр MCAP (капитализация / white-list)

Этот фильтр защищает от торговли низколиквидными шиткоинами, которые:

  • могут пампиться сидящими группами

  • могут закрыть позицию одним большим ордером

  • не имеют реальной рыночной глубины

Фильтр использует файл с кешем о лоу-кап тикерах, которые есть на bingX. Сторонний скрипт постоянно обновляет этот список и перезаписывает сам файл. Сейчас я использую монетки до 150млн капитализации.

Почему это важно:

  • 90% фейковых пампов происходят на токенах с капитализацией < $100M

  • ликвидность в стакане низкая, так что актив двигается гораздо легче

  • можно попасть на сквиз 20–50%. Так как торгуем со стопами, то можно не боятся выноса в большой убыток. А вот хороший профит можно забрать с трейлингом без проблем.

Этот фильтр — один из самых важных для реальной торговли.

Как фильтры работают вместе

Сигнал →
Проверка включена ли стратегия →
MCAP фильтр →
Фильтр дисбаланса стакана →
Открытие позиции →
Трейлинг / тейки / стоп

Handler сигналов: сердце бота

Этот блок — самый важный во всём проекте.
В телеграмм группы (куда собственно и идёт пересылка сообщений о пампах) присылаются текстовые уведомления. Таких группы 2: 12% signals и 5% signals. Handler превращает их в полноценные торговые операции на бирже.

Вот формальная задача:

Принять сообщение → извлечь тикер → определить тип сигнала → пройти фильтры → открыть сделку → установить стопы/тейки → отправить уведомление в Telegram.

Именно это делает следующий обработчик.

1. Получение и первичная фильтрация сигнала

@client.on(events.NewMessage(chats=[channel_5long, channel_12short]))
async def handler(event):
    raw_text = event.message.message or ""
    text = raw_text.strip()

    # Фильтруем сигнал — он должен начинаться с ? или ?
    if not text.startswith("?") and not text.startswith("?"):
        return

Сигналы о пампе или дампе приходят только с эмодзи — остальное можно игнорировать. Это экономит ресурсы и избавляет от ложных запусков.

2. Извлечение тикера

ticker_match = re.search(r'\$([A-Z]{2,12})\b', text)
if not ticker_match:
    print("Не найден тикер в сообщении")
    return

token = ticker_match.group(1).upper()
symbol = f"{token}-USDT"
Здесь бот вытаскивает $BTC, $XRP, $SOL из текста.

Почему важно делать именно так? сигналы могут содержать текст, числа, описание • но формат $TOKEN стабилен

3. Oпределение канала = определение стратегии

chat = await event.get_chat()
is_5_channel = chat_id == str(channel_5long_id) or "5%" in chat_title.lower()
is_12_channel = chat_id == str(channel_12short_id) or "12%" in chat_title.lower()

4. Определение направления и стратегии

Интересный момент — преобразование сигналов.

5%:

? = long
? = short

12% — наоборот:

? = short
? = long

И код:

side = 'long' if text.startswith("?") else 'short'

if side == 'long' and is_5_channel:
    strategy = '5long'
elif side == 'long' and is_12_channel:
    strategy = '12long'
    side = 'short'
elif side == 'short' and is_12_channel:
    strategy = '12short'
    side = 'long'
elif side == 'short' and is_5_channel:
    strategy = '5short'

5. Получение пользователей из БД

c.execute("SELECT user_id FROM users WHERE api_key IS NOT NULL AND api_secret IS NOT NULL")

Бот открывает сделки для каждого пользователя с API-ключами, то есть работает сразу для всех подписчиков.

6. Проверка включена ли стратегия

if not settings.get(f"enable_{strategy}", 0):
    continue

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

7. Фильтр капитализации (MCAP filter)

if settings.get("mcap_filter", 0):
    if token not in ALLOWED_TICKERS:
        continue

Что делает фильтр
Отбирает монетки с низкой ликвидностью, которые ходят гораздо легче и не так подвержены влиянию общего рынка

8. Фильтр дисбаланса ORDBOOK IMBALANCE

До открытия сделки мы рассчитываем дисбаланс ордеров, показал это ранее

✔ Для 5% сигналов — фильтр по тренду

if strategy == "5long" and imbalance < 0: continue
if strategy == "5short" and imbalance > 0: continue

✔ Для 12% сигналов — фильтр против тренда

if strategy == "12long" and imbalance > 0: continue
if strategy == "12short" and imbalance < 0: continue

То есть бот смотрит, когда монетка действительно готова идти в нашем направлении и есть поддержка в виде ордеров в стакане.

9. Фильтр COOL-DOWN

Чтобы не открывать одну и ту же сделку много раз:

if now - last_trade_times[user_id][trade_key] < cooldown:
    continue

Открытие позиции

После выполнения всех данных фильтров, их расчёта, пришло время открыть позицию. Подтянув данные из базы данных о тейках и стопах расчитываем цены для них и открываем позицию с помощью bingX клиента (упомянул в прошлой статье). Делаем мы это следующим образом:

stop_pct = settings.get(f"stop_{strategy}", 0.05)
sl_level = round(mark_price * (1 - stop_pct) if side == "long" else mark_price * (1 + stop_pct), precision)
order = bx.place_market_order(side, qty, symbol, sl_level)

tp_str = settings.get(f"tp_{strategy}", "5,10,15")
tp_percents = parse_levels(tp_str)

tp_prices = [
    mark_price * (1 + p) if side=="long" else mark_price * (1 - p)
    for p in tp_percents]

bx.set_multiple_tp(symbol, qty, mark_price, side, tp_prices)

trail_act = settings.get(f"trail_active_{strategy}", 0)
trail_rate = settings.get(f"trail_rate_{strategy}", 0)

if trail_act > 0 and trail_rate > 0:
    act_price = mark_price * (1 + trail_act) if side == "long" else mark_price * (1 - trail_act)
    bx.set_trailing(symbol, qty, act_price, trail_rate, side)

bot_sender.send_message(
    user_id,
    f"Deal opened: {strategy}, imbalance={imbalance:.2f}, stop={stop_pct}, tp={tp_percents}"
)

last_trade_times[user_id][trade_key] = now

Что делает handler шаг за шагом

Получение сигнала
→ Проверка формата
→ Извлечение тикера
→ Определение канала (5%/12%)
→ Определение стратегии (5long/12short...)
→ Получение всех пользователей
→ Фильтр "стратегия выключена"
→ Фильтр MCAP
→ Фильтр дисбаланса рынка
→ Фильтр Cooldown
→ Получение биржевой цены
→ Расчёт количества
→ Расчёт стоп-лосса
→ Открытие позиции (MARKET + SL)
→ Выставление тейков
→ Выставление трейлинга
→ Уведомление пользователя
→ Запись времени сделки

Этот обработчик полностью автономен, безопасен и стабилен.

Вывод

Таким образом, мы написали полностью автономную стратегию. Вопрос лишь в том - как каждый пользователь настроит данные стратегии. Важная часть этого бота - комбинирование. Кроме того, если вы уверены, например, в лонговом нарративе на рынке, можно оставить лишь лонговых ботов. Аналогично, если думаете, что рынок будет пдаать в ближайшей перспективе. Этих настроек вполне достаточно для грамотной и полноценной работы по данной торговой стратегии.
Безусловно, сейчас бот находится в тестовом режиме. Обратите внимание что сейчас он в тестнете. Но каждый может его протестировать и подобрать свои настройки. Со временем будут выпущены готовые торговые модели и бот уже будет запущен в mainnet. Так как тут настройки субъективны, то обязательно нужно всё протестировать временем.

Продукт абсолютно бесплатен и будет оставаться таковым - пробуйте и эксперементируйте - @parsertoyosha_bot.

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