Блог им. RomellaAkumov

Полностью автоматизируем трейдинг по аукционной теории — от базы до python робота

Полностью автоматизируем трейдинг по аукционной теории — от базы до python робота.

В классическом алготрейдинге рынок часто моделируется как временной ряд: индикаторы, скользящие средние, осцилляторы. Аукционная теория рассматривает рынок иначе — как процесс распределения объёма по ценовым уровням, где цена ищет баланс между спросом и предложением.

Ключевым элементом такого подхода является Volume Profile, а именно Point of Control (POC) — уровень цены, на котором за выбранный период был проторгован максимальный объём. В терминах аукционной теории POC соответствует зоне максимального согласия участников рынка.

В статье рассматривается создание алгоритмического торгового бота, основанного на реакции цены относительно:

  • POC

  • Value Area High (VAH)

  • Value Area Low (VAL)

В качестве основы используется Python‑скрипт back.py, предназначенный для параметрического бэктеста стратегии.

Все скрипты из статьи я выложил на github для вашего удобства.

Архитектура backtest‑скрипта

Скрипт логически разделён на несколько уровней:

  1. Загрузка и подготовка рыночных данных (Binance Futures)

  2. Расчёт Volume Profile и POC

  3. Генерация торговых сигналов

  4. Рыночная структура (swing‑экстремумы)

  5. Управление риском (SL / TP)

  6. Симуляция сделок

  7. Анализ результатов

Такое разделение важно: в дальнейшем те же блоки будут переиспользованы в real‑time боте практически без изменений.

Расчёт Volume Profile и POC

Ключевая функция стратегии — 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>

Что происходит:

  1. Берётся диапазон цен за весь доступный период

  2. Добавляется буфер, чтобы исключить краевые искажения

  3. Диапазон разбивается на фиксированное число ценовых бинов

Далее каждая свеча распределяет свой объём по пересекаемым ценовым уровням:

<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>

Таким образом формируется реальный объёмный профиль

Расчёт POC и Value Area

<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 происходит без участия объёма.

Рыночная структура: swing-экстремумы

Для управления сделкой используется структура рынка.

Internal swings

<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 — первая зона, в которой можно получить реакцию

  • ближних целей

External swings

<code>def detect_external_swings(df, win=20):
  if low[i] == low[L:R].min():
  swing_low[i] = True</code>

Используются как цели ликвидности и структурные стоп-лоссы

Stop Loss: приоритет структуры

<code>def calculate_sl(df, entry_idx, entry_price, direction):
  if direction == 'long':
    return last_swing_low</code>

Логика:

  1. SL ставится за ближайший структурный экстремум

  2. Если экстремума нет — fallback на ATR

Это принципиально отличает стратегию от индикаторных систем.

Take Profit: ATR, Liquidity, FTA

<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

Метрики считаются по сделкам, а не по свечам.

Почему этот подход работает

  1. Используется реальное распределение объёма

  2. Входы строятся от логики аукциона, а не индикаторов

  3. Риск контролируется структурой рынка

  4. Бэктест максимально приближен к реальному исполнению

Запустив бектест, мы получим ряд данных. Для примера:

<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 реализации — одна:

корректно и без искажений перенести готовую стратегию в живой рынок.

Общая архитектура real-time бота

Ключевой принцип — жёсткое разделение ответственности:

Binance (данные) → Strategy Engine → BingX (исполнение)

  • Binance используется исключительно как источник свечей

  • BingX — только как торговая площадка, так как имеет меньшие комисии

Это позволяет избежать логических расхождений и упрощает отладку.

Execution layer: BingxClient

Для грамотной работы нам необходим 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

  • нет логики сопровождения

Позиция живёт на стороне биржи.

Ключевые проблемы real-time и как они решены

1. Drift между backtest и live

  • одинаковый код

  • одинаковый порядок расчётов

2. Рассинхронизация свечей

  • только закрытые бары

3. API latency

  • threading

4. Ошибка исполнения

  • торговая логика изолирована от AP

Вывод

Этот real-time бот — не отдельная система, а прямое продолжение бэктест-движка.

Если стратегия работает в истории, она будет вести себя так же и в реальном рынке — с учётом комиссии, проскальзывания и latency.

Именно это и является основной задачей real-time реализации.

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

795 | ★2
10 комментариев
Комментировать ИИ тексты и коды не буду — это бесполезно.

