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

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

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

Введение

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

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

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

Такую идею развивает фреймворк D2Skill. В авторской работе политика дополняется динамическим банком навыков двух уровней. Task Skills дают высокоуровневую поддержку на уровне всей задачи. Step Skills работают с отдельными состояниями траектории и помогают исправлять локальные ошибки. Банк строится по опыту обучения. Его содержимое расширяется и очищается по мере накопления данных. Полезность навыков оценивается ретроспективно. Для этого авторы сравнивают прогоны политики без навыков и с применением навыков.

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

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

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

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

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

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

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

В этой статье мы построим объект верхнего уровня. Он объединит два банка внутри Актёра и включит их в существующий вычислительный процесс. Банк Task Skills будет работать с более общим представлением торговой задачи. Банк Step Skills получит локальное представление прогнозного сценария. Нам нужно сохранить исходную архитектуру, причинный порядок обучения и независимость Критика.

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


Основные задачи объекта верхнего уровня

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

Это первая задача нового уровня. D2Skill должен усиливать модель, а не перестраивать её вокруг себя. У нас уже есть рабочий вычислительный процесс. Для него определены входы и выходы, размерности тензоров, прямой и обратный проходы, вычисления на GPU через OpenCL и обновление весов. Если ради памяти изменить несколько частей сети, результат будет трудно интерпретировать. Мы не поймём, помогли навыки или поведение изменилось из-за новой архитектуры.

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

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

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

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

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

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

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

Task Skills и Step Skills должны работать независимо. Это не требует двух разных алгоритмов банка. Поиск, Candidate, жизненный цикл, Influence и Utility уже реализованы в CD2SkillBank. Различие задаётся входными данными и точкой применения коррекции. Каждый банк должен видеть только своё латентное пространство.

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

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

Это разделение важно ещё по одной причине. Task Skill и Step Skill могут влиять на итоговое действие в одну сторону. Могут и частично компенсировать друг друга. Заранее назначить одной коррекции больший вес нельзя. Лучше вернуть каждую в её собственный участок модели. Дальнейший вычислительный процесс сам объединит их влияние.

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

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

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

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

Обратный проход тоже нужно разделить по двум уровням. Банк Task Skills должен получить псевдоостаток своего представления. Банк Step Skills должен получить градиент сценарной ветви. Общий градиент здесь не подходит. Два навыка живут в разных пространствах и корректируют разные части Актёра.

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

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

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

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

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

Требования к объекту верхнего уровня теперь определены. Он должен встроиться в действующий ORION и сохранить базовый вычислительный путь. Task Skills и Step Skills нужно развести по разным представлениям. Навыки применяются только при подходящем контексте, а обучение сохраняет причинный порядок. Критик остаётся без D2Skill. Банки работают на GPU и сохраняются вместе с Актёром. Эти требования и перенесём в реализацию.


Построение объекта верхнего уровня

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

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

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

Объект CD2Skill наследуем от CNeuronScenarioCrossAttention. Родительский класс сохраняет основную логику сценарного внимания. Новый объект добавляет два банка навыков и управляет их работой.

