preview
Нейросети в трейдинге: Принятие торговых решений с учётом неопределённости (Модули прогнозирования и планирования)

Нейросети в трейдинге: Принятие торговых решений с учётом неопределённости (Модули прогнозирования и планирования)

MetaTrader 5Торговые системы |
62 0
Dmitriy Gizlyk
Dmitriy Gizlyk

Введение

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

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

В первой статье мы заложили основу такого подхода. Вместо анализа отдельных ценовых значений мы начали описывать рынок через карту плотности состояний. Это позволяет рассматривать новый бар не отдельно, а в контексте накопленной истории. Высокая плотность на карте показывает области, где рынок бывал часто. Такие зоны имеют хорошую статистическую поддержку. Низкая плотность указывает на области рыночной пустоты, где движение цены обычно менее предсказуемо.

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

Однако карта состояний и оценка неопределённости ещё не дают готового торгового решения. Они помогают ответить на вопросы: где находится рынок сейчас и насколько можно доверять этому состоянию. Но трейдеру нужно понимать, что делать дальше. Стоит ли открывать позицию, уменьшить объём сделки, подождать более благоприятной ситуации или вовсе отказаться от входа? Здесь начинается этап прогнозирования и планирования действий.

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

В этой статье мы сделаем следующий шаг в адаптации UncAD. Рассмотрим модули прогнозирования и планирования, которые используют карту рыночных состояний и оценку её неопределённости. Наша цель — сделать процесс принятия решений более осторожным и практичным. Если рынок хорошо знаком модели, она может действовать увереннее. Если карта разрежена и неопределённость высока, система должна снижать риск или отказаться от сделки.

Авторская визуализация фреймворка UncAD



Модуль прогнозирования

В авторской постановке задачи модуль прогнозирования отвечает за генерацию возможных траекторий движения транспортных средств в текущей дорожной ситуации. Система анализирует сцену, учитывает карту местности, взаимодействие между участниками движения и строит несколько вариантов дальнейшего развития событий. Каждый такой вариант сопровождается оценкой уверенности. Это принципиальный момент. Модель не пытается угадать единственную правильную траекторию. Она рассматривает набор вероятных сценариев и передаёт их дальше в контур планирования.

При прямом переносе этой идеи на финансовый рынок мы сразу приходим к участникам процесса. На дороге такими участниками являются автомобили, пешеходы и другие видимые объекты. На рынке это покупатели, продавцы, маркет-мейкеры, крупные фонды, алгоритмические системы и спекулянты. Есть и участники, которые временно вне позиции, но готовы войти при изменении условий. Проблема в том, что таких участников очень много. Более того, мы не видим их напрямую. У нас нет полной картины их намерений, горизонтов, объёмов и ограничений.

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

Иными словами, мы не наблюдаем участников рынка по отдельности, но видим результат их совместного действия. Цена становится следом коллективного решения. Объём показывает интенсивность участия. Индикаторы и производные признаки помогают описать форму этого движения, его устойчивость и внутреннюю динамику. Поэтому в нашей адаптации прогнозируемыми объектами становятся не участники рынка, а одномерные временные ряды, через которые проявляется рыночное состояние.

Под такими рядами мы можем понимать цену, объём, волатильность, отдельные индикаторы, производные от цены признаки, отношения между признаками, спредовые характеристики и другие компоненты рыночного описания. Каждый такой ряд несёт собственную часть информации о текущем состоянии рынка. Цена показывает итоговое направление давления. Объём указывает на интенсивность участия. Волатильность отражает уровень напряжения. Индикаторы помогают описать структуру движения. Вместе они формируют более полную картину, чем один ценовой ряд.

На этом этапе может показаться парадоксальным, что мы начинали работу с попытки уйти от прямого прогнозирования цены, а теперь снова к нему возвращаемся. Но это уже другая задача. Ранее мы отказались от наивной постановки, в которой модель должна просто назвать будущую цену или направление следующего бара. Такой подход слишком узок. Он заставляет модель делать вид, будто рынок развивается по одной заранее известной линии.

