Блог им. RomellaAkumov

OI‑сигналы и автоматический трейдинг: пишем трейдинг телеграм бота с фильтрами

Введение

Цель этой статьи — максимально подробно и практично разобрать реальный Python‑проект автоматического трейдинга. Это не концепт, а рабочий бот, который: непрерывно анализирует рынок Binance Futures, ищет сигналы по открытому интересу (Open Interest), применяет набор защитных фильтров, работает с множеством пользователей одновременно, управляется через Telegram‑интерфейс и при необходимости открывает реальные сделки через API биржи BingX.

Далее я последовательно разберу всю логику и все функции основного файла main.py, объясняя, как и зачем они реализованы именно так.

Общая архитектура проекта

Архитектурно проект разделён на два слоя: Управляющий слой — получение данных, расчёт сигналов, фильтры, Telegram‑интерфейс, логика пользователей и принятие решений. Исполнительный слой — работа с торговым API BingX: подпись запросов, установка плеча, открытие ордеров, трейлинг.

Ключевая идея — вся торговая логика должна быть независима от конкретной биржи. В main.py мы оперируем функции вроде place_market_order(), а не HTTP‑запросами. Клиент для биржи я написал самостоятельно и использую его во всех своих проектах и статьях. Ссылка на файла проекта, включая этот клиент: github

Хранение пользователей и состояния

Бот поддерживает одновременную работу с большим количеством пользователей. Для этого используется простая, но надёжная модель хранения состояния в JSON:

<code>USERS_FILE = Path("users.json")


def load_users():
if USERS_FILE.exists():
return json.loads(USERS_FILE.read_text())
return {}


def save_users(users):
USERS_FILE.write_text(json.dumps(users, indent=4))


users = load_users()</code>

Каждый пользователь — это словарь с настройками торговли, фильтрами, чёрным списком и служебными метаданными. Такой подход позволяет легко сериализовать состояние, не зависеть от БД, безопасно переживать перезапуск процесса.

Вспомогательные функции и утилиты

Процентные изменения

<code>def pct(now, past):
  if past == 0:
  return 0.0
  return (now - past) / past * 100.0</code>

Эта функция используется повсеместно: и для цены, и для OI. Явное вынесение её в утилиту снижает вероятность логических ошибок и делает расчёты единообразными.

Отправка сообщений в Telegram

<code>def send_alert(chat_id, text):
    try:
        bot.send_message(
            chat_id=chat_id,
            text=text,
            parse_mode="HTML"
        )
    except Exception as e:
        print(f"Telegram error {chat_id}: {e}")</code>

Работа с Binance Futures API

Данные рынка получаются исключительно через публичные эндпоинты Binance Futures:

<code>def binance_get(endpoint, params=None):
  url = BINANCE_FAPI_URL + endpoint
  r = requests.get(url, params=params, timeout=REQUEST_TIMEOUT)
  r.raise_for_status()
  return r.json()</code>

Поверх неё реализованы функции, работающие с апи. Например:

<code>def get_symbols():
  data = binance_get("/fapi/v1/exchangeInfo")
  return [
  s["symbol"]
  for s in data["symbols"]
  if s["contractType"] == "PERPETUAL"
  and s["quoteAsset"] == "USDT"
  and s["status"] == "TRADING"
  ]</code>

Здесь мы получаем все фьючерсные символы, которые торгуются к валюте USDT. Это позволят перебирать только релевантные активы.

Логика OI‑сигнала

Основная аналитика сосредоточена в функции check_symbol.

<code>oi_4h = get_oi_hist(symbol, 48)
oi_24h = get_oi_hist(symbol, 288)</code>

48 и 288 точек соответствуют 4 и 24 часам при таймфрейме 5 минут. Это делает расчёт полностью детерминированным.

Далее извлекаются крайние значения и рассчитываются проценты:

<code>oi_now = float(oi_4h[-1]["sumOpenInterestValue"])
oi_4h_ago = float(oi_4h[0]["sumOpenInterestValue"])
oi_growth_4h = pct(oi_now, oi_4h_ago)</code>

Сигнал формируется не только по росту OI, но и по соотношению с ценой с помощью коэффициента PRICE_OI_RATIO = 0,5:

<code>signal_4h = (
oi_growth_4h >= OI_4H_THRESHOLD and
price_growth_4h <= oi_growth_4h * PRICE_OI_RATIO
)</code>

Это принципиально: стратегия ищет накопление, а не импульс.

Анти‑спам и защита от переоткрытия

Каждый пользователь имеет словарь last_signal_time:

<code>last_signals = user_data.get("last_signal_time", {})
if symbol in last_signals:
  if datetime.utcnow() - datetime.fromisoformat(last_signals[symbol]) < timedelta(hours=SIGNAL_COOLDOWN_HOURS):
    continue</code>

Этот механизм предотвращает повторные входы по одному инструменту и делает автотрейдинг более контролируемым.

Фильтры: объём и ликвидность

Минимальный OI

<code>if oi_now < MIN_OI_USDT:
  return</code>

Этот фильтр встроен в код. В целом, его основная цель — отсеять неверные данные и совсем низколиквидные монетки, которые иногда могут затесаться на binance.

Фильтр объёма

<code>def check_volume_filter(symbol, multiplier):
  klines = get_klines(symbol, Vol_period)
  volumes = [float(k[5]) for k in klines[:-1]]
  avg_volume = sum(volumes) / len(volumes)
  current_volume = float(klines[-1][5])
  return current_volume >= avg_volume * multiplier</code>

Этот фильтр позволяет торговать только в моменты повышенной активности рынка. На практике при тестировании он действительно кратко уменьшил количество сделок и повысил винрейт в любых рыночных условиях. Его отключение уводило стратегию в убыток, так что такой фильтр отлично себя показывает в данной ТС. Каждый пользователь может поставить персональный мультипликатор (multiplier) в настройках.

