preview
Нейросети в трейдинге: Долговременная память торговых сценариев (Объект верхнего уровня)

Нейросети в трейдинге: Долговременная память торговых сценариев (Объект верхнего уровня)

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

Введение

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

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

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

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

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

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


От найденного сценария к торговому решению

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

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

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

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

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

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

Базовую логику можно представить следующим образом.

Алгоритм принятия решения

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

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

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


Представление исторического контекста для Актёра

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

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

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

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

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

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

Память возвращает не один пример, а набор Top-K. Это важно для рынка, где одинаковая предыстория допускает несколько разумных действий. Актёр видит распределение опыта: какие варианты встречались, насколько они близки к текущему контексту и каким был их средний итог. Тем самым память предоставляет аргументы для сравнения кандидатов, а не готовую команду купить или продать.

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

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

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

Фиксированная форма выхода при этом сохраняется. Пустые позиции заполняются нулями, а число и ширина токенов не меняются от запроса к запросу. Для GPU-контура это важно: архитектура Актёра не перестраивается в зависимости от наполнения памяти. Меняется только содержимое контекста и степень его допуска к решению.

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


Объект верхнего уровня: от кандидатов действий к итоговому решению

Объект CNeuronVLADriverRAGMPI расширяет ранее созданный класс CNeuronMomADMPI. Базовая MPI-логика объединяет кандидаты действий, недавнюю историю политики и текущий рынок. Новый объект добавляет отдельную ветвь, которая сопоставляет полученное представление выбора с действиями из двухуровневой RAG-памяти.

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

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

Связь между объектами реализована через готовые OpenCL-буферы. Это позволяет не выгружать результаты поиска на CPU и не собирать их повторно перед каждым решением. Одновременно сохраняется чёткая граница владения: память создаёт и обновляет контекст, а верхнеуровневая политика использует его только для чтения.

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

