preview
Нейросети в трейдинге: Сеточная аппроксимация событийного потока как инструмент анализа ценовых паттернов (ADM-модуль)

Нейросети в трейдинге: Сеточная аппроксимация событийного потока как инструмент анализа ценовых паттернов (ADM-модуль)

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

Введение

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

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

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

В общем виде архитектура EEMFlow следует классической, проверенной временем схеме Encoder–Decoder с многомасштабной обработкой. Однако за внешней знакомостью скрывается важное отличие. Здесь энкодер не просто извлекает признаки, а работает с уже адаптированным событийным пространством. Корреляционные модули ищут не совпадения плотности, а согласованность движения. А блоки реконструкции восстанавливают детали, опираясь на уверенность в локальной структуре, а не на слепую интерполяцию.

Центральным элементом этой логики является Adaptive Density Module (ADM) — модуль адаптации плотности. Именно он связывает сырые события и всю последующую архитектуру в единое целое. ADM отвечает за то, чтобы данные, поступающие в модель, имели стабильные статистические свойства независимо от того, находится ли система в спокойном режиме или в фазе резкого импульсного движения. Для финансовых рынков это особенно важно. Здесь смена режимов — не исключение, а норма.

В структуре ADM выделяется 2 крупных блока:

  • Multi-Density Changer (MDC),
  • Multi-Density Selector (MDS).

Multi-Density Changer (MDC) — базовый, но принципиально важный компонент. Его задача проста по формулировке и сложна по сути: преобразовать событийный поток таким образом, чтобы различия в плотности перестали доминировать над структурой движения. MDC не пытается угадать рынок и не добавляет предсказательной логики, он выполняет работу, которую традиционно делают хорошие инженеры данных: приводит хаотичный вход к аккуратному, контролируемому виду.

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

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



Модуль селекции признаков

После того как Multi-Density Changer (MDC) сформировал многомасштабное представление событийного потока, мы оказываемся в принципиально новой точке. Перед нами три параллельных представления одного и того же рынка, каждое из которых отражает его поведение в своём режиме плотности. Это уже серьёзный шаг вперёд. Но на этом работа ADM не заканчивается. Наоборот — именно здесь она по-настоящему начинается.

Рынок не живёт одновременно в трёх режимах. В каждый конкретный момент времени он тяготеет к одному из них, иногда плавно, иногда резко. Задача системы — не просто иметь несколько вариантов представления, а уметь выбрать актуальное, не теряя при этом информации и не впадая в жёсткие переключения. Именно эту роль в архитектуре EEMFlow играет Multi-Density Selector (MDS).

Если MDC отвечает на вопрос как представить поток событий при разных плотностях, то MDS отвечает на более тонкий и, пожалуй, более важный вопрос — какое из этих представлений имеет наибольшую ценность прямо сейчас?

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

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

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

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

Для реализации функционала MDS-модуля мы создаем новый объект — CNeuronMDS. Его появление не случайно и отражает роль MDS в общей структуре фреймворка. Этот модуль не является обычным нейроном в классическом понимании. Он работает с несколькими входными представлениями одновременно и выполняет селекцию на уровне плотностных масштабов, а не отдельных признаков.

class CNeuronMDS   :  public CNeuronBaseOCL
  {
protected:
   uint                    iDimension;
   uint                    iUnits;
   uint                    iVariables;
   CNeuronBaseOCL          cConcat;
   CLayer                  cScores;
   CNeuronTransposeVRCOCL  cTranspose;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override { return false; }
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override { return false; }
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *second) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override { return false; }
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput,
                                        CBufferFloat *SecondGradient,
                                        ENUM_ACTIVATION SecondActivation = None) override;

public:
                     CNeuronMDS(void) {};
                    ~CNeuronMDS(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint window, uint units_count, uint variables,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void) override const  {  return defNeuronMDS;   }
   //--- 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 bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   virtual void      SetActivationFunction(ENUM_ACTIVATION value) override { };
  };

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

Внутренние параметры iDimension, iUnits и iVariables задают геометрию обрабатываемых данных и позволяют жёстко зафиксировать форму входных и выходных представлений. Это важный момент. MDS работает не с абстрактными сигналами, а с уже структурированными признаками, сформированными MDC. Любая неоднозначность в размерностях здесь недопустима, так как она напрямую влияет на корректность дальнейших вычислений.

Формирование весов реализуется через блок нейронных слоев cScores. Именно на этом этапе вычисляются оценки релевантности плотностного представления в разрезе каждого отдельного признака. Это принципиально отличает MDS от грубых механизмов глобального взвешивания. Селектор не назначает один общий коэффициент для всего масштаба плотности. Напротив, он анализирует вклад каждого признака отдельно, формируя тонкую, локально-адаптивную схему взвешивания.

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

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

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

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