Теперь речь идёт не о точечном прогнозе цены, а о прогнозировании нескольких рыночных рядов в связке. Модель должна оценить, как могут измениться цена, объём и производные признаки в ближайшем будущем. Более того, нас интересует не один вариант развития событий, а несколько возможных траекторий с оценкой их вероятности. Это ближе к реальной работе трейдера. Опытный участник рынка редко мыслит одной линией. Он рассматривает продолжение импульса, замедление, откат, пробой уровня, ложный выход или переход в боковой режим. Но каждый такой сценарий должен подтверждаться не одним признаком, а согласованным поведением нескольких рыночных рядов.

В оригинальном фреймворке UncAD для этой цели используются агентные запросы. Сначала они извлекают признаки участников из промежуточного представления сцены. Затем между агентами выполняется взаимодействие через механизм внимания. После этого обновлённые агентные представления сопоставляются с картой местности. В результате модель получает уточнённые представления, которые учитывают и поведение участников, и ограничения окружающей среды. Финальный MLP-декодер формирует мультимодальные траектории и оценки уверенности по каждому варианту.

В нашей задаче роль таких запросов меняется. Мы не можем назначить отдельный запрос каждому реальному участнику торгов. Вместо этого запросы разумно связать с отдельными одномерными временными рядами. Один запрос может описывать ценовой ряд. Другой — объём. Третий — волатильность. Следующие — отдельные индикаторы или синтетические признаки, полученные из исходных котировок. Таким образом, запросы представляют не скрытых участников рынка, а наблюдаемые каналы, через которые проявляется их совокупное влияние.

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

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

Именно здесь возникает ключевое отличие от автономного вождения. На дороге будущие траектории участников должны избегать пересечения, потому что пересечение может означать столкновение. В трейдинге логика обратная. Прогнозные траектории цены, объёма, волатильности и индикаторов должны не расходиться, а подтверждать друг друга. Если цена указывает на продолжение роста, но объём слабеет, волатильность сжимается, а карта состояния разрежена, вес такого прогноза должен быть ниже. Если же несколько рядов дают согласованную картину, доверие к сценарию может быть выше.

В нашей реализации модуль прогнозирования представлен классом CNeuronUncAD_UGP. Название отражает его назначение: Uncertainty-Guided Prediction, то есть прогнозирование, направляемое оценкой неопределённости. Он должен согласовать между собой отдельные рыночные ряды, затем сопоставить их с картой рыночных состояний и только после этого передать уточнённое представление дальше по сети.

class CNeuronUncAD_UGP  :  public CNeuronSpikeMHCrossAttention
  {
protected:
   CNeuronMultiMixAttention      cSelfAttention;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput,
                                        CBufferFloat *SecondGradient,
                                        ENUM_ACTIVATION SecondActivation = None) override;

public:
                     CNeuronUncAD_UGP(void) {};
                    ~CNeuronUncAD_UGP(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint window, uint units, uint context_window,
                          uint context_units, uint heads, uint key_dimension,
                          uint candidates, uint topK,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void) override const  {  return defNeuronUncAD_UGP;   }
   //--- methods for working with files
   virtual bool      Save(int const file_handle) override;
   virtual bool      Load(int const file_handle) override;
   //---
   virtual void      SetOpenCL(COpenCLMy *obj) override;
   virtual void      TrainMode(bool flag) override;
   virtual bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   virtual bool      Clear(void) override;
  };

Класс CNeuronUncAD_UGP наследуется от CNeuronSpikeMHCrossAttention. Это не случайный выбор. На уровне логики нам нужен механизм, который умеет связывать один набор признаков с другим. В оригинальном фреймворке UncAD агентные запросы после внутреннего взаимодействия сопоставляются с картой сцены. В нашей постановке роль таких запросов выполняют отдельные одномерные временные ряды: цена, объём, волатильность, индикаторы и другие производные признаки. А роль карты сцены выполняет карта рыночных состояний с оценкой неопределённости.

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

Внутри объекта объявлен дополнительный модуль cSelfAttention типа CNeuronMultiMixAttention. Это важный элемент. Перед тем как обращаться к карте состояний, модель должна согласовать анализируемые ряды между собой. Цена не должна рассматриваться отдельно от объёма. Волатильность не должна жить собственной жизнью в стороне от импульса. Индикаторный сигнал не должен слепо восприниматься как самостоятельная истина. Поэтому первым этапом выполняется Self-Attention по входному представлению. Он помогает модели понять, какие ряды подтверждают друг друга, какие ослабляют общий сигнал, а какие могут указывать на внутреннее противоречие рыночной картины.

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

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