Многим «платформам» с ГитХаб скоро придет трындец. Он уже приходит. Проще с нуля через ИИ написать готовое с коннекторами, бэктестерами и блэк-джеками, чем изучать чужое поделие и изделие.
avatar
Просто трейдер, мне кажется от обилия ии у вас уже просто развилась необоснованная паранойя
avatar
ИИ? И?
avatar
Раз уж Вы себя позиционируете как крипто-маньяка. Такой вопрос. А Вы собственную крипто биржу не пытались написать, хотя бы маленькую?
avatar

Synthetic, приветствую! Во-первых это, само собой, просто никнейм, не стоит это так вопспринимать).

И всё же я являюсь одним из действующих разработчиков алгоритмов для биржи, работал с ребятами с нуля. Сейчас биржа находится на пре-релиз стадии и должна выйти в январе — вероятно, начале.

Так что опыт имеется) Но писать в одного биржу, без полноценной команды, это нерационально и бессмысленно — силы не окупятся!

avatar
Roman crypto_maniac, 
Но писать в одного биржу, без полноценной команды, это нерационально и бессмысленно — силы не окупятся!
Речь не о том, чтоб окупилось. А о том, чтоб понимать, что на бирже в самом деле происходит. Внутри этой самой matching engine.
avatar

Synthetic, ну, как я уже сказал, я имею исчерпывающее для моей работы представление об основных механизмах работы биржевых протоколов и самой биржи.
А тратить свои силы в одного на изучение всего этого «изнутри» по-моему, извините, но не иначе как глупость и смысла не имеет!

Лучше потратить своё время на разработку более используемых практических алгоритмов. Ведь цель любого трейдера — заработать в первую очередь

avatar
Roman crypto_maniac, 
ну, как я уже сказал, я имею исчерпывающее для моей работы представление об основных механизмах работы биржевых протоколов и самой биржи.
По Вашим публикациям этого как-то незаметно.
Более того, наблюдается некоторый когнитивный диссонанс.
С одной стороны:
Ведь цель любого трейдера — заработать в первую очередь
А с другой:
Лучше потратить своё время на разработку более используемых практических алгоритмов
Ваши «более используемые практические алгоритмы» — это банальный «теханализ», который, как правило, не позволяет никому заработать, но сам по себе хорошо продается, потому как излагается словами и выражениями, «понятными» даже полуграмотным людям. 
avatar

Synthetic, более используемых и само собой прибыльных!) 

Не надо так интерпретировать слова! Ведь я буквально сказал о практических алгоритмах. Т.е. применимых на практике. Убыточные стратегии на практике не применимы.

Ну и тот факт, что потратить своё время и написать качественный ботов с торговой логикой, вывести 1-2 штуки в профит и просто заниматься их менеджментом — явно более выгодно, чем заниматься «изучением и копанием» — я думаю неоспорим

avatar

Synthetic, Ну и насчёт «по вашим публикациям незаметно» мне конечно смешно. Вы, вероятно, очередной дилетант. Обратите внимание, что говоря о бесполезности ваших доводов про биржу, я ничуть не говорил о бесполезности ваших мыслей и конкретно вашей некомпентентности/глупости.

Если для вас экспертность в публикациях, то жду хотя бы пару-тройку адекватно прибыльных ботов, код которых вы полностью опубликуете!) Только дурак на такое согласится, и я думаю мы оба с вами это понимаем. 

avatar

Читайте на SMART-LAB:
Реальные доходы: новый выпуск «Лампы Трампа» с Элвисом Марламовым
Рынки в дисбалансе: рубль держится, а золото, палладий и алюминий становятся звездами инвестиций. Долговые обязательства компаний, перспективы...
Фото
Как с умом воспользоваться нашей скидкой?
Сейчас мы сохраняем возможность обучаться по сниженной цене, понимаем текущую экономическую ситуацию. В ближайшее время стоимость обучения...
Фото
NZD/CAD: цены испытывают давление под натиском продавцов?
Котировки кросс-курса NZD/CAD оттолкнулись от нисходящей трендовой линии, попутно сформировав свечную модель «медвежье поглощение». Судя по всему,...
Фото
Потенциальные инвест идеи 2026 и РИСКИ их исполнения
Традиционный ежегодный пост в начале года. Прогнозы, планы и мысли на будущее 25 год был достаточно сложным годом для российского инвестора —...

теги блога Roman crypto_maniac

....все тэги



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