Цель этой статьи — максимально подробно и практично разобрать реальный Python‑проект автоматического трейдинга. Это не концепт, а рабочий бот, который: непрерывно анализирует рынок Binance Futures, ищет сигналы по открытому интересу (Open Interest), применяет набор защитных фильтров, работает с множеством пользователей одновременно, управляется через Telegram‑интерфейс и при необходимости открывает реальные сделки через API биржи BingX.
Далее я последовательно разберу всю логику и все функции основного файла main.py, объясняя, как и зачем они реализованы именно так.
Общая архитектура проекта
Архитектурно проект разделён на два слоя:
Управляющий слой (
main.py) — получение данных, расчёт сигналов, фильтры, Telegram‑интерфейс, логика пользователей и принятие решений.Исполнительный слой (
bingx_client.py) — работа с торговым API BingX: подпись запросов, установка плеча, открытие ордеров, трейлинг.
Ключевая идея — вся торговая логика должна быть независима от конкретной биржи. В main.py мы оперируем функции вроде place_market_order(), а не HTTP‑запросами. Клиент для биржи я написал самостоятельно и использую его во всех своих проектах и статьях. Ссылка на файла проекта, включая этот клиент: github
Хранение пользователей и состояния
Бот поддерживает одновременную работу с большим количеством пользователей. Для этого используется простая, но надёжная модель хранения состояния в JSON:
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()
Каждый пользователь — это словарь с настройками торговли, фильтрами, чёрным списком и служебными метаданными. Такой подход позволяет легко сериализовать состояние, не зависеть от БД, безопасно переживать перезапуск процесса.
Вспомогательные функции и утилиты
Процентные изменения
def pct(now, past):
if past == 0:
return 0.0
return (now - past) / past * 100.0
Эта функция используется повсеместно: и для цены, и для OI. Явное вынесение её в утилиту снижает вероятность логических ошибок и делает расчёты единообразными.
Отправка сообщений в Telegram
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}")
Работа с Binance Futures API
Данные рынка получаются исключительно через публичные эндпоинты Binance Futures:
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()
Поверх неё реализованы функции, работающие с апи. Например:
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"
]
Здесь мы получаем все фьючерсные символы, которые торгуются к валюте USDT. Это позволят перебирать только релевантные активы.
Логика OI‑сигнала
Основная аналитика сосредоточена в функции check_symbol.
oi_4h = get_oi_hist(symbol, 48)
oi_24h = get_oi_hist(symbol, 288)
48 и 288 точек соответствуют 4 и 24 часам при таймфрейме 5 минут. Это делает расчёт полностью детерминированным.
Далее извлекаются крайние значения и рассчитываются проценты:
oi_now = float(oi_4h[-1]["sumOpenInterestValue"])
oi_4h_ago = float(oi_4h[0]["sumOpenInterestValue"])
oi_growth_4h = pct(oi_now, oi_4h_ago)
Сигнал формируется не только по росту OI, но и по соотношению с ценой с помощью коэффициента PRICE_OI_RATIO = 0,5:
signal_4h = (
oi_growth_4h >= OI_4H_THRESHOLD and
price_growth_4h <= oi_growth_4h * PRICE_OI_RATIO
)
Это принципиально: стратегия ищет накопление, а не импульс.
Анти‑спам и защита от переоткрытия
Каждый пользователь имеет словарь last_signal_time:
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
Этот механизм предотвращает повторные входы по одному инструменту и делает автотрейдинг более контролируемым.
Фильтры: объём и ликвидность
Минимальный OI
if oi_now < MIN_OI_USDT:
return
Этот фильтр встроен в код. В целом, его основная цель - отсеять неверные данные и совсем низколиквидные монетки, которые иногда могут затесаться на binance.
Фильтр объёма
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
Этот фильтр позволяет торговать только в моменты повышенной активности рынка. На практике при тестировании он действительно кратко уменьшил количество сделок и повысил винрейт в любых рыночных условиях. Его отключение уводило стратегию в убыток, так что такой фильтр отлично себя показывает в данной ТС. Каждый пользователь может поставить персональный мультипликатор (multiplier) в настройках.
Чёрный список (blacklist)
Каждый пользователь может управлять списком запрещённых инструментов с помощью команд /blacklist_add и /blacklist_remove, а также смотреть символы в /blacklist_show.
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")
Проверка перед сделкой:
if symbol in user_data.get("blacklist", []):
continue
Это важнейший элемент риск‑менеджмента при реальной торговле.
Переход от сигнала к сделке
qty = (margin_usdt * leverage) / price_now
stop_price = price_now * (1 - stop_loss_pct / 100)
tp_price = price_now * (1 + take_profit_pct / 100)
Все параметры сделки вычисляются исходя из настроек каждого юзера
Открытие сделки делаем с помощью функции bingx_client. Все сделки будут в лонг, так как OI лонг сигналы работают лучше шортовых. Так что пераметр long передаём явно, остальные вычисляли ранее (s - символ на бирже bingx):
resp = bx.place_market_order('long', qty, s, stop_price, tp_price)
Telegram‑интерфейс и кнопки
Меню настроек реализовано через inline‑кнопки:
keyboard = [
[InlineKeyboardButton("Торговля", callback_data='toggle_trading')],
[InlineKeyboardButton("Плечо", callback_data='set_leverage')]
]
Каждая кнопка обрабатывается единым обработчиком:
def button_handler(update, context):
data = query.data
if data == 'toggle_trading':
users[chat_id]['trading_enabled'] = not users[chat_id]['trading_enabled']
Это позволяет легко работать с масштабирование интерфейса, логика понятная и простая. Также такая логика не перегружает код.
Универсальная обработка пользовательского ввода
def set_value(update, context, key, type_func=str):
value = type_func(update.message.text.strip())
users[chat_id][key] = value
save_users(users)
Благодаря этому добавление нового параметра требует минимального количества кода.
Основной цикл работы
for symbol in symbols:
check_symbol(symbol)
time.sleep(0.15)
Функция check_symbols выполняет абсолютно всю логику - проверка OI, применение фильтров, проверка на blacklist и открытие сделок при наличии сигнала. Таким образом, мы перебираем все binance символы и открываем сделки на бирже с помощью готового клиента. Я выбрал биржей для открытия bingX из-за комиссий, тем более что binance в России сейчас имеет проблемы.
Заключение
Надеюсь, что у меня получилось объяснить основную логику кода и функций. Всё что вам нужно, чтобы запустить этот проект - получить токен в @botfather и вставить его в переменную BOT_TOKEN. Напоминаю, что весь код я выложил на github.
Данный бот показывает среднюю прибыль в течение 2 недель около 2% в среднем. Были как удачные дни, так и не очень удачные, в которые амплитуда депозита могла быть и более 5% в обе стороны. Так что используйте бота осторожно. Он имеет встроенную функцию работы с testnet, обязательно тестируйте свои настройки сначала там! Я имею следующие настройки сейчас:

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