class CD2Skill : public CNeuronScenarioCrossAttention
  {
private:
   CD2SkillBank                 m_task_bank;
   CD2SkillBank                 m_step_bank;
   bool                         m_ready;
   bool                         m_online_direction_update;
   ENUM_D2SKILL_MODE            m_mode;

protected:
   virtual bool      feedForwardSecond(CNeuronBaseOCL *NeuronOCL,
                                       CNeuronBaseOCL *Forecast) override;
   virtual bool      calcInputGradientsSecond(CNeuronBaseOCL *NeuronOCL,
         CNeuronBaseOCL *Forecast) override;
   virtual bool      updateInputWeightsSecond(CNeuronBaseOCL *NeuronOCL,
         CNeuronBaseOCL *Forecast) override;
   bool              InitBanks(ENUM_OPTIMIZATION optimization_type, uint batch);
   bool              TaskSkillEnabled(void) const;
   bool              StepSkillEnabled(void) const;
   bool              SkillUpdatesEnabled(void) const;
   bool              OnlineDirectionUpdatesEnabled(void) const;
   bool              UpdateOnlineDirection(CD2SkillBank *bank, CBufferFloat *gradient);

public:
                     CD2Skill(void);
   virtual          ~CD2Skill(void);
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *opencl,
                          uint scenarios = 21, uint horizon = 12, uint latent = 16,
                          bool critic = false, ENUM_OPTIMIZATION optimization_type = ADAM,
                          uint batch = 1, uint variables = 1, uint stack_size = 1,
                          uint top_k = 1) override;
   virtual int       Type(void) const override;
   virtual bool      Save(const int file_handle) override;
   virtual bool      Load(const int file_handle) override;
   bool              Load(const int file_handle, COpenCLMy *opencl);
   virtual void      SetOpenCL(COpenCLMy *opencl) override;
   virtual void      TrainMode(bool flag) override;
   virtual bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   virtual bool      UpdateInputStateSecond(CObject *SourceObject,
                                            CNeuronBaseOCL *Forecast) override;
   virtual bool      Clear(void) override;
   bool              SetMode(const ENUM_D2SKILL_MODE mode);
   bool              SetOnlineDirectionUpdate(const bool enabled);
   ENUM_D2SKILL_MODE Mode(void) const;
   bool              ResetEpisodeInfluence(void);
   bool              Ready(void) const;
   CD2SkillBank      *TaskBank(void);
   CD2SkillBank      *StepBank(void);
   CD2SkillBank      *Bank(void);
   const CD2SkillBank *Bank(void) const;
   const CD2SkillBank *TaskBank(void) const;
   const CD2SkillBank *StepBank(void) const;
   bool              Enable(const bool task, const bool step);
   bool              TaskFeedForward(CNeuronBaseOCL *source);
   bool              StepFeedForward(CNeuronBaseOCL *source);
   bool              TaskUpdate(CBufferFloat *state, CBufferFloat *gradient);
   bool              StepUpdate(CBufferFloat *state, CBufferFloat *gradient);
   bool              TaskUtility(const float delta_j);
   bool              StepUtility(const float delta_j);
   bool              BackwardSecond(CNeuronBaseOCL *source, CNeuronBaseOCL *forecast);
  };

В объявлении класса видно, что архитектура остаётся компактной. Постоянная память представлена полями m_task_bank и m_step_bank. Оба имеют тип CD2SkillBank. Поле m_mode задаёт текущий режим D2Skill. Поле m_ready показывает, завершена ли инициализация. Отдельный флаг m_online_direction_update разрешает ограниченную подстройку направления навыка при онлайн-калибровке.

Остальную архитектуру мы получаем от CNeuronScenarioCrossAttention. Внутри нового класса нет второго механизма сценарного внимания или копии прогнозного потока. Мы используем уже существующие компоненты ORION и добавляем память только в нужных местах.

Эта роль отражена и в наборе методов. feedForwardSecond организует прямой проход с двумя банками. calcInputGradientsSecond возвращает градиенты и обучает навыки. updateInputWeightsSecond обслуживает обычные веса. Save и Load сохраняют полный объект. Публичные методы управления позволяют отдельно включать банки, выбирать режим и передавать Utility после завершения последовательности.

Инициализация начинается с базового слоя ORION. Это соответствует нашему требованию совместимости.

bool CD2Skill::Init(uint numOutputs, uint myIndex, COpenCLMy *opencl,
                    uint scenarios, uint horizon, uint latent, bool critic,
                    ENUM_OPTIMIZATION optimization_type, uint batch,
                    uint variables, uint stack_size, uint top_k)
  {
   if(critic)
      ReturnFalse;
   m_ready = false;
   m_mode = D2_DISABLED;
   if(!CNeuronScenarioCrossAttention::Init(numOutputs, myIndex, opencl,
                                           scenarios, horizon, latent, false,
                                           optimization_type, batch, variables,
                                           stack_size, top_k))
      ReturnFalse;
   if(!InitBanks(optimization_type, batch))
      ReturnFalse;
   m_task_bank.SetEnabled(false);
   m_step_bank.SetEnabled(false);
   m_ready = true;
   return(true);
  }

