
Динамика капитала на отложенных рыночных данных (период: 2025-03-01 — 2025-06-01), с учётом комиссий и проскальзываний.
Итоговое изменение баланса: +144.23%
Введение
Приветствую вас, уважаемые читатели!
Цель данной статьи — предоставить вам полное техническое руководство по созданию торгового агента (проект), обученного с помощью обучения с подкреплением (Reinforcement Learning), на основе архитектуры Dueling Double Deep Q-Network (D3QN) с использованием Prioritized Experience Replay (PER).
Агент разработан для торговли на фьючерсном рынке Binance Futures. Он принимает решения на основе минутных рыночных данных, включая: open
, high
, low
, close
, volume
, volume_weighted_average
, num_trades
.
Основная цель агента — максимизировать итоговый PnL (прибыль/убыток с учётом комиссий и проскальзывания), в данном проекте ключевым этапом оценки стратегии агента выступает реалистичный бэктест, моделирующий поведение в условиях, максимально приближенных к реальной торговле.
Задачи проекта
Построить интеллектуальную систему, способную:
Самостоятельно принимать прибыльные торговые решения в условиях высокой волатильности;
Эффективно выявлять скрытые закономерности из исторических данных;
Демонстрировать высокую обобщающую способность на ранее не встречавшихся данных.
Также в задачи проекта входит создание:
Гибкой архитектуры, пригодной для развития в сторону более продвинутых подходов (Dreamer, Soft Actor-Critic и др.);
Базовой платформы для тестирования новых гипотез, функций вознаграждения, архитектур нейросетей, буферов и метрик.
Что вы найдёте в статье
Я подготовил исчерпывающее описание всей системы. Все части проекта воспроизводимы. Код, данные и конфигурации доступны в открытом доступе, все ссылки представлены в конце данного раздела и в финальной части статьи.
Раздел |
Содержание |
---|---|
Данные |
Подробное описание логики формирования сигналов, критериев волатильности и структуры обучающих сессий. |
Среда |
Механика торговли, детальный расчёт прибыли и убытков (PnL), описание ограничений и особенностей обработки действий агента. |
Агент |
Архитектура D3QN, буфер Prioritized Experience Replay (PER) и стратегия ε-жадности, позволяющая агенту исследовать рынок и принимать оптимальные решения. |
Обучение |
Логика тренировочного цикла, методы валидации, системы логирования и сохранения обученной модели. |
Тестирование |
Финальные метрики, реальные примеры торговых сессий, подробная визуализация поведения агента. |
Бэктест |
Реалистичная симуляция торговли: комиссии, проскальзывания, риск-менеджмент, интеллектуальный выбор действий и независимая оценка стратегии. |
Архитектура проекта |
Модульная структура, взаимосвязи компонентов и принципы их взаимодействия. |
Выводы |
Обзор ограничений текущей системы, перспективы дальнейшего развития и план по переходу к архитектурам следующего поколения (от DQN до Model-Based RL, SAC и др.). |
Об авторе
Меня зовут Юрий. Я «Senior Quantitative Researcher» с более чем десятилетним опытом в области машинного обучения и обучения с подкреплением (RL). Основная специализация — проектирование интерпретируемых, масштабируемых и воспроизводимых систем, применяемых на реальных финансовых рынках.
Мотивация
На большинстве доступных ресурсов тема RL в трейдинге либо чрезмерно упрощена, либо лишена инженерной строгости, либо не адаптирована к реалиям рынка. Основная мотивация — восполнить этот пробел, продемонстрировав, как построить работающую RL-систему:
на реальных рыночных данных;
с полным контролем качества и рисков;
с высокой воспроизводимостью.
Данный проект создан как реальная исследовательская лаборатория, способная:
генерировать и проверять гипотезы;
выявлять слабые места в RL для трейдинга;
сравнивать SOTA-агентов на реальных данных с честным baseline.
Надеюсь, этот проект пробудит в вас исследовательский интерес и станет вашим надёжным фундаментом для изучения возможностей и ограничений использования RL в алгоритмическом трейдинге.
Визуализация и контроль
Для глубокого понимания и эффективного мониторинга работы агента в проект встроены модули визуализации. Вы сможете отслеживать:
Динамику кривой награды и функции потерь, отражающие прогресс обучения агента;
Изменения показателя Win Rate, демонстрирующие долю прибыльных сделок;
Распределение PnL по тестовым сессиям, отражающее общую картину эффективности;
Подробную визуализацию действий агента, как в наиболее успешных, так и в наименее удачных торговых сессиях.
Исследовательский подход
Чтобы адекватно оценить производительность RL агента, я внедрил независимую baseline-модель — CNN классификатор, обученный в рамках классического обучения с учителем.
Данная модель использует те же тестовые данные, одинаковые метрики (mean PnL, win rate) и оформлена в отдельный модуль со своей конфигурацией.
Baseline-модель даёт нам отправную точку для адекватной оценки RL-агента.
Каждый эксперимент — это изолированная единица, определяемая конфигурацией:
Все гиперпараметры, пути и настройки определяются через
configs/*.py
;При запуске автоматически создаётся директория по имени эксперимента;
Внутри — логи, модели и графики: всё хранится по папкам, с учётом продакшен-подхода, применяемого в исследованиях.
Это избавляет от ручного контроля и делает проект масштабируемым и повторяемым.
Бонус
В конце статьи вас ждёт дополнительный бонус — возможность наблюдать за работой обученного ИИ-агента на Binance Futures в реальном времени.
? Код, данные:
Исходный код: github.com/YuriyKolesnikov/rl-trading-binance
Датасеты: huggingface.co/datasets/ResearchRL/open-rl-trading-binance-dataset
2. Описание и подготовка данных
Общая логика формирования обучающего датасета
Ключевая идея заключается в том, чтобы обучать агента только на тех участках рыночной истории, где возникла значимая волатильность, потенциально создающая благоприятные условия для краткосрочной торговли.
Вместо непрерывного сэмплирования рынка, как это часто делается в обобщённых RL-реализациях, здесь отбираются локальные торговые сессии, привязанные к рыночным аномалиям.
2.1 Обоснование
Минутные котировки демонстрируют нестационарное, шумовое поведение. В таких условиях эффективное обучение возможно только на ярко выраженных переходах — импульсах, определяющих ключевые моменты перераспределения ликвидности. Именно поэтому обучение проводится не на случайных точках, а на мощных волатильных сигналах, которые:
сопровождаются быстрым движением цены более чем на 5% в течение короткого временного интервала;
возникают на фоне предшествующей рыночной инерции (стабильность до сигнала);
потенциально вызывают сильное продолжение импульса или резкий откат.
2.2 Логика извлечения торговых сессий
Каждая торговая сессия строится вокруг скользящего окна длиной 10 минут, где выполняется условие:
Далее применяются фильтры:
Исключаются сигналы, окружённые шумом. Предшествующие 90 минут анализируются на отсутствие сильных движений:
Где — коэффициент контрастности (в данной реализации равен 5.0).
Если сигнал проходит фильтр, формируется обучающее окно:
Секция |
Длина |
Назначение |
---|---|---|
Пре-сигнал |
90 мин |
Входные признаки для агента |
Пост-сигнал |
60 мин |
Торговая сессия |
Общее окно |
150 мин |
Единица обучающей выборки |
2.3 Структура датасета
Каждый элемент датасета представляет собой:
np.ndarray
формы(150, 7)
— 150 минут × 7 каналов;Каналы:
open
,high
,volume_weighted_average
,low
,close
,volume
,num_trades
;Уникальный ключ:
(TICKER, datetime)
.
Название |
Кол-во примеров |
Период |
Назначение |
---|---|---|---|
Train |
24 104 |
[2020-01-14 — 2024-08-31) |
Обучение |
Validation |
1377 |
[2024-09-01 — 2024-12-01) |
Оценка модели |
Test |
3400 |
[2024-12-01 — 2025-03-01) |
Финальный тест |
Backtest |
3186 |
[2025-03-01 — 2025-06-01) |
Реалистичная эмуляция |
2.4 Примеры визуализаций
Для повышения прозрачности процесса реализована генерация графиков сигналов из всех данных.
Вертикальная линия на 90-й минуте указывает момент начала торговой сессии.
Визуализация строится по каналу
close
.Заголовок графика содержит:
Ticker Name
,datetime
(точное время сигнала в UTC)
Графики формируются для всех четырех подвыборок: Train, Validation, Test, Backtest.




2.5 Механика обработки и нормализации данных
Формирование корректного состояния на каждом шаге взаимодействия агента с рынком требует строгой обработки данных, включая:
фильтрацию и сортировку каналов;
преобразование в относительные значения;
нормализацию;
логарифмические преобразования;
защиту от не числовых и нестабильных значений.
Все ключевые функции реализованы в модуле utils.py
и являются частью единого data pipeline:
Функция |
Описание |
---|---|
|
Загружает датасет из |
|
Выбирает подмножество каналов, например |
|
Вычисляет среднее и стандартное отклонение по каналам |
|
Формирует финальную версию состояния для обучения агента |
Вывод
Агент обучается не на всём рынке, а на отобранных высоковолатильных сессиях;
Структура данных воспроизводима и управляется конфигурацией;
Все этапы подготовки данных изолированы в модуле
utils
, обеспечивая чистую архитектуру и контроль качества входных данных.
3. Постановка задачи
Алгоритмическая торговля требует от агента принятия решений в условиях частичной информации, транзакционных издержек и изменчивой рыночной динамики.
В рамках данного проекта задача формализуется как эпизодическая задача обучения с подкреплением (RL), где агент учится оптимальной политике управления позицией в течение фиксированного временного интервала после появления сигнала высокой волатильности.
3.1 Формализация: RL как торговый процесс
Каждый эпизод начинается в момент возникновения сигнала высокой волатильности (см. раздел 2). Агенту предоставляется:
90 минут рыночной истории — в качестве входных признаков;
60 шагов торговой сессии — окно принятия решений (по одной минуте на шаг).
На каждом шаге выполняется следующий цикл взаимодействия агента со средой:
Агент получает текущее наблюдение
;
На основе состояния
прогнозирует оптимальное дискретное действие
;
-
Среда возвращает:
новое состояние
,
скалярную награду
,
флаг завершения эпизода;
Переход
сохраняется в буфер опыта;
При достаточном заполнении буфера агент обновляет параметры
-функции.
Формально цикл описывается следующей схемой:
3.2 Возможные действия агента
Код |
Действие |
Описание |
---|---|---|
0 |
Hold / Wait |
Ничего не делать |
1 |
Open Long |
Открыть длинную позицию |
2 |
Open Short |
Открыть короткую позицию |
3 |
Close Position |
Закрыть позицию |
Ограничения:
Нельзя открыть новую позицию, если активна текущая;
На последнем шаге сессии позиция закрывается принудительно.
Состояние:
Наблюдение включает:
Нормализованные рыночные данные за последние N минут;
Экстра-фичи: позиция, нереализованный PnL, прошедшее/оставшееся время;
One-hot кодированная история последних действий.
# Пример формирования состояния в среде:
extras = [position, unrealized_pnl, time_elapsed, time_remaining]
state = np.concatenate([normalized.flatten(), extras, action_history_onehot])
Награда:
Формируется по формуле:
reward = (pnl_change / initial_balance) - inaction_penalty
Где:
pnl_change
— реализованный доход при закрытии позиции;inaction_penalty
— штраф за бездействие.
Альтернативная математическая запись:
где ( = 0.001 ) — штраф за бездействие вне позиции.
3.3 Описание среды: step()
Функция step()
в среде TradingEnvironment
содержит всю необходимую логику, включая:
открытие / закрытие позиций,
расчёт прибыли,
комиссии,
проскальзывание.
3.4 Целевая функция: максимизация дисконтированной награды
Цель агента — стратегия: максимизирующая ожидаемую кумулятивную награду:
где:
(
= 60 ) — длительность сессии;
(
= 0.99 ) — коэффициент дисконтирования.
3.5 Уравнение Беллмана в контексте трейдинга
Используется модифицированное уравнение Беллмана для Double DQN:
Реализация в agent.learn()
:
next_actions = self.policy_net(next_states).argmax(dim=1)
next_q_values = self.target_net(next_states).gather(1, next_actions.unsqueeze(1)).squeeze(1)
target_q_values = rewards + gamma * next_q_values * (1 - dones)
loss = F.smooth_l1_loss(current_q_values, target_q_values)
Интерпретация:
Агент открыл Long, рынок вырос → (
> 0 ),
увеличивается;
Агент выбрал Hold при явной возможности войти → (
0 ),
снижается;
Target-сеть стабилизирует обучение через задержку обновлений.
3.6 Псевдокод обучения агента
FOR each episode:
s₀ ← env.reset()
FOR t = 1 to T:
aₜ ← ε-greedy(sₜ)
sₜ₊₁, rₜ ← env.step(aₜ)
buffer.add(sₜ, aₜ, rₜ, sₜ₊₁)
IF len(buffer) > TRAIN_START:
batch ← buffer.sample()
loss ← update_Q(batch)
sₜ ← sₜ₊₁
3.7 Ограничения среды
Элемент |
Значение |
---|---|
Комиссия (Taker) |
0.04% от объёма |
Проскальзывание |
±0.05% |
Штраф за бездействие |
−0.001 по умолчанию |
Эти параметры приближают поведение среды к реальной торговле на Binance Futures.
Заключение
Таким образом, агент решает задачу активного управления позицией в условиях высокой рыночной неопределённости, ограниченного времени, транзакционных издержек и штрафов за неэффективные действия.
Формализация задачи строго соответствует реалиям алгоритмической торговли, где каждое решение несёт как потенциальный риск, так и потенциальную прибыль.
4. Архитектура проекта
Проект построен как исследовательская платформа, пригодная для запуска массовых автоматизированных экспериментов.
Все модули управляются через конфигурации и поддерживают строгую изоляцию артефактов по экспериментам.
4.1 Поток данных
-
train.py
:загружает данные через
utils.load_npz_dataset()
;формирует среду:
TradingEnvironment(data[i])
;инициирует агента D3QN и буфер PER.
-
Внутри цикла:
агент получает
state
, выбираетaction
;env.step(action)
→next_state
,reward
,done
;буфер сохраняет переход; вызывается
agent.learn()
.
-
model.py
:обрабатывает
state
через CNN;-
вычисляется:
-
replay_buffer.py
:обновляет приоритеты на основе TD-ошибок;
обеспечивает семплирование важных переходов через SumTree.
-
Логгинг:
записываются
reward
,loss
,ε
,win_rate
;периодическая валидация - метрики сравниваются, сохраняется
best.pth
.
-
Бэктест и оптимизация:
backtest_engine.py
: запускает симуляцию торговли на новых данных;optimize_cfg.py
: автоматический поиск оптимальной стратегии через Optuna;Бэктест интегрирован в пайплайн: использует тот же агент, конфигурации и предобработку;
Поддерживает параллельные сделки, логгинг, стратегии фильтрации действий, риск-менеджмент.
4.2 Структура проекта
? rl_trading_binance/
│
├── train.py # Обучение агента
├── test_agent.py # Тестирование агента
├── backtest_engine.py # Симуляция торговли
├── optimize_cfg.py # Поиск оптимальной конфигурации
├── baseline_cnn_classifier.py # Baseline: CNN классификатор
├── config.py # Базовая структура конфигов (pydantic)
├── configs/ # Отдельные конфиги под эксперименты
│ ├── alpha.py
│ ├── alpha_baseline_cnn.py
│ └── ...
│
├── model.py # Dueling CNN + Q-value head
├── agent.py # D3QN Agent (PER + epsilon decay)
├── replay_buffer.py # Prioritized Experience Replay (SumTree)
├── trading_environment.py # RL-среда с расчётом PnL
├── utils.py # Логгинг, нормализация, визуализация, метрики
└── output/
└── experiment_name/ # Результат
├── logs/
├── plots/
├── saved_models/
└── optuna_cfg_optimization_results/
4.3 Конфигурационная система (cfg)
Я внедрил промышленный подход к управлению параметрами: все модули используют централизованный cfg-объект, основанный на pydantic.BaseModel
. Он разбит на логические блоки:
Конфиг-блок |
Назначение |
---|---|
|
Пути к данным, логам, моделям, артефактам |
|
Структура входных последовательностей |
|
Каналы (OHLCV и доп.), нормализация |
|
Гиперпараметры RL обучения |
|
Архитектура нейросети (CNN + Dueling) |
|
Параметры логирования и валидации |
|
Настройки стратегии выбора действия |
... |
Другие: |
Каждый новый эксперимент описывается как отдельный configs/cfg_name.py
, например:
# configs/alpha.py
from config import MasterConfig
cfg = MasterConfig()
ACTION_HISTORY_LEN = 3
cfg.model.cnn_maps = [32, 64, 128]
cfg.model.cnn_kernels = [7, 5, 3]
cfg.model.cnn_strides = [2, 1, 1]
cfg.model.dense_val = [128, 64]
cfg.model.dense_adv = [128, 64]
cfg.model.additional_feats = 4 + ACTION_HISTORY_LEN * 4
cfg.model.dropout_p = 0.1
cfg.trainlog.num_val_ep = 3500
cfg.trainlog.val_freq = 1000
cfg.trainlog.episodes = 55_000
cfg.trainlog.plot_top_n = 10
cfg.per.buffer_size = 230_000
cfg.rl.batch_size = 16
cfg.rl.learning_rate = 1e-4
cfg.rl.train_start = 10_000
cfg.seq.agent_history_len = 30
cfg.seq.agent_session_len = 10
cfg.seq.action_history_len = ACTION_HISTORY_LEN
cfg.backtest_mode = True
cfg.backtest.max_parallel_sessions = 2
cfg.backtest.position_fraction = 0.5
cfg.backtest.selection_strategy = "advantage_based_filter"
При запуске:
python train.py configs/alpha.py
вся структура логирования, моделей и графиков будет сохранена по пути:
output/alpha/
├── logs/
├── plots/
└── saved_models/
Это делает весь проект воспроизводимым, масштабируемым и идеально управляемым.
4.4 CNN baseline модуль
В проекте реализованн один baseline-модуль, который оформлен как независимый .py
-скрипт. Он следуют тем же правилам запуска, что и основной агент:
Модуль |
Описание |
---|---|
|
Классификация при помощи CNN сети с числом параметров, равным RL-сети |
CNN baseline:
использует
cfg
изconfigs/*.py
;автоматически создаёт
output/name_cfg/
;логирует все метрики (
PnL
,WinRate
,ROC AUC
);обучается и валидируется отдельно (
cnn_classifier
);использует идентичную нормализацию, как и RL-агент (
calculate_normalization_stats
,apply_normalization
);предсказывает классы направления (↑/↓), где таргет: 1, если end_price > start_price; иначе 0.
Это позволяет честно сравнивать baseline-модель и RL-агента по метрикам:
mean PnL
— суммарный средний доход на сессию;win rate
— доля прибыльных сделок.
Вывод
Проект представляет собой целостную, инженерно выверенную систему, где каждая компонента решает строго определённую задачу. Архитектура легко расширяема, воспроизводима и готова к интеграции в продакшен-среду алгоритмической торговли.
5. RL-среда (TradingEnvironment)
Центральный элемент проекта — это симулированная торговая среда, построенная на базе gym.Env
(совместима с Gymnasium).
Она моделирует поведение крипторынка на основе исторических минутных котировок, управляет внутренним состоянием торговли, рассчитывает прибыль/убыток и формирует наблюдение для агента.
5.1 Ключевые элементы среды
Среда работает с одной торговой сессией продолжительностью N шагов. Каждый шаг моделирует одну минуту торговли.
Переменная |
Описание |
---|---|
|
Текущий баланс агента (в USD) |
|
Текущая позиция: 1 — Long, -1 — Short, 0 — нет |
|
Цена входа в позицию |
|
Совокупный реализованный PnL за эпизод |
|
Номер текущего шага в торговой сессии |
|
Количество завершённых сделок |
|
Количество сделок с положительным PnL |
|
Последние k действий (для one-hot-истории) |
5.2 Пространства среды
Action Space (дискретный):
self.action_space = spaces.Discrete(4) # A = {0: Hold, 1: Long, 2: Short, 3: Close}
Observation Space:
self.observation_space = Box(
low=-inf,
high=inf,
shape=(obs_dim,),
dtype=np.float32
)
5.3 Reward Function
Награда формируется из реализованного изменения PnL, нормализованного на начальный баланс, плюс штраф за бездействие вне позиции.
Где:
(
) — прирост реализованной прибыли за текущий шаг
(
) — штраф за бездействие
(
), если агент вне позиции и выбрал
Hold
5.4 Торговая логика step(action)
def step(self, action: int) -> Tuple[np.ndarray, float, bool, bool, dict]:
...
Упрощенная логика действий:
if action == 1 and position == 0:
entry_price = price * (1 + slippage)
position = 1
elif action == 2 and position == 0:
entry_price = price * (1 - slippage)
position = -1
elif action == 3 and position != 0:
if position == 1:
pnl = (sell_price - entry_price) * qty
else:
pnl = (entry_price - buy_price) * qty
realized_pnl += pnl - fee
position = 0
Особенность: Action Masking на последнем шаге
В конце каждой торговой сессии, если позиция остаётся открытой, агенту принудительно навязывается единственное допустимое действие — CLOSE
. Это реализовано через механизм action masking:
if self.step_id == max_steps - 1 and self.position is not None:
forced_action = CLOSE
Чтобы минимизировать влияние принудительного закрытия, в вектор наблюдений были добавлены две временные характеристики: elapsed time
и remaining time
до окончания сессии. Эти признаки позволяют агенту учитывать временной контекст и самостоятельно принимать решение о закрытии позиции до наступления последнего шага.
Такой подход позволяет сместить поведение агента в сторону более осмысленного планирования, при котором сделки закрываются на пике прибыльности, а не в результате внешнего принуждения.
5.5 Формирование состояния (_get_observation())
Каждое состояние — это конкатенация нормализованного окна цен и дополнительных признаков.
Компоненты:
window = self.current_seq[start:end]
normalized = apply_normalization(window, stats, ...)
Extras:
extras = np.array([
float(self.position),
unrealized_pnl,
time_elapsed,
time_remaining
])
Action History:
hist_onehot = np.zeros(ACTION_HISTORY_LEN * NUM_ACTIONS)
for i, a in enumerate(history_actions):
if a is not None:
hist_onehot[i * NUM_ACTIONS + a] = 1
Финальное наблюдение:
state = np.concatenate([
normalized.flatten(),
extras,
hist_onehot
])
5.6 Псевдокод среды
initialize(balance=10_000, position=0, entry_price=0.0)
FOR each episode:
load sequence from data
FOR t in 0 to 59:
state ← get_observation()
action ← agent(state)
IF action == Open AND no position:
enter position
IF action == Close AND have position:
exit position
update balance, position, realized_pnl
reward ← calc_reward(pnl, penalty_if_no_actions)
obs_next ← get_observation()
done ← (t == N)
return (obs_next, reward, done, info)
Вывод
Торговая среда эмулирует реалистичную рыночную механику с высоким уровнем детализации.
TradingEnvironment:
учитывает комиссии, проскальзывание, штрафы
предоставляет агенту частичные наблюдения с памятью действий
защищает от некорректных действий с помощью action masking
возвращает релевантные награды, способствующие обучению прибыльного поведения
6. Архитектура агента (D3QN + PER)
Алгоритм, лежащий в основе агента, - это Dueling Double Deep Q-Network с Prioritized Experience Replay (D3QN + PER). Выбор данной архитектуры обусловлен следующими требованиями:
стабильность обучения и высокая способность обобщения;
способность различать «важные» состояния от неинформативных;
снижение эффекта переоценки Q-функции (overestimation).
6.1 Почему Dueling DQN особенно эффективен в трейдинге?
Классический DQN напрямую оценивает значение каждого действия в состоянии Q(s, a), не разделяя вклад самого состояния и специфики действия. Это делает модель чувствительной к рыночному шуму и усложняет обучение в ситуациях, где различия между действиями несущественны.
В Dueling DQN эта проблема решается через декомпозицию функции ценности на две составляющие:
V(s) - скалярная оценка полезности состояния вне зависимости от действия;
A(s, a) - преимущество действия a относительно среднего уровня действий в этом состоянии.
Формула:
Такой подход особенно полезен в трейдинге, где зачастую важно понять не что делать, а стоит ли делать что-либо вообще.
Пример:
Рассмотрим два сценария:
-
Пустое состояние ближе к концу сессии: цены стабильны, объёмы снижаются, сигналов нет.
V(s) будет низким: рыночная ситуация не представляет интереса.
A(s, a) ≈ 0 для всех a: ни одно из действий не даёт ощутимого преимущества.
-
Сильный импульс на рынке: высокая волатильность и явная направленность движения.
V(s) будет высоким: ситуация потенциально прибыльная.
A(s, Long) >> A(s, Short): преимущество действия Long ярко выражено.
Таким образом, Dueling DQN позволяет агенту более чётко дифференцировать рыночные состояния, эффективно игнорировать нейтральные фазы и сфокусироваться на действительно ценных возможностях для входа в позицию.
Double DQN
Решает проблему переоценки Q-значений путём разнесения действий:
одна сеть выбирает действие (policy);
вторая оценивает результат (target).
Формула:
Применяется в методе learn()
.
Prioritized Experience Replay (PER)
Обеспечивает фокусировку обучения на значимых переходах. Вероятность выборки пропорциональна TD-ошибке:
Где ( ) — TD-ошибка, (
) — сглаживающий параметр.
6.2 Класс агента D3QN_PER_Agent
Описан в agent.py
. Реализует:
инициализацию policy и target сетей;
(
)-жадную стратегию;
буфер PER;
логику обучения.
Кэш предсказаний для бэктеста
Чтобы ускорить массовую симуляцию и повторное использование модели, агент поддерживает дисковое кэширование Q-значений:
Каждое состояние ассоциируется с ключом (тикер, время), по которому сохраняются Q-оценки;
При следующем обращении к тому же состоянию, модель не вызывается — результат берётся из
qval_cache.pkl
;Это снижает время бэктеста в десятки раз и делает возможным реальный перебор сотен конфигураций в Optuna;
Кэш автоматически сохраняется и загружается при запуске агента.
# Пример использования кэша:
action = agent.select_action(
state, training=False, use_cache=True, cache_key=("BTCUSDT", datetime.utcnow())
)
6.3 Псевдокод D3QN + PER
FOR each episode:
s₀ ← env.reset()
FOR t = 1 to T:
aₜ ← ε-greedy(sₜ)
sₜ₊₁, rₜ ← env.step(aₜ)
buffer.add(sₜ, aₜ, rₜ, sₜ₊₁)
IF len(buffer) ≥ TRAIN_START:
B ← buffer.sample(BATCH_SIZE)
Compute target_q via Double DQN
Compute loss = SmoothL1(Q - target)
Backprop with PER weights
Clip gradients
Update policy_net
Every N steps: sync target_net
Вывод
Архитектура D3QN + PER объединяет лучшие практики обучения с подкреплением:
Dueling — разделение оценки состояния и действий;
Double — снижение переоценки Q-значений;
PER — фокусировка на значимых обучающих переходах;
Clip Gradients, Target Sync и Epsilon Decay — стабильность и устойчивость обучения.
Результат — надёжная и масштабируемая реализация D3QN, подходящая для сложных рыночных условий.
7. Нейронная сеть (Dueling CNN-Net)
Для аппроксимации Q-функции используется гибридная архитектура, включающая:
Сверточный блок (CNN) — извлекает краткосрочные рыночные паттерны из временных рядов;
Dueling head — разделённые потоки Value и Advantage, объединяемые в итоговые Q-значения.
Такой дизайн обеспечивает устойчивость к шуму, адаптивность к рыночному контексту и интерпретируемость поведения модели.
7.1 Почему CNN?
На первом этапе я использовал сверточную нейросеть (CNN) как стартовую архитектуру по следующим причинам:
CNN хорошо подходит для обработки локальных паттернов в временных рядах;
она проста в реализации, быстро обучается и легко масштабируется;
структура CNN хорошо отражает идею движущегося окна, что интуитивно соответствует анализу рыночных данных.
Выбор CNN продиктован стратегией постепенного наращивания сложности: сначала — интерпретируемый, стабильный baseline, затем — переход к более выразительным архитектурам, таким как:
iTransformer (Inverted Transformers) — специализируется на time-series;
Perceiver IO — эффективно обрабатывает данные переменной длины;
Temporal Fusion Transformer — SOTA в мультивариативных временных рядах.
CNN выступает как инженерный якорь, позволяющий выстроить надёжный и прозрачный фундамент, прежде чем двигаться к более сложным и вычислительно затратным подходам.
7.2 Основные компоненты
Feature Extractor (CNN):
for i in range(len(cnn_maps)):
Conv2D(in_channels → out_channels, kernel=(k, 1), stride=(s, 1))
ReLU
Concatenation:
combined = torch.cat([cnn_flat, extras], dim=1)
Value Head:
value = self.value_stream(combined) # shape: (batch, 1)
Advantage Head:
advantage = self.advantage_stream(combined) # shape: (batch, num_actions)
Q-финализация:
q_value = value + (advantage - advantage.mean(dim=1, keepdim=True))
7.3 Потенциал для расширения
Возможные направления для дальнейшего развития:
Замена CNN на более выразительные архитектуры (см. раздел 7.1): Perceiver IO, TFTransformer, iTransformer;
Увеличение receptive field через dilated convolutions — для захвата более длительных рыночных зависимостей.
Вывод
Несмотря на простоту, классическая CNN демонстрирует высокую эффективность при работе с рыночными временными рядами.
CNN:
обучается быстро;
обладает компактной реализацией (подходит для embedded / edge-сценариев);
легко интерпретируема и отлаживаема;
и главное — достаточна для достижения конкурентоспособных результатов, что подтверждается проведёнными экспериментами.
Такой баланс делает её оптимальной отправной точкой для RL-проектов, ориентированных на реальные рынки и продакшн-развёртывание.
8. Буфер PER (Prioritized Experience Replay)
Буфер воспроизведения опыта с приоритетами (PER) — это ключевая техника ускорения обучения в DQN-семействе. Вместо равновероятной выборки опытов, как в классическом replay buffer, здесь применяется динамическая приоритезация на основе ошибки обучения (TD-ошибки).
Это позволяет чаще повторно обучаться на важных переходах, ускоряя сходимость и повышая стабильность обучения.
8.1 Идея Prioritized Experience Replay
Prioritized Experience Replay (Schaul et al., 2015) предлагает выбирать те переходы, на которых агент ошибается сильнее. Каждому элементу i сопоставляется приоритет pᵢ, определяющий вероятность выборки:
где:
(
) — TD-ошибка с защитой от нуля;
(
) — степень приоритезации ((
) равномерная);
( ε ) — положительная константа, исключающая нулевые приоритеты.
8.2 Реализация через SumTree
Для эффективного семплирования по приоритету используется дерево отрезков — SumTree. Это бинарное дерево, в котором каждый узел хранит сумму приоритетов поддерева.
Свойства SumTree:
-
Корень:
Листья: приоритеты отдельных элементов
Пример:
[30]
/ \
[12] [18]
/ \ / \
[5] [7] [10] [8]
8.3 Псевдокод буфера
class PrioritizedReplayBuffer:
def add(exp, td_error):
p = (abs(td_error) + eps) ** alpha
sum_tree.add(p)
data[ptr] = exp
ptr = (ptr + 1) % capacity
def sample(batch_size):
segment = sum_tree.total / batch_size
batch = []
for i in range(batch_size):
z = uniform(i * segment, (i+1) * segment)
idx, p = sum_tree.find(z)
prob = p / sum_tree.total
w = (1 / (N * prob)) ** beta
batch.append((data[idx], w, idx))
return batch
def update_priorities(indices, td_errors):
for i, delta in zip(indices, td_errors):
p = (abs(delta) + eps) ** alpha
sum_tree.set(i, p)
8.4 Связь с агентом
Буфер интегрирован в agent.learn()
следующим образом:
batch = buffer.sample(batch_size)
...
td_errors = abs(target_q - current_q)
buffer.update_priorities(indices, td_errors)
Таким образом:
Агент фокусирует внимание на самых проблемных примерах;
Но сохраняет контроль над смещением (через importance sampling weights).
Вывод
PER — ключевой компонент в условиях высокой нестабильности, где:
важные события редки;
сигналы асимметричны;
цена ошибки велика.
SumTree и TD-ошибка позволяют систематически и с приоритетом обучаться на значимых ситуациях, избегая потери времени на бессмысленные шаги.
9. Обучение
Процесс обучения реализован в модуле train.py
и включает:
загрузку и предобработку данных;
инициализацию среды, агента и буфера;
запуск цикла обучения с валидацией;
логирование и сохранение лучших весов;
генерацию графиков и отчётных артефактов.
9.1 Цикл обучения
Центральный цикл реализует классическую схему обучения агента с использованием буфера воспроизведения и механизма ε-жадности:
FOR episode IN range(1, EPISODES + 1):
obs ← env.reset()
total_reward ← 0
losses ← []
WHILE not done:
action ← agent.select_action(obs)
next_obs, reward, done, _, info ← env.step(action)
agent.store_experience(obs, action, reward, next_obs, done)
IF buffer.ready():
loss ← agent.learn()
IF loss:
losses.append(loss)
agent.increment_step()
obs ← next_obs
total_reward += reward
логгирование: reward, avg_loss, epsilon, win_rate
IF валидация включена AND episode % VAL_FREQ == 0:
metrics ← evaluate(agent, val_env)
IF улучшение:
save best.pth
В процессе обучения используется механизм delayed reward, отражающий отложенное влияние решения на итоговую прибыль.
9.2 Логирование и контроль
В проект встроено детальное логирование всех ключевых величин:
используется
logging
(stdout + файл) иtqdm
для визуального контроля;создаются отдельные директории логов и графиков для каждого эксперимента:
output/logs/
,output/plots/
;сохраняются: значения на каждом шаге + сглаженное среднее (moving average).
9.3 Метрики обучения
Reward
Суммарная награда за эпизод. Отражает изменение баланса с учётом позиции, комиссии и проскальзывания.Loss
Ошибка TD-обновления. Служит индикатором стабильности обучения. Рост может сигнализировать о переобучении или расхождении Q-функции.-
Win Rate
Процент торговых сессий, завершившихся положительным итоговым PnL.
Проект автоматически сохраняет визуализации всех ключевых метрик:
График |
Назначение |
---|---|
|
Награда по эпизодам |
|
Ошибка TD обучения |
|
Процент успешных сессий |
|
Динамика ε-жадности |
Все графики сохраняются в папке output/plots/
.
Пример: training_rewards.png

Анализ обучающей динамики:
Начало обучения: средние награды находятся около нуля. Это нормально — агент только начинает осваивать среду и действует почти случайно.
Положительная динамика: скользящее среднее демонстрирует плавный рост, а расширение верхнего хвоста распределения наград указывает на постепенное освоение стратегии. Несмотря на сохраняющуюся волатильность отдельных эпизодов агент уверенно начинает извлекать выгоду из структуры среды.
Конец обучения: к финалу обучения скользящее среднее стабилизируется выше нуля, без резких провалов — это признак того, что агент научился избегать грубых ошибок и всё лучше справляется с задачей принятия решений.
Итог:
Агент демонстрирует стабильное обучение, однако тренд указывает на то, что потенциал далеко не исчерпан.
После завершения обучения сохраняется:
лучшая модель
best.pth
(по метрике на валидации);финальная модель, которая фиксирует веса в конце обучения
final.pth
.
Вывод
Обучающий pipeline выстроен по всем стандартам индустриального RL:
регулярная валидация и логирование;
контроль динамики метрик;
автоматическое сохранение моделей;
полная воспроизводимость через конфигурационный запуск.
10. Тестирование и визуализация
Оценка качества обученного агента проводится в модуле test_agent.py
на отложенных, ранее не встречавшихся данных (test_data.npz
).
Цель — проверить способность модели обобщать стратегию на новых рыночных сценариях без утечек данных из train/val.
10.1 Структура скрипта test_agent.py
Последовательность действий:
Загрузка тестового датасета.
Подготовка входных данных.
Инициализация среды с тестовыми сессиями.
Загрузка лучшей обученной модели.
Прогон заданного числа тестовых эпизодов.
Расчёт метрик, отрисовка результатов, сохранение артефактов.
10.2 Примеры сессий
Скрипт отрисовывает лучшие и худшие торговые сессии по заданной метрике (pnl или win_rate).
Отрисовка выполняется через plot_sessions()
.
На каждом графике отображаются:
кривая цен закрытия;
вертикальная линия начала сессии;
действия агента в виде цветных маркеров: ◦ серый — Hold ◦ зелёный — Long ◦ синий — Short ◦ красный — Close
подпись: тикер, время, метрика (PnL или win rate)
Пример: profitable_session_1

Profitable Session 1
Тикер: HIGHUSDT
Агент открыл LONG практически в момент локального минимума и зафиксировал прибыль на резком отскоке вверх.
Результат: +1805.02 USDT
Пример: profitable_session_2

Profitable Session 2
Тикер: COOKIEUSDT
В данной сессии агент закрывает две прибыльные позици, вначале открывает LONG эксплуатируя сильный положительный импульс, далее фиксирует прибыль и открывает короткую позицию которую также закрывает в плюсе.
Результат: +2562.19 USDT
Пример: unprofitable_session_1

Unprofitable Session 1
Тикер: BRETTUSDT
Агент открыл LONG сразу после просадки цены, далее на первых минутах после открытия позиции Тикер показывал ожидаемое поведение, но затем последовал резкий спад.
Результат: −577.59 USDT
Вывод: стратегия удержания агента не учла нарастающий нисходящий импульс. Но в реальной торговле подобные ситуации перекрываются риск-менеджментом (Stop Loss).
Пример: unprofitable_session_2

Тикер: ARCUSDT
Агент стартует с SHORT в момент, когда цена достигает нисходящего пика сигнала, но рынок быстро отскакивает и агент понимая это закрывает позицию. Далее агент вероятно попытался компенсировать убыток, открыв LONG на откате, но движение вверх оказалось слабым, и позиция была закрыта без существенного восстановления.
Результат: −161.46 USDT
Вывод: стратегия входа была ошибочной, но попытка переориентироваться в середине сессии демонстрирует адаптивное поведение агента.
Такой формат визуализации действий позволяет глубоко понять логику агента: когда вошёл, когда вышел, насколько эффективно распознал импульс или флэт.
10.3 Метрики качества
Во время теста вычисляются агрегированные метрики:
Метрика |
Описание |
---|---|
Test_mean_reward |
Средняя награда за эпизод |
Test_mean_pnl |
Средний реализованный PnL за эпизод |
Test_win_rate |
Средняя доля прибыльных сделок |
Test_all_pnls |
Массив PnL по всем сессиям (для гистограммы) |
Финальные результаты RL-агента на тестовых данных:
▸ Средняя награда (mean_reward):
0.00285
▸ Средний PnL за сессию (mean_pnl):
+28.47 USDT
▸ Доля прибыльных сессий (win_rate):
55.67%
> Важно подчеркнуть:
Эти результаты были получены при условиях, далёких от максимума возможностей архитектуры.
В конфигурации эксперимента намеренно использовались укороченные сессии (10 минут торговли вместо 60) и ограниченный исторический контекст (30 минут вместо 90).
Цель — обеспечить визуальную интерпретируемость и возможность воспроизведения на обычной CPU-машине.
Несмотря на столь сжатый временной горизонт, агент демонстрирует стабильное поведение и положительное математическое ожидание, превышающее уровень комиссий и проскальзывания.
Это указывает на то, что даже в "демо-режиме" RL-агент уже способен обучиться распознаванию рыночных паттернов, адекватной стратегии входа/выхода и управлению позицией.
Форма распределения наград и стабильность поведения на ранее не встречавшихся данных (test_data.npz
) подтверждают обобщающую способность стратегии. На практике это означает, что агент не переобучается под конкретные сценарии, а усваивает общие принципы, применимые к широкому спектру рыночных условий.
Для оценки состоятельности RL-агента в проект был внедрён честный baseline — сверточный классификатор (CNN), обученный в парадигме supervised learning.
Результаты baseline-модели:
▸ Средний PnL:
–27.95 USDT
▸ Win Rate:
47.85%
CNN-модель, несмотря сопоставимое число параметров (~256k), не смогла обучиться прибыльной стратегии, она уступает RL-агенту по всем метрикам, что указывает на слабую способность различать прибыльные и убыточные сессии.
Вероятная причина: supervised-подход обучается на статической разметке, не видя последствий своих решений. В то время как агент в рамках RL оптимизирует стратегию с учётом delayed reward, комиссий, риска и временного контекста. Это фундаментальное преимущество подхода обучения с подкреплением в торговле.
10.4 Распределения
Распределение PnL:
Показывает, как часто агент зарабатывает или теряет в рамках одной сессии.
Идеально:
распределение асимметрично сдвинуто вправо. Это означает, что агент ограничивает убытки и старается максимизировать прибыль.
Распределение Win Rate:
Показывает долю прибыльных сделок на одну сессию.
Чем ближе к 1.0, тем стабильнее стратегия.
10.5 Визуализация
Все графики сохраняются автоматически:
test_pnl_distribution.png
test_win_rate_distribution.png
profitable_session_*.png
unprofitable_session_*.png
Путь: output/plots/
Вывод
Скрипт test_agent.py
позволяет:
объективно оценить обобщающую способность агента;
визуально проверить решения агента на новых данных;
получить графическую и статистическую обратную связь о качестве стратегии.
Этот этап критически важен, так как демонстрирует, насколько поведение агента надёжно и воспроизводимо в новых рыночных условиях.
11. Бэктест: реалистичная оценка торгового агента
Оценка стратегии в условиях, приближенных к реальной торговле, требует тщательно спроектированного бэктеста.
В рамках данного проекта я реализовал модуль backtest_engine.py — независимый компонент системы, предназначенный для симуляции реальных торговых условий и объективной оценки производительности агента на ранее не использовавшихся данных.
Цель бэктеста это проверить способность агента адаптироваться к рыночной неопределённости, проявлять инициативу и соблюдать дисциплину управления капиталом.
11.1 Особенности реализации
Бэктест реализован как полноценный симулятор торговли, с учётом всех ключевых аспектов реального рынка:
-
Реалистичность исполнения:
Учитываются комиссии (0.04%) и проскальзывания (±0.05%);
Поддерживается риск-менеджмент: стоп-лосс, тейк-профит, трейлинг-стоп.
-
Логгинг сделок:
Все действия агента, включая направление, объём, результат, изменение цены и влияние на баланс, записываются в лог backtest_session.log с привязкой к времени.
11.2 Интеллектуальные стратегии выбора действий
Агент может использовать одну из двух стратегий принятия решения:
Advantage-based filter, действие принимается только если его advantage превосходит заданный порог уверенности:
-
Ensemble Q-Filter (MC Dropout), выполняется несколько стохастических проходов по сети:
вычисляются средние Q-значения и дисперсия (неуверенность);
действие принимается, только если одновременно выполнены условия по уверенности и допустимому уровню неопределённости
, такой подход имитирует логику "soft ensemble" и позволяет гибко управлять допустимым уровнем риска.
11.3 Оптимизация через Optuna
Для нахождения оптимальных гиперпараметров стратегии реализован отдельный модуль optimize_cfg.py, построенный на базе Optuna:
-
Параметры поиска включают:
пороги уверенности (long_thr, short_thr, close_thr);
включение/отключение риск-менеджмента;
значения stop_loss, take_profit, trailing_stop;
ограничение на дисперсию в ensemble_q_filter.
Бэктест вызывается внутри каждой trial-сессии (run_backtest()), используя кэшированные предсказания;
Каждая конфигурация сохраняется, логи ведутся отдельно по триалам;
Лучшее решение сохраняется как best_backtest_cfg.json, а графики (optuna_history.png, pareto.png) визуализируют динамику поиска.
11.4 Метрики бэктеста
Система метрик реализована в MetricsCollector и предоставляет полный срез поведения агента. Все метрики автоматически логируются и могут использоваться в качестве целевых для Optuna.
Метрика |
Описание |
---|---|
final_balance_change |
Финальное изменение капитала в % |
total_trades |
Общее число совершённых сделок |
profit_days |
Доля торговых дней с положительным результатом |
accuracy |
Доля правильных предсказаний |
sharpe, sortino |
Классические risk-adjusted показатели |
max_drawdown |
Максимальная просадка по балансу |
avg_trade_amount |
Средний размер сделки |
total_commission |
Общие потери на комиссиях |
correct_avg_change |
Среднее изменение цены при верных сделках |
incorrect_avg_change |
Среднее изменение цены при ошибках |
Финальные метрики симуляции на отложенном датасете (backtest_data.npz):
▸ Финальная доходность портфеля: +144.23%
▸ Sharpe коэффициент: 1.85
▸ Sortino коэффициент: 2.05
▸ Accuracy сигналов: 69.6%
▸ Максимальная просадка: –22.49%
▸ Кол-во торговых дней: 56
▸ Прибыльных дней: 44 (78.57%)
▸ Общее число сделок: 112
▸ Средняя сделка: 11,324.29 USDT
▸ Сделок в день: ~2.00
▸ Комиссионные издержки: –9.68%
Помимо аггрегированных метрик, сохраняется кривая баланса backtest_balance_curve.png
(данный график уже был продемонстрирован в начале статьи) и полный лог сделок.

Агент демонстрирует сбалансированный профиль риск-доходности:
достигнута итоговая доходность +144.23%, что соответствует среднему дневному приросту капитала на уровне +1.61%.
Значения коэффициентов Sharpe (1.85) и Sortino (2.05) указывают на то, что стратегия обеспечивает положительное соотношение доходности к риску, без чрезмерной зависимости от редких экстремальных результатов.
Примечательно также поведение на уровне микростатистики:
▸ Доля верных long-сделок: 69.9% (из 93 позиций)
▸ Доля верных short-сделок: 68.4% (из 19 позиций)
▸ Среднее изменение цены при успешных трейдах: +4.38%
▸ Средний убыток при ошибке: –3.67%
Результаты были получены с теми же ограничениями, что и на тренировке: модель с 256k параметров, сессии по 10 минут, контекст — 30 минут. Это всего лишь малая часть от потенциала, заложенного в архитектуру проекта.
Демонстрационный режим: скрытый потенциал, ограничения модели и данных.
Чтобы обеспечить высокую воспроизводимость эксперимента на любой машине (включая ноутбук без GPU), а также упростить визуальный анализ поведения агента, в проекте был активирован облегчённый режим:
▸ Размер модели: ~256,000 параметров
▸ История: 30 минут (вместо 90)
▸ Сессия: 10 минут (вместо 60)
Такая конфигурация была выбрана осознанно, чтобы:
ускорить цикл обучения и тестирования;
дать пользователю возможность запустить весь пайплайн даже на CPU;
показать поведение агента на коротких трейдах — с полной визуализацией всех шагов.
Реалистичная длина сессии в продакшене предполагает:
Контекст: 90 минут
Длительность: 60 минут
Модель > 1 млн параметров
Таким образом, продемонстрированные результаты — это лишь незначительная доля от потенциальных возможностей проекта. Это делает текущую реализацию идеальной отправной точкой для масштабирования и развития:
Увеличить глубину модели
Расширить временной горизонт
Применить более мощные архитектуры: iTransformer, Perceiver IO
11.5 Режим использования
Бэктест и оптимизация запускаются командами:
# Запуск бэктеста для накопления кэша
python backtest_engine.py configs/alpha.py
# Оптимизация стратегии
python optimize_cfg.py configs/alpha.py --trials 100 --jobs 1
После завершения — можно загрузить best_backtest_cfg.json и использовать найденную стратегию в реальной торговле.
11.6 Пример логов бэктеста
Логирование торговли и метрик реализовано в духе классических торговых платформ. Ниже фрагменты реального лога:
Начало сессии и действия агента:
[Starting backtest...]:
[INFO] : Got 1 signals @ Date: 2025-03-04 Time: 05:19 For Tickers -> GPSUSDT
[INFO] : (SHORT) SELL 27445.27879275 GPSUSDT for 0.17786 at 2025-03-04 05:20
[INFO] : (CLOSE) BUY TP 27434.30068123 GPSUSDT for 0.15744 at 2025-03-04 05:21 PnL = +556.42
...
[INFO] : Got 1 signals @ Date: 2025-04-06 Time: 21:43 For Tickers -> AUCTIONUSDT
[INFO] : (LONG) BUY 653.41295438 AUCTIONUSDT for 13.64841 at 2025-04-06 21:43
[INFO] : (CLOSE) SELL TP 653.15158920 AUCTIONUSDT for 13.97151 at 2025-04-06 21:46 PnL = +203.81
Сводка всех сделок:
[Trades Summary]:
...
[INFO] : 2025-03-27 17:31 LONG MUBARAKUSDT 5903: +601.23 (+10.18% | +5.09%) PRICE CHANGE: +10.27%
[INFO] : 2025-04-14 15:26 LONG OMUSDT 11094: +893.56 ( +8.05% | +4.03%) PRICE CHANGE: +8.14%
[INFO] : 2025-04-29 06:14 LONG INITUSDT 12111: -236.20 ( -1.95% | -0.98%) PRICE CHANGE: -1.87%
[INFO] : 2025-05-21 02:31 LONG SXTUSDT 13663: +611.52 ( +4.48% | +2.24%) PRICE CHANGE: +4.56%
...
Финальные метрики:
[Final Metrics]:
[INFO] : total_commission = -9.68%
[INFO] : avg_commission = -9.13
[INFO] : max_loss = -3474.09
[INFO] : max_profit = 5119.57
[INFO] : total_trade_days = 56
[INFO] : profit_days = 44 (78.57%)
[INFO] : final_balance_change = 144.23%
[INFO] : exp_day_change = 1.61%
[INFO] : max_drawdown = -22.49%
[INFO] : sharpe = 1.85
[INFO] : sortino = 2.05
[INFO] : trades_sharpe = 0.18
[INFO] : trades_sortino = 0.19
[INFO] : accuracy = 69.6%
[INFO] : total_trades = 112
[INFO] : total_longs = 93
[INFO] : total_shorts = 19
[INFO] : longs_correct = 65 (69.9%)
[INFO] : shorts_correct = 13 (68.4%)
[INFO] : correct_avg_change = 4.38%
[INFO] : incorrect_avg_change = -3.67%
[INFO] : avg_trade_amount = 11324.29
[INFO] : trades_per_day = 2.00
Вывод:
Модуль бэктеста в проекте реализован с прицелом на максимальную инженерную реалистичность. Он моделирует реальные торговые условия, включает механизмы контроля риска и позволяет проводить масштабную гиперпараметрическую оптимизацию.
Благодаря множеству метрик, система предоставляет глубокую аналитику поведения агента и его устойчивости на новых, ранее не виденных рыночных данных.
12. Выводы
12.1 Итоговый результат
В рамках данного проекта я спроектировал, реализовал и обучил торгового агента на базе обучения с подкреплением (Reinforcement Learning) с использованием Dueling Double DQN и Prioritized Experience Replay для краткосрочной торговли на Binance Futures.
Агент получает нормализованные окна минутных рыночных данных и принимает одно из четырёх действий: HOLD, LONG, SHORT, CLOSE.
Система продемонстрировала:
устойчивое обучение на тысячах рыночных сессий;
положительный средний PnL на тестовых данных;
интерпретируемое поведение с возможностью визуализации;
масштабируемость, конфигурационную гибкость и модульность.
12.2 Поведенческие шаблоны агента
В процессе анализа я выявил, что агент обучается не просто максимизировать краткосрочную прибыль, но и развивает поведенческие паттерны, характерные для рационального трейдера:
Идентификация импульсов - например, вход в SHORT на экстремумах с последующим выходом при признаках отката;
Избежание сделок на флэтовых участках - сниженная активность при малой волатильности;
Дисциплина во времени - грамотное завершение позиций ближе к окончанию сессии, до принудительного закрытия;
12.3 Обоснование эффективности
Для объективной оценки агент был сопоставлен с CNN baseline-моделью:
Сверточный классификатор (CNN) - архитектура сопоставимая по мощности с агентной моделью.
Результат: агент превзошёл baseline-модель по метрикам mean PnL и Win Rate, что подтверждает состоятельность его стратегии в условиях частично наблюдаемой среды и задержанного вознаграждения.
12.5 Заключение
Проект демонстрирует архитектуру, способную решать прикладные задачи в трейдинге с помощью Reinforcement Learning в условиях высокой волатильности и ограниченности данных.
Данная реализация является инфраструктурным фундаментом для дальнейшего исследовательского и инженерного прогресса:
Переход от value-based к policy-based методам: Actor-Critic, A3C, PPO, SAC;
Модельно-ориентированное обучение: внедрение Dreamer и MuZero с внутренним прогнозированием среды;
Расширение пространства действий: от дискретных к непрерывному управлению объёмом и риском (DDPG, TD3);
Интеграция ансамблей агентов и адаптивных reward shaping функций;
Надеюсь этот проект послужит основой для вашего следующего шага, где вы построите продвинутую архитектуру RL агента, способного планировать и обучаться во внутренней модели мира с целью адаптироваться к сложной, стохастической природе финансовых рынков.
Бонус: Наблюдайте за агентом в реальном времени
Чтобы продемонстрировать работоспособность данной системы на практике, запущен онлайн AI-агент с более продвинутой архитектурой, который в реальном времени анализирует рынок Binance Futures и публикует свои действия в Telegram.
Что делает агент:
Мониторит рынок по всем тикерам, каждую минуту.
Фиксирует сигналы: публикует тикер, направление сделки и уровень уверенности.
Входит и выходит из позиции при оптимальном соотношении риск/награда.
Публикует результат сделки: прибыль или убыток.

Вы можете:
Наблюдать за поведением агента в реальных рыночных условиях.
Анализировать его логику для совершенствования навыков торговли.
Использовать его аналитику для принятия решений.
Важное примечание: агент работает в экспериментальном режиме. Все решения, принимаемые пользователями на основе информации из канала, осуществляются под их личную ответственность.
Проект является исследовательским и предоставляется бесплатно исключительно в образовательных целях.
Что дальше: путь к полной автономии
В рамках этого проекта мы с вами реализовали центральный элемент торговой системы - интеллектуального агента на базе RL, способного обучаться, принимать осмысленные решения и адаптироваться к рынку.
Но для перехода от исследовательского проекта к полноценной автономной торговой системе потребуется ещё два важных компонента:
1. Поток рыночных данных (Stream Layer)
Чтобы агент мог действовать в режиме реального времени или обучаться на исторических данных, необходимо обеспечить непрерывный поток актуальных данных с биржи.
Что для этого нужно:
-
Подключение к Binance API (python-binance, ccxt):
Загрузка исторических данных (kline 1m);
Подписка на WebSocket для live-обновлений.
-
Локальное хранилище:
PostgreSQL + TimescaleDB - отличное решение для временных рядов;
Запись данных по всем тикерам.
-
Автоматизация:
Используйте Airflow для оркестрации задач;
Контролируйте полноту, корректность и актуальность данных.
Минимальный стек:
python-binance - данные
PostgreSQL + TimescaleDB - база
Airflow - расписание
2. Исполнение торговых решений (Execution Layer)
Решения агента необходимо конвертировать в прибыльные сделки на реальной бирже.
Что для этого нужно:
-
Интерпретация действий:
LONG / SHORT → MARKET / LIMIT - ордер;
CLOSE → отмена / закрытие позиции.
-
Связь с Binance (через REST API):
Получение баланса, открытие и контроль ордеров;
Проверка исполнения и расчёт PnL.
-
Безопасность и контроль:
Запуск через Binance Testnet;
Поддержка dry-run (логика без реальных сделок);
Система алертов (Telegram / email).
Минимальный стек:
python-binance - исполнение
Telegram Bot API - нотификации
Практический вывод
У вас уже есть мозг системы - стратегически мыслящий RL-агент.
Вам необходимо добавить:
Поток данных → агент начнёт видеть рынок в реальном времени;
Модуль исполнения → агент сможет действовать и зарабатывать.
Реализация этих модулей это отличная возможность усовершенствовать свои навыки и глубже понять:
работу с API бирж;
потоковую обработку данных;
надежную автоматизацию и fault-tolerant дизайн.
Начните с малого: напишите стример данных в базу и модуль dry-run исполнения.
Вы удивитесь, как быстро из прототипа вырастает полноценная торговая система.
Если вы дошли до этого раздела, то я хочу вас поздравить, вы уже на другом уровне.
Теперь ваша следующая задача - завершить систему и стать архитектором собственной AI-трейдинговой платформы.
Исходный код, датасеты и работа агента в режиме реального времени:
Репозиторий с кодом: github.com/YuriyKolesnikov/rl-trading-binance
Датасеты на Hugging Face: huggingface.co/datasets/ResearchRL/open-rl-trading-binance-dataset
Онлайн-сигналы и верификация прогнозов агента: t.me/binance_ai_agent
Комментарии (14)
Yozh-lyudoyed
05.08.2025 18:37Я правильно понял, что трейдинг ведётся по одной переменной? То есть берутся в расчёт данные по одному инструменту? Не пытались улучшить результат за счёт анализа взаимозависимых активов? (ну, типа, когда ставка по доллару растёт, доллар растёт, а золото падает, а когда медь растёт, то акции растут и пр.)
quantAIengineer Автор
05.08.2025 18:37Спасибо, это действительно хороший вопрос.
Да, в текущей реализации агент оперирует минутными данными одного инструмента за сессию. Это сознательное ограничение — основной фокус был на построении устойчивой архитектуры, способной обучаться и действовать в условиях частичной наблюдаемости и высокой волатильности без внешних признаков. Такой подход упрощает трассировку причинно-следственных связей и позволяет глубже интерпретировать поведение агента.
Тем не менее, идея анализа кросс-активных зависимостей (intermarket signals) интересна и перспективна. Архитектура проекта модульная, и расширение входного пространства дополнительными каналами — вопрос конфигурации. Можно, например, включать признаки от коррелированных тикеров, индексов, ставок и других рыночных индикаторов. Но важно учитывать: каждый новый источник — это рост размерности и потенциального шума, что требует строгого контроля качества и борьбы с утечками.
Кроме того, в перспективе возможно добавление текстовых данных — новостей, твитов, постов, комментариев с форумов. Но здесь критически важно находить именно первоисточник информации с точной временной меткой. Только так можно привязать событие к сигналу и адекватно оценить его предсказательную силу. Без этого — высокая вероятность ретроспективного искажения.
Для начала, как прикладную рекомендацию, можно попробовать включить минутные котировки Binance SPOT. Некоторые эксперименты показывают высокую краткосрочную корреляцию между фьючерсами и спотом, как по направлению движения, так и по динамике ликвидности. Это может дать агенту полезный контекст без значительного усложнения архитектуры.
SatCat
05.08.2025 18:37Ваще отлично! не часто так все подробно и с исходниками!
OHLCV -- конечно нет! это все МЛщики мучают.. просто в топку, это нормально не работает.
Советую замутить хотя бы анализ по пробоям/отбоем уровней или какие-нить еще схожие именно "визуал"-данные.
quantAIengineer Автор
05.08.2025 18:37Благодарю за отзыв и за совет.
Что касается OHLCV — согласен, сами по себе эти данные являются только низкоуровневым представлением рыночной динамики. В отрыве от контекста они действительно слабо информативны.
Однако в данной системе они проходят через мощную цепочку преобразований: логарифмирование, нормализация, агрегация контекста, архитектурная фильтрация через CNN + dueling Q-head. То есть модель не опирается напрямую на "сырые свечки", а учится выделять стабильные поведенческие паттерны.
Тем не менее, направление, которое вы упомянули — визуальные или структурные сигналы, — очень перспективно. Их вполне можно внедрить как отдельный канал признаков.
myltik81
05.08.2025 18:37Проходил уже все это, в конечном варианте было 3 сетки, dreamer фантазировал на 200 тиков вперёд, A2C ценность сделки и обоснование и сетка которая наблюдала за двумя этими сетками, и на основании их действий обучалась и подсказывала. В итоге лучшее решение что приняла модель - не торговать вообще.
quantAIengineer Автор
05.08.2025 18:37Благодарю за честный комментарий — опыт, которым вы делитесь, очень показателен.
Такая ситуация часто возникает, когда модель не получает чётких поведенческих ориентиров и работает в среде с низкой сигнальной насыщенностью. В результате — оптимальная стратегия сводится к полной пассивности.
В моём случае ключевой акцент был не на усложнении архитектуры, а на жёсткой формализации среды и структуры вознаграждения. Агент обучается только на сессиях с выраженной волатильностью, действие HOLD без позиции — штрафуется. Это формирует стратегию, в которой агент учится именно действовать, а не избегать риска.
Модели вроде Dreamer перспективны, но, по моему опыту, без чёткой поведенческой логики склонны к неопределённости или пассивности. Поэтому усложнение архитектуры имеет смысл лишь после стабилизации базовой стратегии.
ITS_HOT
05.08.2025 18:37Шикарно! Как раз в процессе написания подобного бота с применением RL, но планирую прикрутить также анализ новостей. Как считаете, достаточно будетанализировать тональность или прям текст векторизовать и подавать ему в качестве эмбеддингов?
quantAIengineer Автор
05.08.2025 18:37Спасибо, рад, что статья оказалась в тему.
Если речь о включении новостей как дополнительного канала признаков, то многое зависит от архитектуры. В целом, тональность — это агрессивная компрессия, которая может работать в простых конфигурациях, но теряет нюансы. Если есть возможность, то лучше использовать эмбеддинги из трансформеров, синхронизированные с рыночными окнами.
Для этого подойдут модели семейства sentence-transformers или специализированные финверсии типа FinBERT. Ниже минимальный пример извлечения эмбеддингов с mean pooling:
from transformers import AutoTokenizer, AutoModel import torch # Загружаем компактную модель из семейства Sentence Transformers model_name = "sentence-transformers/all-MiniLM-L6-v2" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModel.from_pretrained(model_name) # Текстовая новость (можно брать заголовок или краткое содержание) text = "Bitcoin breaks $70K amid macroeconomic pressure." # Токенизация с усечением и паддингом inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True) with torch.no_grad(): outputs = model(**inputs) attention_mask = inputs['attention_mask'] last_hidden = outputs.last_hidden_state # Вычисление эмбеддинга через среднее по токенам с учётом attention mask embeddings = (last_hidden * attention_mask.unsqueeze(-1)).sum(1) / attention_mask.sum(1, keepdim=True)
Полученный embeddings можно подавать в агент как часть входного состояния, синхронизированную по таймстемпу с рыночными признаками.
Если планируете двигаться в сторону полноценной мультимодальной модели, рекомендую обратить внимание на Perceiver IO. Он изначально спроектирован для совместной обработки разнородных входов (временные ряды, тексты, изображения) и позволяет гибко совмещать ценовые данные с текстовыми эмбеддингами в рамках единой архитектуры.
TryDotAtwo
05.08.2025 18:37Прикольно, то есть ставим на комп и зарабатываем или как?
Robomanus
05.08.2025 18:37Нет, ставишь на комп и допиливаешь в направлении постепенного перехода от улучшенного DQN к policy-based или модель-ориентированным методам, сопровождаемый:
-расширением данных и признаков,
-учётом неопределённости и риска в награде,
-реальным stream + execution контуром.
Таким образом надо:
1. Переписать replay-буфер: PER + β schedule; добавить n-step sampling.
2. Заменить ε-жадность на NoisyLinear; переобучить базовую сеть.
3. Докрутить Optuna для n-step, γ, β, noisy_σ; 200 trials достаточно.
4. Интегрировать risk-penalty в reward и пересчитать метрики.
5. Ветка A (механика): Transformer-encoder вместо CNN, затем C51.
6. Ветка B (action space): прототип SAC (continuous size), backtest 2025-03—06.
7. Развёртывание dry-run на Binance Testnet с real-time stream.
8. Собрать baseline Dreamer на тех же данных; сравнить sample-efficiency.
9. Выбрать победителя → live ключи под строгими лимитами.
lexa_15
05.08.2025 18:37зарегался чтоб плюсануть, оч круто !
quantAIengineer Автор
05.08.2025 18:37Спасибо, ценю ваш отклик. Рад, что проект оказался вам интересен.
Greem4ik
Было интересно почитать. Вопрос с чем связано такой маленький бэктест в 3 месяца, а не 1 год например? Ведь чем больше временной интервал, тем точнее полученный результат
quantAIengineer Автор
Спасибо за отличный вопрос.
В алгоритмической торговле действительно существует два конкурирующих подхода к выбору длины бэктест-периода. Приведу несколько аргументов в пользу ограниченного окна в 3 месяца.
Во-первых, крипторынок — крайне динамичная и нестационарная среда, особенно на внутридневном уровне. Его статистические свойства (среднее, дисперсия, волатильность) быстро меняются. Стратегии, основанные на паттернах, актуальных год назад, легко теряют свою эффективность сегодня. Поэтому чрезмерное удлинение периода часто снижает обобщающую способность модели к текущим рыночным условиям.
Во-вторых, есть объективные ограничения по данным. Например, на дату 2025-08-05 на Binance Futures активно торгуются 505 тикеров. Если же взять потенциальный годовой бэктест от последней даты сбора (2025-06-01), то на 2024-06-01 в торговле было лишь 247 тикеров. Это означает, что при расширении окна мы теряем до 51% информации — не в объёме котировок, а в рыночном разнообразии: многие современные паттерны просто отсутствуют, особенно для новых листингов.
Для сравнения, на классических рынках вроде NASDAQ, где торгуются ~3500 тикеров и существуют минутные данные как минимум с 2008 года, годовой бэктест более уместен. Но даже в этом случае всё необходимо проверять эмпирически. На практике (в том числе по моему опыту) перенос подходов, работающих на фондовых рынках, в крипту часто приводит к провалу — условия слишком разные.
Наконец, если обратиться к академическому и профессиональному сообществу, строгого правила по длине бэктеста нет. Всё чаще на первый план выходит не временной интервал, а достаточное количество сделок, охватывающих разные рыночные фазы, включая периоды drawdown. Такой подход даёт более честную оценку стратегии, чем расширение по времени.