Search
Write a publication
Pull to refresh

RL-агент для алгоритмической торговли на Binance Futures: архитектура, бэктест, результаты

Level of difficultyHard
Reading time32 min
Views1K
Кривая баланса на бэктестеДинамика капитала на отложенных рыночных данных (период: 2025-03-01 — 2025-06-01), с учётом комиссий и проскальзываний. Итоговое изменение баланса: +144.23%
Кривая баланса на бэктесте
Динамика капитала на отложенных рыночных данных (период: 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 в реальном времени.

📎 Код, данные:

2. Описание и подготовка данных

Общая логика формирования обучающего датасета

Ключевая идея заключается в том, чтобы обучать агента только на тех участках рыночной истории, где возникла значимая волатильность, потенциально создающая благоприятные условия для краткосрочной торговли.


Вместо непрерывного сэмплирования рынка, как это часто делается в обобщённых RL-реализациях, здесь отбираются локальные торговые сессии, привязанные к рыночным аномалиям.

2.1 Обоснование

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

  • сопровождаются быстрым движением цены более чем на 5% в течение короткого временного интервала;

  • возникают на фоне предшествующей рыночной инерции (стабильность до сигнала);

  • потенциально вызывают сильное продолжение импульса или резкий откат.

2.2 Логика извлечения торговых сессий

Каждая торговая сессия строится вокруг скользящего окна длиной 10 минут, где выполняется условие:

\text{Volatility} = \left| \frac{P_{\text{close}(t+10)} - P_{\text{close}(t)}}{P_{\text{close}(t)}} \right| \geq 0.05

Далее применяются фильтры:

  • Исключаются сигналы, окружённые шумом. Предшествующие 90 минут анализируются на отсутствие сильных движений:

\text{Filter} = \frac{1}{N} \sum_{i=1}^{N} \left| \frac{P_{i+10} - P_i}{P_i} \right| < \frac{\text{Volatility}}{\lambda}

Где \lambda — коэффициент контрастности (в данной реализации равен 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.

Train Example
Train Example
Validation Example
Validation Example
Test Example
Test Example
Backtest Example
Backtest Example

2.5 Механика обработки и нормализации данных

Формирование корректного состояния на каждом шаге взаимодействия агента с рынком требует строгой обработки данных, включая:

  • фильтрацию и сортировку каналов;

  • преобразование в относительные значения;

  • нормализацию;

  • логарифмические преобразования;

  • защиту от не числовых и нестабильных значений.

Все ключевые функции реализованы в модуле utils.py и являются частью единого data pipeline:

Функция

Описание

load_npz_dataset(path: str)

Загружает датасет из .npz, возвращая список сессий и метаинформацию

select_and_arrange_channels(...)

Выбирает подмножество каналов, например ['close', 'volume']

calculate_normalization_stats(...)

Вычисляет среднее и стандартное отклонение по каналам

apply_normalization(...)

Формирует финальную версию состояния для обучения агента

Вывод

  • Агент обучается не на всём рынке, а на отобранных высоковолатильных сессиях;

  • Структура данных воспроизводима и управляется конфигурацией;

  • Все этапы подготовки данных изолированы в модуле utils, обеспечивая чистую архитектуру и контроль качества входных данных.

3. Постановка задачи

Алгоритмическая торговля требует от агента принятия решений в условиях частичной информации, транзакционных издержек и изменчивой рыночной динамики.


В рамках данного проекта задача формализуется как эпизодическая задача обучения с подкреплением (RL), где агент учится оптимальной политике управления позицией в течение фиксированного временного интервала после появления сигнала высокой волатильности.

3.1 Формализация: RL как торговый процесс

Каждый эпизод начинается в момент возникновения сигнала высокой волатильности (см. раздел 2). Агенту предоставляется:

  • 90 минут рыночной истории — в качестве входных признаков;

  • 60 шагов торговой сессии — окно принятия решений (по одной минуте на шаг).

На каждом шаге выполняется следующий цикл взаимодействия агента со средой:

  1. Агент получает текущее наблюдение s_t​;

  2. На основе состояния s_t​ прогнозирует оптимальное дискретное действие a_t \in \{0, 1, 2, 3\};

  3. Среда возвращает:

    • новое состояние s_{t+1},

    • скалярную награду r_t​,

    • флаг завершения эпизода;

  4. Переход (s_t, a_t, r_t, s_{t+1}) сохраняется в буфер опыта;

  5. При достаточном заполнении буфера агент обновляет параметры Q-функции.

Формально цикл описывается следующей схемой:

s_t \xrightarrow{a_t} (r_t, \, s_{t+1})

3.2 Возможные действия агента

Код

Действие

Описание

0

Hold / Wait

Ничего не делать

1

Open Long

Открыть длинную позицию

2

Open Short

Открыть короткую позицию

3

Close Position

Закрыть позицию

Ограничения:

  • Нельзя открыть новую позицию, если активна текущая;

  • На последнем шаге сессии позиция закрывается принудительно.

Состояние: ( s_t \in \mathbb{R}^D )

Наблюдение включает:

  1. Нормализованные рыночные данные за последние N минут;

  2. Экстра-фичи: позиция, нереализованный PnL, прошедшее/оставшееся время;

  3. One-hot кодированная история последних действий.

# Пример формирования состояния в среде:
extras = [position, unrealized_pnl, time_elapsed, time_remaining]
state = np.concatenate([normalized.flatten(), extras, action_history_onehot])

Награда: ( r_t )

Формируется по формуле:

reward = (pnl_change / initial_balance) - inaction_penalty

Где:

  • pnl_change — реализованный доход при закрытии позиции;

  • inaction_penalty — штраф за бездействие.

Альтернативная математическая запись:

r_t = \frac{\Delta \text{PnL}}{\text{Initial Balance}} - \lambda \cdot \mathbb{I}_{\text{Hold, No Position}}

где ( \lambda = 0.001 ) — штраф за бездействие вне позиции.

3.3 Описание среды: step()

Функция step() в среде TradingEnvironment содержит всю необходимую логику, включая:

  • открытие / закрытие позиций,

  • расчёт прибыли,

  • комиссии,

  • проскальзывание.

3.4 Целевая функция: максимизация дисконтированной награды

Цель агента — стратегия:  \pi(a \mid s) максимизирующая ожидаемую кумулятивную награду:

\pi^* = \arg\max_\pi \mathbb{E} \left[ \sum_{t=0}^{T-1} \gamma^t \cdot r_t \right]

где:

  • ( T= 60 ) — длительность сессии;

  • ( \gamma = 0.99 ) — коэффициент дисконтирования.

3.5 Уравнение Беллмана в контексте трейдинга

Используется модифицированное уравнение Беллмана для Double DQN:

Q(s, a) \leftarrow r + \gamma \cdot Q_{\text{target}}(s', \arg\max_{a'} Q_{\text{policy}}(s', a'))

Реализация в 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, рынок вырос → ( r > 0 ), Q увеличивается;

  • Агент выбрал Hold при явной возможности войти → ( r \approx 0 ), Q снижается;

  • 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 Поток данных

  1. train.py:

    • загружает данные через utils.load_npz_dataset();

    • формирует среду: TradingEnvironment(data[i]);

    • инициирует агента D3QN и буфер PER.

  2. Внутри цикла:

    • агент получает state, выбирает action;

    • env.step(action)next_state, reward, done;

    • буфер сохраняет переход; вызывается agent.learn().

  3. model.py:

    • обрабатывает state через CNN;

    • вычисляется:

      Q(s, a) = V(s) + \left( A(s, a) - \frac{1}{|\mathcal{A}|} \sum_{a'} A(s, a') \right)

  4. replay_buffer.py:

    • обновляет приоритеты на основе TD-ошибок;

    • обеспечивает семплирование важных переходов через SumTree.

  5. Логгинг:

    • записываются reward, loss, ε, win_rate;

    • периодическая валидация - метрики сравниваются, сохраняется best.pth.

  6. Бэктест и оптимизация:

    • 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. Он разбит на логические блоки:

Конфиг-блок

Назначение

paths

Пути к данным, логам, моделям, артефактам

seq

Структура входных последовательностей

data

Каналы (OHLCV и доп.), нормализация

rl

Гиперпараметры RL обучения

model

Архитектура нейросети (CNN + Dueling)

trainlog

Параметры логирования и валидации

smart

Настройки стратегии выбора действия

...

Другие: market, device, eps, debug и т.д.

Каждый новый эксперимент описывается как отдельный 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-скрипт. Он следуют тем же правилам запуска, что и основной агент:

Модуль

Описание

baseline_cnn_classifier.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 шагов. Каждый шаг моделирует одну минуту торговли.

Переменная

Описание

balance

Текущий баланс агента (в USD)

position

Текущая позиция: 1 — Long, -1 — Short, 0 — нет

entry_price

Цена входа в позицию

realized_pnl

Совокупный реализованный PnL за эпизод

step_idx

Номер текущего шага в торговой сессии

closed_trades

Количество завершённых сделок

profitable_trades

Количество сделок с положительным PnL

history_actions

Последние 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, нормализованного на начальный баланс, плюс штраф за бездействие вне позиции.

r_t = \frac{\Delta \text{PnL}_t}{\text{InitialBalance}} - \lambda \cdot 1_{\text{Idle}}

Где:

  • ( \Delta \text{PnL}_t ) — прирост реализованной прибыли за текущий шаг

  • ( \lambda ) — штраф за бездействие

  • ( 1_{\text{Idle}} = 1 ), если агент вне позиции и выбрал 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 относительно среднего уровня действий в этом состоянии.

Формула:

Q(s,a) = V(s) + \left(A(s,a) - \frac{1}{|A|} \sum_{a'} A(s,a')\right)

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

Пример:

Рассмотрим два сценария:

  1. Пустое состояние ближе к концу сессии: цены стабильны, объёмы снижаются, сигналов нет.

    • V(s) будет низким: рыночная ситуация не представляет интереса.

    • A(s, a) ≈ 0 для всех a: ни одно из действий не даёт ощутимого преимущества.

  2. Сильный импульс на рынке: высокая волатильность и явная направленность движения.

    • V(s) будет высоким: ситуация потенциально прибыльная.

    • A(s, Long) >> A(s, Short): преимущество действия Long ярко выражено.

Таким образом, Dueling DQN позволяет агенту более чётко дифференцировать рыночные состояния, эффективно игнорировать нейтральные фазы и сфокусироваться на действительно ценных возможностях для входа в позицию.

Double DQN

Решает проблему переоценки Q-значений путём разнесения действий:

  • одна сеть выбирает действие (policy);

  • вторая оценивает результат (target).

Формула:

Q_{\text{target}} = r + \gamma \cdot Q_{\theta^-}(s', \arg\max_{a'} Q_{\theta}(s', a'))