Метод Init сначала проверяет назначение объекта. Если запрошен режим Критика, инициализация завершается неудачей. Для Критика мы сохраняем обычный CNeuronScenarioCrossAttention. Память D2Skill используется только в Актёре.

Далее вызывается родительский Init со всеми параметрами сценарного слоя. Сначала создаётся полноценный объект ORION. Только после этого метод InitBanks добавляет память навыков. Если любой этап завершился ошибкой, CD2Skill не считается готовым.

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

Размерности двух банков определяются в методе InitBanks.

bool CD2Skill::InitBanks(ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   const uint task_dimension = iQueries * iScenarios * iDimension;
   const uint step_dimension = iScenarios * iVariables * iHorizon * iDimension;
   if(task_dimension == 0 || step_dimension == 0 || !OpenCL)
      ReturnFalse;
   if(!m_task_bank.Init(task_dimension, 100000, OpenCL, iScenarios,
                        optimization_type, batch))
      ReturnFalse;
   if(!m_step_bank.Init(step_dimension, 100001, OpenCL, iScenarios,
                        optimization_type, batch))
      ReturnFalse;
   m_task_bank.SetActivationFunction(None);
   m_step_bank.SetActivationFunction(None);
   return(true);
  }

Банк Task Skills получает размер представления запросов Актёра. Оно содержит отдельный запрос для каждого торгового контекста и каждого прогнозного сценария. В нашей реализации это состояние формируется компонентом cQueryProjection.

Банк Step Skills работает с другим пространством. Его размер включает сценарии, прогнозируемые переменные, горизонт и латентную размерность. Это соответствует выходу компонента cWz, который проецирует скрытое состояние прогнозных сценариев.

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

Для Актёра ORION уже формирует три группы запросов. Они описывают баланс, открытые позиции и календарный контекст. Каждый запрос разворачивается на все прогнозные сценарии. Выход cQueryProjection уже содержит состояние торговой задачи и связь с набором возможных продолжений рынка. Это делает его естественным контекстом для Task Skills.

Компонент cWz решает другую задачу. Он проецирует латентное состояние каждого сценария на всём горизонте прогноза. Здесь ещё нет итогового торгового решения, зато уже присутствует структура возможного движения рынка. Это делает выход cWz подходящим контекстом для Step Skills.

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

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

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

Первым формируется контекст Task Skills. Как и в исходном ORION, компонент cQueryProjection обрабатывает состояние Актёра. Затем полученное представление передаётся банку.

   if(!cQueryProjection.FeedForward(NeuronOCL))
      ReturnFalse;
   CNeuronBaseOCL *task_source = cQueryProjection.AsObject();
   if(TaskSkillEnabled())
     {
      if(!m_task_bank.FeedForward(task_source))
         ReturnFalse;
      task_source = m_task_bank.AsObject();
     }
   else
      if(m_mode == D2_COLLECT && m_task_bank.Enabled() &&
         !m_task_bank.Observe(task_source))
         ReturnFalse;

Здесь видно разделение режимов. Если Task Skills разрешено применять, вызывается метод FeedForward банка. Он выполняет поиск и возвращает скорректированное представление. Указатель task_source переключается на выход банка. Дальнейший ORION работает уже с этим состоянием.

Если подходящего навыка нет, коррекция равна нулю. Базовое представление не меняется. Это сохраняет исходное поведение модели.

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

По такому же принципу подключается банк Step Skills. Но его запрос формируется из прогнозного потока.

   if(!cWz.FeedForward(Forecast))
      ReturnFalse;
   CNeuronBaseOCL *step_source = cWz.AsObject();
   if(StepSkillEnabled())
     {
      if(!m_step_bank.FeedForward(step_source))
         ReturnFalse;
      step_source = m_step_bank.AsObject();
     }
   else
      if(m_mode == D2_COLLECT && m_step_bank.Enabled() &&
         !m_step_bank.Observe(step_source))
         ReturnFalse;

Компонент cWz проецирует латентные состояния прогноза. Это представление поступает в банк Step Skills. При активной памяти банк возвращает коррекцию через FeedForward. В режиме сбора метод Observe только регистрирует состояние.