bool CNeuronUncAD_UGP::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                            uint window, uint units, uint context_window,
                            uint context_units, uint heads, uint key_dimension,
                            uint candidates, uint topK,
                            ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(window == 0 || units == 0 || context_window == 0 || context_units == 0 ||
      heads == 0 || key_dimension == 0 || candidates == 0 || topK == 0)
      ReturnFalse;
   if(!CNeuronSpikeMHCrossAttention::Init(numOutputs, myIndex, open_cl,
                                          window, key_dimension, heads,
                                          units, context_window, context_units,
                                          optimization_type, batch))
      ReturnFalse;

Такая проверка выглядит простой, но она защищает объект от бессмысленной конфигурации. Для механизмов внимания нулевые размерности недопустимы. Здесь лучше остановить создание слоя сразу, чем потом искать ошибку в глубине OpenCL-вычислений, где, как известно, отладка редко превращается в прогулку по набережной.

После проверки параметров вызывается инициализация родительского класса CNeuronSpikeMHCrossAttention. На этом этапе создаётся основной контур кросс-внимания. Ему передаются параметры текущего представления и контекстного входа. Фактически здесь подготавливается механизм, который позднее будет связывать уточнённые рыночные ряды с картой состояния рынка.

Затем инициализируется внутренний слой cSelfAttention. Он получает параметры входного окна, количество элементов, число голов, размерность ключей, количество кандидатов и topK.

   uint index = 0;
   if(!cSelfAttention.Init(0, index, OpenCL, window, units, heads,
                           key_dimension, candidates, topK, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }

Алгоритм прямого прохода реализован в методе feedForward. На вход он получает два источника данных: объект NeuronOCL и буфер SecondInput. Первый источник содержит представление анализируемых рыночных рядов. Второй источник используется как контекст для кросс-внимания. В нашей архитектуре он связан с картой рыночных состояний и оценкой неопределённости.

bool CNeuronUncAD_UGP::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
   if(!NeuronOCL || !SecondInput)
      ReturnFalse;
   if(NeuronOCL.Neurons() < Neurons())
      ReturnFalse;

Перед началом вычислений метод проверяет наличие входных данных и соответствие размерности. Если входной объект содержит меньше нейронов, чем требуется текущему слою, дальнейшие вычисления не выполняются.

Далее выполняется первый смысловой этап — cSelfAttention.

   if(!cSelfAttention.FeedForward(NeuronOCL))
      ReturnFalse;

На этом шаге модель анализирует взаимодействие между исходными временными рядами. Она не строит финальный прогноз, а формирует более зрелое внутреннее представление. Цена, объём, волатильность и индикаторы начинают рассматриваться не как отдельные колонки данных, а как связанные компоненты единой рыночной ситуации. Это особенно важно для трейдинга. Сильный сигнал редко живёт в одном признаке. Как правило, он проявляется через согласованность нескольких рядов.

После этого результат передаётся в родительский механизм кросс-внимания.

   if(!CNeuronSpikeMHCrossAttention::feedForward(cSelfAttention.AsObject(), SecondInput))
      ReturnFalse;
//---
   return true;
  }

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

В результате CNeuronUncAD_UGP связывает карту рыночной среды с модулем планирования. Он ещё не принимает торговое решение. Это важно подчеркнуть. Его задача — подготовить качественное прогнозное представление. Сначала он согласует отдельные временные ряды между собой, затем сверяет их с картой состояний и неопределённостью. Только после этого последующие блоки смогут формировать возможные варианты будущего движения и выбирать наиболее рациональное торговое действие.


Модуль планирования

После модуля прогнозирования переходим к следующему важному элементу фреймворка UncADмодулю планирования. Здесь снова приходится сместить акценты. В авторской постановке задачи этот модуль оценивает поведение ego-агента, то есть управляемого транспортного средства, в контексте текущей дорожной обстановки. Система учитывает прогнозные траектории других участников движения, карту местности, возможные ограничения и на этой основе выбирает будущую траекторию собственного автомобиля.

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

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

