Нейросети в трейдинге: Диффузионная генерация торговых планов (DPCC)
Введение
Торговое решение зависит от прогноза рынка и текущего состояния счёта. Открытие позиции меняет свободную маржу и возможный убыток при движении цены. Накопленные потери сокращают запас до установленного лимита. Поэтому один и тот же сигнал может потребовать разного объёма позиции или отказа от входа.
В предыдущих статьях мы учитывали распределение возможных результатов при обучении торговой политики. Спектральная оценка усиливала внимание к неблагоприятным исходам. Для выбора действия нужна и проверка конкретных ограничений счёта. План с привлекательной оценкой риска может потребовать слишком много маржи или превысить допустимую просадку.
Рассмотрим запланированный донабор позиции через несколько баров. Текущий объём ещё укладывается в лимиты. Однако неблагоприятное движение цены уменьшит средства к моменту донабора. Прежнее намерение увеличить позицию окажется невыполнимым. Поправка раннего действия изменит и последующие состояния счёта. Поэтому проверять нужно всю последовательность.
Торговым планом будем называть последовательность будущих действий. Рассчитаем историю счёта на заданном горизонте и определим момент и причину нарушения. Исполним только первое действие выбранного плана. После нового наблюдения повторим расчёт с фактического состояния счёта.
Идею такого планирования предложили Ralf Römer, Alexander von Rohr и Angela P. Schoellig в работе Diffusion Predictive Control with Constraints. Метод DPCC объединяет диффузионную генерацию траекторий с проверкой динамики и ограничений. Он может учитывать ограничения, которых не было в демонстрациях. Перенос этого свойства на финансовый рынок требует отдельной проверки.
Диффузионная модель формирует траекторию из шума за несколько шагов. После каждого шага DPCC корректирует предложение по известной модели системы и заданным ограничениям. Следующее уточнение начинается уже с исправленной траектории. Так ограничения участвуют в самой генерации.
В торговой адаптации ценовые сценарии будет задавать отдельная прогнозная модель. Генератор предложит планы действий, а финансовая модель рассчитает последствия каждого плана. Сначала отсеем варианты, нарушающие ограничения. Затем оставшиеся планы можно сравнить по спектральной оценке результатов.
В первой статье разберём устройство DPCC и начнём его торговую адаптацию со сценарного расчёта. Два OpenCL-кернела дадут историю счёта и диагностику нарушений для заданного плана. Диффузионный генератор и механизм коррекции подключим позже.
Алгоритм DPCC
Исходная область DPCC — робототехника. Авторы обучают политику по демонстрациям и используют её для управления со скользящим горизонтом. Авторы проверили метод в симуляторе манипулятора. Финансовая постановка в этой статье представляет нашу адаптацию подхода.
Представим перенос рабочего органа манипулятора в заданную область. В демонстрациях робот обходил препятствие с разных сторон. После обучения новое препятствие перекрыло один из маршрутов. Теперь планировщику нужно согласовать знакомые способы движения с новым ограничением.
Динамическая система и план действий
Обозначим состояние системы через st, а действие — через at. Номинальная модель f задаёт расчётный переход. Возмущение wt описывает отклонение реального перехода от модели:
![]()
В исходной постановке модель f известна. Норма возмущения ограничена величиной δ. Такое обозначение отделяет границу ошибки от коэффициента дисконтирования финансового результата.
Модель предсказывает новое положение робота после управляющего действия. Неточность расчёта или внешнее воздействие могут сместить фактическое положение. Граница возмущения задаёт максимальное допустимое расхождение. Её обоснование требуется отдельно от построения самой модели.
Прогнозная траектория объединяет будущие состояния и действия. Примем горизонт H, содержащий H переходов. Ему соответствуют H действий и H + 1 состояний:
![]()
Индексы траектории отсчитываются от текущего момента. Начальное наблюдаемое состояние фиксировано. Последующие состояния должны получаться из него по номинальной модели и запланированным действиям.
Для каждого момента задаются допустимые множества состояний 𝒮t + h и действий 𝒜t + h. В робототехнике такие множества ограничивают рабочую область, скорость и управляющее воздействие. В DPCC ограничения задаются при использовании обученной модели. Они могут отличаться от условий сбора демонстраций.
Две разрешённые точки пространства могут оказаться слишком далеко друг от друга для перехода за один такт. Проверка границ пропустит обе точки. Однако выполнить такой переход робот не сможет. Поэтому требуется проверить согласованность всей траектории с моделью f. Для торгового счёта аналогичную связь задаёт пересчёт средств и маржи после действия.
Диффузионная генерация траекторий
Диффузионное планирование использует распределение целых траекторий. Этот подход предложен в работе Diffuser и положен в основу DPCC. Генератор обучается по демонстрациям успешного выполнения задачи. Они содержат состояния, действия и цель. По этим примерам модель осваивает разные способы её достижения.
Представим траекторию единым массивом состояний и действий на всём горизонте. При обучении к исходному примеру τ0 добавляется шум. Его уровень определяется номером диффузионного шага j:
![]()
Здесь αj = 1 − βj. Величина ᾱj равна произведению коэффициентов αi от первого шага до j. Параметры βj задают расписание зашумления. Сеть предсказывает добавленный шум по искажённому примеру и номеру шага. Это классическая схема DDPM.
Индекс j относится к генерации, а не к будущему торговому бару. На одном диффузионном шаге уточняется вся траектория. Горизонт H определяет её длину во времени. Число обратных шагов J задаёт количество последовательных уточнений. Увеличение одного параметра не означает увеличения другого.
Генерация начинается со случайного массива τJ ∼ 𝒩(0, I). Обратный процесс проходит от J к нулю. На очередном шаге модель вычисляет среднее значение следующего предложения. К нему добавляется случайная составляющая:
![]()
Величина μθ зависит от параметров сети, текущего массива и контекста c. Коэффициент σj определяет масштаб шума на обратном шаге. В параметризации DDPM среднее вычисляется через предсказание шума εθ.
В исходном DPCC контекст содержит текущее состояние и цель. Он участвует в каждом обратном шаге сети.
Усреднение разных маршрутов обхода может направить робота прямо в препятствие. Поэтому генератор предлагает несколько кандидатов. Каждый из них отдельно проверяется при новых ограничениях.
Проекция с учётом динамики
Проекция ищет допустимую траекторию рядом с предложением генератора. Мера близости в авторском DPCC — квадрат евклидова расстояния между массивами состояний и действий. Поиск одновременно учитывает границы этих величин и уравнения переходов. Начальное наблюдаемое состояние при этом сохраняется.
Связь с динамикой определяет допустимые поправки. Нельзя произвольно сдвинуть только одно будущее состояние и оставить прежние действия. После такого изменения переход может перестать соответствовать модели. Проекция согласует траекторию целиком. Уже допустимое предложение не требует поправки.
Слишком тесные ограничения могут исключить все доступные траектории. Тогда проектор не вернёт допустимый результат. Такую неудачу нужно отличать от успешной проекции.
В итоговом алгоритме DPCC сеть сначала вычисляет среднее значение следующего предложения. Затем к нему добавляется шум и применяется проекция. Этот порядок записан в формуле 16 авторской работы. Добавление шума после проекции могло бы снова вывести траекторию за допустимые границы.
Исправленная траектория поступает на следующий шаг генерации. Сеть продолжает уточнение с учётом внесённой поправки. В DPCC эта связь сохраняется на всех обратных шагах. Однократное исправление в конце генерации не даёт такого взаимодействия.
Вероятностное обоснование авторов использует приближение для замкнутого выпуклого допустимого множества. В экспериментах рассматриваются и невыпуклые ограничения. Поэтому нужно различать условия теоретического вывода и практическую работу алгоритма. При приближённом решении найденная допустимая траектория также не обязана быть ближайшей к исходной.
Запас на ошибку модели
Проекция проверяет траекторию по номинальной динамике. Реальный переход может отклониться от расчётного. Для учёта этого расхождения авторы сужают допустимое множество состояний. Внутри исходной границы остаётся запас на возмущение. Действия затем выбираются по более строгому условию для будущего состояния.
Рассмотрим верхнюю границу состояния b и максимальную ошибку δ. Номинальное следующее состояние ограничим величиной b − δ. Тогда положительное отклонение в пределах δ ещё укладывается в исходный предел b. В многомерном случае вокруг расчётной точки должна помещаться вся область возможных отклонений.
Вывод о соблюдении ограничений требует нескольких предпосылок. Начальное состояние должно быть допустимым. Возмущение должно оставаться в принятой границе. На каждом новом шаге необходима траектория, допустимая при суженных ограничениях. Найденный сейчас план сам по себе не доказывает существования следующего решения.
Для торгового счёта похожую роль играет резерв свободной маржи. Он оставляет запас на менее благоприятное исполнение или движение цены. Но произвольно выбранный резерв не равен доказанной границе ошибки. Без обоснованной оценки рыночного и исполнительного отклонения это практический запас, а не гарантия, аналогичная сужению допустимого множества в исходном DPCC.
Выбор траектории и обновление плана
DPCC-T выбирает допустимую траекторию с наименьшим отклонением от перекрывающегося участка предыдущего плана. При сравнении учитывается уже выполненное действие. Это помогает не менять начатый маршрут без причины. Близость к прежнему плану не отменяет проверки текущих ограничений.
DPCC-C выбирает траекторию с наименьшей суммой затрат на проекции. Это сумма квадратов расстояний до исправленных траекторий за все обратные шаги. Малая последняя поправка ещё не означает малого общего вмешательства.
Из выбранной траектории выполняется только первое действие. После нового наблюдения горизонт сдвигается вперёд, и расчёт повторяется с фактического состояния. Остальные действия помогают оценить последствия ближайшего решения и могут измениться при следующем расчёте.
Переход к торговым планам
На финансовом рынке управляемой частью станет счёт. Действие меняет объём позиции, защитные уровни и связанные денежные показатели. Движение цены задаётся внешним сценарием. В принятой адаптации собственные сделки не изменяют этот сценарий. Поэтому цену нельзя исправлять вместе с неудачным торговым планом.
Генератор будет предлагать последовательности действий. Состояния счёта получим их исполнением в финансовой модели. Один план пройдёт через весь набор ценовых сценариев. У него появятся разные истории средств и маржи. Такое разделение отличается от совместной генерации состояний и действий в исходном DPCC.
Вернёмся к донабору позиции. Уменьшение раннего объёма изменит расходы, плавающий результат и доступную маржу. Поэтому каждую пробную поправку нужно заново провести через сценарии. Для этого и нужен общий финансовый переход.
Проверка покажет нарушения ограничений и даст денежную оценку плана. Высокая оценка не отменяет превышения лимита. Спектральную оценку будем применять только к кандидатам, прошедшим сценарный фильтр. Это критерий нашей адаптации, отличный от DPCC-T и DPCC-C.
Сценарно допустимым будем называть план без нарушений на переданных сценариях при принятой модели исполнения. Это не равнозначно допустимой траектории исходного DPCC. Наш расчёт пока лишь проверяет план. Он не исправляет его и не возвращает поправку в обратную диффузию.
Полнота сценарного набора в этой статье не обосновывается. Гэпы, расширение спреда, нехватка ликвидности, проскальзывание и экстремальные движения учитываются лишь при их включении в сценарии или условия исполнения. Поэтому сценарная допустимость относится к расчётному набору, а не к рынку вообще. Спектральная оценка эту границу не снимает.
Практическая реализация для MetaTrader 5
Сценарный расчёт для MetaTrader 5 выполним в OpenCL. Управление буферами и запуск кернелов организуем средствами MQL5. На вход поступит готовый план. На выходе получим историю счёта и денежную оценку по каждому ценовому сценарию.
Подготовлены два OpenCL-кернела. DPCCRollout проводит счёт через весь горизонт планирования и сохраняет результаты отдельных сценариев. DPCCRolloutReduce объединяет проверки по сценариям. Его выход содержит признак сценарной допустимости каждого плана и общий системный статус расчёта.
Исходные данные и результаты
Обозначим через K число ценовых сценариев, через H — горизонт планирования. Буквой S обозначим вместимость текущего пакета планов. В DPCCRollout ей соответствует slot_capacity, а в DPCCRolloutReduce — slots. Для одного пакета эти значения должны совпадать. При обычной оценке S = M, где M — число исходных кандидатов. При проверке пробных поправок вместимость пакета может превышать M. Какие строки реально используются, показывает маска slot_active.
На каждом шаге действие содержит шесть компонентов: BuyVolume, BuyTP, BuySL, SellVolume, SellTP и SellSL. Разность объёмов покупки и продажи задаёт целевую позицию. Нулевое значение означает её закрытие.
Целевой объём задаёт итоговую позицию после действия. При донаборе открывается только разница между целевым и текущим объёмом. Разворот сначала закрывает прежнюю позицию, затем открывает противоположную. Совпадение целевой и текущей позиций не требует открытия или закрытия объёма. Защитные уровни при этом могут измениться.
Компоненты BuyTP и BuySL относятся к длинной позиции. Для короткой позиции используются SellTP и SellSL. Они задают расстояния до защитных уровней через масштабы инструмента. В расчёте также учитываются сторона котировки, спред и направление позиции.
В таблице указаны размеры буферов для одного пакета. Смещение plan_offset задаёт начало пакета в общем буфере планов. Оно измеряется в элементах типа float. Начало нужного участка маски активности задаёт slot_active_offset. Смещения позволяют работать с частью уже подготовленных данных.
| Буфер | Элементы пакета | Назначение |
|---|---|---|
| plans | S × H × 6 | Последовательности торговых действий. |
| slot_active | S | Маска используемых слотов. |
| returns | S × K | Дисконтированная денежная оценка G каждой пары план–сценарий. |
| states | S × K × (H + 1) × 24 | Исходное состояние и состояния после каждого перехода. |
| violations | S × K × 8 | Сводка нарушений и локальный статус пары. |
| system_status | S × K | Ошибки контекста и численного выполнения пары. |
| branch_diagnostics | S × K × H × 4 | Диагностика выбора внутрибарной ветви. |
У буфера plans нет отдельной оси сценариев. Одна последовательность действий проверяется на всех K вариантах движения цены. Общий набор сценариев обеспечивает одинаковые внешние условия для сравнения планов и пробных поправок.
Буфер context состоит из восьми последовательных секций. В них находятся OHLC-сценарии, исходный счёт, свойства инструмента, ограничения, условия исполнения, число календарных событий, сами события и маска исходных кандидатов. В кернел передаются смещения секций и общая длина буфера. Запись счёта занимает 24 значения. Она содержит баланс, средства, позицию, защитные уровни и маржу. Маска slot_active относится к текущему пакетному вызову и не заменяет исходную маску кандидатов внутри context.
Кернел DPCCRollout
Распределим работу по двум измерениям: ценовым сценариям и слотам планов. Один рабочий элемент OpenCL обрабатывает пару план–сценарий со своим состоянием счёта и участком выходных буферов.
const uint k = (uint)get_global_id(0);
const uint slot = (uint)get_global_id(1);
if(k >= K || slot >= slot_capacity)
return;
const size_t pair = (size_t)slot * K + k;
const size_t state_offset = pair * ((size_t)H + 1) * 24;
const size_t violation_offset = pair * 8;
const size_t diagnostic_offset = pair * H * 4;Индекс pair объединяет номера слота и сценария. По нему вычисляются смещения истории счёта, нарушений и диагностики. Разные пары независимы. Внутри пары переходы последовательны: каждый использует средства, позицию и маржу после предыдущего действия.
Сначала кернел обнуляет выходы своей пары. Индексы отсутствующего нарушения и невыбранной внутрибарной ветви получают −1. Это происходит до проверок входа. Даже раннее завершение не оставит результаты предыдущего запуска.
Функция DPCCValidateRolloutContext проверяет расположение секций, диапазоны параметров и исходный счёт. Затем она проверяет OHLC, условия исполнения и календарь. Проверка предшествует выходу по маске неактивного слота. Поэтому отключение кандидата не скрывает ошибку общего контекста.
const int checked = DPCCValidateRolloutContext(context, k, K, H, E, context_M,
off_account, off_instrument,
off_limits, off_execution,
off_calendar_count, off_calendar,
off_R_active, R_total, expected_price_basis,
expected_quote_adapter, gamma, volume_scale,
price_scale, max_quote_age_seconds,
max_last_age_seconds, money_scale);
if(checked != 0)
{
system_status[pair] = (float)checked;
violations[violation_offset + 5] = (float)checked;
return;
}
const float active = slot_active[(size_t)slot_active_offset + slot];
if(!isfinite(active) || (active != 0.0f && active != 1.0f))
{
system_status[pair] = (float)DPCC_STATUS_INVALID;
violations[violation_offset + 5] = (float)DPCC_STATUS_INVALID;
return;
}
if(active == 0.0f)
return;Маска принимает только ноль или единицу. Единица включает слот, ноль штатно отключает его. При корректном контексте отключение не меняет system_status. Любое другое значение маски — ошибка входа.
Исходное состояние счёта
Для расчёта создаём структуру DPCCAccountState и копируем в неё 24 значения исходного счёта. Одновременно сохраняем начальную запись в states. Следующие записи соответствуют состояниям после переходов. Поэтому горизонт из H шагов требует H + 1 записей.
Поле equity_anchor связывает модельную переоценку с исходными средствами счёта. Сначала рассчитываем текущий результат позиции и стоимость обеспечения для соответствующего режима. Затем находим постоянную поправку. Она хранится в DPCCAccountState за пределами публичной записи из 24 значений.
const float initial_pnl = DPCCProfit(account[2], account[3], account[account[2] >= 0.0f ? 15 : 16],
0.0f, instrument[23], instrument[12], instrument[13], instrument);
const float initial_collateral = (int)instrument[11] == DPCC_CALC_COLLATERAL ?
fabs(account[2]) * instrument[2] * state.native_mark *
instrument[24] * instrument[12] :
0.0f;
state.equity_anchor = account[1] - account[0] - initial_pnl + account[6] - initial_collateral;
if(!isfinite(initial_pnl) || !isfinite(initial_collateral) || !isfinite(state.equity_anchor))
{
system_status[pair] = (float)DPCC_STATUS_NUMERIC;
violations[violation_offset + 5] = (float)DPCC_STATUS_NUMERIC;
return;
}Поле account[2] хранит знаковый объём позиции. Поэтому в режиме обеспечения для исходной стоимости используется его модуль. Обеспечение остаётся неотрицательным и для длинной, и для короткой позиции.
При переоценке функция DPCCMark добавляет equity_anchor к балансу и другим составляющим средств. Поправка остаётся постоянной на всём горизонте. Сами средства меняются под влиянием цены, действий и издержек.
Для штатно отключённого слота финансовый переход не запускается. После очистки returns и system_status остаются нулевыми, а violations не содержит нарушений. В states нет рассчитанной истории. Кернел DPCCRolloutReduce устанавливает validity = 0. Это служебное заполнение, а не финансовый результат плана.
Проверка плана и прохождение горизонта
Для активного слота создаём структуру DPCCRiskState. Перед моделированием проверяем весь план: конечность компонентов, разность объёмов и знаки используемых параметров. Проверка охватывает и действия на дальнем горизонте. Они должны быть корректны даже при досрочном переходе счёта в терминальное состояние.
Некорректные компоненты означают отсутствие сценарной допустимости плана. В показанной ветви функция DPCCRiskAdd сохраняет номер нарушения и его шаг. Поле numeric_status получает код DPCC_STATUS_INVALID:
if(!valid_action)
{
DPCCRiskAdd(&risk, 15, 1.0f, 1.0f, h, 1, -1);
state.numeric_status = DPCC_STATUS_INVALID;
}Локальный DPCC_STATUS_INVALID относится к кандидату, а не ко всему запуску. После этой ветви нет выхода, и общий цикл продолжается. Однако DPCCRolloutReduce установит validity кандидата в ноль. Его денежная оценка исключается из выбора. Это не системная ошибка в system_status.
Разность двух конечных объёмов может выйти за представимый диапазон. Такое переполнение обрабатывается отдельно: кернел записывает DPCC_STATUS_NUMERIC в system_status. В отличие от локального нарушения, эта ошибка отклоняет результаты всего запуска.
Перед первым переходом проверяем ограничения исходного счёта. Затем начинаем последовательный цикл по горизонту. На каждом шаге выбираем действие текущего плана и данные нужного сценария.
DPCCObserve(&state, &risk, limits, volume_scale, 0, 0);
float result = 0.0f;
float discount = 1.0f;
for(uint h = 0; h < H; h++)
{
const float equity_before = state.account[1];
__global const float *action = plans + (size_t)plan_offset + ((size_t)slot * H + h) * 6;
__global const float *ohlc = context + ((size_t)k * H + h) * 4;
__global const float *execution = context + off_execution + ((size_t)k * H + h) * 24;
__global const float *calendar = context + off_calendar + ((size_t)k * H + h) * E * 6;
float branch[4];
DPCCRiskState step_risk;
DPCCRiskInit(&step_risk);
DPCCAccountStep(&state, &step_risk, action, ohlc, instrument, execution, limits, calendar,
(uint)context[off_calendar_count + h], volume_scale, price_scale, h, H, branch);
DPCCRiskMerge(&risk, &step_risk);
result += discount * (state.account[1] - equity_before);
discount *= gamma;
for(int i = 0; i < 24; i++)
states[state_offset + ((size_t)h + 1) * 24 + i] = state.account[i];
for(int i = 0; i < 4; i++)
branch_diagnostics[diagnostic_offset + (size_t)h * 4 + i] = branch[i];
if(state.numeric_status == DPCC_STATUS_NUMERIC || !isfinite(result) || !isfinite(discount))
{
system_status[pair] = (float)DPCC_STATUS_NUMERIC;
state.numeric_status = DPCC_STATUS_NUMERIC;
result = 0.0f;
break;
}
}
returns[pair] = result;Финансовый переход выполняет функция DPCCAccountStep. Её входы включают состояние счёта, действие, OHLC, условия исполнения, ограничения и календарь. Сначала обрабатываются события до открытия бара. Затем следуют переоценка на открытии, проверка защитных уровней и целевое действие. После этого рассчитывается движение цены внутри бара.
Внутри бара рассматриваются два пути: Open–High–Low–Close и Open–Low–High–Close. Нарушения фиксируются в обеих ветвях, но историю счёта продолжает только одно выбранное состояние. При выборе учитываются защитный выход, средства в конце бара и показатели нарушений. При сравнении выходов по Stop Loss и Take Profit выбирается ветвь со Stop Loss.
Обе ветви начинаются из состояния после текущего действия. На следующий бар переносится только выбранное состояние. Нарушения обеих ветвей сохраняются, но альтернативное продолжение не рассчитывается. Поэтому проверка не охватывает полное дерево сочетаний внутрибарных путей и не доказывает безопасность всех продолжений. Функция DPCCAccountStep вызывается из DPCCRollout без отдельного запуска через OpenCL.
На последнем шаге предусмотрено закрытие обычной позиции. Его расходы входят в результат при разрешённом исполнении. Невозможность закрытия фиксируется как нарушение. В режиме обеспечения такое закрытие не выполняется. Поэтому оценка на конечном горизонте учитывает принятые правила завершения позиции.
Регистрация нарушения не исправляет план: DPCCRollout только читает plans. Например, нехватка маржи для донабора попадёт в диагностику, а расчёт последствий может продолжиться. Шаг, фаза и категория укажут место проблемы. Подбор меньшего объёма или другого действия остаётся задачей последующих этапов.
Переменная equity_before хранит средства до перехода. Их изменение после вызова даёт результат шага с учётом переоценки позиции и расходов. Из этих приращений складывается оценка плана:
![]()