bool CNeuronMDS::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                      uint window, uint units_count, uint variables,
                      ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window * units_count * variables,
                                                                 optimization_type, batch))
      return false;
   activation = None;

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

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

   iDimension = window;
   iUnits = units_count;
   iVariables = variables;

Следующим шагом инициализируется объект конкатенации данных.

   uint index = 0;
   if(!cConcat.Init(0, index, OpenCL, 4 * Neurons(), optimization, iBatch))
      return false;
   cConcat.SetActivationFunction(None);

Здесь следует обратить внимание на один принципиальный момент. Multi-Density Selector получает данные сразу из двух источников. Первый — это исходное событийное представление, поступающее на вход ADM, то есть данные, ещё не прошедшие плотностную адаптацию. Второй — это уже сформированное много-плотностное представление, полученное на выходе MDC. Таким образом, MDS изначально работает не в отрыве от исходного потока, а в его контексте.

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

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

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

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

  CNeuronConvOCL*      conv = NULL;
   CNeuronBatchNormOCL* norm = NULL;
   CNeuronSoftMaxOCL*   softmax = NULL;
   cScores.Clear();
   cScores.SetOpenCL(OpenCL);
   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(0, index, OpenCL, 4 * iDimension, 4 * iDimension, iDimension,
                                  iUnits, iVariables, optimization, iBatch) ||
      !cScores.Add(conv))
      return false;
   conv.SetActivationFunction(None);

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

   index++;
   norm = new CNeuronBatchNormOCL();
   if(!norm ||
      !norm.Init(0, index, OpenCL, conv.Neurons(), iBatch, optimization) ||
      !cScores.Add(norm))
      return false;
   norm.SetActivationFunction(SoftPlus);

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

   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(0, index, OpenCL, iDimension, iDimension, 4, iUnits,
                                 iVariables, optimization, iBatch) ||
      !cScores.Add(conv))
      return false;
   conv.SetActivationFunction(None);

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

   index++;
   norm = new CNeuronBatchNormOCL();
   if(!norm ||
      !norm.Init(0, index, OpenCL, conv.Neurons(), iBatch, optimization) ||
      !cScores.Add(norm))
      return false;
   norm.SetActivationFunction(SoftPlus);

Завершающим элементом cScores становится слой SoftMax. Именно здесь формируются нормированные веса релевантности. Причём не глобально для всего масштаба, а по каждому признаку отдельно. Установка числа голов iUnits*iVariables подчёркивает покомпонентный характер селекции. Каждый признак получает собственное распределение доверия по плотностным каналам. Это ключевой момент всей архитектуры MDS и принципиальное отличие от грубых схем выбора режима.

   index++;
   softmax = new CNeuronSoftMaxOCL();
   if(!softmax ||
      !softmax.Init(0, index, OpenCL, norm.Neurons(), optimization, iBatch) ||
      !cScores.Add(softmax))
      return false;
   softmax.SetHeads(iUnits * iVariables);

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

   index++;
   if(!cTranspose.Init(0, index, OpenCL, iUnits * iVariables, 4, iDimension, optimization, iBatch))
      return false;
   cTranspose.SetActivationFunction(None);
//---
   return true;
  }

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

Алгоритм прямого прохода Multi-Density Selector реализован в методе feedForward. Именно здесь происходит последовательная трансформация анализируемых данных в единое взвешенное представление признаков, актуальное для текущего состояния рынка.

bool CNeuronMDS::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
   if(!NeuronOCL || !SecondInput)
      return false;
   if(!Concat(NeuronOCL.getOutput(), SecondInput, cConcat.getOutput(), iDimension,
                                             3 * iDimension, iUnits * iVariables))
      return false;

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

Далее формируется последовательность вычислений через блок cScores. Каждый элемент этого блока применяется поочередно к предыдущему результату.

   CNeuronBaseOCL* prev = cConcat.AsObject();
   CNeuronBaseOCL* curr = NULL;
   for(int i = 0; i < cScores.Total(); i++)
     {
      curr = cScores[i];
      if(!curr ||
         !curr.FeedForward(prev))
         return false;
      prev = curr;
     }

Здесь реализуется принцип покомпонентного взвешивания. Каждый признак получает собственное распределение релевантности по плотностным каналам. Благодаря SoftMax на выходе формируются нормированные веса, а BatchNorm и SoftPlus обеспечивают стабильность численных значений, предотвращая резкие перепады при изменении рыночной активности.

После выполнения операций блока cScores, осуществляется транспонирование конкатенированного тензора, объединяющего исходные данные и многоплотностное представление от MDC.

   if(!cTranspose.FeedForward(cConcat.AsObject()))
      return false;
   if(!MatMul(cTranspose.getOutput(), curr.getOutput(), Output, iDimension, 4, 1, iUnits * iVariables, true))
      return false;