Текущее направление движения такого ego-агента определяется открытыми позициями. Если мы находимся вне рынка, наше транспортное средство фактически стоит на месте. Оно сохраняет возможность выбрать направление, но пока не принимает рыночный риск. Если открыта длинная позиция, счёт уже движется вместе с ростом цены и против падения. Если открыта короткая позиция, ситуация обратная: положительный результат зависит от снижения цены, а рост рынка становится неблагоприятным движением среды. Поэтому планирование должно учитывать не только прогноз рынка, но и то, в каком положении уже находится сам счёт.

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

В авторском фреймворке траектория ego-агента должна согласовываться с дорожной сценой. Автомобиль не может выбирать маршрут в вакууме. Он должен учитывать разметку, границы дороги, препятствия и прогноз движения других участников. В нашей постановке торговый счёт также не может рассматриваться отдельно от рынка. Открытая позиция должна соответствовать прогнозируемому движению цены, подтверждаться поведением объёма и индикаторов, а также не противоречить карте рыночных состояний. Если рынок движется в одну сторону, а наш счёт через открытую позицию фактически движется в другую, возникает конфликт.

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

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

Особенно важно, что нахождение вне рынка здесь также является полноценным состоянием. Это не отсутствие решения, а один из вариантов планирования. Если карта состояния разрежена, прогнозные ряды противоречат друг другу, а неопределённость повышена, отказ от входа может быть лучшим действием. В классическом трейдинге это хорошо известное правило: не каждая ситуация требует сделки. Иногда самая сильная позиция — это отсутствие позиции. Нейронная сеть тоже должна получить право не торговать, когда рыночная сцена не даёт достаточной опоры.

Алгоритм модуля планирования реализуем в объекте CNeuronUncAD_UGPL. Его название можно расшифровать как Uncertainty-Guided Planning, то есть планирование, направляемое неопределённостью. Если предыдущий блок помогал уточнить прогнозные представления рыночных рядов, то здесь задача становится более прикладной. Модель должна не только оценить, как может измениться рынок, но и определить поведение ego-агента. В нашей постановке это торговый счёт с текущими позициями, ограничениями и доступным риском.

class CNeuronUncAD_UGPL :  public CNeuronSpikeMHCrossAttention
  {
protected:
   CParams                     cMode;
   CNeuronSpikeMHCrossAttention cModeFusion;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput,
                                        CBufferFloat *SecondGradient,
                                        ENUM_ACTIVATION SecondActivation = None) override;

public:
                     CNeuronUncAD_UGPL(void) {};
                    ~CNeuronUncAD_UGPL(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint window, uint units, uint context_window,
                          uint context_units, uint mode_units, uint heads,
                          uint key_dimension,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void) override const  {  return defNeuronUncAD_UGPL;  }
   //--- methods for working with files
   virtual bool      Save(int const file_handle) override;
   virtual bool      Load(int const file_handle) override;
   //---
   virtual void      SetOpenCL(COpenCLMy *obj) override;
   virtual void      TrainMode(bool flag) override;
   virtual bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   virtual bool      Clear(void) override;
  };

Класс CNeuronUncAD_UGPL, как и предыдущий объект прогнозирования, наследуется от CNeuronSpikeMHCrossAttention. Это указывает на ключевую роль механизма кросс-внимания. Планирование не может выполняться только по состоянию счёта. Оно должно учитывать внешний контекст: прогнозные представления рыночных рядов, карту плотности состояний и оценку неопределённости. Поэтому базовый механизм кросс-внимания используется как инструмент сопоставления нашего торгового состояния с рыночной средой.

Внутри класса объявлены два дополнительных элемента: cMode и cModeFusion. Первый объект, cMode, представляет собой набор обучаемых модальных параметров. В авторской работе похожую роль выполняют Mode Queries, полученные из кластеров типовых траекторий движения. В нашей реализации они становятся латентными режимами планирования. Это не жёстко заданные команды вроде buy, sell и hold. Скорее, это обучаемые шаблоны возможного поведения счёта: сопровождение позиции, усиление направления, снижение риска, переход в ожидание или подготовка к развороту. Модель сама уточняет содержание этих режимов в процессе обучения.