class CNeuronVLADriverRAGMPI : public CNeuronMomADMPI
  {
protected:
   CBufferFloat      *pRAGTokens;
   CBufferFloat      *pRAGAbsoluteRelevance;
   uint               iRAGTokenCount;
   uint               iRAGTokenWidth;
   //--- Public second input is a frozen ScenarioEmbedding.  These two
   //--- neurons are Actor-owned and expand it into the MPI market K/V tokens.
   uint               iMarketEmbeddingSize;
   CNeuronBaseOCL     cMarketEmbedding;
   CNeuronBaseOCL     cMarketProjection;
   CNeuronBaseOCL     cMarketState;
   //--- One-input convolution supplies a single non-normalized trainable bias.
   //--- Its bias is the Actor-owned null-scenario threshold; the fixed input is zero.
   CNeuronConvOCL     cNullScenarioThreshold;
   CNeuronBaseOCL     cNullThresholdInput;
   CNeuronCrossAttention cPolicyRAG;
   CBufferFloat       cGatedRAGTokens;
   CBufferFloat       cRAGBranchActivity;
   CBufferFloat       cRAGBranchActivityGradient;
   CBufferFloat       cRAGBranchDelta;
   CBufferFloat       cRAGBranchOutput;
   CBufferFloat       cRAGResidualGradient;
   CBufferFloat       cRAGGradient;
//---
   virtual bool      RAGTokenGate(void);
   virtual bool      RAGTokenGateGradient(void);
   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:
                     CNeuronVLADriverRAGMPI(void) : pRAGTokens(NULL),
                                                    pRAGAbsoluteRelevance(NULL),
                                                    iRAGTokenCount(0),
                                                    iRAGTokenWidth(0),
                                                    iMarketEmbeddingSize(0) {};
                    ~CNeuronVLADriverRAGMPI(void) {};
//---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint latent_dim, uint candidates, uint market_dim,
                          uint market_units, uint stack_size, uint topK,
                          uint heads, uint key_dimension,
                          uint rag_token_count, uint rag_token_width,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
//---
   virtual int       Type(void) override const { return defNeuronVLADriverRAGMPI; }
   bool              SetRAGTokens(CBufferFloat *tokens,CBufferFloat *absolute_relevance,
                                   const uint token_width);
   CBufferFloat      *GetRAGTokens(void) const { return pRAGTokens; }
   uint              RAGTokenCount(void) const { return iRAGTokenCount; }
   uint              RAGTokenWidth(void) const { return iRAGTokenWidth; }
//---
   virtual bool      Save(const int file_handle) override;
   virtual bool      Load(const int 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;
  };

Указатели pRAGTokens и pRAGAbsoluteRelevance связывают объект с выходами памяти. Буферы остаются во владении CNeuronRAGMemory; Актёр только читает их в прямом проходе. Поля cMarketEmbedding и cMarketProjection преобразуют текущий ScenarioEmbedding в рыночные токены базовой политики.

За использование исторического опыта отвечают cNullScenarioThreshold и cPolicyRAG. Первый компонент задаёт обучаемую границу применимости памяти. Второй сопоставляет сформированные предпочтения Актёра с историческими действиями и их результатами. Буферы cRAGBranchDelta и cRAGBranchOutput хранят не новое действие, а остаточную поправку к текущему представлению выбора.

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

Функционально объект состоит из трёх контуров. Рыночный контур преобразует ScenarioEmbedding в форму, понятную MPI-политике. Базовый контур формирует предпочтения из кандидатов и недавней истории. Исторический контур проверяет качество поиска, сопоставляет предпочтения с действиями памяти и строит ограниченную поправку. Такое разделение облегчает интерпретацию: можно отдельно анализировать базовое состояние, активность памяти и результат её влияния.

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

bool CNeuronVLADriverRAGMPI::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                  uint latent_dim, uint candidates, uint market_dim,
                                  uint market_units, uint stack_size, uint topK,
                                  uint heads, uint key_dimension,
                                  uint rag_token_count, uint rag_token_width,
                                  ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   pRAGTokens = NULL;
   pRAGAbsoluteRelevance = NULL;
   iRAGTokenCount = rag_token_count;
   iRAGTokenWidth = rag_token_width;
   iMarketEmbeddingSize = market_dim;
   if(iRAGTokenCount == 0 || iRAGTokenWidth < 2 || iMarketEmbeddingSize == 0)
      ReturnFalse;

Затем создаётся унаследованная MPI-политика. Внешний рыночный вход нового объекта представлен одним компактным эмбеддингом, тогда как родительский механизм работает с последовательностью K/V-токенов. Поэтому в базовый класс передаётся ширина одного внутреннего рыночного токена и требуемое число таких представлений.

//--- The external market input is a single frozen ScenarioEmbedding.  The
//--- inherited MPI still consumes market_units K/V tokens, each latent-wide.
   if(!CNeuronMomADMPI::Init(numOutputs, myIndex, open_cl, latent_dim, candidates,
                             latent_dim, market_units, stack_size, topK, heads,
                             key_dimension, optimization_type, batch))
      ReturnFalse;

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

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

   if(!cMarketEmbedding.Init(market_units * latent_dim, 4, OpenCL, iMarketEmbeddingSize,
                             optimization, iBatch))
      ReturnFalse;
   if(!cMarketProjection.Init(0, 5, OpenCL, market_units * latent_dim,
                              optimization, iBatch))
      ReturnFalse;

Слой cMarketState понадобится для хранения представления выбора после RAG-корректировки. Его размер совпадает с внутренним пространством политики.

   if(!cMarketState.Init(0, 6, OpenCL, latent_dim, optimization, iBatch))
      ReturnFalse;

Далее создаётся механизм Null Scenario. Любой поиск Top-K возвращает несколько ближайших записей, даже если весь накопленный опыт далёк от текущего рынка. Обучаемый порог позволяет отличить содержательную аналогию от формального результата ранжирования.

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

   if(!cNullThresholdInput.Init(0, 7, OpenCL, 1, optimization, iBatch))
      ReturnFalse;
   if(!cNullScenarioThreshold.Init(0, 8, OpenCL, 1, 1, 1, 1, optimization, iBatch))
      ReturnFalse;

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

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

   if(!cPolicyRAG.Init(0, 9, OpenCL, latent_dim, key_dimension, heads, 1,
                       iRAGTokenCount, iRAGTokenWidth, optimization, iBatch))
      ReturnFalse;

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

   if(!cGatedRAGTokens.BufferInit(iRAGTokenCount * iRAGTokenWidth, 0))
      ReturnFalse;
   if(!cGatedRAGTokens.BufferCreate(OpenCL))
      ReturnFalse;
   if(!cRAGBranchActivity.BufferInit(1, 0))
      ReturnFalse;
   if(!cRAGBranchActivity.BufferCreate(OpenCL))
      ReturnFalse;
   if(!cRAGBranchActivityGradient.BufferInit(1, 0))
      ReturnFalse;
   if(!cRAGBranchActivityGradient.BufferCreate(OpenCL))
      ReturnFalse;
   if(!cRAGBranchDelta.BufferInit(iWindow, 0))
      ReturnFalse;
   if(!cRAGBranchDelta.BufferCreate(OpenCL))
      ReturnFalse;
   if(!cRAGBranchOutput.BufferInit(iWindow, 0))
      ReturnFalse;
   if(!cRAGBranchOutput.BufferCreate(OpenCL))
      ReturnFalse;
   if(!cRAGResidualGradient.BufferInit(iWindow, 0))
      ReturnFalse;
   if(!cRAGResidualGradient.BufferCreate(OpenCL))
      ReturnFalse;
   if(!cRAGGradient.BufferInit(iRAGTokenCount * iRAGTokenWidth, 0))
      ReturnFalse;
   if(!cRAGGradient.BufferCreate(OpenCL))
      ReturnFalse;
   if(!cNullThresholdInput.getOutput().Fill(0.0f))
      ReturnFalse;
   if(!cNullScenarioThreshold.GetWeightsConv())
      ReturnFalse;
   if(cNullScenarioThreshold.GetWeightsConv().Total() < 2)
      ReturnFalse;
   if(!cNullScenarioThreshold.GetWeightsConv().Fill(0.0f))
      ReturnFalse;
//---
   return true;
  }

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

