Блог им. RomellaAkumov
Цель этой статьи — максимально подробно и практично разобрать реальный 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:
<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. Это позволят перебирать только релевантные активы.
Основная аналитика сосредоточена в функции 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>Этот механизм предотвращает повторные входы по одному инструменту и делает автотрейдинг более контролируемым.
<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_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>Меню настроек реализовано через 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, обязательно тестируйте свои настройки сначала там! Я имею следующие настройки сейчас:

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