Чёрный список (blacklist)

Каждый пользователь может управлять списком запрещённых инструментов с помощью команд /blacklist_add и /blacklist_remove, а также смотреть символы в /blacklist_show.

<code>def blacklist_add(update: Update, context):
    chat_id = str(update.effective_chat.id)

    if not context.args:
        update.message.reply_text("Использование: /blacklist_add BTCUSDT")
        return

    symbol = context.args[0].upper()

    users[chat_id].setdefault("blacklist", [])
    if symbol not in users[chat_id]["blacklist"]:
        users[chat_id]["blacklist"].append(symbol)
        save_users(users)

    update.message.reply_text(f"⛔ {symbol} добавлен в чёрный список")

def blacklist_remove(update: Update, context):
    chat_id = str(update.effective_chat.id)

    if not context.args:
        update.message.reply_text("Использование: /blacklist_remove BTCUSDT")
        return

    symbol = context.args[0].upper()

    if symbol in users[chat_id].get("blacklist", []):
        users[chat_id]["blacklist"].remove(symbol)
        save_users(users)

    update.message.reply_text(f"✅ {symbol} удалён из чёрного списка")

def blacklist_show(update: Update, context):
    chat_id = str(update.effective_chat.id)
    blacklist = users.get(chat_id, {}).get("blacklist", [])

    if not blacklist:
        update.message.reply_text("📭 Чёрный список пуст")
        return

    text = "<b>⛔ Чёрный список:</b>\n\n"
    text += "\n".join(f"• {s}" for s in sorted(blacklist))

    update.message.reply_text(text, parse_mode="HTML")
</code>

Проверка перед сделкой:

<code>if symbol in user_data.get("blacklist", []):
  continue</code>

Это важнейший элемент риск‑менеджмента при реальной торговле.

Переход от сигнала к сделке

<code>qty = (margin_usdt * leverage) / price_now
stop_price = price_now * (1 - stop_loss_pct / 100)
tp_price = price_now * (1 + take_profit_pct / 100)</code>

Все параметры сделки вычисляются исходя из настроек каждого юзера

Открытие сделки делаем с помощью функции bingx_client. Все сделки будут в лонг, так как OI лонг сигналы работают лучше шортовых. Так что пераметр long передаём явно, остальные вычисляли ранее (s — символ на бирже bingx):

<code>resp = bx.place_market_order('long', qty, s, stop_price, tp_price)</code>

Telegram‑интерфейс и кнопки

Меню настроек реализовано через inline‑кнопки:

<code>keyboard = [
[InlineKeyboardButton("Торговля", callback_data='toggle_trading')],
[InlineKeyboardButton("Плечо", callback_data='set_leverage')]
]</code>

Каждая кнопка обрабатывается единым обработчиком:

<code>def button_handler(update, context):
  data = query.data
  if data == 'toggle_trading':
  users[chat_id]['trading_enabled'] = not users[chat_id]['trading_enabled']</code>

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

Универсальная обработка пользовательского ввода

<code>def set_value(update, context, key, type_func=str):
  value = type_func(update.message.text.strip())
  users[chat_id][key] = value
  save_users(users)</code>

Благодаря этому добавление нового параметра требует минимального количества кода.

Основной цикл работы

<code>for symbol in symbols:
  check_symbol(symbol)
  time.sleep(0.15)</code>

Функция check_symbols выполняет абсолютно всю логику — проверка OI, применение фильтров, проверка на blacklist и открытие сделок при наличии сигнала. Таким образом, мы перебираем все binance символы и открываем сделки на бирже с помощью готового клиента. Я выбрал биржей для открытия bingX из-за комиссий, тем более что binance в России сейчас имеет проблемы.

Заключение

Надеюсь, что у меня получилось объяснить основную логику кода и функций. Всё что вам нужно, чтобы запустить этот проект — получить токен в @botfather и вставить его в переменную BOT_TOKEN. Напоминаю, что весь код я выложил на github.
Данный бот показывает среднюю прибыль в течение 2 недель около 2% в среднем. Были как удачные дни, так и не очень удачные, в которые амплитуда депозита могла быть и более 5% в обе стороны. Так что используйте бота осторожно. Он имеет встроенную функцию работы с testnet, обязательно тестируйте свои настройки сначала там! Я имею следующие настройки сейчас:

OI‑сигналы и автоматический трейдинг: пишем трейдинг телеграм бота с фильтрами

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

Данная публикация является личным мнением автора. Мнение владельца сайта может не совпадать с мнением автора.
798 | ★1

Читайте на SMART-LAB:
Фото
💡 Если ставка не спешит вниз
Ключевая ставка остаётся на уровне 14% с июля. По прогнозу аналитиков ВТБ Мои Инвестиции, к концу 2027 года она может снизиться до 12,25%....
Фото
Раскрытие отчетности за девять месяцев и эфир с топ-менеджментом: save the date 📅
Совсем скоро ноябрь, а значит, наступает время подвести итоги работы Positive Technologies за третий квартал и девять месяцев. Конечно, самый...
Фото
Напоминание для акционеров
Уже завтра представим финансовые результаты за 9 месяцев 2026 года, а в 11:00 проведём звонок с аналитиками. Ссылку прикрепим позже. ✅ 3...
Фото
Банковский сектор сентябрь 2026 г. - рекордная прибыль, но рост все менее равномерный
Банки отчитались за сентябрь. Что интересного мы узнали?  За 12 месяцев прибыль сектора составляет уже 4,5 трлн руб. при прогнозе ЦБ 3,9–4,4...

теги блога Roman crypto_maniac

....все тэги



UPDONW
Новый дизайн