Метод feedForward получает кандидатов через основной вход, а ScenarioEmbedding — через второй. Сначала проверяется размер вектора. Затем контролируется его принадлежность тому же OpenCL-контексту.

bool CNeuronVLADriverRAGMPI::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
   if(!OpenCL || !NeuronOCL || !SecondInput ||
      SecondInput.Total() != int(iMarketEmbeddingSize))
      ReturnFalse;
   if(SecondInput.GetOpenCL() != OpenCL || SecondInput.GetIndex() < 0)
      ReturnFalse;

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

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

//--- Copy on the device rather than bind: the encoder owns SecondInput and stays frozen.
   CBufferFloat *market_embedding = cMarketEmbedding.getOutput();
   if(!market_embedding || market_embedding.Total() != SecondInput.Total() ||
      market_embedding.GetOpenCL() != OpenCL || market_embedding.GetIndex() < 0)
      ReturnFalse;
   if(market_embedding != SecondInput)
     {
       if(cMarketEmbedding.Neurons() > SecondInput.Total())
          ReturnFalse;
       if(!Concat(SecondInput, SecondInput, market_embedding, 1, 0,
                  cMarketEmbedding.Neurons()))
         ReturnFalse;
     }
   if(!cMarketProjection.FeedForward(cMarketEmbedding.AsObject()))
      ReturnFalse;
   CBufferFloat *market = cMarketProjection.getOutput();
   if(!market || market.Total() != int(iWindow_K * iUnits_K) ||
      market.GetOpenCL() != OpenCL || market.GetIndex() < 0)
      ReturnFalse;

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

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

//--- push the previous MPI scenario before overwriting Output
   if(!cStack.FeedForward(GetPointer(this)))
      ReturnFalse;

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

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

//--- TTM builds the weighted scenario from the strategy pool and recent scenarios
   if(!cTTM.FeedForward(NeuronOCL, cStack.getOutput()))
      ReturnFalse;

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

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

//--- D_cur = actions stack attends to market
   if(!cPolicyMarket.FeedForward(cStack.AsObject(), market))
      ReturnFalse;

Оба потока объединяются в cTTMHistory. Полученное представление уже учитывает кандидаты, недавние решения и текущий рынок. Только теперь у Актёра появляется содержательный запрос к историческим действиям. Память сопоставляется не с абстрактным рыночным вектором, а с конкретным состоянием формируемой политики.

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

//--- Q_hist = TTM scenario attends to the current action-aware history
   if(!cTTMHistory.FeedForward(cTTM.AsObject(), cPolicyMarket.getOutput()))
      ReturnFalse;

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

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

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