Применяется в методе learn().

Prioritized Experience Replay (PER)

Обеспечивает фокусировку обучения на значимых переходах. Вероятность выборки пропорциональна TD-ошибке:

p_i \propto (|\delta_i| + \varepsilon)^\alpha

Где ( \delta_i ) — TD-ошибка, ( \varepsilon ) — сглаживающий параметр.

6.2 Класс агента D3QN_PER_Agent

Описан в agent.py. Реализует:

  • инициализацию policy и target сетей;

  • ( \varepsilon )-жадную стратегию;

  • буфер 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-функции используется гибридная архитектура, включающая:

  1. Сверточный блок (CNN) — извлекает краткосрочные рыночные паттерны из временных рядов;

  2. 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ᵢ, определяющий вероятность выборки:

P(i) = \frac{pᵢ^\alpha}{\sumₖ pₖ^\alpha}

где:

  • ( pᵢ = |δᵢ| + ε ) — TD-ошибка с защитой от нуля;

  • ( \alpha \in [0, 1] ) — степень приоритезации (( \alpha = 0 \rightarrow ) равномерная);

  • ( ε ) — положительная константа, исключающая нулевые приоритеты.

8.2 Реализация через SumTree

Для эффективного семплирования по приоритету используется дерево отрезков — SumTree. Это бинарное дерево, в котором каждый узел хранит сумму приоритетов поддерева.