Второй объект, cModeFusion, отвечает за объединение текущего представления ego-агента с модальными запросами. Это очень важный этап. Состояние счёта само по себе ещё не даёт плана. Например, наличие длинной позиции не означает, что её нужно удерживать. Всё зависит от того, в каком режиме находится рынок, как изменились прогнозные ряды и насколько высока неопределённость. Поэтому модель сначала берёт текущее состояние счёта и сопоставляет его с набором возможных режимов поведения. Так формируется более богатое представление для планирования.

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

bool CNeuronUncAD_UGPL::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                             uint window, uint units, uint context_window,
                             uint context_units, uint mode_units, uint heads,
                             uint key_dimension,
                             ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(window == 0 || units == 0 || context_window == 0 || context_units == 0 ||
      mode_units == 0 || heads == 0 || key_dimension == 0)
      ReturnFalse;
   if(!CNeuronSpikeMHCrossAttention::Init(numOutputs, myIndex, open_cl,
                                          window, key_dimension, heads,
                                          units, context_window, context_units,
                                          optimization_type, batch))
      ReturnFalse;

Для таких архитектур лучше сразу остановить инициализацию, чем потом героически искать ошибку в OpenCL-буферах. Героизм в отладке хорош, но ещё лучше — не создавать ему поводов.

После проверки параметров вызывается инициализация родительского класса CNeuronSpikeMHCrossAttention. На этом этапе создаётся основной механизм взаимодействия с внешним контекстом. Далее этот механизм будет сопоставлять объединённое представление ego-агента и режимов планирования со входом SecondInput. В нашей архитектуре этот второй вход может содержать уточнённое прогнозное представление рынка, карту состояний или объединённый контекст, в котором уже присутствует оценка неопределённости.

Далее инициализируется объект cMode. Его размер задаётся как window * mode_units. Это означает, что для каждого шага окна формируется набор модальных элементов.

   uint index = 0;
   if(!cMode.Init(0, index, OpenCL, window * mode_units, optimization, iBatch))
      ReturnFalse;
   cMode.SetActivationFunction(None);

Важно, что для cMode отключается функция активации. Здесь нам не нужно искусственно ограничивать значения нелинейным преобразованием. Этот блок играет роль обучаемых параметров режима, а не результата классического нейронного преобразования входных данных. Иными словами, сеть получает набор свободных модальных векторов, которые могут адаптироваться под структуру задачи.

После этого создаётся слой cModeFusion. Он также построен на механизме CNeuronSpikeMHCrossAttention. Его задача — связать входное представление NeuronOCL с модальными параметрами cMode.

   index++;
   if(!cModeFusion.Init(0, index, OpenCL, window, key_dimension, heads,
                        units, window, mode_units, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }

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

Алгоритм прямого прохода реализован в методе feedForward. На вход метод получает основной объект NeuronOCL и дополнительный буфер SecondInput. Первый источник содержит представление ego-агента, то есть текущее торговое состояние. Второй источник передаёт внешний рыночный контекст.

bool CNeuronUncAD_UGPL::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
   if(!NeuronOCL || !SecondInput)
      ReturnFalse;
   if(NeuronOCL.Neurons() < Neurons())
      ReturnFalse;

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

Первым вычислительным шагом вызывается генерация токенов рабочих режимов.

   if(bTrain)
      if(!cMode.FeedForward())
         ReturnFalse;

Это внутренние параметры модели, которые хранят типовые режимы будущего поведения. В авторской задаче такие режимы связаны с вариантами движения автомобиля. В нашей задаче — с вариантами поведения торгового счёта.

Затем выполняется cModeFusion.

   if(!cModeFusion.FeedForward(NeuronOCL, cMode.getOutput()))
      ReturnFalse;

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

После объединения состояния счёта с модальными режимами результат передаётся в родительский механизм кросс-внимания.

   if(!CNeuronSpikeMHCrossAttention::feedForward(cModeFusion.AsObject(), SecondInput))
      ReturnFalse;
//---
   return true;
  }