//---
   return true;
  }

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

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

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



Модуль Адаптивной плотности

На данном этапе мы уже реализовали два ключевых субмодуля Adaptive Density Module:

  • MDC, формирующий многоплотностное представление событийного потока,
  • MDS, обеспечивающий адаптивное взвешивание этих представлений на уровне отдельных признаков.
Каждый из этих модулей выполняет свою строго определённую задачу. И следующий логический шаг — объединение этих субмодулей в единое целое, чтобы получить законченный и самодостаточный блок ADM. Объединение подразумевает не простое «сведение воедино», а создание последовательного конвейера: сначала MDC преобразует поток, затем MDS на его основе формирует окончательное адаптивное представление.

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

Для объединения двух субмодулей мы создаём новый объект CNeuronSpikeADM, который наследуется от CNeuronMDS. Этот объект становится единой оболочкой Adaptive Density Module, объединяющей многоплотностное представление и механизм взвешивания признаков в рамках одного функционального блока.

class CNeuronSpikeADM : public CNeuronMDS
  {
protected:
   CNeuronSpikeConvBlock   cConv;
   CNeuronSpikeMDC         cMDC;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
                                          override { return feedForward(NeuronOCL); }
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *second)
                                     override {  return updateInputWeights(NeuronOCL); }
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput,
                                                                CBufferFloat *SecondGradient, 
                                                     ENUM_ACTIVATION SecondActivation = None)
                                           override { return calcInputGradients(NeuronOCL); }

public:
                     CNeuronSpikeADM(void) {};
                    ~CNeuronSpikeADM(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint window, uint units_count, uint variables,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void) override const  {  return defNeuronSpikeADM;   }
   //--- 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 bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   //---
   virtual void      SetActivationFunction(ENUM_ACTIVATION value) override { };
   virtual void      TrainMode(bool flag) override;
   virtual bool      Clear(void) override;
  };

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

Метод инициализации задаёт все ключевые параметры работы объединённого блока ADM и обеспечивает корректную инициализацию его внутренних компонентов.

bool CNeuronSpikeADM::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                           uint window, uint units_count, uint variables,
                           ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronMDS::Init(numOutputs, myIndex, open_cl, window, units_count,
                                     variables, optimization_type, batch))
      return false;

Сначала вызывается инициализация родительского класса, что задаёт структуру Multi-Density Selector, включая размерности анализируемых данных. Таким образом, MDS получает готовую конфигурацию для покомпонентного взвешивания плотностных каналов.

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

   if(!cConv.Init(0, 0, OpenCL, window, window, window, units_count, variables,
                                                         optimization, iBatch))
      return false;

cMDC — формирует многоплотностное представление, обеспечивая параллельные каналы плотности, которые будут оцениваться и взвешиваться MDS.

   if(!cMDC.Init(0, 1, OpenCL, window, window, units_count, variables, optimization, iBatch))
      return false;
//---
   return true;
  }

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

В результате метод инициализации полностью готовит новый объект к работе как цельного Adaptive Density Module: он объединяет MDC и MDS в единый конвейер обработки.

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

bool CNeuronSpikeADM::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cConv.FeedForward(NeuronOCL))
      return false;

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

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

   if(!cMDC.FeedForward(cConv.AsObject()))
      return false;

Наконец, управление передается родительскому классу, который получает два входа: результат предварительной свёртки и многоплотностное представление.

   if(!CNeuronMDS::feedForward(cConv.AsObject(), cMDC.getOutput()))
      return false;
//---
   return true;
  }

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

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



Интеграция ADM в модель

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

Следовательно, следующим шагом мы попробуем интегрировать реализованный ADM в одну из ранее рассмотренных моделей и произведем оценку его эффективности. Такой эксперимент позволит не только проверить корректность реализации, но и продемонстрировать практическую ценность Adaptive Density Module в реальных сценариях событийного анализа.

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

Для реализации задуманного эксперимента мы создаём новый объект CNeuronBATvADM, наследующийся от CNeuronBAT. Основная цель этого класса — интеграция Adaptive Density Module в существующую структуру BAT.

class CNeuronBATvADM :  public CNeuronBAT
  {
public:
                     CNeuronBATvADM(void) {};
                    ~CNeuronBATvADM(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint dimension, uint embed_size,
                          uint stack_size, uint variables,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void)   override const   {  return defNeuronBATvADM;   }
  };

Особенность CNeuronBATvADM заключается в том, что переопределяется только метод инициализации. Это позволяет на этапе конфигурации добавить ADM в вычислительный конвейер BAT, сохранив при этом все остальные функциональные возможности фреймворка. Благодаря такой архитектурной простоте новый объект становится гибкой платформой для эксперимента.

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

bool CNeuronBATvADM::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                      uint dimension, uint embed_size,
                      uint stack_size, uint variables,
                      ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBAT::Init(numOutputs, myIndex, open_cl, dimension, embed_size, 
                              stack_size, variables, optimization_type, batch))
      return false;

Затем, в инициализированный блок подготовки данных добавляется новый объект CNeuronSpikeADM, который представляет собой реализованный нами Adaptive Density Module.

   int index = cPrepare.Total();
   CNeuronSpikeADM* amd = new CNeuronSpikeADM();
   if(!amd ||
      !amd.Init(0, index, OpenCL, embed_size, stack_size, variables, optimization, iBatch) ||
      !cPrepare.Add(amd))
     {
      DeleteObj(amd)
      return false;
     }
//---
   return true;
  }

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

В результате метод инициализации формирует полностью готовый к работе объект CNeuronBATvADM, где ADM интегрирован как часть подготовки данных. Такой подход сохраняет все возможности BAT, при этом открывает путь для экспериментальной оценки влияния Adaptive Density Module на результативность модели, демонстрируя практическую гибкость и расширяемость фреймворка.

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



Тестирование

Обучение торговой политики и её тестирование на исторических данных проводилось в несколько последовательных этапов. Сначала модель проходила офлайн-обучение на котировках валютной пары EURUSD с таймфреймом H1 за период с Января 2024 по Июнь 2025 года. Этот временной отрезок выступал в роли тренировочной площадки. Модель изучала исторические паттерны, анализировала динамику цен и объёмы сделок, выявляла закономерности между ключевыми признаками и формировала прогнозы.

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

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

За период тестирования модель показала устойчивый рост депозита с $100 до $162, демонстрируя ROI62% при контролируемых просадках до 26,5%. Каждая сделка в среднем добавляла около 1% депозита, что говорит о взвешенном подходе к риску.

Статистика сделок подтверждает стабильность: 55% прибыльных, с лучшей отдачей на нисходящих импульсах, средняя прибыль превышает средний убыток, а кривые Balance и Equity демонстрируют плавный, ступенчатый рост без резких разгонов. Sharpe Ratio = 3.64 — показатель высокой стабильности и низкой дисперсии доходности.

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



Заключение

В ходе работы был реализован один из ключевых компонентов фреймворка EEMFlow — Adaptive Density Module, объединяющий многоплотностное представление и адаптивное взвешивание признаков. Его интеграция в существующие модели, на примере BAT, показала практическую эффективность. Модуль позволяет системе учитывать разные масштабы плотности рынка и выделять наиболее релевантные признаки для текущего состояния.

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

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


Ссылки


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

#ИмяТипОписание
1Study.mq5СоветникСоветник офлайн обучения моделей
2StudyOnline.mq5 Советник Советник онлайн обучения моделей
3Test.mq5СоветникСоветник для тестирования модели
4Trajectory.mqhБиблиотека классаСтруктура описания состояния системы и архитектуры моделей
5NeuroNet.mqhБиблиотека классаБиблиотека классов для создания нейронной сети
6NeuroNet.clБиблиотекаБиблиотека кода OpenCL-программы
Прикрепленные файлы |
MQL5.zip (3455.17 KB)
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Выборочные методы марковских цепей Монте-Карло. Алгоритм HMC Выборочные методы марковских цепей Монте-Карло. Алгоритм HMC
В статье исследуется гамильтонов алгоритм Монте-Карло (HMC) — золотой стандарт сэмплирования из сложных многомерных распределений. Представлена полноценная реализация HMC на языке MQL5, которая включает адаптивную настройку матрицы масс, поиск моды апостериорного распределения (MAP) с помощью метода оптимизации L-BFGS и комплексной диагностикой.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Торговый инструментарий MQL5 (Часть 8): Внедрение и использование EX5-библиотеки для управления историей в коде Торговый инструментарий MQL5 (Часть 8): Внедрение и использование EX5-библиотеки для управления историей в коде
В заключительной статье этой серии вы узнаете, как легко импортировать и применять EX5-библиотеку для управления историей (History Manager) в исходном коде MQL5 для обработки истории сделок в вашем аккаунте MetaTrader 5. С помощью простых вызовов функций в MQL5, занимающих всего одну строку кода, вы сможете эффективно управлять своими торговыми данными и анализировать их. Кроме того, вы научитесь создавать различные скрипты для анализа истории сделок и разрабатывать советник на основе ценовых показателей в качестве практических примеров использования. Используемый в качестве примера советник применяет данные о ценах и библиотеку History Manager EX5 для принятия обоснованных торговых решений, корректировки объемов сделок и реализации стратегий восстановления на основе ранее закрытых сделок.