Свойства SumTree:

  • Корень:

     \text{tree}[0] = \sum_i p_i

  • Листья: приоритеты отдельных элементов

Пример:

       [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 R_{episode} = \sum_{t=1}^{T} r_t

    Суммарная награда за эпизод. Отражает изменение баланса с учётом позиции, комиссии и проскальзывания.

  • Loss L = \mathbb{E}_{(s,a,r,s')} \left[ (Q(s,a) - y)^2 \right]
    Ошибка TD-обновления. Служит индикатором стабильности обучения. Рост может сигнализировать о переобучении или расхождении Q-функции.

  • Win Rate \text{WinRate} = \frac{\text{Положительных сессий}}{\text{Общее число сессий}}

    Процент торговых сессий, завершившихся положительным итоговым PnL.

Проект автоматически сохраняет визуализации всех ключевых метрик:

График

Назначение

training_rewards.png

Награда по эпизодам

training_losses.png

Ошибка TD обучения

training_win_rate.png

Процент успешных сессий

epsilon_decay.png

Динамика ε-жадности

Все графики сохраняются в папке output/plots/.

Пример: training_rewards.png

График отражает прогресс агента в процессе обучения на протяжении 55_000 эпизодов.
График отражает прогресс агента в процессе обучения на протяжении 55_000 эпизодов.

Анализ обучающей динамики:

  • Начало обучения: средние награды находятся около нуля. Это нормально — агент только начинает осваивать среду и действует почти случайно.

  • Положительная динамика: скользящее среднее демонстрирует плавный рост, а расширение верхнего хвоста распределения наград указывает на постепенное освоение стратегии. Несмотря на сохраняющуюся волатильность отдельных эпизодов агент уверенно начинает извлекать выгоду из структуры среды.

  • Конец обучения: к финалу обучения скользящее среднее стабилизируется выше нуля, без резких провалов — это признак того, что агент научился избегать грубых ошибок и всё лучше справляется с задачей принятия решений.

Итог:

  • Агент демонстрирует стабильное обучение, однако тренд указывает на то, что потенциал далеко не исчерпан.

После завершения обучения сохраняется:

  • лучшая модель best.pth (по метрике на валидации);

  • финальная модель, которая фиксирует веса в конце обучения final.pth.

Вывод

Обучающий pipeline выстроен по всем стандартам индустриального RL:

  • регулярная валидация и логирование;

  • контроль динамики метрик;

  • автоматическое сохранение моделей;

  • полная воспроизводимость через конфигурационный запуск.

10. Тестирование и визуализация

Оценка качества обученного агента проводится в модуле test_agent.py на отложенных, ранее не встречавшихся данных (test_data.npz).

Цель — проверить способность модели обобщать стратегию на новых рыночных сценариях без утечек данных из train/val.

10.1 Структура скрипта test_agent.py

Последовательность действий:

  1. Загрузка тестового датасета.

  2. Подготовка входных данных.

  3. Инициализация среды с тестовыми сессиями.

  4. Загрузка лучшей обученной модели.

  5. Прогон заданного числа тестовых эпизодов.

  6. Расчёт метрик, отрисовка результатов, сохранение артефактов.

10.2 Примеры сессий

Скрипт отрисовывает лучшие и худшие торговые сессии по заданной метрике (pnl или win_rate).
Отрисовка выполняется через plot_sessions().

На каждом графике отображаются:

  • кривая цен закрытия;

  • вертикальная линия начала сессии;

  • действия агента в виде цветных маркеров: ◦ серый — Hold ◦ зелёный — Long ◦ синий — Short ◦ красный — Close

  • подпись: тикер, время, метрика (PnL или win rate)

Пример: profitable_session_1

Profitable Session 1
Profitable Session 1
  • Тикер: HIGHUSDT

  • Агент открыл LONG практически в момент локального минимума и зафиксировал прибыль на резком отскоке вверх.

  • Результат: +1805.02 USDT

Пример: profitable_session_2

Profitable Session 2
Profitable Session 2
  • Тикер: COOKIEUSDT

  • В данной сессии агент закрывает две прибыльные позици, вначале открывает LONG эксплуатируя сильный положительный импульс, далее фиксирует прибыль и открывает короткую позицию которую также закрывает в плюсе.

  • Результат: +2562.19 USDT

Пример: unprofitable_session_1

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 превосходит заданный порог уверенности:
    Adv(a)=Q(a)−Q(Hold)>Threshold

  • Ensemble Q-Filter (MC Dropout), выполняется несколько стохастических проходов по сети:

    • вычисляются средние Q-значения и дисперсия (неуверенность);

    • действие принимается, только если одновременно выполнены условия по уверенности и допустимому уровню неопределённости (σ): Adv(a)>θa​,Uncertainty(a)<σmax​, такой подход имитирует логику "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 (данный график уже был продемонстрирован в начале статьи) и полный лог сделок.

Backtest Balance Curve
Backtest Balance Curve

Агент демонстрирует сбалансированный профиль риск-доходности:

  • достигнута итоговая доходность +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.

Что делает агент:

  • Мониторит рынок по всем тикерам, каждую минуту.

  • Фиксирует сигналы: публикует тикер, направление сделки и уровень уверенности.

  • Входит и выходит из позиции при оптимальном соотношении риск/награда.

  • Публикует результат сделки: прибыль или убыток.

Live Signal + Prediction                 |                Verification Example
Live Signal + Prediction | Verification Example

Вы можете:

  • Наблюдать за поведением агента в реальных рыночных условиях.

  • Анализировать его логику для совершенствования навыков торговли.

  • Использовать его аналитику для принятия решений.

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

Проект является исследовательским и предоставляется бесплатно исключительно в образовательных целях.

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-агент.
Вам необходимо добавить:

  1. Поток данных → агент начнёт видеть рынок в реальном времени;

  2. Модуль исполнения → агент сможет действовать и зарабатывать.

Реализация этих модулей это отличная возможность усовершенствовать свои навыки и глубже понять:

  • работу с API бирж;

  • потоковую обработку данных;

  • надежную автоматизацию и fault-tolerant дизайн.

Начните с малого: напишите стример данных в базу и модуль dry-run исполнения.
Вы удивитесь, как быстро из прототипа вырастает полноценная торговая система.
Если вы дошли до этого раздела, то я хочу вас поздравить, вы уже на другом уровне.


Теперь ваша следующая задача - завершить систему и стать архитектором собственной AI-трейдинговой платформы.


Исходный код, датасеты и работа агента в режиме реального времени:

Tags:
Hubs:
+5
Comments3

Articles