Здесь уже сформированное представление плана сопоставляется с внешним рыночным контекстом. Это соответствует авторской идее взаимодействия ego-запросов с прогнозными агентными признаками и картой местности. В нашей адаптации этот этап проверяет, насколько выбранный режим поведения счёта согласуется с прогнозной динамикой цены, объёма, индикаторов, картой плотности состояний и уровнем неопределённости.

Такой порядок вычислений важен. Мы не пытаемся сначала получить торговый сигнал, а потом подобрать под него объяснение. Напротив, модель последовательно формирует план: сначала описывает текущее состояние ego-агента, затем связывает его с возможными режимами поведения, после чего проверяет эти режимы на соответствие рыночной среде. Это ближе к реальной торговой логике. Сначала трейдер понимает, где находится его счёт и какую позицию он уже несёт. Затем оценивает варианты действий. И только потом сверяет их с рынком.

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

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


Заключение

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

В модуле прогнозирования мы отказались от прямого сопоставления с отдельными участниками рынка. Вместо этого прогнозируемыми объектами стали одномерные временные ряды: цена, объём, волатильность, индикаторы и производные признаки. Через них проявляется суммарное действие всех участников торгов. Объект CNeuronUncAD_UGP согласует эти ряды между собой и сопоставляет их с картой рыночных состояний. В результате модель получает не одиночный прогноз цены, а более цельное представление возможной динамики рынка с учётом плотности состояний и неопределённости.

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


Ссылки


Программы, используемые в статье

# Имя Тип Описание
1 Study.mq5 Советник Советник офлайн-обучения моделей
2 StudyOnline.mq5 Советник Советник онлайн-обучения моделей
3 Test.mq5 Советник Советник для тестирования модели
4 Trajectory.mqh Библиотека класса Структура описания состояния системы и архитектуры моделей
5 NeuroNet.mqh Библиотека класса Библиотека классов для создания нейронной сети
6 NeuroNet.cl Библиотека Библиотека кода OpenCL-программы

Проект представлен на forge.mql5.io/dng.

Прикрепленные файлы |
MQL5.zip (3810.6 KB)
Алгоритм оптимизации кита-белухи — Beluga Whale Optimization (BWO) Алгоритм оптимизации кита-белухи — Beluga Whale Optimization (BWO)
Кандидат в нашу рейтинговую таблицу — Beluga Whale Optimization, метаэвристика, построенная на трёх моделях поведения кита-белухи: парном плавании, охоте с полётом Леви и обновлении популяции через падение кита. По ходу реализации обнаружилось, что алгоритм не столько оптимизирует, сколько считывает геометрию тестового стенда, разбираем механизм этого и собираем честную перспективную модификацию BWOm.
Разработка инструментария для анализа Price Action (Часть 57): Создание модуля классификации состояния рынка на MQL5 Разработка инструментария для анализа Price Action (Часть 57): Создание модуля классификации состояния рынка на MQL5
В этой статье представлен модуль классификации состояния рынка на MQL5, который интерпретирует поведение цены по данным закрытых свечей. Анализируя сжатие и расширение волатильности, а также согласованность структуры, инструмент классифицирует состояние рынка как сжатие, переходное состояние, расширение или тренд и тем самым формирует четкий контекст для анализа Price Action.
Разработка инструментария для анализа Price Action (Часть 58): Модуль анализа сжатия диапазона и классификации зрелости Разработка инструментария для анализа Price Action (Часть 58): Модуль анализа сжатия диапазона и классификации зрелости
В продолжение предыдущей статьи, где был представлен модуль классификации состояния рынка, в этой части мы сосредоточимся на реализации основной логики выявления и оценки зон сжатия. В статье представлена система обнаружения сжатия диапазона и оценки зрелости на языке MQL5, которая анализирует зоны рыночной консолидации, опираясь только на динамику цены.
Переосмысливаем классические стратегии (Часть 18): Алгоритмический поиск свечных паттернов Переосмысливаем классические стратегии (Часть 18): Алгоритмический поиск свечных паттернов
Эта статья помогает новым участникам сообщества искать и находить собственные свечные паттерны. Описание этих паттернов может оказаться сложной задачей, поскольку требует ручного поиска и творческого подхода к выявлению усовершенствований. В этой статье мы представляем свечной паттерн поглощения и показываем, как его можно усовершенствовать для создания более прибыльных торговых стратегий.