Блог им. RomellaAkumov
В классическом алготрейдинге рынок часто моделируется как временной ряд: индикаторы, скользящие средние, осцилляторы. Аукционная теория рассматривает рынок иначе — как процесс распределения объёма по ценовым уровням, где цена ищет баланс между спросом и предложением.
Ключевым элементом такого подхода является Volume Profile, а именно Point of Control (POC) — уровень цены, на котором за выбранный период был проторгован максимальный объём. В терминах аукционной теории POC соответствует зоне максимального согласия участников рынка.
В статье рассматривается создание алгоритмического торгового бота, основанного на реакции цены относительно:
POC
Value Area High (VAH)
Value Area Low (VAL)
В качестве основы используется Python‑скрипт back.py, предназначенный для параметрического бэктеста стратегии.
Все скрипты из статьи я выложил на github для вашего удобства.
Скрипт логически разделён на несколько уровней:
Загрузка и подготовка рыночных данных (Binance Futures)
Расчёт Volume Profile и POC
Генерация торговых сигналов
Рыночная структура (swing‑экстремумы)
Управление риском (SL / TP)
Симуляция сделок
Анализ результатов
Такое разделение важно: в дальнейшем те же блоки будут переиспользованы в real‑time боте практически без изменений.
Ключевая функция стратегии — calculate_volume_profile.
<code>def calculate_volume_profile(df, bins=100, buffer_ratio=0.05): price_min = df['low'].min() price_max = df['high'].max() buffer = (price_max - price_min) * buffer_ratio price_min -= buffer price_max += buffer price_bins = np.linspace(price_min, price_max, bins) bin_centers = (price_bins[:-1] + price_bins[1:]) / 2 volume_profile = np.zeros(bins - 1)</code>
Берётся диапазон цен за весь доступный период
Добавляется буфер, чтобы исключить краевые искажения
Диапазон разбивается на фиксированное число ценовых бинов
Далее каждая свеча распределяет свой объём по пересекаемым ценовым уровням:
<code>for i in range(len(df)): low = df['low'].iloc[i] high = df['high'].iloc[i] vol = df['volume'].iloc[i] bin_indices = np.digitize([low, high], price_bins) - 1 start_bin = max(0, min(bin_indices)) end_bin = min(bins - 2, max(bin_indices)) bin_vol = vol / (end_bin - start_bin + 1) for b in range(start_bin, end_bin + 1): volume_profile[b] += bin_vol</code>
Таким образом формируется реальный объёмный профиль
<code>poc_index = np.argmax(volume_profile) poc = bin_centers[poc_index]</code>
Value Area считается классическим способом — через накопление 68% объёма от POC наружу.
Логика входа реализована в generate_signals().
<code>if (prev_close <= va_low * (1 + threshold) and close > va_low): signals.iloc[i] = 'Buy'</code>
цена находилась в зоне дисбаланса ниже VAL
затем вернулась внутрь value area
рынок «принял» цену обратно
Short-сигнал зеркален относительно VAH.
<code>if vol < avg_vol_i * min_volume_ratio: continue</code>
Сигнал игнорируется, если возврат в value происходит без участия объёма.
Для управления сделкой используется структура рынка.
<code>def detect_internal_swings(df): if low[i] < low[i-1] and low[i] < low[i+1]: swing_low[i] = True</code>
Используются для:
First Trouble Area — первая зона, в которой можно получить реакцию
ближних целей
<code>def detect_external_swings(df, win=20): if low[i] == low[L:R].min(): swing_low[i] = True</code>
Используются как цели ликвидности и структурные стоп-лоссы
<code>def calculate_sl(df, entry_idx, entry_price, direction):
if direction == 'long':
return last_swing_low</code>Логика:
SL ставится за ближайший структурный экстремум
Если экстремума нет — fallback на ATR
Это принципиально отличает стратегию от индикаторных систем.
<code>if tp_mode == 'liquidity': tp = tp_liquidity(df, i, direction)</code>
TP может быть:
фиксированным по волатильности
по цели ликвидности
по первой проблемной зоне
Каждая сделка проходит фильтр, чтобы риск-ревард был более 1. В ином случае эта сделка просто не выгодна.
<code>for j in range(i + 1, i + max_bars):
if bar_low <= sl:
exit_price = sl</code>Модель исполнения:
проверка SL → TP
без подглядывания в будущее
одна позиция = одна сделка
Комиссии учитываются с двух сторон.
<code>winrate = len(wins) / trades_cnt * 100 profit_factor = wins.sum() / abs(losses.sum())</code>
Также рассчитываются:
Sharpe Ratio (annualized)
Max Drawdown
Net PnL
Метрики считаются по сделкам, а не по свечам.
Используется реальное распределение объёма
Входы строятся от логики аукциона, а не индикаторов
Риск контролируется структурой рынка
Бэктест максимально приближен к реальному исполнению
Запустив бектест, мы получим ряд данных. Для примера:
<code>Trades: 54, Net PnL (USD): 937.03, Winrate: 57.41%, MaxDD: -3.85 Result: Trades=54, Net PnL=937.03, Winrate=57.41% Trades: 45, Net PnL (USD): 937.38, Winrate: 60.00%, MaxDD: -3.36 Result: Trades=45, Net PnL=937.38, Winrate=60.00% Result: Trades=0, Net PnL=0.00, Winrate=0.00%</code>
Здесь мы можем увидеть, что есть действительно неплохая стратегия с винрейтом в 60%. Она дала нам 937$ прибыли при входе на 0.002 BTC в каждой сделке.
Параметры следующие:
{'bins': 100, 'threshold': 0.003, 'tp_mode': 'liquidity', 'atr_coeff': 2, 'min_tp_pct': 0.003, 'min_volume_ratio': 1.2, 'require_trend_confirmation': False}
В real-time боте будем использовать именно их.
Часть 2. Реализация real-time ботаВ этой части не повторяется логика бэктеста и не объясняются основы стратегии.
Задача real-time реализации — одна:
корректно и без искажений перенести готовую стратегию в живой рынок.
Ключевой принцип — жёсткое разделение ответственности:
Binance (данные) → Strategy Engine → BingX (исполнение)
Binance используется исключительно как источник свечей
BingX — только как торговая площадка, так как имеет меньшие комисии
Это позволяет избежать логических расхождений и упрощает отладку.
Для грамотной работы нам необходим bingX client — для удобства я написал SDK библиотеку со всеми функциями для этой биржи. Это позволить не переписывать сложные функции с подписями и запросами для каждой стратегии, а использовать один скрипт. Он вместе со стратегией хранится на github.
Инициализируем библиотеку:
<code>bingx_client = BingxClient( api_key=API_KEY, api_secret=API_SECRET, symbol="BTCUSDT" )</code>
Сделки будем открывать с помощью функции:
<code>def place_market_order(self, side: str, qty: float, symbol: str = None, stop: float = None, tp: float = None):</code>
<code>df = fetch_klines_paged( SYMBOL, INTERVAL, total_bars=2000, client=binance_client )</code>
Особенности:
подгружается достаточно длинная история для корректного Volume Profile
данные каждый цикл пересобираются заново
используются только закрытые свечи
Это дороже по API, но гарантирует идентичность логики с бэктестом.
<code>df = detect_internal_swings(df) df = detect_external_swings(df) df = calculate_atr(df) df = generate_signals(df, params)</code>
Важно:
порядок вызовов полностью совпадает с бэктестом
никаких оптимизаций или «ускорений» не используется
Любое отклонение здесь приводит к несовпадению сигналов.
if df['long_signal'].iloc[-2]:
Бот:
не реагирует на текущую формирующуюся свечу
не пересчитывает POC intra-bar
Это сознательный компромисс:
меньше сделок
но полное соответствие backtest → live
<code>def open_order_bingx(direction, qty, entry_idx, df): entry_price = float(df.iloc[entry_idx]['close']) sl = calculate_sl(...) tp = tp_liquidity(...) or tp_fta(...)</code>
Все параметры сделки:
entry
stop-loss
take-profit
рассчитываются до отправки ордера.
Биржа не принимает решений — она только исполняет.
<code>threading.Thread( target=open_order_bingx, args=(direction, QTY, entry_idx, df) ).start()</code>
Это решает проблему прерывания основного цикла, это не является чем-то основным, но всё же делает логику безопаснее.
<code>while True: run_live_bot(params) time.sleep(60)</code>
Минимализм цикла — осознанный выбор:
нет хранения позиций
нет локального state
нет логики сопровождения
Позиция живёт на стороне биржи.
1. Drift между backtest и live
одинаковый код
одинаковый порядок расчётов
2. Рассинхронизация свечей
только закрытые бары
3. API latency
threading
4. Ошибка исполнения
торговая логика изолирована от AP
Этот real-time бот — не отдельная система, а прямое продолжение бэктест-движка.
Если стратегия работает в истории, она будет вести себя так же и в реальном рынке — с учётом комиссии, проскальзывания и latency.
Именно это и является основной задачей real-time реализации.
Бот способен на дистанции действительно принести иксы, что является отличным результатом. Исходя из бектеста можем сказать, что частота сделать — чуть меньше 1 сделки в день с довольно долгим периодом удержания. Так что этот скрипт — действительно качественный и консервативный свинг — робот для крипторынка.
Многим «платформам» с ГитХаб скоро придет трындец. Он уже приходит. Проще с нуля через ИИ написать готовое с коннекторами, бэктестерами и блэк-джеками, чем изучать чужое поделие и изделие.
Synthetic, приветствую! Во-первых это, само собой, просто никнейм, не стоит это так вопспринимать).
И всё же я являюсь одним из действующих разработчиков алгоритмов для биржи, работал с ребятами с нуля. Сейчас биржа находится на пре-релиз стадии и должна выйти в январе — вероятно, начале.
Так что опыт имеется) Но писать в одного биржу, без полноценной команды, это нерационально и бессмысленно — силы не окупятся!
Synthetic, ну, как я уже сказал, я имею исчерпывающее для моей работы представление об основных механизмах работы биржевых протоколов и самой биржи.
А тратить свои силы в одного на изучение всего этого «изнутри» по-моему, извините, но не иначе как глупость и смысла не имеет!
Лучше потратить своё время на разработку более используемых практических алгоритмов. Ведь цель любого трейдера — заработать в первую очередь
Более того, наблюдается некоторый когнитивный диссонанс.
С одной стороны:А с другой:Ваши «более используемые практические алгоритмы» — это банальный «теханализ», который, как правило, не позволяет никому заработать, но сам по себе хорошо продается, потому как излагается словами и выражениями, «понятными» даже полуграмотным людям.
Synthetic, более используемых и само собой прибыльных!)
Не надо так интерпретировать слова! Ведь я буквально сказал о практических алгоритмах. Т.е. применимых на практике. Убыточные стратегии на практике не применимы.
Ну и тот факт, что потратить своё время и написать качественный ботов с торговой логикой, вывести 1-2 штуки в профит и просто заниматься их менеджментом — явно более выгодно, чем заниматься «изучением и копанием» — я думаю неоспорим
Synthetic, Ну и насчёт «по вашим публикациям незаметно» мне конечно смешно. Вы, вероятно, очередной дилетант. Обратите внимание, что говоря о бесполезности ваших доводов про биржу, я ничуть не говорил о бесполезности ваших мыслей и конкретно вашей некомпентентности/глупости.
Если для вас экспертность в публикациях, то жду хотя бы пару-тройку адекватно прибыльных ботов, код которых вы полностью опубликуете!) Только дурак на такое согласится, и я думаю мы оба с вами это понимаем.