Допущенные токены поступают в cPolicyRAG. Запросом служит состояние cTTMHistory, то есть текущие предпочтения Актёра. Контекстом становятся действия второго уровня памяти. Модель оценивает, какие текущие варианты согласуются с положительным опытом, какие повторяют неудачные решения и где статистика поддерживает более осторожное действие.

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

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

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

   CNeuronBaseOCL *final_query = cTTMHistory.AsObject();
   if(!!pRAGTokens)
     {
      if(!RAGTokenGate())
         ReturnFalse;
      if(!cPolicyRAG.FeedForward(cTTMHistory.AsObject(), GetPointer(cGatedRAGTokens)))
         ReturnFalse;
      if(!Different(cPolicyRAG.getOutput(), cTTMHistory.getOutput(),
                    GetPointer(cRAGBranchDelta), iWindow))
         ReturnFalse;
      if(!ScalarToVector(GetPointer(cRAGBranchActivity), GetPointer(cRAGBranchDelta),
                         GetPointer(cRAGBranchOutput), iWindow))
         ReturnFalse;
      if(!SumAndNormalize(cTTMHistory.getOutput(), GetPointer(cRAGBranchOutput),
                          cMarketState.getOutput(), iWindow, false, 0, 0, 0, 1.0f))
         ReturnFalse;
      final_query = cMarketState.AsObject();
     }

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

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

После корректировки модель ещё раз сопоставляет полученное представление с текущим рынком. Если память не использовалась, на этот этап поступает базовое состояние cTTMHistory. Если RAG-ветвь была активна, используется cMarketState с внесённой поправкой.

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

//--- parent cross-attention is the final MPI market refine block
   if(!CNeuronCrossAttention::feedForward(final_query, market))
      ReturnFalse;
//---
   return true;
  }

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

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

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


Заключение

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

Объект CNeuronVLADriverRAGMPI связывает этот опыт с базовой MPI-политикой. Актёр сначала формирует собственные предпочтения из кандидатов, истории решений и текущего рынка. Затем RAG-ветвь сопоставляет их с историческими действиями. Положительный опыт может усилить кандидата, отрицательный — снизить его привлекательность, а механизм Null Scenario подавляет память при отсутствии достаточно близкого контекста.

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

Таким образом, первая статья определила единицу торговой памяти, вторая организовала её хранение и поиск, а текущая работа встроила найденный опыт действий в механизм принятия решения. На следующем этапе объединим созданные компоненты в полные модели Актёра и Критиков. Затем рассмотрим обучение и проверим адаптированную версию VLADriver-RAG в тестере стратегий MetaTrader 5.


Ссылки


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

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

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

Прикрепленные файлы |
MQL5.zip (3956.03 KB)
Моделирование рынка: Position View (XIV) Моделирование рынка: Position View (XIV)
Теперь мы реализуем это решение, поскольку MQL5 следует тому же принципу, что и событийно-ориентированное программирование. Разработчики часто используют эту модель при создании DLL. Я знаю, что поначалу событийно-ориентированная модель работы покажется запутанной и лишённой логики. Но в этой статье я изложу принципы событийно-ориентированного программирования более понятно, чтобы вы, если только начинаете, правильно поняли, как оно работает. Понимание того, что я начну объяснять в этой статье, поможет вам в вашей жизни как программиста.
Автоматизация торговых стратегий в MQL5 (Часть 50): Стратегия Turtle Soup на снятии ликвидности Автоматизация торговых стратегий в MQL5 (Часть 50): Стратегия Turtle Soup на снятии ликвидности
В статье создаётся автоматическая программа MQL5 для торговли по стратегии Turtle Soup против ложных пробоев N-барных максимумов и минимумов. Реализуются обнаружение снятия ликвидности, подтверждающие закрытия обратно внутрь уровня, фильтры глубины и возраста экстремума, необязательная проверка тела разворотной свечи, динамические и статические стоп-лоссы, два режима тейк-профита, трейлинг по пунктам и наглядная разметка графика.
Создание и использование Скиллов для создания артефактов MQL Создание и использование Скиллов для создания артефактов MQL
В статье показано, как использовать концепцию Скиллов в MetaTrader 5 для генерации MQL5‑скриптов с помощью встроенного AI Assistant и Model Context Protocol. Разбираем структуру файла Скилла SKILL.md, условие активации по тегу Artifact, проверку ТЗ, входных параметров и выбор шаблона. Результат — воспроизводимый процесс и единый стиль артефактов при меньших трудозатратах.
Создание торговых систем с искусственным интеллектом на MQL5 (Часть 5): Сворачиваемая боковая панель и всплывающие окна истории чатов Создание торговых систем с искусственным интеллектом на MQL5 (Часть 5): Сворачиваемая боковая панель и всплывающие окна истории чатов
В части 5 серии о торговых системах с ИИ в MQL5 мы дополняем советник с интеграцией ChatGPT сворачиваемой боковой панелью. Малое и большое всплывающие окна истории упрощают и делают более плавным выбор чатов, при этом сохраняются обработка многострочного ввода, постоянное зашифрованное хранение чатов между сеансами и генерация ИИ торговых сигналов по данным графика.