gkaliostro8
03.01.2026 12:23Идея очень хорошая, но после накопления импульс может быть в любую сторону. А что если добавить дивергенцию, и тогда процент профита увеличится

negrbluad Автор
03.01.2026 12:23благодарю за оценку)
Частично вы, безусловно, правы. Но на практике чаще мы видим всё таки, что рост OI сопряженный с ростом цены указывает именно на продолжение движения. Дивергенции бы кратко снизили количество сделок, но на самом деле повысили их качество.
В целом довольно много фильтров можно внедрить, даже те же классические трендовые индикаторы
Ibirseven
Как же у вас всё просто, все данные по пользователю и его настройкам хранятся в одном словаре?) где проверка на существование текущей позиции, чтобы не открывать новую? Что с уровнями ТП и СЛ, устанавливаться сразу при открытии позиции? В общем вопросов куча.
negrbluad Автор
ну так посмотрите код! Специально оставил ссылку.
1. В bingx_client стоп и тейк устанавливаются сразу в позицию. Позиция не откроется если с ними будет что-то не так - просто выдаст ошибку по всей позиции
2.Обратите внимание на словарь last_trade_time - он создаётся по каждому юзеру для позиций. Это очень важный момент, эта проверка не даст открыть новую позицию. Поэтому смысл проверки позиций отпадает
3. Может возникнуть вопрос - а что если позиция по окончании кулдауна открыта? И тогда произойдёт действительно докуп. Но только при формировании НОВОГО сигнала. Такой докуп чаще всего приводит к тейкам, т.е. к повышенной прибыли.