Два банка не передают коррекции друг другу. Task Skill меняет представление торговой задачи. Step Skill меняет сценарное представление. Они встречаются позже внутри исходного вычислительного пути ORION.

Следующий фрагмент показывает эту точку.

   if(!cWu.FeedForwardBuffer(forecast.GetLogU()))
      ReturnFalse;
   if(!SumAndNormalize(step_source.getOutput(), cWu.getOutput(), cTokenSum.getOutput(),
                       1, false, 0, 0, 0, 1))
      ReturnFalse;
   if(!cTokenPE.FeedForward(cTokenSum.AsObject()))
      ReturnFalse;
   if(!cTokensTranspose.FeedForward(cTokenPE.AsObject()))
      ReturnFalse;
   if(!Concat(cTokensTranspose.getOutput(), cTokensTranspose.getOutput(), cKV.getOutput(),
              iScenarios * iDimension, iScenarios * iDimension, iVariables * iHorizon))
      ReturnFalse;
   if(!cAttention.FeedForward(task_source, cKV.getOutput()))
      ReturnFalse;

Скорректированный step_source объединяется с оценкой неопределённости прогноза. Затем формируются сценарные токены. Скорректированный task_source используется как запрос для компонента cAttention. Дальнейшая работа кросс-внимания остаётся прежней.

Для торговой модели это даёт понятное разделение ролей. Task Skills помогают уточнить, что означает текущая задача для Актёра. Step Skills уточняют интерпретацию прогнозного состояния. После этого ORION сам связывает оба представления и формирует дальнейший контекст решения.

D2Skill не выдаёт отдельный сигнал Buy или Sell. Он не обходит базовый Актёр. Память только меняет скрытые представления. Это сохраняет прежний порядок обработки сценариев, истории и выходных слоёв.

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

   if(!NeuronOCL.CalcHiddenGradients(cQueryProjection.AsObject()))
      ReturnFalse;
   if(SkillUpdatesEnabled() && m_task_bank.Enabled() &&
       !m_task_bank.UpdateFromGradient(cQueryProjection.getOutput(), cQueryProjection.getGradient()))
      ReturnFalse;
   if(SkillUpdatesEnabled() && m_step_bank.Enabled() &&
       !m_step_bank.UpdateFromGradient(cWz.getOutput(), cWz.getGradient()))
      ReturnFalse;
   if(!UpdateOnlineDirection(GetPointer(m_task_bank), m_task_bank.getGradient()) ||
      !UpdateOnlineDirection(GetPointer(m_step_bank), m_step_bank.getGradient()))
      ReturnFalse;

Для Task Skills используется выход cQueryProjection и его градиент. Для Step Skills используются выход и градиент cWz. Каждый банк обучается в собственных координатах, и два псевдоостатка не смешиваются.

Дальнейшая обработка выполняется уже знакомым методом UpdateFromGradient объекта CD2SkillBank. В предыдущей статье мы подробно рассмотрели этот алгоритм. Банк проверяет контекст и направление, обновляет существующий навык или продолжает формирование Candidate. Здесь повторять внутреннюю механику не требуется.

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

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

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

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

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

Обычное обучение сети при этом не меняется. D2Skill не подменяет оптимизатор. Унаследованные параметры сценарного внимания продолжают получать градиенты и обновляться штатно, когда это разрешено выбранным этапом обучения. Банки используют собственные правила накопления. Обновление весов и обновление памяти остаются разными операциями.

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

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

Поток данных


Заключение

В начале статьи мы поставили задачу перейти от отдельного банка навыков к полной двухуровневой адаптации Актёра. D2Skill должен был остаться дополнением к существующей модели. Нам требовалось сохранить вычислительный процесс ORION, разделить Task Skills и Step Skills и не переносить память политики в Критик.

Для решения задачи создан объект CD2Skill. Он наследует CNeuronScenarioCrossAttention и использует два экземпляра CD2SkillBank. Банк Task Skills работает с представлением торговой задачи. Банк Step Skills получает сценарное представление прогноза. Каждая коррекция возвращается в своё латентное пространство. Два уровня остаются независимыми и не смешиваются внутри одного вектора.

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

Поставленная задача решена на уровне объекта верхнего уровня. D2Skill встроен в Актёр без замены базовой архитектуры ORION. При отключённой памяти модель сохраняет исходный путь, а Критик продолжает работать без D2Skill. Следующий этап уже относится к полной торговой системе. Нам предстоит построить модель, организовать обучение и онлайн-калибровку, а затем проверить вклад Task Skills и Step Skills на исторических данных.


Ссылки


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

#ИмяТипОписание
1NeuroNet_D2Skill.mqhБиблиотека классовРеализация CD2SkillItem, CD2SkillBank и CD2Skill
2NeuroNet.clOpenCL-программаЯдра OpenCL для поиска, обновления, жизненного цикла и Utility
3NeuroNet_Declarations.mqhОбъявления классовИнтерфейсы объектов D2Skill
4NeuroNet_Core.mqhОбщий APIСоздание объектов D2Skill и регистрация ядер OpenCL
5NeuroNet_Definitions.mqhОпределенияСостояния, режимы и идентификаторы ядер OpenCL

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

Прикрепленные файлы |
MQL5.zip (4049.1 KB)
Интеграция MQL5 с пакетами обработки данных (Часть 7): Построение многоагентных сред для межсимвольного взаимодействия Интеграция MQL5 с пакетами обработки данных (Часть 7): Построение многоагентных сред для межсимвольного взаимодействия
В статье представлена полная интеграция Python и MQL5 для многоагентной торговли: получение данных из MT5, расчет индикаторов, решения отдельных агентов и взвешенный консенсус, формирующий единое торговое решение. Сигналы сохраняются в JSON-файле, отдаются через Flask и используются советником MQL5 для исполнения сделок с расчетом объема позиции и уровней SL/TP на основе ATR. Маршруты Flask обеспечивают безопасное управление жизненным циклом и мониторинг состояния.
Мастер-класс по созданию пользовательских индикаторов (Часть 1): Разработка индикатора Supertrend на MQL5 Мастер-класс по созданию пользовательских индикаторов (Часть 1): Разработка индикатора Supertrend на MQL5
Создайте индикатор Supertrend без перерисовки на языке MQL5 для MetaTrader 5 с нуля. Используются хэндл iATR и CopyBuffer для получения волатильности, буферы привязываются через SetIndexBuffer, а графические построения (DRAWCOLORCANDLES и две полосы, отображаемые линиями) настраиваются через PlotIndexSetInteger. Логика обновляется только на закрытых барах и использует EMPTY_VALUE для скрытия неактивных полос, при этом используются входные параметры atrPeriod и atrMultiplier. Получается аккуратный индикатор, накладываемый поверх графика, готовый для использования в советниках, с документированными буферами для стратегий и сигналов.
Изучение стандартной библиотеки MQL5 (Часть 13): Применение библиотеки математических решателей в торговле Изучение стандартной библиотеки MQL5 (Часть 13): Применение библиотеки математических решателей в торговле
Мы представляем полный рабочий процесс для адаптивной фильтрации в MQL5 с использованием решателя CNlEq, подобного алгоритму Левенберга — Марквардта. Советник подбирает параметры модели VAMAC — две экспоненциальные скользящие средние (EWMA) с масштабированием на основе ATR — путем передачи остатков и Якобиана через цикл механизма обратного обмена с решателем CNlEq, с возможностью использования численных или аналитических производных. Код, инструкции по настройке и тесты по паре GBPUSD на таймфрейме H1 показывают, как заменить статические пороги динамической переоценкой на закрытии каждого бара.
Индикатор Sigma Score для MetaTrader 5: Простой детектор статистических аномалий Индикатор Sigma Score для MetaTrader 5: Простой детектор статистических аномалий
Создайте практический индикатор Sigma Score для MetaTrader 5 с нуля и изучите, что он на самом деле измеряет – z-оценку логарифмических доходностей: на сколько стандартных отклонений последнее движение отличается от недавнего среднего. В статье рассматривается каждый блок кода в OnInit(), OnCalculate() и OnDeinit(), а также интерпретация пороговых значений, например ±2. Индикатор Sigma Score применяется как простой индикатор рыночного стресса для торговли на основе возврата к среднему и моментума.