- BrainTools - https://www.braintools.ru -

Речь пойдёт о разработке системы алгоритмической торговли с использованием обучения [1] с подкреплением [2] (reinforcement learning [3], далее RL). Пройдём путь от идеи до работающей системы с разумной детализацией. Расскажу про свои ошибки [4]: радовался single-seed успехам, верил Claude Code на слово, считал лучшую модель годной для production, в то время как на самом деле это был верхний квартиль сид-шума. Внутри Mixture-of-Experts по рыночным режимам, shadow-routing на rolling Sharpe, hybrid expert-swap, методология валидации, а также небольшой список всего того, что не сработало. Подойдёт в качестве инструкция для ai-агента, чтобы повторить опыт [5] и понять почему оно работает.
Это исследовательский проект, целью которого была проверка гипотезы, что на основе RL можно построить что-то работающее с возможностью дальнейшего совершенствования, т.к. в открытом доступе ничего работающего найти не удалось. Не являюсь профессионалом квант-трейдером, поэтому для экспертов рассуждения могут казаться дилетантскими. Буду рад разумной критике и рекомендациям. Какие-то детали оказались за пределами содержания для экономии, т.к. получили статус “очевидно”. Это не статья про то как обыграть рынок и заработать, а попытка реализовать и объяснить почему сработало.
В разработке системы и подготовке материалов мне помогал Claude Code [6], поэтому немного уделю внимание [7] этой совместной работе. Список заслуг с чем Claude справлялся на пути к результату:
Эксперименты. Если у вас полно идей, но нет времени на их реализацию или вы теряете мотивацию [8] на половине пути, то производительные coding-агенты приходят к вам на помощь. После согласования задачи с агентом тот запускал десятки параллельных экспериментов, агрегировал информацию, аккуратно оформлял результаты в виде отчётов, сопровождал результаты своими выводами и предложениями. Здесь базовых возможностей сlaude code cli, skills, tools, mcp [9] как правило хватало.
Research. Разбираться к нюансах важно, но первые несколько итераций поиска и проверки идей/подходов может выполнить ai-агент. Пришлось усовершенствовать помощника и добавить skill (planning-with-files [10]) и mcp (mcp-sequential-thinking [11], sequential-thinking-tools [12], searxng [13]). В результате обзор статей с arxiv делался прямо в консоли или через аккруратный markdown-файл со всеми необходимыми ссылками.
Code. Тут вроде бы всё очевидно. Однако я бы хотел обратить внимание на тот факт, что AI-агент может написать или сконфигурировать инструменты, которые упрощают решение задачи. Так на смену tensorboard сначала пришли интерактивные графики, а затем и продвинутые дашборды, где я мог распутать AI-агента, когда тот потерпел очередную неудачу на пути решения поставленной слишком абстрактно задачи.
RL для трейдинга обычно звучит как обещание финансовой сингулярности, когда алгоритм сам торгует, сам себя совершенствует, а вы изымаете прибыль. На практике это красивые картинки внутри обучающей выборки и провал в период кризиса или существенного изменения консенсуса/сентимента. Во всяком случае мой опыт таков:
код из репозиториев нерабочий;
результат из статей не воспроизводятся;
выбираем другой seed и получаем доходность с противоположным знаком;
SOTA с конференции хуже градиентного бустинга;
ноутбуки с золотыми медалями с kaggle требуют массу данных, которые даже с подпиской не достать.
Может мне не повезло. Каждая новая попытка откладывалась на будущее. Будущее наступило с подпиской 5xMAX. Далее на примере гипотез, допущений и экспериментов получилась архитектура ещё одной торговой системы.
Большинство экспериментов документированы, но всё же не все. Идентификатор последнего 137. Детали всего того, что было до 80 эксперимента безвозвратно утеряны и остались только короткие записи в findings.md, т.к. однажды я попросил Claude почистить мусор. Подкаталог docs/experiments/оказался в .gitignore. Claude посчитал мусором всё то, что не относится к текущей задаче и не описано в CLAUDE.md.
Могучий AI мог бы всё сделать сам?! Возьми DDPG [14], сырые OHLCV [15], несколько миллионов timesteps и получим работающую систему! Не сработало, поэтому далее я упрощал задачу для RL-агента, чтобы тот из шума рыночных данных смог выделить сигнал и на нём научиься торговать.
Первое допущение, исходя из которого будем действовать дальше:
По своей природе обучение с подкреплением подходит для задачи автоматизации торговли финансовыми активами. Воспользуемся RL не в качестве “оптимального решения”, а как наименее фатальный тип ошибки. Из пространства допущений, которые все одинаково ложны при торговле на реальном рынке, MDP-допущение – наиболее устойчиво к частичным нарушениям.
Reinforcement Learning формулирует задачу так: есть агент, который взаимодействует со средой по шагам. На каждом шаге агент видит состояние s_t, выбирает действие a_t согласно своей политике π(a|s), среда возвращает награду r_t и новое состояние s_{t+1}. Цель обучения – максимизировать суммарную ожидаемую награду по всем траекториям:
γ ∈ [0, 1]– фактор дисконтирования. Ключевое отличие от supervised-learning: нет размеченных примеров правильных действий. Есть только скалярный сигнал награды, и агент сам должен выяснить, какие действия его максимизируют.
Торговля – это Markov Decision Process [16] (или Partially Observable MDP), а не supervised prediction problem [17]. Развернём:
Существует s_t, который содержит достаточно информации, чтобы оптимальное действие a*_t было функцией только от s_t (а не от всей истории целиком). То есть марковское свойство [18] приблизительно выполнено на выбранном наблюдении.
Целевая функция при этом зависит от всей цепочки шагов: оптимизируем , а не единственную награду на момент
r_t. Действие сегодня меняет s_{t+1}, которое меняет доступные действия и награды завтра.
Политика π(a|s) представима параметризованной моделью умеренной ёмкости (в нашем случае MLP 256×256), и существуют данные для обучения этой модели так, чтобы она обобщалась на будущий период.
Можно:
Применять множество RL-алгоритмов.
Напрямую оптимизировать зависимые от последовательности действий метрики (sharpe, drawdown penalty, turnover cost).
Выучить реактивное поведение [19] – быть осторожнее после просадки, агрессивнее после восстановления, и всё это без явного программирования чётких правил.
НЕ следует:
В рынке есть истинная функция цены от фичей, а могучий RL нам её найдёт. Это мышление [20] из классического обучения с учителем. В RL нет аналога истинных значений целевой функции – только награда, зависящая от различных последовательностей шагов.
Увеличение сети или операций – путь к лучшему результату. На самом деле если мы при плохо сконструируем состояние рынка для агента, то наш алгоритм переобучится ещё быстрее за счёт глубокой сети или большого бюджета вычислений.
Функция награды (далее reward) – это просто метрика. На самом деле reward – это полная и формальная спецификация того, чего мы хотим, со всеми известными RL-патологиями (агенту проще сидеть в деньгах, терпеть катастрофические просадки ради потенциальной выгоды или действовать вовсе неразумно, потому что вы ошиблись с постановкой).
На текущем этапе добавляем:
python как основной язык разработки;
gymnasium как базовая библиотека моделирования торговой среды;
stable-baselines3 в качестве библиотеки с готовыми реализациями различных RL-алгоритмов;
ta-lib для импорта функций рассчёта традиционных индикаторов (например, bb_position [21]);
tensorboard для глубокого анализа метрик и траекторий обучения;
сырые данные можно взять на yahoo finance [22] (дневные котировки без ограничений).
Очередное допущение:
Торговать будем не отдельным активом, а портфелем ценных бумаг
Поможем нашему агенту и заранее дадим возможность диверсифицировать риски. У него должна быть возможность приобретать защитные активы в период кризиса и делать ставку на рост в спокойные времена.
Пусть наша торговая стратегия – это функция, которая принимает историю цен и отдаёт веса активов для ребалансировки портфеля. Классический ML-подход: предсказывать будущие доходности, оптимизировать портфель по прогнозу (mean-variance, Black-Litterman [23]).
Задача не дифференцируема по целевой метрике. Sharpe ratio, Calmar, MaxDrawdown – это свойства траектории, а не одного шага. Минимизация MSE на предсказание доходности – прокси, который плохо коррелирует с Sharpe финальной стратегии.
Действия сегодня меняют состояние завтра. Комиссия за ребалансировку, проскальзывания, высокий бумажный доход после хорошей недели и просадка после плохой. Классический “predict-then-optimize” эту динамику игнорирует.
Reward shaping – нативный рычаг. В RL вы можете напрямую написать: “награждай за высокий Sharpe за последние N шагов, штрафуй за просадку и ребалансировку”. Это формализуется в reward(s_t, a_t), и агент сам выучит политику, оптимизирующую такую композицию.
Исследование включёно по умолчанию. Политика стохастическая (в случае SAC) – агент пробует вариации от своей оптимальной стратегии, что в финансах полезно: детерминированные правила склонны попадать в “выученные” паттерны, которые перестают работать.
Торговать будем на дневных данных
На рынке каждое мгновение что-то происходит и это отражается в цене активов. Чем выше дискретность событий, тем больше там шума. Дабы скомпенсировать пространство возможных решений, время и доступные ресурсы – используем дневные данные [24].
Для создания эффективной торговой стратегии (выучивания оптимальной политики) следует взять активы из различных секторов и с минимальной корреляцией между собой.
|
Тикер |
Класс |
Зачем |
|
BTC |
Криптовалюта |
Высоковолатильный risk-on актив |
|
NDX |
Индекс |
Технологический рост |
|
XAU |
Металл |
Классический safe-haven |
|
XAG |
Металл |
Металл с свойствами промышленного сектора |
|
LCOc1 |
Нефть |
Сырьевой цикл |
|
TLT |
Долгосрочные облигации |
Отрицательная корреляция с акциями в кризис |
Изначально там был природный газ (NG) вместо облигаций (TLT), но агент так и не научился его торговать из-за высокой волатильности. К тому же для портфеля нужен был явный safe-haven. Торговля медью (HG как промышленный металл) также показала себя хуже в сравнении с серебром (XAG).
|
BTC |
NDX |
XAU |
XAG |
TLT |
LCOc1 |
|
|---|---|---|---|---|---|---|
|
BTC |
1.000 |
0.276 |
0.100 |
0.147 |
−0.018 |
0.073 |
|
NDX |
0.276 |
1.000 |
0.085 |
0.220 |
−0.091 |
0.212 |
|
XAU |
0.100 |
0.085 |
1.000 |
0.763 |
0.242 |
0.100 |
|
XAG |
0.147 |
0.220 |
0.763 |
1.000 |
0.111 |
0.156 |
|
TLT |
−0.018 |
−0.091 |
0.242 |
0.111 |
1.000 |
−0.182 |
|
LCOc1 |
0.073 |
0.212 |
0.100 |
0.156 |
−0.182 |
1.000 |
средняя попарная корреляция: +0.146 (сильная диверсификация – типичный multi-asset институциональный портфель имеет ~0.3–0.5);
медиана: +0.111;
максимум: +0.76 (XAU ↔ XAG – внутрикластерно);
минимум: −0.18 (TLT ↔ LCOc1 – бонды vs нефть).
В RL-арсенале есть on-policy методы (PPO, A2C, TRPO) и off-policy (DQN для дискретных действий, DDPG/TD3/SAC для непрерывных). Для торговли целевыми весами портфеля нам нужны непрерывные действия, поэтому on-policy-методы не применимы напрямую, а из off-policy выбор сужается до DDPG/TD3/SAC.
SAC [25] (Soft Actor-Critic) – это off-policy, actor-critic метод с тремя важными особенностями:
Maximum entropy objective: политика максимизирует не только ожидаемую награду, но и свою энтропию. Это поддерживает мотивацию [26] агента исследовать на протяжении всего обучения – критично, потому что в нашей среде эпизоды обучения короткие, и агент склонен схлопываться в локальный максимум слишком быстро. Обращаю внимание, что для дневных данных не так уж и много истории в масштабе обучения RL-агентов. Особенно если учесть предварительную обработку, то у меня в обучающей выборке оказалось около 2300 дней на каждый тикер.
Replay buffer (off-policy): позволяет переиспользовать исторические переходы из состояния в состояние для обучения. В нашем случае это критично из-за редкости некоторых режимов – без буфера агент бы их вовсе мог не встретить. Режимам далее уделим особое внимание.
Непрерывное действие через stochastic policy – actor-сеть выдаёт параметры нормального распределения, из которого выбирается действие. Это создаёт градиент для обучения.
На практике я сравнивал SAC с PPO, TQC, TD3, DroQ и PopArt-SAC. Результаты: PPO (через Gaussian policy) оказалася наименее эффективен для малого количества примеров и никак не сходился, TQC сильно переобучался, DroQ был слишком консервативным, PopArt давал примерно то же, что и классический SAC. Наверняка можно обучить рабочий алгоритм (попасть в локальный максимум) с любым из перечисленных методов, и встанет вопрос устойчивости политики. В далёком прошлом как-то мне удалось обучить RL-алгоритм, который давал замечательные результаты и это был не SAC, но модель оказалась невоспроизводима.
State – вектор признаков (технические индикаторы, cross-sectional rank, carry-фичи) плюс состояние портфеля (текущие веса, cash ratio, equity slope, drawdown от пика). Финальная размерность: 60 фичей.
Action – непрерывный вектор длины 6 (число тикеров), нормализованные значения [-1, 1], которые переводятся в целевые веса активов в портфеле.
Reward – композиция из различных подходов. Основа – логарифмически преобразованная доходность портфеля. Добавки: RCAA (cross-sectional alignment с режимом), drawdown penalty, DSR (онлайн-градиент Sharpe).
Episode – случайная длина от 100 до 200 торговых дней. Агент стартует с начальным капиталом, ежедневно предпринимает действие, получает награду, а в конце эпизода позиции закрываются для подсчёта метрик. Это непохоже на реальный рынок (в реальности у нас бесконечный эпизод), но нужно для обучения, а иначе replay buffer переполнится одной длинной последовательностью действий.
Инициализируется случайная политика π_0.
Агент исполняет в среде N шагов (collection), складывая (s, a, r, s') кортеж в replay buffer.
Из буфера выбирается минибатч, на нём делается один шаг градиентного спуска для actor (обновить π) и critic (обновить Q-функцию).
Повторять [27] с шага 2, пока не достигнем заданного числа N или не сработает early stopping по валидационной метрике.
SAC использует простой MLP для actor (pi) и critic (qf):
|
Сеть |
Hidden layers |
Активация |
|---|---|---|
|
Actor |
|
ReLU |
|
Critic (Q-function) |
|
ReLU |
numpy.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
random.seed(seed)
env.reset(seed=seed)
gym.utils.seeding.np_random(seed)
Что ещё нужно добавить в торговую систему (все параметры ниже нормализованы, либо жёстко обрезаются левая и правая границы):
|
Группа |
dim |
Детали |
|---|---|---|
|
Рыночные фичи × 6 тикеров |
48 |
close_price, macd_signal, day_sin, price_vs_ema50, atr_14, price_change_1d, rsi_14, bb_position * подробные формулы приводить здесь не буду, т.к. реализация стандартная (можно взять в ta-lib [28]) ** не используется объём торгов и фичи, которые на нём основаны |
|
Вес позиции × 6 тикеров |
6 |
Текущий вес позиции в момент времени |
|
Cash-ratio |
1 |
Доля денежных средств в NAV (чистая стоимость активов) |
|
Time since last rebalance |
1 |
Насколько давно агент в последний раз ребалансировал портфель |
|
Last action turnover |
1 |
Значение последней “значимой” ребалансировки (такой, что превысила порог 0.02). Обновляется только на шагах ребалансировки и в промежутках остаётся прежней |
|
Pending rebalance mass |
1 |
Среда ведёт список |
|
Equity-slope |
1 |
Для последних 20 значений NAV:
Семантика: equity_slope > 1: агент в устойчивом восходящем тренде – NAV растёт быстрее, чем собственная волатильность equity_slope ≈ 0: NAV болтается, нет чёткого направления equity_slope < −1: доходность падает, проигрываем |
|
Drawdown |
1 |
Текущая просадка от максимально достигнутой доходности за эпизод |
|
Итого |
60 |
|
Хорошей практикой является подход к чтению информации в Интернете с запиской на полях “как именно ты меня обманываешь?”. Если приложить достаточно усилий, то конфигурацию торговой системы (гиперпараметры алгоритма для распределения тестовой выборки) можно подобрать под будущее поведение [29] рынка.
Модель, которая работает только в идеальных условиях – это переобучение
Для того, чтобы получить достоверный результаты, были предприняты следующие шаги:
никаких bfill [30], даже самых незначительных (запрещено на уровне CLAUDE.md);
gap между выборками минимум 30 торговых дней;
фичи нормализуются с rolling Z-score по обучающей выборке;
детектор режимов рынка обучается только на train выборке, сохраняется как артефакт и впоследствии применяется в режиме read-only при работе с валидационной и тестовой выборками;
walk-forward валидация на 5 окнах;
не менее 3 seed для каждого окна;
in-sample валидация на обучающей выборке;
у каждой метрики должна быть вторичная её подтверждающая;
тестовую выборку алгоритм и его производные (например, детектор режимов) на этапе обучения не видит никогда.
Далее приведу таблицу с окнами. Тренировочкая выборка и окно валидации располагаются с gap в 30 дней до тестового периода.
|
Окно |
Тестовый период |
Контекст |
|---|---|---|
|
WF1 |
2020-07 → 2021-06 |
Восстановление после COVID, восходящий тренд |
|
WF2 |
2021-07 → 2022-06 |
2022, медвежий рынок – ключевойстресс-тест |
|
WF3 |
2022-07 → 2023-06 |
Смешаный режим, сниженные ставки и восстановление |
|
WF4 |
2023-07 → 2024-06 |
Бычий рынок в технилогическом секторе и криптовалютах |
|
WF5 |
2024-09 → 2026-04 |
Настоящее время |
В обязательном порядке нужно с чем-то сравниваться. Если посмотреть внимательно на избранные активы в портфеле, то все они идут вверх и вправо. Залог успеха любой торговой стратегии! Поэтому нам нужен сильный бенчмарк. Регулярно прогоняем полный список для лучшей модели:
buy-and-hold;
equal-weight с ежемесячной ребалансировкой;
inverse-volatility D/W/M;
momentum × inverse-volatility (D/W);
long-short варианты momentum × inv_vol.
Правило: если агент не обгоняет лучший из этих бенчмарков на том же периоде, с теми же комиссиями, с тем же risk-free rate – модель не становится новым baseline.
Весьма уверенные результаты бенчмарков на WF5 (2024-09 → 2026-04), 0.04% комиссия, risk-free rate 8% (), проскальзывание
±50% вокруг базы:
|
Strategy |
Sharpe |
MDD |
|---|---|---|
|
equal_weight weekly |
1.248 |
−13.4% |
|
equal_weight daily |
1.215 |
−13.8% |
|
inverse_vol weekly |
1.158 |
−9.3% |
|
inverse_vol monthly |
1.086 |
−8.5% |
|
equal_weight buy & hold |
1.025 |
−19.4% |
Что если модель случайно приходит к пропорциональной аллокации, близкой к равновесному портфелю, и её “альфа” – просто эффект возврата к среднему или постоянный выбор “правильных” активов, а не своевременная покупка?
Метод: берём реальные действия модели (веса per-ticker per-day) и перемешиваем даты 100 раз, сохраняя распределение весов на каждый день. Считаем Sharpe shuffled-траекторий.
Пример результата:
shuffled sharpe: 0.144 ± 0.23;
реальный sharpe: 1.097;
4.2σ значимость – timing создаёт реальную альфу.
Дополнительно: средние веса модели (timing-stripped, monthly rebalance) дают Sharpe 1.11 – хуже чем equal-weight monthly (1.29). Это значит: модельная альфа – это полностью daily timing-решения, а не «умный средний вес».
Это один из самых важных тестов, который производился для оценки достоверности модели. Если shuffle-test не показывает значимости – модель не работает, независимо от того, как выглядят её NAV-графики.
На примере устойчивой модели торговой системы (метрики на WF5 + mean WF Sharpe):
|
Метрика |
Значение |
Пояснения |
|
Sharpe WF5 |
1.452 |
Доходность на единицу общего риска. Отвечает на вопрос “какую доходность получаем на единицу волатильности“. Непонятно на сколько WF5 репрезентативный участок для итоговой оценки торговой системы, поэтому ориентируемся на Sharpe 0.9-1.5 на горизонте в 1 год |
|
Sharpe |
0.916 |
|
|
CAGR WF5 |
35.77 |
Эквивалентная годовая ставка сложного процента |
|
MDD WF5 |
13.58 |
Максимальная просадка от локального пика до последующего минимума (в процентах от пика) |
|
Calmar WF5 |
2.635 |
Сколько процентов годовой доходности получаем на каждый процент максимальной исторической боли [31] |
|
Sortino WF5 |
1.721 |
То же, что и Sharpe, но игнорирует upside volatility (наказывает только убытки) |
|
Win Rate WF5 |
66% |
Частота прибыльных сделок Trades: 1056 BUY/SELL | HOLD: 1356 | Win Rate (SELL): 359/548 (66%) | 401 days |
|
CV 3seed |
0.2 |
Разброс Sharpe между несколькими тренировками для одной архитектуры с разными random seeds |
Несколько тезисов, исходя из которых следует менять веса в портфеле:
размером позиции нужно управлять таким образом, чтобы никакой кризис не обнулил ваш портфель;
консервативный размер позиции напрямую лишает доходности;
завышая риск мы превращаем торговлю в лудоманию.
Неоднозначно. В итоге в системе по умолчанию отключены возможности торговли с плечом, а также короткие позиции.
[SAC actor outputs]
↓ raw action w_raw ∈ R^6
Action mapping:
w_target = 0.25 · tanh(w_raw / 0.25)
→ target weight ∈ [-0.25, +0.25] (доля NAV на тикер)
↓
Long-only clip:
w_target = clip(w_target, 0, 0.25)
↓
Turnover soft-cap (если |Δw| > 0.3 суммарно):
w_target = smooth_cap(w_target)
↓
[Конвертация доли → доллары]:
position_dollars = w_target × current_nav
↓
Position sizing guard:
nav_for_cap = max(current_nav, 0.8 × peak_nav)
hard_cap = max_position_value_pct × nav_for_cap (= 2.0 × nav_for_cap)
position_dollars = min(position_dollars, hard_cap)
↓
Execution (market orders)
Любые искусственные ограничения со стороны среды (например, stop loss или post clipping весов, которые выдал агент) советую вводить на этапе инференса. Так вам удастся избежать расхождения политики RL-агента, когда предпринято одно действие, а среда получает другое и реагирует на действие иначе. Правила для action space:
turnover soft-cap: если L1-delta между текущими и новыми весами > 0.3, применяется softmax-smoothing;
rebalance_epsilon=0.01: если целевой вес портфеля отличается от текущего меньше чем на 1%, то ребалансировка не происходит;
weight_smoothing_alpha=0.2: EMA-сглаживание целевых весов между шагами.
Универсальный состав фич мне подобрать не удалось. Вероятно я застрял в неком локальном оптимуме. Ниже два набора и пример существенной разницы в результатах.
custom_test_b = [
'price_vs_ema50', 'bb_width', 'atr_14_norm', 'up_days_10',
'vol_5d'
]
optimal_9_1d = [
'close', 'price_vs_ema50', 'atr_14', 'rsi_14',
'macd_signal', 'bb_position', 'day_sin', 'price_change_1h',
]
|
Метрика |
custom_test_b |
optimal_9_1d |
Δ |
|---|---|---|---|
|
CAGR (valid period) |
15.88% |
39.45% |
+23.57pp |
|
CAGR (full data period) |
23.42% |
60.37% |
+36.95pp |
|
Sharpe (valid period) |
0.32 |
1.22 |
+0.90 |
|
MDD (valid period) |
21.3% |
15.93% |
−5.37pp |
Есть различные техники оценки, но в итоге я остановился на optimal_9_1d. Интуитивный подбор, ablation feature importance [32] и прочие техники не принесли результатов на том бюджете времени, что я этому этапу отвёл. В тоже время ни один reward-tuning, ни один алгоритмический (TQC/DroQ/PopArt-SAC) эксперимент, ни extended training (500k vs 100k шагов) не дали и десятой доли такого роста. Пространства для дальнейших оптимизаций достаточно.
Гиперпараметры – это важно, но в основном для того, чтобы не испортить модель
Ниже итоговая формула награды эксперта. В последующих главах будет много подробностей о её компонентах.
r_t = portfolio_reward(ret_t, loss_mul)
+ rcaa_reward(alpha_over_basket, regime, scale=5.0, signs={1:+1, 2:-1, 3:+1})
+ dsr_reward(sharpe_gradient, scale=1.0, halflife=20)
+ drawdown_penalty(current_dd, cap=0.15, scale=2.0)
- turnover_cost(delta_weights, soft_cap=0.3, scale=0.05)
- transaction_fees(trades, fee_bps=4.0 + slippage_bps)
Компоненты:
|
|
|
|
|
|
|
|
|
|
|
EWMA Sharpe с |
|
|
|
Квадратичный штраф за просадку |
В паре с единственным агентом успеха достичь не удалось. Я объяснил для себя это запредельным шумом в данных, который я сам и породил через обилие фич и тяжёлого описания состояния среды. В тоже время без этого RL-агент либо переобучался, либо недообучался. В лучшем случае получалась equal weight стратегия, но финансовые рынки ведут себя радикально по-разному в разных режимах:
защитное поведение на падающем рынке;
агрессивное накопление во время роста;
удержание позиций в боковике;
осторожный набор во время хаоса.
Отсюда возникла идея Mixture-of-Experts с режим-детектором.
Забегая вперёд я попробовал HMM [33], MLP [34] и даже отдельного RL-агента, но все они оказались хуже строгого классификатора на основе правил. Четыре макрорежима:
|
Режим |
ID |
Идея |
|---|---|---|
|
sideways |
0 |
Отсутствие тренда и низкая волатильность |
|
uptrend |
1 |
Направленное движение вверх |
|
downtrend |
2 |
Устойчивый нисходящий тренд |
|
chaotic |
3 |
Высокая волатильность без тренда |
2 режима (bull/bear) – проигрывает, потому что boring-sideways и chaotic-bear требуют разного поведения; объединение этих периодов в одного Эксперта даёт среднее и немотивированное поведение.
5–6 режимов (high-vol uptrend, high-vol downtrend, high-vol chaotic) – провалились в следствии недостатка данных. После разметки исторического окна 2017–2024 для чистого high_vol_uptrend осталось 6 дней. Можно конечно двигать ползунки, но радикально это сути не меняло и результаты были невыдающиеся. На 5 режимах (+downtrend_volatile) новый период в 99 дней эффекта не возымел.
Итоговый классификатор работает на 6 признаках. По тому как оно работает можно ещё одну статью написать, поэтому буду краток:
|
№ |
Признак |
Описание |
|---|---|---|
|
F1 |
ROC-10 |
10-дневная rate of change рисковых тикеров (NDX, BTC) |
|
F2 |
log_vol_ratio |
log(vol_20d / vol_60d) – short-term vs long-term vol |
|
F3 |
VoV (vol-of-vol) |
двух-EMA метрика вариации волатильности |
|
F4 |
VIX z-score |
VIX, стандартизированный по 252-дневному rolling-окну |
|
F5 |
avg pairwise corr |
Средняя парная корреляция по risky pair’ам (60-дневное окно) |
|
F6 |
signal ROC |
10-day ROC сигнальных тикеров |
log_ret[t, i] = log(close[t, i] / close[t-1, i]) # per-ticker log-returns
roc10[t, i] = Σ log_ret[t-9:t+1, i] # 10-day rolling sum
F1[t] = mean_i(roc10[t, i]) # cross-ticker average
market_tickers – подмножество для усреднения. Сейчас используется ['NDX', 'BTC']. Остальные тикеры исключены, чтобы F1 не подавлялся контрциклическим движением защитных активов в кризисы. F1 играет роль термометра аппетита к риску, а не среднее по всему портфелю.
Роль в классификации: F1 вместе с F6 разделяет трендовые режимы (uptrend/downtrend) от sideways/chaotic. Интерпретация:
F1 > +0.05 (~5% за 10 дней): устойчивый uptrend в risk-активах
F1 ≈ 0: боковик или смешанное движение
F1 < −0.05: медвежий тренд
log_ret[t, i] = log(close[t, i] / close[t-1, i])
vol_20[t, i] = std(log_ret[t-19:t+1, i]) × √252 # annualized
vol_60[t, i] = std(log_ret[t-59:t+1, i]) × √252
mean_vol_20[t] = mean_i(vol_20[t, i]) # cross-ticker mean
mean_vol_60[t] = mean_i(vol_60[t, i])
F2[t] = log(mean_vol_20[t] / mean_vol_60[t])
Измеряет ускорение/замедление волатильности:
F2 > 0: 20-дневная волитальность выше 60-дневной → рынок нагревается
F2 ≈ 0: стабильная волатильность
F2 < 0: волатильность падает → рынок остывает
Отражает разницу между нарастающей волатильностью и стабильной. Вместе с F3 формирует группу ориентированных на волатильность признаков, которые показывают степень хаоса на рынке, а не только направление.
per_vol[t, i] = std(log_ret[t-20:t+1, i]) × √252 # 21-day annualized vol per ticker
mean_vol[t] = mean_i(per_vol[t, i]) # cross-ticker mean vol
F3_batch[t] = std(mean_vol[t-19:t+1]) # 20-day rolling std of vol itself
r²_t = mean_i(r_t,i²) # cross-ticker mean squared return
fast_var_t = λ_fast · fast_var_{t-1} + (1-λ_fast) · r²_t # fast EMA variance, halflife=10
slow_var_t = λ_slow · slow_var_{t-1} + (1-λ_slow) · r²_t # slow EMA variance, halflife=40
vov_raw_t = |fast_var_t − slow_var_t|
F3_t = λ_smooth · F3_{t-1} + (1-λ_smooth) · vov_raw_t # smoothing, halflife=20
Измеряет насколько сама волатильность варьируется. Т.е. не как сильно двигаются цены, а насколько непредсказуемы движения между днями. Интерпретация:
низкий F3: устойчивый режим (стабильный тренд или устойчивый боковик) – волатильность сегодня ≈ волатильности вчера;
высокий F3: хаотичный режим – сегодня тишина, завтра крупные свечи, послезавтра снова тишина.
fast_halflife=10: реагирует на движения недельной частоты;
slow_halflife=40: меняется за 6–8 недель;
smooth_halflife=20: финальное сглаживание для стабилизации сигнала.
μ[t] = mean(VIX[t-251:t+1])
σ[t] = std(VIX[t-251:t+1]) # clip: lower bound 1e-4
F4[t] = (VIX[t] − μ[t]) / σ[t]
VIX z-score > +1.5: implied volatility сильно выше нормы → crisis;
VIX z-score ≈ 0: нормальная среда;
VIX z-score < −1: предвестник крупного движения.
log_ret[t, i] = log(close[t, i] / close[t-1, i])
# Исключаем signal_tickers (TLT) из набора
risk_cols = [c for c in log_ret.columns if c not in exclude_cols]
pairs = all (i, j) pairs from risk_cols
for each pair (i, j):
corr_ij[t] = rolling_corr(log_ret[i], log_ret[j], window=60)
F5[t] = mean over all pairs of corr_ij[t]
Для инференса использует не rolling, а EMA correlation matrix:
risk_indices = [indices of tickers NOT in signal_tickers]
sub_corr = corr_matrix[risk_indices, risk_indices]
triu_values = sub_corr[upper triangle, k=1] # off-diagonal
F5 = mean(triu_values)
F5 ≈ 0.1–0.3: нормальная диверсификация;
F5 ≈ 0.5+: corollation crisis или все risk-активы движутся синхронно (классический risk-off signal);
F5 < 0.1: необычно низкая взаимосвязь, частый предвестник слома тренда.
F5 считается только по risk-активам. Контрциклические выносятся в F6. window=60даёт SE(Pearson) ≈ 0.13 – достаточно стабильный сигнал. F5 описывает поведение толпы, когда VIX ещё не успел отреагировать и может опережать F4 на несколько дней в некоторых кризисных эпизодах.
log_ret[t, i] = log(close[t, i] / close[t-1, i]) # только для signal_tickers
roc[t, i] = Σ log_ret[t-9:t+1, i] # 10-day sum per signal ticker
F6[t] = mean over signal_tickers of roc[t]
Измеряет направление контрциклических активов. В нашем случае signal_tickers = ['TLT']. Интерпретация для TLT:
F6 > 0.02 (TLT растёт на ≥2% за 10 дней): инвесторы бегут в бонды, risk-off;
F6 ≈ 0: нейтрально, бонды стабильны;
F6 < −0.02 (TLT падает): инвесторы продают бонды и возвращаются в риск.
Консистентность с F1, что делает F1 и F6 сравнимыми и разница F1 − F6 интерпретируется как спред между направлением рисковых активов и спасительных.
Новый режим фиксируется только после подтверждения (преодоление порога):
chaotic: 1 день (если начался хаос – откликаться мгновенно);
downtrend: 2 дня;
sideways: 2 дня;
uptrend: 5 дней (ложные uptrend-сигналы дороги – консервативно).
Для каждого режима обучается отдельный SAC-агент, видящий только шаги своего режима во время обучения. Эксперты независимы и нет никаких общих soft-сетей. Пороги (калиброваны для периода 2017-2023):
|
Фича |
Квантиль порога |
Интерпретация |
|---|---|---|
|
F1 (ROC-10) < −0.03 |
p20 |
negative momentum → downtrend candidate |
|
F1 (ROC-10) > +0.03 |
p80 |
positive momentum → uptrend candidate |
|
F2 (log vol ratio) > +0.2 |
p85 |
volatility spike → chaotic candidate |
|
F4 (VIX z-score) > +1.5 |
p90 |
stress → chaotic strong signal |
|
F5 (avg pairwise corr) > 0.7 |
p90 |
всё коррелирует → chaotic |
Идея RCAA (Regime-Conditional Alpha Adjustment) – в разных режимах агент должен вознаграждаться за разное поведение. Ниже пример конфигурации:
rcaa_lossm = {
'regime_alignment_scale': 5.0,
'regime_alignment_signs': {
1: +1.0, # uptrend: бонус за перформанс выше корзины
2: -1.0, # downtrend: штраф за перформанс выше корзины (сигнал защищаться)
3: +1.0,
},
'loss_multiplier': 1.3, # асимметричный риск: убытки весят больше прибылей
...
}
Знак -1 для downtrend звучит контринтуитивно: зачем штрафовать за то, что агент обгоняет бенчмарк на падающем рынке? Потому что мы хотим, чтобы cross-sectional сигнал переключал в консервативные тикеры. Т.е. случайный эксперт не должен быть умнее назначенного и обязан держать специализированные для него активы. Если агент обгоняет equal-weight корзину в downtrend, то это обычно значит, что он сидит в тех же растущих активах, но с большим плечом, а нам нужна ротация!
Пришло время особой уличной магии. Детектор выдаёт текущий режим, но мы не используем его напрямую для выбора эксперта на инференсе. Вместо этого каждый эксперт постоянно торгует на скользящем окне, мы считаем его rolling 60-day Sharpe, и если эксперт-претендент значимо обгоняет дежурного по своему тренду, то роутинг переключается даже если детектор говорит держаться за назначенного.
Параметры shadow routing:
_shadow_sharpe_window = 60 # days
_shadow_override_threshold = 1.0
_shadow_min_challenger_sharpe = 0.5
Почему это работает: детектор классифицирует рынок, а нам нужно выбрать эксперта, чья политика лучше работает на данном рынке прямо сейчас. Эти две задачи не совпадают. Эксперт, обученный на uptrend, может внезапно оказаться лучшим и в начале sideways, если его политика совпала с микро-структурой момента.
Прибавка в Sharpe по итогу реализации оказалась скромная (примерно +0.06 относительно одиночного SAC), зато уменьшилась дисперсия и модель стала предсказуемо себя вести в кризисные периоды
Идея простая: штрафовать/поощрять агента за изменение Sharpe на каждом шаге, а не за мгновенную доходность. По замыслу такой подход должен был дать более стабильную политику. Главный кандидат – Differential Sharpe Ratio (DSR), онлайн-оценка градиента Sharpe, добавляемая к reward:
# Упрощённо, halflife=20, scale=1.0
ewma_ret = α * r_t + (1-α) * ewma_ret_prev
ewma_ret2 = α * r_t² + (1-α) * ewma_ret2_prev
sharpe_t = ewma_ret / sqrt(ewma_ret2 - ewma_ret²)
dsr = dsr_scale * (sharpe_t - sharpe_t_prev)
reward += dsr
DSR добавил менее 1% от изначальной награды и при этом поменял всё. Суть в механизме распространения сигнала:
RCAA и drawdown_penalty добавляются к reward и их вклад в градиент пропорционален их величине. Чтобы они повлияли на policy, их нужно “докрутить” до значимого размера;
DSR – это задающий форму градиента элемент награды. Пользу даёт не величина, а согласованность и DSR заставляет градиент указывать в сторону политики с более стабильным Sharpe, даже если добавочный вклад мал.
Коэффициент при DSR = 1.0 и это оптимальное значение, поднимать его не следует. Не всё, что влияет на policy через reward, можно тюнить через масштаб.
Если DSR для одного seed показывает защитную ротацию естественным образом, а другие seed эксперименты такое же поведение не демонстрируют, то давайте заставим всех делать одно и то же: добавим явный штраф за высокий gross для downtrend-эксперта:
if regime == gpen_regime: # regime == 2 == downtrend
gross = sum(abs(positions[t]) * price) / NAV
gross_penalty = -gpen_scale * max(0, gross - gpen_cap) ** 2
reward += gross_penalty
Настройки: scale=0.1, cap=0.7 – soft-градиент, ≤1% вклада в reward, активируется только для downtrend-эксперта (у которого regime_filter=2 во время обучения). Гипотеза такова, что хирургическое вмешательство сделает защитное поведение консистентным (не ломая других экспертов). Результат для 3 seeds:
|
Config |
Sharpe mean |
Std |
dn_gross mean |
|---|---|---|---|
|
Оригинальный DSR |
1.258 |
0.012 |
95.7% |
|
DSR + dngross |
0.918 |
0.157 |
99.1% |
Провал:
Sharpe упал на −0.34 на всех трёх сидах;
защитное поведение, которое мы пытались воспроизвести, исчезло – естественные 88% в активах и 12% в cash превратились в исходные 98.6%.
Вмешалась особая уличная магия (shadow-routing):
DSR seed123 распределение режимов: sideways 59.5% / uptrend 35.0% / downtrend 2.1% / chaotic 3.3%;
DSR+dngross seed123 распределение режимов: sideways 62.5% / uptrend 9.0% / downtrend 8.5% / chaotic 20.0%.
Shadow-routing радикально всё перестроил несмотря на то, что штраф касался ТОЛЬКО downtrend-эксперта во время обучения. Даже слабая (scale=0.1) модификация reward-функции изменила траекторию сходимости SAC-градиента для downtrend-эксперта достаточно, чтобы rolling Sharpe на инференсе стал другим. Shadow-mode на основе этой новой статистики начал выбирать других экспертов на другие дни.
per-regime reward должен был стать безопасным локальным изменением. На деле это изменило всё, потому что любая политика одного эксперта взаимодействует с остальными через shadow-routing. Сам Shadow-routing – это не механизм инференса, а отдельное inference-time пространство оптимизации, на которое можно воздействовать без переобучения
Формально эксперимент был провален. Но как я писал выше стоило едва разобраться, что у Claude пошло не так и анализ показал любопытную деталь: сам downtrend-эксперт из dngross-модели имел хорошую политику! Он знал, как вести себя в нисходящем рынке и проблема была в роутинге, т.к. shadow-mode внутри dngross-экосистемы активировал его на неправильных днях. Что будет, если взять этого downtrend-эксперта и вставить в DSR-экосистему (с её стабильным роутингом с тремя другими экспертами)? Это был бы zero-cost эксперимент – чистое копирование файлов и без обучения. Результаты:
|
Метрика |
DSR seed42 |
hybrid_seed42 |
|---|---|---|
|
Sharpe |
1.240 |
1.452 |
|
CAGR |
33.5% |
35.77% |
|
MDD |
16.8% |
13.58% |
|
Calmar |
2.00 |
2.635 |
|
Per-regime downtrend Sharpe |
+1.71 |
+3.66 |
|
CV (3 seeds) |
– |
0.2 |
|
Shuffle-test |
– |
4.2σ |
Дальше можно ещё много чего комбинировать для получения Best-of-each как в таблице ниже, но seed variance будет очень большой и модель перестаёт быть стабильной.
|
Метрика |
tlt_bestofeach_v1 (WF5) |
|---|---|
|
Sharpe |
1.511 |
|
CAGR |
39.01 |
|
Calmar |
2.953 |
Комбинации, которые в найденной литературе не встречались:
Rule-based regime detector + per-regime SAC эксперты вместо soft-mixture или end-to-end attention. Концептуально проще, позволяет независимо обучать экспертов, а затем их комбинировать. Требует ручной калибровки порогов детектора режимов.
Shadow-mode routing на rolling Sharpe – не классический gating (который параметризуется и обучается). Независимая inference-time ось, параметризуется 3 гиперпараметрами (окно, threshold, min challenger). В литературе такой подход обычно называется “online algorithm selection”, но для RL-трейдинга прямого применения не встречал.
Hybrid expert-swap (cross-run assembly) – пересобрать ансамбль из экспертов, обученных в разных запусках. Как оптимизационная техника известна (Policy Distillation, PopulationBased Training), но специфическая практика “взять эксперта из одного seed + 3-х других экспертов из другого” – не встречал.
Differential Sharpe Ratio – конкретно применение DSR в композиции с MoE встречается редко, потому что большинство современных RL-трейдинг статей используют cumulative return или log-utility. DSR в нашем эксперименте оказался сильнее всех новомодных альтернатив.
По публично известным Sharpe систематических стратегий (с учётом, что серьёзные фонды не раскрывают детали):
|
Тип стратегии |
Типичный net Sharpe |
|---|---|
|
Multi-strategy hedge funds |
1.0 – 1.5 |
|
Systematic CTA / trend-following |
0.5 – 0.8 |
|
Risk-parity (Bridgewater-style) |
0.7 – 1.0 |
|
Factor investing (equity) |
0.6 – 1.0 |
|
Market-making / HFT |
3.0 – 6.0 |
|
Renaissance Technologies Medallion |
~3.0 (по слухам) |
В общем это не прорыв, но вполне добросовестный результат на понятной и воспроизводимой архитектуре с оговорками:
Портфель мал (6 тикеров). Академические статьи обычно работают с 30–100 активами (больше диверсификация, ниже комиссионные издержки, сложнее обучение). Изложенный подход при 50 тикерах погибнет в шуме.
Дневная частота. Часовые или внутредневные стратегии требуют кратно больших вычислительных ресурсов и данных.
Упрощённая модель комиссий. Fee 4 bps + stochastic slippage не заменяет полноценного влияния на рынок при сделках с большими объёмами.
Настоящий Sharpe неизвестен, live-доходность 5% за 2 недели на момент написания статьи.
Sharpe 1.5 реалистично и укладывается в ожидаемый диапазон от применённых техник.
Ниже справочно привожу таблицу с перечнем того, что не сработало. Следует, однако, отметить, что при должных усилиях, наверняка, сработает.
|
Эксперимент |
Результат |
|---|---|
|
SAC tuning |
Более глубокие сети чаще переобучаются, чем дают пользу |
|
LightGBM per-asset signal |
Отношение сигнала к шуму, которое удалось выделить, оказалось недостаточно для получения пользы от классического ML |
|
TQC / DroQ / PopArt |
Алгоритмический тюнинг не дал прироста |
|
500k extended training |
Переобучение |
|
cs_rank feature |
Дополнительные перекрёстные фичи (например, rsi_14) не сработали |
|
Risk-state obs |
Добавленные в наблюдаемое состояние среды фичи (например, связанные с риском) агент игнорирует, если они напрямую не участвуют в расчёте награды |
|
RCAA ×20 Per-regime gross penalty |
Усиление отдельных компонентов награды или штрафов ломает ребалансировку на основе shadow portfolio |
|
Action-averaging ensemble |
Похожие модели усредняются в лучшем случае в равновесный портфель для любого режима рынка |
|
Stop-loss overlay |
Агент сам учится ограничивать риск, а stop-loss играет роль усилителя штрафа и не идёт на пользу |
|
Universe expansion (×2) |
6 скрупулёзно отобранных > 10 случайных |
|
GMM-HMM режим-детектор |
Любая нестабильность классификации режимов рушит ожидаемую доходность |
|
LLM-аугментация |
Долго и не добавляет alpha |
RL – наименее фатальный тип ошибки при проектировании торговой системы.
Методология и структура мышления об экспериментах > Архитектура. MoE, DSR, hybrid expert-swap, regime detector – частные технические находки, которые в другом контексте могут не сработать. Дисциплина multi-seed validation, distribution comparison, shuffle-test, walk-forward по разным режимам, документирование провалов наравне с успехами и тщательные разборы – переносится на любой ML-проект.
Feature engineering > всё остальное. То, что всегда работает: аккуратная работа с данными, очистка и рукотворные фичи. Если в RL-трейдинге нет очевидного прорыва то шансы, что он сидит в правильной функции награды – низкие.
Single-seed сравнения – генератор случайных выводов. Любое усложнение архитектуры при нестабильной базе не подтверждает и не опровергает никакие результаты ваших экспериментов. Разница в 0.1–0.3 Sharpe между любыми двумя точками – статистически неразличимый шум.
Data leakage – самая дорогая ошибка. Одна строчка bfill даёт высокий валидационный Sharpe и катастрофически низкий результат на тестах, даже если утечка произошла для вспомогательного параметра (при расчёте награды) в архитектуре.
Hybrid expert-swap – мощный приём, доступный за счёт MoE. Архитектура модульна, где Эксперт – это отдельный .zip с весами, его можно копировать, комбинировать и тестировать в разных сценариях.
95% экспериментов провёл Claude, но положительный результат достигался только тогда, когда принимал участие человек.
Короткие позиции. Т.е. в лучшем случае агент сможет частично сидеть в деньгах, TLT и XAU.
Ребалансировка чаще, чем раз в день. Агент смотрит на цены закрытия предыдущего дня и совершает максимум 6 сделок.
Изменить состав или количество тикеров в портфеле. Добавить/убрать тикер = переобучить всё с нуля и с неизвестным результатом.
Торговля с плечом. Фактически используется весь NAV.
Оборот за 1 торговый день более 0.3 . Защита от осцилляций и ограничение реакции [35] на резкие события.
Существенно отличающийся от 1 000 000 портфель. Влияние крупных ордеров на рынок не учитывается, поэтому в портфеле только ликвидные активы.
Активы, для которых VIX не является репрезентативной мерой системного риска.
Обязательный короткий блок:
Историческая доходность ≠ доходность в будущем.
Результаты backtest ≠ реальная эксплуатация.
Работа выполнена в академических целях.
Чтобы RL-стратегия сработала наперёд недостаточно её обучить. Нужно построить инфраструктуру, которая вас остановит прежде, чем вы потеряете деньги. Большая часть проекта это не модель, а инфраструктура вокруг модели, направленная на подтверждение, что всё работает в соответствии с ожиданиями.
SAC (per expert):
learning_rate = 2.4e-4
tau = 1e-4
gamma = 0.995
batch_size = 256
buffer_size = 1_000_000
target_entropy = auto (-1.0 for chaotic)
use_sde = False
net_arch: pi=[256,256], qf=[256,256]
Action mapping:
max_per_ticker = 0.25
pipeline order: tanh → long-only clip →
turnover smoothing (α=0.2) →
rebalance epsilon (0.01) →
nav-cap (max(nav, 0.8×peak_nav))
Reward (p103_rcaa_ch_dsr1):
portfolio_reward.loss_mul = 1.3
rcaa.scale = 5.0
rcaa.signs = {1: +1.0, 2: -1.0, 3: +1.0}
dsr.scale = 1.0
dsr.halflife = 20
drawdown_penalty.cap = 0.15
drawdown_penalty.scale = 2.0
Carry (in obs):
carry_decay = 0.9
carry_horizon = 5 days
trade_bonus_turnover_threshold = 0.02
Training schedule:
timesteps_per_expert = 100_000
early_stopping: patience=3, eval_freq=2000, n_eval_episodes=5
episode max_steps = 100
* упущено в повествовании, но важно для реализации
Автор: allowq
Источник [36]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/29653
URLs in this post:
[1] обучения: http://www.braintools.ru/article/5125
[2] подкреплением: http://www.braintools.ru/article/5528
[3] reinforcement learning: https://en.wikipedia.org/wiki/Reinforcement_learning
[4] ошибки: http://www.braintools.ru/article/4192
[5] опыт: http://www.braintools.ru/article/6952
[6] Claude Code: https://claude.com/product/claude-code
[7] внимание: http://www.braintools.ru/article/7595
[8] мотивацию: http://www.braintools.ru/article/9537
[9] сlaude code cli, skills, tools, mcp: https://code.claude.com/docs/en/best-practices
[10] planning-with-files: https://github.com/OthmanAdi/planning-with-files
[11] mcp-sequential-thinking: https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking
[12] sequential-thinking-tools: https://github.com/spences10/mcp-sequentialthinking-tools
[13] searxng: https://github.com/ihor-sokoliuk/mcp-searxng
[14] DDPG: https://arxiv.org/pdf/1509.02971
[15] OHLCV: https://en.wikipedia.org/wiki/Open-high-low-close_chart
[16] Markov Decision Process: https://en.wikipedia.org/wiki/Markov_decision_process
[17] supervised prediction problem: https://en.wikipedia.org/wiki/Supervised_learning
[18] марковское свойство: https://en.wikipedia.org/wiki/Markov_property
[19] поведение: http://www.braintools.ru/article/9372
[20] мышление: http://www.braintools.ru/thinking
[21] bb_position: https://github.com/TA-Lib/ta-lib-python/blob/master/docs/func_groups/momentum_indicators.md
[22] yahoo finance: https://github.com/ranaroussi/yfinance
[23] mean-variance, Black-Litterman: https://en.wikipedia.org/wiki/Modern_portfolio_theory
[24] дневные данные: https://ru.wikipedia.org/wiki/%D0%A2%D0%B0%D0%B9%D0%BC%D1%84%D1%80%D0%B5%D0%B9%D0%BC
[25] SAC: https://arxiv.org/abs/1812.05905
[26] мотивацию: http://www.braintools.ru/article/9384
[27] Повторять: http://www.braintools.ru/article/4012
[28] ta-lib: https://github.com/TA-Lib/ta-lib-python/tree/master
[29] поведение: http://www.braintools.ru/article/5593
[30] bfill: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.bfill.html
[31] боли: http://www.braintools.ru/article/9901
[32] ablation feature importance: https://arxiv.org/pdf/1910.00174
[33] HMM: https://en.wikipedia.org/wiki/Hidden_Markov_model
[34] MLP: https://en.wikipedia.org/wiki/Multilayer_perceptron
[35] реакции: http://www.braintools.ru/article/1549
[36] Источник: https://habr.com/ru/articles/1030056/?utm_campaign=1030056&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.