Буква G обозначает накопленный дисконтированный результат пары план–сценарий. Переменная discount начинается с единицы и после каждого шага умножается на gamma. При коэффициенте ниже единицы поздние приращения средств получают меньший вес.
Возьмём изменения средств −20 и +50 денежных единиц. При gamma = 0,99 получим −20 + 0,99 × 50 = 29,5. При gamma = 1 результат составит 30. Буфер returns хранит эту дисконтированную денежную оценку, а не процентную доходность. При gamma < 1 она зависит от времени прибыли и убытка. Поэтому её нельзя считать фактическим итоговым PnL или простым изменением средств.
После каждого перехода сохраняем состояние счёта и диагностику ветвей. Терминальным считается состояние после исчерпания средств. Последующие вызовы DPCCAccountStep оставляют его неизменным. Переход к нему уже учтён в результате, а дальнейшие приращения равны нулю. Обычное закрытие позиции само по себе не прекращает моделирование.
Цикл прерывается при численной ошибке внутри перехода или неконечном значении результата либо коэффициента дисконтирования. Кернел устанавливает системный код ошибки и записывает ноль в буфер результата. Этот ноль не используется для сравнения планов.
Сохранение нарушений
В структуре DPCCRiskState предусмотрено 16 позиций для учёта нарушений. Они охватывают финансовые ограничения и техническую корректность действий. В каждой позиции сохраняется максимальная нормированная величина превышения. После прохождения горизонта вычисляем два сводных показателя:
float maximum = 0.0f;
float sum = 0.0f;
for(int i = 0; i < DPCC_RISK_COUNT; i++)
{
maximum = fmax(maximum, risk.maximum[i]);
sum += risk.maximum[i];
}Переменная maximum содержит наибольшее накопленное нарушение. В sum складываются максимумы по категориям. Повторная фиксация того же превышения сама по себе сумму не увеличивает.
Нарушения нормируются по заранее заданным масштабам объёма, цены и денег. В интерфейс входят, в частности, volume_scale, price_scale и money_scale. Эти масштабы должны быть одинаковыми для всех сравниваемых поправок. Значения maximum и sum зависят от них и не имеют общей денежной шкалы. Это показатели для сравнения поправок, а не сумма убытка или спектральная мера риска.
Прибыль к концу горизонта не отменяет промежуточного нарушения. План может восстановить средства после просадки и завершиться с положительным результатом. Но запись о превышении лимита сохранится.
| Индекс в записи violations | Содержимое |
|---|---|
| 0 | Шаг самого раннего нарушения. Значение −1 означает отсутствие записи. |
| 1 | Категория самого раннего нарушения по шагу и фазе. |
| 2 | Максимальная нормированная величина нарушения. |
| 3 | Сумма максимумов по категориям нарушений. |
| 4 | Признак терминального состояния счёта. |
| 5 | Локальный код состояния пары. Он может пометить некорректный кандидат без системной ошибки запуска. |
| 6 | Фаза самого раннего нарушения внутри перехода. |
| 7 | Индекс действия, связанного с нарушением. При отсутствии определённого действия сохраняется −1. |
Фаза уточняет момент нарушения: до текущего действия, при его выполнении или позднее внутри перехода. Падение средств ниже лимита на открытии уже предшествует текущему действию. Его поправка не устранит прежнее превышение. Причину нужно искать на более ранних шагах. Нарушение в исходном состоянии останется фактом при любом будущем плане. При одинаковых шаге и фазе сохраняется ранее выбранная запись.
Кернел DPCCRolloutReduce
После DPCCRollout определим сценарную допустимость планов и общий системный статус. Кернел DPCCRolloutReduce запускается в одном измерении с числом рабочих элементов slots + 1. Параметр slots равен slot_capacity первого кернела. Дополнительный элемент собирает общий статус.
Каждый из первых рабочих элементов обслуживает один слот. Маска задаёт начальное значение valid: единица для активного слота, иначе ноль. Затем проверяются все K сценариев. Нарушение, ненулевой статус или неконечная сводная величина обнуляют сценарную допустимость. Достаточно одного проблемного сценария.
__kernel void DPCCRolloutReduce(__global const float *system_status,
__global const float *violations,
__global const float *slot_active,
__global float *validity,
__global float *aggregate_system,
uint K, uint slots, uint slot_active_offset)
{
const size_t slot = get_global_id(0);
if(slot < slots)
{
float valid = slot_active[(size_t)slot_active_offset + slot] == 1.0f ? 1.0f : 0.0f;
for(uint k = 0; k < K; k++)
{
const size_t pair = slot * K + k;
if(system_status[pair] != 0.0f || violations[pair * 8] >= 0.0f ||
violations[pair * 8 + 5] != 0.0f || !isfinite(violations[pair * 8 + 2]) ||
!isfinite(violations[pair * 8 + 3]))
valid = 0.0f;
}
validity[slot] = valid;
}
else
if(slot == slots)
{
float status = 0.0f;
for(size_t pair = 0; pair < (size_t)slots * K; pair++)
{
const float current = system_status[pair];
if(!isfinite(current) || (current != 0.0f && current != 3.0f && current != 5.0f))
status = (float)DPCC_STATUS_NUMERIC;
else
status = fmax(status, current);
}
aggregate_system[0] = status;
}
}Элемент буфера validity равен единице только для активного плана, который прошёл проверку по всем сценариям. Это жёсткий сценарный фильтр: переданный сценарий может отклонить план независимо от своей вероятности. Вероятности и хвостовые веса подключаются позже, при спектральном сравнении уже прошедших фильтр вариантов.
Буфер system_status хранит системный статус расчёта пары. Поле violations[5] дополнительно отмечает локальную невалидность кандидата. Для расчёта validity проверяются оба поля. В aggregate_system собираются только значения system_status. Локальный ненулевой код при нулевом системном исключает план, но не весь запуск.
Дополнительный рабочий элемент собирает system_status всех пар. При корректном контексте неактивные слоты оставляют его нулевым. Коды — малые целые значения, записанные в float без арифметических преобразований. Маска проверена на точные 0 и 1. Поэтому сравнение с константами не требует допуска на округление.
Код 5 означает численную ошибку и имеет приоритет над кодом 3 — ошибкой исходных данных в system_status. Неожиданное или неконечное значение также даёт код 5. Ноль в aggregate_system подтверждает отсутствие системных ошибок, но не сценарную допустимость планов.
Нехватка свободной маржи отклоняет план, а ошибка контекста или переполнение — весь запуск. Поэтому сначала проверяем общий статус, затем маску сценарной допустимости. Только после этого сравниваем денежные оценки.
Класс CNeuronScenarioRolloutOCL связывает оба кернела с прямым проходом библиотеки нейронных сетей. После объединения статусов на CPU считывается один элемент. Результаты, состояния и нарушения остаются на GPU для следующих операций.
Заключение
Мы разобрали связь DPCC с диффузионным планированием и начали торговую адаптацию со сценарного расчёта. Для заданного плана получаем историю средств, маржи и позиции. Она позволяет проверить ближайшее действие с учётом запланированного продолжения.
Кернел DPCCRollout рассчитывает последствия плана, а DPCCRolloutReduce объединяет проверки по сценариям. Диагностика сохраняет шаг и фазу нарушения. Положительный результат не скрывает превышения лимита, а системная ошибка отделена от локальной невалидности кандидата.
Сценарная допустимость относится только к переданным сценариям, модели исполнения и принятому правилу внутрибарного пути. Оценка G помогает сравнивать планы, но не доказывает их безопасность. Проверка не заменяет обоснования сценарной модели и дальнейшей коррекции плана.
Но это только начало пути. В следующей статье будет продолжена работа по адаптации DPCC к задачам финансовых рынков.
Ссылки
- Diffusion Predictive Control with Constraints
- Römer R., von Rohr A., Schoellig A. P. Diffusion Predictive Control with Constraints. PMLR, 283:791–803, 2025.
- Denoising Diffusion Probabilistic Models
- Planning with Diffusion for Flexible Behavior Synthesis
- Нейросети — это просто (Часть 32): Распределённое Q-обучение
- Нейросети — это просто (Часть 33): Квантильная регрессия в распределённом Q-обучении
- Нейросети — это просто (Часть 34): Полностью параметризированная квантильная функция
- Другие статьи серии
Файлы проекта
В таблице приведены файлы сопровождающего проекта. В этой статье разобраны кернелы из NeuroNet.cl. Программы обучения и тестирования перечислены для ориентации в проекте. Их работа в текущую практическую часть не входит.
| # | Имя | Тип | Описание |
|---|---|---|---|
| 1 | Trajectory.mqh | Общий модуль | Построение моделей и вспомогательные функции обучения |
| 2 | StudyForecast.mq5 | Советник | Обучение прогнозной модели |
| 3 | Study.mq5 | Советник | Обучение базовой модели |
| 4 | StudyOnline.mq5 | Советник | Онлайн-обучение модели |
| 5 | Test.mq5 | Советник | Тестирование модели на отложенном периоде |
| 6 | NeuroNet.mqh | Библиотека классов | Библиотека классов нейронных сетей |
| 7 | NeuroNet.cl | OpenCL-программа | OpenCL-кернелы библиотеки нейронных сетей |
Исходные файлы доступны в репозитории проекта.
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Нейронная сеть на практике: Функции активации
От начального к среднему уровню: Подокна (IV)
От базового к среднему уровню: Ресурсы
Стресс-тестирование последовательностей сделок методом Монте-Карло в MQL5
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования