Português
preview
Нейросети в трейдинге: Мультимодальный агент, дополненный инструментами (FinAgent)

Нейросети в трейдинге: Мультимодальный агент, дополненный инструментами (FinAgent)

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

Введение

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

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

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

Несмотря на успехи в анализе финансовых данных, LLM-агенты сталкиваются с рядом ограничений:

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

Указанные ограничения попытались решить авторы работы "A Multimodal Foundation Agent for Financial Trading: Tool-Augmented, Diversified, and Generalist", в которой был представлен фреймворк FinAgent — мультимодальный базовый агент, объединяющий текстовую и визуальную информацию для анализа рыночной динамики и исторических данных. Основные компоненты FinAgent включают обработку мультимодальных данных для выявления ключевых рыночных тенденций, двухуровневый модуль рефлексии, анализирующий как краткосрочные, так и долгосрочные решения, систему памяти для минимизации шума в результатах анализа и модуль принятия решений, который интегрирует экспертные знания и передовые торговые стратегии.


Алгоритм FinAgent

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

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

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

Процесс получения данных в FinAgent базируется на использовании большой языковой модели (LLM), которая преобразует рыночную информацию в текстовые запросы. Эти запросы затем используются для поиска схожих данных в исторической базе модуля памяти. Применение методов векторного сходства повышает точность поиска и помогает сосредоточиться на наиболее значимой информации. Дополнительно система использует специализированные текстовые поля, что улучшает обработку данных и позволяет избежать потери важных деталей. Итоговые данные структурируются и обобщаются, что упрощает процесс анализа и минимизирует влияние второстепенной информации.

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

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

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

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

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

Авторская визуализация фреймворка FinAgent представлена ниже.

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


Реализация средствами MQL5

После рассмотрения теоретических аспектов фреймворка FinAgent, мы переходим к практической части нашей статьи, в которой реализуем свое видение предложенных подходов средствами MQL5.

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

А начнем мы свою работу с создания модулей низко- и высокоуровневой рефлексии.

Модуль низкоуровневой рефлексии

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

Измененный модуль низкоуровневой рефлексии мы реализуем в рамках объекта CNeuronLowLevelReflection, структура которого представлена ниже.

class CNeuronLowLevelReflection :   public CNeuronMemory
  {
protected:
   CNeuronLSTMOCL    cChangeLSTM;
   CNeuronMambaOCL   cChangeMamba;
   CNeuronRelativeCrossAttention cCrossAttention[2];
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override;

public:
                     CNeuronLowLevelReflection(void) {};
                    ~CNeuronLowLevelReflection(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint window, uint window_key,
                          uint units_count, uint heads,
                          ENUM_OPTIMIZATION optimization_type, uint batch) override;
   //---
   virtual int       Type(void) override   const   {  return defNeuronLowLevelReflection; }
   //---
   virtual bool      Save(int const file_handle) override;
   virtual bool      Load(int const file_handle) override;
   //---
   virtual bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   virtual void      SetOpenCL(COpenCLMy *obj) override;
   //---
   virtual bool      Clear(void) override;
  };

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

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

Все внутренние объекты объявлены статично, что позволяет нам оставить пустыми конструктор и деструктор класса. А инициализация всех объявленных и унаследованных объектов осуществляется в методе Init.

bool CNeuronLowLevelReflection::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                            uint window, uint window_key, uint units_count, uint heads,
                                       ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, window, window_key, units_count, heads,
                                                                      optimization_type, batch))
      return false;

Следует обратить внимание, что структура параметров данного метода полностью унаследована от родительского класса. И в теле метода мы сразу вызываем одноименный метод родительского класса, передав ему все полученные параметры.

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

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

   int index = 0;
   if(!cChangeLSTM.Init(0, index, OpenCL, window, units_count, optimization, iBatch))
      return false;
   index++;
   if(!cChangeMamba.Init(0, index, OpenCL, window, 2 * window, units_count, optimization, iBatch))
      return false;

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

   for(int i = 0; i < 2; i++)
     {
      index++;
      if(!cCrossAttention[i].Init(0, index, OpenCL, window, window_key, units_count, heads,
                                                window, units_count, optimization, iBatch))
         return false;
     }
//---
   return true;
  }

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

Следующим этапом нашей работы является построение алгоритмов прямого прохода модуля низкоуровневой рефлексии в рамках метода feedForward.

bool CNeuronLowLevelReflection::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cChangeLSTM.FeedForward(NeuronOCL))
      return false;
   if(!cChangeMamba.FeedForward(NeuronOCL))
      return false;

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

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

   if(!cCrossAttention[0].FeedForward(NeuronOCL, cChangeLSTM.getOutput()))
      return false;
   if(!cCrossAttention[1].FeedForward(cCrossAttention[0].AsObject(), cChangeMamba.getOutput()))
      return false;

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

   return CNeuronMemory::feedForward(cCrossAttention[1].AsObject());
  }

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

bool CNeuronLowLevelReflection::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      return false;

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

Затем, средствами родительского класса, мы спускаем градиент ошибки через модуль памяти до уровня блоков кросс-внимания.

   if(!CNeuronMemory::calcInputGradients(cCrossAttention[1].AsObject()))
      return false;
   if(!cCrossAttention[0].calcHiddenGradients(cCrossAttention[1].AsObject(),
                       cChangeMamba.getOutput(), cChangeMamba.getGradient(), 
                                (ENUM_ACTIVATION)cChangeMamba.Activation()))
      return false;

И распределяем полученную погрешность между рекуррентными блоками.

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

   if(!NeuronOCL.calcHiddenGradients(cCrossAttention[0].AsObject(), cChangeLSTM.getOutput(),
                      cChangeLSTM.getGradient(), (ENUM_ACTIVATION)cChangeLSTM.Activation()))
      return false;

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

   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(cChangeMamba.getPrevOutput(), false) ||
      !NeuronOCL.calcHiddenGradients(cChangeMamba.AsObject()) ||
      !SumAndNormilize(NeuronOCL.getGradient(), temp, temp, iWindow, false, 0, 0, 0, 1))
      return false;
   if(!NeuronOCL.calcHiddenGradients(cChangeLSTM.AsObject()) ||
      !SumAndNormilize(NeuronOCL.getGradient(), temp, temp, iWindow, false, 0, 0, 0, 1))
      return false;

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

   if(!NeuronOCL.SetGradient(temp, false))
      return false;
//---
   return true;
  }

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

На этом мы завершаем рассмотрение алгоритмов построения методов модуля низкоуровневой рефлексии. С полным кодом данного класса и всех его методов вы можете самостоятельно ознакомиться во вложении.

Модуль высокоуровневой рефлексии

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

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

Кроме того, модуль высокоуровневой рефлексии должен предложить конкретные рекомендации по оптимизации торговой стратегии. Эти рекомендации направленны на повышение доходности торговых операций и/или снижение рисков.

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

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

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

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

Таким образом, мы приходим к четырем потокам исходной информации:

  • Действия агента;
  • Состояние окружающей среды;
  • Финансовый результат (состояние счета);
  • Память.

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

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

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

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

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

Описанные выше решения мы реализуем в рамках нового объекта CNeuronHighLevelReflection, структура которого представлена ниже.

class CNeuronHighLevelReflection :  public CNeuronMemory
  {
protected:
   CNeuronBaseOCL    cAccount;
   CNeuronLSTMOCL    cHistoryAccount;
   CNeuronRelativeCrossAttention cActionReason;
   CNeuronLSTMOCL    cHistoryActions;
   CNeuronRelativeCrossAttention cActionResult;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override { return false; }
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override { return false; }
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput,
                                        CBufferFloat *SecondGradient, 
                                        ENUM_ACTIVATION SecondActivation = None) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override {return false; }
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL, 
                                        CBufferFloat *SecondInput) override;

public:
                     CNeuronHighLevelReflection(void) {};
                    ~CNeuronHighLevelReflection(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint window, uint window_key, uint units_count, uint heads,
                          uint desc_account, uint actions_state,
                          ENUM_OPTIMIZATION optimization_type, uint batch) override;
   //---
   virtual int       Type(void) override   const   {  return defNeuronHighLevelReflection; }
   //---
   virtual bool      Save(int const file_handle) override;
   virtual bool      Load(int const file_handle) override;
   //---
   virtual bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   virtual void      SetOpenCL(COpenCLMy *obj) override;
   //---
   virtual bool      Clear(void) override;
  }; 

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

Все внутренние объекты объявлены статично, что позволяет нам оставить пустыми конструктор и деструктор класса. Инициализация всех объявленных и унаследованных объектов осуществляется в методе Init.

bool CNeuronHighLevelReflection::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                      uint window, uint window_key, uint units_count,
                                      uint heads, uint desc_account, uint actions_state,
                                      ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, 3, window_key, actions_state / 3,
                           heads, optimization_type, batch))
      return false;

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

На выходе данного объекта, как уже было сказано выше, мы ожидаем получить некоторое латентное представление тензора действий агента. Каждая торговая операция нашего агента характеризуется тремя параметрами: объем операции и торговые уровни (стоп-лосс и тейк-профит). Сделки на покупку и продажу представлены разными строками тензора, что позволяет отказаться от дополнительного параметра направления торговой операции.

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

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

   int index = 0;
   if(!cAccount.Init(0, index, OpenCL, desc_account, optimization, iBatch))
      return false;

И тут же инициализируем рекуррентный блок отслеживания изменений состояния счета.

   index++;
   if(!cHistoryAccount.Init(0, index, OpenCL, desc_account, 1, optimization, iBatch))
      return false;

Для анализа обоснованности принятия последнего торгового решения мы воспользуемся блоком кросс-внимания, в котором проанализируем зависимости действий агента и тензора описания состояния окружающей среды.

   index++;
   if(!cActionReason.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads,
                          window, units_count, optimization, iBatch))
      return false;

Динамику действий мы собираем в соответствующем рекуррентном блоке.

   index++;
   if(!cHistoryActions.Init(0, index, OpenCL, iWindow, iUnits, optimization, iBatch))
      return false;

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

   index++;
   if(!cActionResult.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads,
                          desc_account, 1, optimization, iBatch))
      return false;;
//---
   return true;
  }

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

Следующим этапом мы переходим к построению алгоритма прямого прохода нашего модуля высокоуровневой рефлексии в рамках метода feedForward.

bool CNeuronHighLevelReflection::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
   if(!NeuronOCL || !SecondInput)
      return false;

В параметрах метода мы получаем указатели на объекты исходных данных двух информационных потоков. И сразу проверяем актуальность полученных указателей.

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

   if(cAccount.getOutput() != SecondInput)
     {
      if(cAccount.Neurons() != SecondInput.Total())
         if(!cAccount.Init(0, 0, OpenCL, SecondInput.Total(), optimization, iBatch))
            return false;
      if(!cAccount.SetOutput(SecondInput, true))
         return false;
     }

Далее мы оценим изменение состояния счета средствами рекуррентного блока.

   if(!cHistoryAccount.FeedForward(cAccount.AsObject()))
      return false;

И проверим обоснованность последнего торгового решения, сопоставив его текущему состоянию окружающей среды средствами блока кросс-внимания.

   if(!cActionReason.FeedForward(this.AsObject(), NeuronOCL.getOutput()))
      return false;

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

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

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

Результаты анализа передаются в рекуррентный блок отслеживания политики поведения агента.

   if(!cHistoryActions.FeedForward(cActionReason.AsObject()))
      return false;

После чего, анализируем политику поведения в контексте финансового результата с помощью блока кросс-внимания.

   if(!cActionResult.FeedForward(cHistoryActions.AsObject(), cHistoryAccount.getOutput()))
      return false;

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

   if(!SwapBuffers(Output, PrevOutput))
      return false;
//---
   return CNeuronMemory::feedForward(cActionResult.AsObject());
  }

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

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

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


Заключение

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

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


Ссылки


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

# Имя Тип Описание
1 Research.mq5 Советник Советник сбора примеров
2 ResearchRealORL.mq5
Советник
Советник сбора примеров методом Real-ORL
3 Study.mq5 Советник Советник обучения моделей
4 Test.mq5 Советник Советник для тестирования модели
5 Trajectory.mqh Библиотека класса Структура описания состояния системы и архитектуры моделей
6 NeuroNet.mqh Библиотека класса Библиотека классов для создания нейронной сети
7 NeuroNet.cl Библиотека Библиотека кода OpenCL-программы
Прикрепленные файлы |
MQL5.zip (2327.7 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Dominic Michael Frehner
Dominic Michael Frehner | 9 янв. 2025 в 13:19

Было бы здорово, если бы вы также могли писать статьи на английском языке.

Полиномиальные модели в трейдинге Полиномиальные модели в трейдинге
Эта статья посвящена ортогональным многочленам. Их применение может стать основой для более точного и эффективного анализа рыночной информации, благодаря чему, трейдер сможет принимать более обоснованные решения.
Алгоритм Большого взрыва и Большого сжатия — BBBC (Big Bang - Big Crunch) Алгоритм Большого взрыва и Большого сжатия — BBBC (Big Bang - Big Crunch)
В статье представлен метод Big Bang - Big Crunch, который имеет две ключевые фазы: циклическое создание случайных точек и их сжатие к оптимальному решению. Этот подход сочетает исследование и уточнение, позволяя постепенно находить лучшие решения и открывая новые возможности в области оптимизации.
Машинное обучение и Data Science (Часть 25): Прогнозирование временных рядов на форексе с помощью рекуррентных нейросетей (RNN) Машинное обучение и Data Science (Часть 25): Прогнозирование временных рядов на форексе с помощью рекуррентных нейросетей (RNN)
Рекуррентные нейронные сети (RNN) ценятся за способность использовать прошлую информацию для прогнозирования будущих событий. Такие прогностические возможности с успехом применяются в различных областях. В этой статье мы применим модели RNN для прогнозирования трендов на рынке Форекс. Посмотрим, смогут ли они повысить точность прогнозирования в трейдинге.
Индикатор силы и направления тренда на 3D-барах Индикатор силы и направления тренда на 3D-барах
Рассмотрим новый подход к анализу рыночных трендов, основанный на трехмерной визуализации и тензорном анализе рыночной микроструктуры.