English Русский 中文 Español Português
preview
取引におけるニューラルネットワーク:金融市場向けマルチモーダルツール拡張エージェント(最終部)

取引におけるニューラルネットワーク:金融市場向けマルチモーダルツール拡張エージェント(最終部)

MetaTrader 5トレーディングシステム |
19 0
Dmitriy Gizlyk
Dmitriy Gizlyk

はじめに

前回の記事では、FinAgentフレームワークの探索を開始しました。FinAgentは金融市場におけるデータ分析と意思決定支援のために設計された高度なツールです。その開発は、複雑かつ急速に変化する市場環境でのリスクを最小化し、効率的に取引戦略を構築するメカニズムの作成に焦点を当てています。FinAgentのアーキテクチャは、相互に接続された5つのモジュールで構成され、それぞれが専門的な機能を果たし、システム全体の適応性を確保しています。

市場分析モジュール(Market Analysis Module)は、価格チャート、マーケットニュース、レポートなど多様な情報源からデータを抽出し、処理する役割を担います。モジュール内でシステムは、価格動向を予測するために使用できる安定したパターンを特定します。

リフレクションモジュール(Reflection Modules)は、モデルの適応と学習プロセスにおいて重要な役割を果たします。低レベルリフレクションモジュール(Low-Level Reflection Module)は、現在の市場シグナル間の相互依存関係を分析し、短期予測の精度を向上させます。対照的に、高レベルリフレクションモジュール(High-Level Reflection Module)は、過去のデータや取引判断の結果を取り込み、蓄積された経験に基づいて戦略を調整します。

メモリーモジュール(Memory Module)は、大量の市場データを長期的に保存します。最新のベクトル類似度技術を活用することでノイズを最小化し、情報検索の精度を向上させます。これは特に長期戦略の開発や複雑な関係性の発見において重要です。

システムの中心には、全モジュールの結果を統合する意思決定モジュール(Decision-Making Module)があります。現在および過去のデータに基づき、最適な取引推奨を生成します。さらに、専門家の知識や従来の指標を統合することで、バランスの取れた根拠ある推奨を提供可能です。

FinAgentフレームワークのオリジナルの視覚化を以下に示します。

前回の記事では、MQL5を用いてFinAgentフレームワークの提案手法の実装を開始しました。低レベルリフレクションモジュールおよびたかイレベルリフレクションモジュールに対応するアルゴリズムを、CNeuronLowLevelReflectionおよびCNeuronHighLevelReflectionオブジェクトとして導入しました。これらのモジュールは、市場シグナル、取引判断の履歴、実際の金融結果を分析し、市場の変化に応じてエージェントの行動方針を適応させます。また、動的なトレンド変化への柔軟な対応や、データ内の主要パターンの特定も可能にします。

特徴的な実装として、リフレクションオブジェクト自体にメモリブロックを統合している点があります。元のフレームワークでは、すべての情報ストリームのメモリは独立したモジュールとして実装されていましたが、リフレクションコンポーネントにメモリを埋め込むことで、データフローやフレームワーク内の要素間の相互作用の構築が簡素化されます。

この作業を継続して、以下の主要モジュールの実装を検討します。それぞれがシステム全体で独自の役割を果たします。

  • 市場分析モジュールは、金融レポート、ニュースフィード、株価情報など多様なデータを処理し、統一フォーマットに変換します。安定したパターンを抽出し、将来の市場動態を予測可能にします。
  • 補助ツール(Auxiliary Tools)は、過去の知見に基づき、歴史的パターン、統計データ、専門家評価を用いて分析と意思決定を支援します。システムの判断の論理的解釈性も提供します。
  • 意思決定支援システム(Decision Support System)は、全モジュールの結果を統合し、適応的かつ最適な取引戦略を生成します。リアルタイムで行動推奨を提供し、トレーダーやアナリストが市場変動に迅速に対応できるようにします。

市場分析モジュールはデータの前処理と統合を担当しており、これは従来の分析手法では検出が難しい隠れたパターンを明らかにする上で重要です。元のFinAgent著者は大規模言語モデル(LLM)を使用してデータの主要要素抽出と次元圧縮をおこないました。しかし、私たちの実装ではLLMは使用せず、時系列分析に特化したモデルを採用し、精度と性能を向上させています。本連載では、多変量時系列を分析および予測するためのいくつかのフレームワークを紹介してきました。それらはいずれもここで適用可能です。本記事の目的では、セグメント化アテンション機構を持つTransformerモデルを選択し、CNeuronPSformerクラスとして実装しました。

とはいえ、これは唯一の解決策ではありません。実際、FinAgentフレームワークはマルチモーダル入力データをサポートしています。このため、時系列を表現し、分析するための異なるアルゴリズムを単独で試すだけでなく、組み合わせて使用することも可能です。このアプローチにより、システムの能力は大幅に拡張され、市場プロセスをより詳細に理解できるようになり、非常に効果的で適応性の高い取引戦略の開発に貢献します。

補助ツールモジュールは、分析対象環境に関する既存知識を全体のモデルアーキテクチャに統合します。このコンポーネントは、移動平均、オシレーター、出来高ベースの指標など、従来の指標に基づく分析シグナルを生成します。これらの指標はいずれもアルゴリズム取引において長年の有効性が証明されています。ただし、このモジュールは標準ツールのみに限定されません。

さらに、テクニカル指標の読み取りに基づく明確に定義されたルールでシグナルを生成することで、モデルの意思決定の解釈性を高め、信頼性と有効性を向上させます。これは戦略的計画やリスク管理において非常に重要な要素です。


補助ツールモジュール

ニューラルモデル内で、従来の指標の出力に基づくシグナル生成モジュールを開発することは、最初に思われるよりもはるかに複雑な作業です。主な難点はシグナルの解釈ではなく、モデルの入力に供給される指標値の評価にあります。

従来の戦略では、シグナルの記述は指標の実際の値に直接依存します。しかし、これらの値はしばしば全く無関係で比較できない分布に属しており、モデル構築に大きな課題を生じさせます。この要因により、アルゴリズムは異種データを分析する必要があるため、学習効率が大幅に低下します。その結果、処理時間の増加、予測精度の低下、その他の悪影響が生じます。これが理由で、私たちは以前のモデルでは正規化されたデータのみを使用することにしました。

正規化プロセスにより、すべての分析対象特徴量は共通で比較可能な範囲にスケーリングされます。これにより、モデル学習の質が大幅に向上します。このアプローチは、測定単位の違いや時間依存の変動による歪みのリスクを最小化します。さらに、正規化により入力が機械学習アルゴリズムにとってより予測可能で扱いやすい形式になるため、深いデータ分析が可能になります。

しかし、正規化は従来戦略でのシグナル生成を大きく複雑化します。従来戦略は生データを前提に設計されており、指標を解釈するための固定閾値を使用します。正規化によりデータが変換されると、閾値のレベルが未定義にシフトしてしまいます。さらに、正規化では従来の指標における二本線のクロスによるシグナル生成が不可能になります。両線が同期してシフトする保証がないためです。その結果、生成されるシグナルが歪んだり、全く現れなくなる可能性があります。このため、指標出力を解釈する新しいアプローチの開発が必要になります。

ここで、シンプルかつ概念的に妥当な解決策を見つけました。本質は、正規化によりすべての特徴量が平均0、分散1に変換される点にあります。その結果、各変数は他の変数と比較可能になり、一種のオシレーターとして解釈できます。このアプローチにより、値が0以上ならば買いシグナル、値が0未満なら売りシグナルという、汎用的なシグナル解釈スキームが得られます。さらに、閾値を導入して「コリドー」を設定することで、弱いまたは曖昧なシグナルを除外できます。これにより、誤検知が最小化され、分析精度が向上し、より根拠のある意思決定が可能になります。

また、一部の特徴量についてシグナルの反転の可能性も考慮します。この問題は、履歴データに適応する学習可能なパラメータを用いることで解決可能です。

このアプローチを適用することで、変化する市場条件に効果的に適応し、より正確で信頼性の高いシグナルを生成できるモデルの基盤を構築できます。

このシグナル生成手法を実装するため、まずOpenCL側でMoreLessEqualカーネルを構築します。この場合、固定閾値を用いたシンプルなアルゴリズムを実装しました。

カーネルパラメータには、同じサイズの2つのデータバッファへのポインタを指定します。一方は入力データを保持し、もう一方は、生成されるシグナルを格納します。シグナルは3つの数値のいずれかで表されます。

  • -1:売り
  • 0:シグナルなし
  • 1:買い
カーネルは、解析対象データバッファのサイズに対応する一次元タスク空間で実行します。

__kernel void MoreLessEqual(__global const float * input,
                            __global float * output)
  {
   const size_t i = get_global_id(0);
   const float value = IsNaNOrInf(input[i], 0);
   float result = 0;

カーネル本体内では、まず現在の処理スレッドを特定し、対応する入力値をローカル変数に即座に読み込みます。必須のステップとして、入力値の検証を行います。無効なデータは自動的に0に置き換えられ、以降の処理中に発生するエラーを防ぎます。

次に、中間結果を保持するためのローカル変数を導入します。初期値として、シグナルが存在しないことを示す値を代入します。

その後、解析対象変数の絶対値をチェックします。シグナルを生成するには、この値が指定された閾値を超えている必要があります。

   if(fabs(value) > 1.2e-7)
     {
      if(value > 0)
         result = 1;
      else
         result = -1;
     }
   output[i] = result;
  }

閾値を超える正の値は買いシグナルを生成し、閾値を下回る負の値は売りシグナルを示します。対応するフラグはローカル変数に格納され、カーネルが終了する前に、このフラグを結果バッファに書き込みます。

上記のアルゴリズムは、順方向処理(フォワードパス)の逐次手順であり、処理には学習可能なパラメータは含まれません。この手法は、計算コストを最小化し、不要な複雑性を回避することを目的とした厳密に決定論的な計算に依存しています。これは、特に大量の情報を処理する場合に重要です。また、このデータフローでは誤差勾配の伝播は適用されません。これは、指標値から得られる安定したシグナルを特定することが目的であり、出力に「適合」させることが目的ではないためです。この特性により、高速かつ高精度な処理が求められるシステムにおいて非常に有用なアルゴリズムとなります。

アルゴリズムをOpenCL側で実装した後は、メインプログラムからカーネルの管理および呼び出しをおこなう仕組みを構築する必要があります。この機能を実装するために、以下のように新しいオブジェクトCNeuronMoreLessEqualを作成します。 

class CNeuronMoreLessEqual :  public CNeuronBaseOCL
  {
protected:
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override {return true; }

public:
                     CNeuronMoreLessEqual(void) {};
                    ~CNeuronMoreLessEqual(void) {};
  };

この新しいオブジェクトの構造は非常にシンプルです。初期化メソッドすら含まれていません。ほとんどの機能は親クラスで処理されており、フォワードパスとバックプロパゲーションのメソッドのみをオーバーライドします。

フォワードパスでは、データバッファへのポインタを、前述のカーネルのパラメータに渡し、その後カーネルを実行キューに登録します。

bool CNeuronMoreLessEqual::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL)
      return false;
   uint global_work_offset[1] = { 0 };
   uint global_work_size[1] = { Neurons() };
   ResetLastError();
   const int kernel = def_k_MoreLessEqual;
   if(!OpenCL.SetArgumentBuffer(kernel, def_k_mle_inputs, NeuronOCL.getOutputIndex()))
     {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      return false;
     }
   if(!OpenCL.SetArgumentBuffer(kernel, def_k_mle_outputs, getOutputIndex()))
     {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      return false;
     }
//---
   if(!OpenCL.Execute(kernel, 1, global_work_offset, global_work_size))
     {
      printf("Error of execution kernel %s: %d; line %d", OpenCL.GetKernelName(kernel), GetLastError(), __LINE__);
      return false;
     }
//---
   return true;
  }

一見すると、バックプロパゲーションメソッドの機能は不明瞭に思えるかもしれません。というのも、前述の通り学習可能なパラメータや勾配伝播が存在しないためです。しかし、ニューラルネットワークのアーキテクチャにおいて、これらのメソッドはすべてのレイヤーで必須であることに注意する必要があります。そうしなければ、対応する親クラスのメソッドが呼ばれ、私たちの特定のアーキテクチャでは誤動作する可能性があります。こうした問題を避けるため、パラメータ更新メソッドはスタブとしてオーバーライドされ、単純にtrueを返すだけにしています。

また、勾配伝播を省略することは、論理的にはゼロ値を渡すことと同等です。そのため、勾配分配メソッドでは、対応するソースデータオブジェクトのバッファを単純にゼロにリセットします。これにより、モデルが正しく動作し、ランタイムエラーのリスクを最小化できます。

bool CNeuronMoreLessEqual::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL || !NeuronOCL.getGradient())
      return false;
   return NeuronOCL.getGradient().Fill(0);
  }

これで補助ツールモジュールに関する作業は終了です。CNeuronMoreLessEqualクラスおよびそのすべてのメソッドのフルコードは添付資料に掲載しています。

現時点で、FinAgentフレームワークのほぼすべての主要モジュールを網羅しました。残るコンポーネントは、全体アーキテクチャの中核要素である意思決定モジュールです。このモジュールは、複数(多くの場合は3つ以上)のデータストリームからの情報を統合する役割を果たします。私たちは、この意思決定モジュールを独立したエンティティとしてではなく、複合フレームワークオブジェクトに直接統合することにしました。この設計により、すべてのシステムコンポーネント間の相互運用性が向上しています。


FinAgentフレームワークの構築

そしていよいよ、これまで作成してきたすべてのモジュールを統合された包括的構造にまとめる時が来ました。これがFinAgentフレームワークであり、各モジュールの統合と相乗的な連携を保証します。異なる機能タイプのモジュールを組み合わせることで、共通の目的を達成します。それは、複雑な市場データを効率的かつ柔軟に分析し、金融市場の動態や特徴を考慮した戦略を開発できるシステムを構築することです。この機能は、新しいオブジェクトCNeuronFinAgentによって実装されます。その構造体は以下のとおりです。

class CNeuronFinAgent   :  public CNeuronRelativeCrossAttention
  {
protected:
   CNeuronTransposeOCL  cTransposeState;
   CNeuronLowLevelReflection  cLowLevelReflection[2];
   CNeuronHighLevelReflection cHighLevelReflection;
   CNeuronMoreLessEqual cTools;
   CNeuronPSformer      cMarketIntelligence;
   CNeuronMemory        cMarketMemory;
   CNeuronRelativeCrossAttention cCrossLowLevel;
   CNeuronRelativeCrossAttention cMarketToLowLevel;
   CNeuronRelativeCrossAttention cMarketToTools;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput,
                       CBufferFloat *SecondGradient, ENUM_ACTIVATION SecondActivation = None) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override;

public:
                     CNeuronFinAgent(void) {};
                    ~CNeuronFinAgent(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint window, uint window_key, uint units_count, uint heads,
                          uint account_descr, uint nactions, uint segments,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void) override   const   {  return defNeuronFinAgent; }
   //---
   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;
  };

この構造では、おなじみのオーバーライド可能なメソッド群と、いくつかの内部オブジェクトを見ることができます。その中には、すでにFinAgentフレームワーク内で実装したモジュールを容易に特定することができます。これらのモジュール間の相互作用を定義する情報フローの構築については、このクラスのメソッドの実装アルゴリズムを検討する際に詳しく説明します。

すべての内部オブジェクトは静的に宣言されているため、クラスのコンストラクタおよびデストラクタは空のままとします。宣言済みおよび継承されたすべてのオブジェクトの初期化は、Initメソッド内でおこないます。このメソッドのパラメータには、作成されるオブジェクトのアーキテクチャを定義するいくつかの定数が含まれます。 

bool CNeuronFinAgent::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                           uint window, uint window_key, uint units_count, uint heads,
                           uint account_descr, uint nactions, uint segments,
                           ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronRelativeCrossAttention::Init(numOutputs, myIndex, open_cl, 3,
                                           window_key, nactions / 3, heads,
                                           window, units_count,
                                           optimization_type, batch))
      return false;

少し先の話になりますが、私たちの意思決定ブロックは、いくつかの順次実行されるクロスアテンション層で構成されます。その最後の層は、親オブジェクトを通じて実装されており、偶然ではなく、この親オブジェクトはCNeuronRelativeCrossAttentionクラスに基づいています。

FinAgentフレームワークの実装出力では、エージェントの行動をテンソル形式で表現した行列を得ることを想定しています。この行列の各行は、個別の行動を表すベクトルです。買いおよび売り操作は、この行列の異なる行で表されます。各操作は取引量、ストップロス価格、テイクプロフィット価格の3つのパラメータで記述されます。その結果、行動行列は3列を持つことになります。

したがって、親クラスの初期化メソッドを呼び出す際には、メインパイプラインのデータ解析ウィンドウを3に設定し、解析対象シーケンス内の要素数を、エージェントの行動を記述するパラメータで指定されたベクトルサイズの3分の1に設定します。この構成により、モデルは二次情報ストリームの文脈内で各操作の有効性を評価できます。二次情報ストリームは、システムが周囲の環境に関する処理済み情報を伝達するために使用されます。そのため、対応するパラメータを転送します。

親クラスの初期化手順が正常に実行された後、新たに宣言した内部オブジェクトの準備に進みます。まず、市場分析モジュールのコンポーネントを初期化します。本記事の実装では、このモジュールは、多変量時系列データ内の安定パターンを検出するセグメント化アテンション(Segmented Attention)Transformerとメモリブロックの2つのオブジェクトで構成されています。

   int index = 0;
   if(!cMarketIntelligence.Init(0, index, OpenCL, window, units_count, segments, 0.2f, optimization, iBatch))
      return false;
   index++;
   if(!cMarketMemory.Init(0, index, OpenCL, window, window_key, units_count, heads, optimization, iBatch))
      return false;

環境を包括的に解析するために、2つの低レベルリフレクションモジュールを使用します。これらは、異なる軸構成で表現された入力データのテンソルに対して並列に動作します。解析対象データを異なる軸構成で表現するために、転置オブジェクトを使用します。

   index++;
   if(!cTransposeState.Init(0, index, OpenCL, units_count, window, optimization, iBatch))
      return false;

次に、2つの低レベルリフレクションオブジェクトを初期化します。これらは、解析対象テンソルのウィンドウ次元とシーケンス長次元を入れ替えることにより、異なる次元構成で表現されたデータを分析します。

   index++;
   if(!cLowLevelReflection[0].Init(0, index, OpenCL, window, window_key, units_count, heads, optimization, iBatch))
      return false;
   index++;
   if(!cLowLevelReflection[1].Init(0, index, OpenCL, units_count, window_key, window, heads, optimization, iBatch))
      return false;

最初の場合、各時刻がデータベクトルで表現される多変量時系列を分析し、これらのベクトルを比較することで、ベクトル間の相互依存関係を明らかにします。次の場合、個々の単変量時系列を分析し、その動態における依存関係や規則性を検出します。

その後、高レベルリフレクションモジュールを初期化します。このモジュールは、エージェントの最近の行動を市場の変動や金融結果の文脈で評価します。

   index++;
   if(!cHighLevelReflection.Init(0, index, OpenCL, window, window_key, units_count, heads, account_descr, nactions,
                                                                                             optimization, iBatch))
      return false;

この段階で、補助ツールモジュールオブジェクトも準備します。

   index++;
   if(!cTools.Init(0, index, OpenCL, window * units_count, optimization, iBatch))
      return false;
   cTools.SetActivationFunction(None);

初期化されたすべてのモジュールの結果は、意思決定モジュールに集約されます。前述の通り、このモジュールはいくつかの順次実行されるクロスアテンションブロックで構成されています。最初の段階では、2つの低レベルリフレクションモジュールからの情報を統合します。

   index++;
   if(!cCrossLowLevel.Init(0, index, OpenCL, window, window_key, units_count, heads, units_count, window,
                                                                                   optimization, iBatch))
      return false;

次に、市場分析モジュールの出力を、低レベルリフレクションモジュールから得られた情報で補強します。

   index++;
   if(!cMarketToLowLevel.Init(0, index, OpenCL, window, window_key, units_count, heads, window, units_count,
                                                                                       optimization, iBatch))
      return false;

次に、事前の知識の層を追加します。

   index++;
   if(!cMarketToTools.Init(0, index, OpenCL, window, window_key, units_count, heads, window, units_count,
                                                                                   optimization, iBatch))
      return false;
//---
   return true;
  }

意思決定モジュールの最終層は、すでに前段階で初期化されています。これは親オブジェクトによって表現されます。

すべての入れ子オブジェクトを正常に初期化した後は、操作の論理結果を呼び出し元プログラムに返して、メソッドを終了します。

次の作業段階は、feedForwardメソッド内でのFinAgentフレームワーク実装のフォワードパスアルゴリズムの構築です。メソッドのパラメータとして、2つの入力データオブジェクトへのポインタを受け取ります。1つ目は現在の環境状態に関する情報を保持し、2つ目は口座状況および現在の金融結果を表します。

bool CNeuronFinAgent::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
   if(!cMarketIntelligence.FeedForward(NeuronOCL))
      return false;
   if(!cMarketMemory.FeedForward(cMarketIntelligence.AsObject()))
      return false;

解析対象となる市場環境に関する情報は、まず市場分析モジュールで初期処理されます。このモジュールでは、セグメント化アテンションTransformerを用いてパターンを特定し、メモリーモジュール内でそれらの安定した組み合わせを検出します。

異なる表現に基づいて検出された2種類のパターンは、市場動態を包括的に分析するために、低レベルリフレクションモジュールに統合されます。

   if(!cTransposeState.FeedForward(cMarketIntelligence.AsObject()))
      return false;
   if(!cLowLevelReflection[0].FeedForward(cMarketIntelligence.AsObject()))
      return false;
   if(!cLowLevelReflection[1].FeedForward(cTransposeState.AsObject()))
      return false;

ここで注意すべき点は、低レベルリフレクションモジュールは現在の市場環境で検出されたパターンのみに作用し、市場分析モジュールのメモリブロックのデータは使用しないことです。このアプローチにより、検出されたパターンに対する即時の市場反応に焦点を当てることができ、履歴データに依存せずに現在の変化やトレンドをより正確に評価できます。

同じロジックは、高レベルリフレクションモジュールにも適用されます。

   if(!cHighLevelReflection.FeedForward(cMarketIntelligence.AsObject(), SecondInput))
      return false;

補足として、高レベルリフレクションモジュールへの入力には、現在の市場環境データ(市場分析モジュールの出力)と金融結果ベクトルの両方が含まれます。また、エージェントの過去の行動テンソルは、高レベルリフレクションモジュールの結果バッファから再帰的に使用されます。

一方、補助ツールモジュールは生データに直接作用します。これは、解析対象の指標値に含まれる既存知識に基づくシグナルを検出するためです。

   if(!cTools.FeedForward(NeuronOCL))
      return false;

次に、意思決定モジュールの処理を整理します。最初の段階では、低レベルリフレクション分析の結果を強化し、単変量シーケンスの動態で特定された依存関係を統合します。これにより、分析精度が向上し、システム相互作用の理解が深まることで、市場環境のより包括的な評価が可能になります。

   if(!cCrossLowLevel.FeedForward(cLowLevelReflection[0].AsObject(), cLowLevelReflection[1].getOutput()))
      return false; 

次の段階では、低レベルリフレクションから得られた情報を、市場分析モジュールのメモリーブロックが生成した安定パターンの表現に統合します。このステップにより、発見された関係性が精緻化および強化され、市場の現在の動態や相互作用に関するより正確で包括的な理解が得られます。

   if(!cMarketToLowLevel.FeedForward(cMarketMemory.AsObject(), cCrossLowLevel.getOutput()))
      return false;

ここで重要なのは、低レベルリフレクションモジュールは現在の市場状態を分析し、個々のパターンに対する市場の反応を特定することです。しかし、これらのパターンの一部は発生頻度が低いため、対応する市場反応は統計的に有意でない場合があります。その場合、情報は低レベルリフレクションモジュールのメモリに保存されます。これは、将来同様のパターンが出現する可能性があるためで、モデルは市場反応に関する追加データを蓄積できます。

それでも、未確認の情報は意思決定に使用できません。そのため、意思決定モジュールでは、安定したパターンのみを利用し、低レベルリフレクションモジュールから対応する反応データを取得して、より正確で根拠のある評価をおこないます。

次に、過去の知見を組み込むことで市場分析の結果を強化します。

   if(!cMarketToTools.FeedForward(cMarketToLowLevel.AsObject(), cTools.getOutput()))
      return false;

ここで注意すべき点は、以前議論されたように、補助ツールモジュールによって生成されるフラグの解釈に対して学習可能なパラメータは導入していないことです。代わりに、この機能はクロスアテンションモジュール内のKeyおよびValue生成パラメータに委譲されています。したがって、これらのフラグの解釈と処理はクロスアテンション機構に直接統合されており、追加の明示的パラメータは不要です。

feedForwardメソッドの最後では、高レベルリフレクションモジュールの結果を、識別された安定パターンおよびそれに対する市場反応の文脈で分析します。この操作は親クラスのツールを使用して実行されます。

   return CNeuronRelativeCrossAttention::feedForward(cHighLevelReflection.AsObject(), cMarketToTools.getOutput());
  }

その後、処理の論理結果は呼び出し元プログラムに返され、feedForwardメソッドは完了します。

フォワードパスの後は、バックプロパゲーション処理を整理します。このセクションでは、誤差勾配分配メソッド(calcInputGradients)のアルゴリズムを詳細に検討し、学習可能パラメータの最適化メソッド(updateInputWeights)は独立して学習するために残します。

bool CNeuronFinAgent::calcInputGradients(CNeuronBaseOCL *NeuronOCL,
                                         CBufferFloat *SecondInput,
                                         CBufferFloat *SecondGradient,
                                         ENUM_ACTIVATION SecondActivation = -1)
  {
   if(!NeuronOCL || !SecondGradient)
      return false;

このメソッドのパラメータには、再び同じ入力データオブジェクトへのポインタが含まれます。今回は、入力データがモデルの最終出力に与える影響に応じた誤差勾配を渡す必要があります。メソッド本体は、まずこれらのポインタの検証から始まります。なぜなら、ポインタが無効であれば、以降の処理は無意味になるためです。

ご存じの通り、誤差勾配の分配は、フォワードパスの情報フローを逆方向に完全に反映します。フォワードパスは、親クラスの対応メソッドの呼び出しで終了しています。したがって、バックプロパゲーションアルゴリズムは、まず親クラスの勾配分配メソッドを呼び出すことから開始されます。この処理により、モデルの誤差は高レベルリフレクションモジュールと、意思決定モジュールの直前のクロスアテンションブロックに分配されます。

   if(!CNeuronRelativeCrossAttention::calcInputGradients(cHighLevelReflection.AsObject(),
                                       cMarketToTools.getOutput(),
                                       cMarketToTools.getGradient(),
                                       (ENUM_ACTIVATION)cMarketToTools.Activation()))
      return false;

次に、意思決定モジュールのすべてのクロスアテンションブロックを順次通じて誤差勾配を伝播させ、モデルの出力に対する各情報フローの影響に従って、フレームワークのすべての情報フローに誤差を分配します。

   if(!cMarketToLowLevel.calcHiddenGradients(cMarketToTools.AsObject(),
                                         cTools.getOutput(),
                                         cTools.getGradient(),
                                         (ENUM_ACTIVATION)cTools.Activation()))
      return false;
//---
   if(!cMarketMemory.calcHiddenGradients(cMarketToLowLevel.AsObject(),
                                         cCrossLowLevel.getOutput(),
                                         cCrossLowLevel.getGradient(),
                                         (ENUM_ACTIVATION)cCrossLowLevel.Activation()))
      return false;

次に、低レベルリフレクションモジュールを通じて誤差勾配を伝播させます。

   if(!cLowLevelReflection[0].calcHiddenGradients(cCrossLowLevel.AsObject(),
         cLowLevelReflection[1].getOutput(),
         cLowLevelReflection[1].getGradient(),
         (ENUM_ACTIVATION)cLowLevelReflection[1].Activation()))
      return false;
   if(!cTransposeState.calcHiddenGradients(cLowLevelReflection[1].AsObject()))
      return false;

この時点で、誤差勾配はフレームワーク内のすべてのモジュールに分配されています。次に、元の入力データレベルで全情報ストリームからデータを収集する必要があります。ここで注意すべきは、すべてのリフレクションモジュールおよび市場分析モジュールのメモリブロックは、セグメント化アテンションTransformerによって生成された前処理データを使用していることです。したがって、まずそのTransformerの出力レベルで誤差勾配を収集します。

最初のステップは、メモリブロックから誤差勾配を転送することです。

   if(!((CNeuronBaseOCL*)cMarketIntelligence.AsObject()).calcHiddenGradients(cMarketMemory.AsObject()))
      return false;

次に、入力データ前処理オブジェクトの誤差勾配バッファへのポインタを置き換え、累積された勾配値を保存できるようにします。

   CBufferFloat *temp = cMarketIntelligence.getGradient();
   if(!cMarketIntelligence.SetGradient(cMarketIntelligence.getPrevOutput(), false) ||
      !((CNeuronBaseOCL*)cMarketIntelligence.AsObject()).calcHiddenGradients(cHighLevelReflection.AsObject(),
                                                            SecondInput, SecondGradient, SecondActivation) ||
      !SumAndNormilize(temp, cMarketIntelligence.getGradient(), temp, 1, false, 0, 0, 0, 1))
      return false;

その後、高レベルリフレクションモジュールの勾配分配メソッドを呼び出します。その後、両方のデータストリームから得られた結果を合算する必要があります。

ここで注意すべき点は、高レベルリフレクションモジュールは2つのデータストリームを扱うことです。したがって、勾配伝播の際、このモジュールはメインストリームと金融結果ストリームの両方からの誤差を同時に処理します。これにより、モデルは解析の両方の重要な側面の誤差を考慮でき、システムのより正確な調整が可能になります。

低レベルリフレクションモジュールも同様の方法で勾配伝播を処理します。ただし、高レベルリフレクションモジュールとは異なり、入力データの単一ソースのみを扱うため、誤差勾配の分配プロセスはより簡略化されています。

   if(!((CNeuronBaseOCL*)cMarketIntelligence.AsObject()).calcHiddenGradients(cLowLevelReflection[0].AsObject()) ||
      !SumAndNormilize(temp, cMarketIntelligence.getGradient(), temp, 1, false, 0, 0, 0, 1))
      return false;
   if(!((CNeuronBaseOCL*)cMarketIntelligence.AsObject()).calcHiddenGradients(cTransposeState.AsObject()) ||
      !SumAndNormilize(temp, cMarketIntelligence.getGradient(), temp, 1, false, 0, 0, 0, 1) ||
      !cMarketIntelligence.SetGradient(temp, false))
      return false;

各反復後には、新たに得られた勾配値をこれまでに累積された勾配に加算する必要があることを忘れないでください。これにより、モデルのすべての誤差が正しく考慮されます。すべての情報フローの処理が完了した後は、元のバッファポインタを初期状態に復元することが重要です。

最後に、誤差勾配をメイン情報ストリームの入力レベルに戻し、メソッドを完了します。この際、処理の論理結果を呼び出し元プログラムに返します。

   if(!NeuronOCL.calcHiddenGradients(cMarketIntelligence.AsObject()))
      return false;
//---
   return true;
  }

なお、補助ツールモジュールは、誤差勾配分配アルゴリズムには参加しません。前述の通り、この情報フローを通じて勾配を伝播する予定はありません。さらに、この文脈でデータソースオブジェクトの勾配バッファをクリアすると、同じオブジェクトがメイン情報ストリームを通じても勾配を受け取るため、逆効果になります。

これで、MQL5におけるFinAgentフレームワークの実装アルゴリズムの解説は完了です。ここで紹介したすべてのオブジェクトとメソッドのフルソースコードは添付資料にあり、参照やさらなる実験に利用できます。添付資料には、完全なプログラムコードおよび本記事作成に使用した学習可能モデルのアーキテクチャも含まれています。すべてのコンポーネントは、以前の記事で紹介した階層型メモリを持つエージェント構築からほぼ変更なしで移植されています。唯一の変更点は、モデルアーキテクチャに関する部分で、単一のニューラル層を前述の統合されたFinAgentフレームワークに置き換えたことです。

//--- layer 2
   if(!(descr = new CLayerDescription()))
      return false;
   descr.type = defNeuronFinAgent;
//--- Windows
     {
      int temp[] = {BarDescr, AccountDescr, 2 * NActions, Segments}; //Window, Account description, N Actions, Segments
      if(ArrayCopy(descr.windows, temp) < int(temp.Size()))
         return false;
     }
   descr.count = HistoryBars;
   descr.window_out = 32;
   descr.step = 4;                              // Heads
   descr.batch = 1e4;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!actor.Add(descr))
     {
      delete descr;
      return false;
     }

残りのすべての層のアーキテクチャは変更なく保持されています。そして今、作業の最終段階に移ります。それは、実際の履歴データに対して実装した手法の有効性を評価することです。



テスト

前回と前々回の記事では、FinAgentフレームワークを詳細に検討しました。この過程で、私たちはその著者たちが提案したアプローチを独自に解釈して実装しました。さらに、フレームワークのアルゴリズムを当社の特定の要件に合わせて適応させました。今回、別の重要な段階に到達しました。それは、実際の履歴データ上で実装したソリューションの有効性を評価することです。

開発の過程で、FinAgentフレームワークのコアアルゴリズムに大幅な変更を加えました。これらの変更は、モデルの運用の重要な側面に影響を与えます。したがって、今回の評価では、元のフレームワークではなく、私たちが適応させたバージョンを評価していることに注意してください。

モデルは、EUR/USD通貨ペアの2023年のH1時間足の履歴データで学習しました。モデルで使用したすべてのインジケーター設定はデフォルト値のままとし、追加のチューニングをおこなわずに、アルゴリズム自体の性能と生データへの適応能力を評価できるようにしました。

初期の学習段階では、以前の研究で準備したデータセットを使用しました。学習アルゴリズムは、エージェントに対して「ほぼ理想的」な目的行動を生成する方式で、学習データセットを逐次更新することなくモデルを学習可能にしました。ただし、この方法は効果的に機能しましたが、学習セットを定期的に更新することで、精度の向上および異なる口座状態のカバレッジ拡大が可能になると考えています。

複数の学習サイクルの後、モデルは学習データとテストデータの両方で安定した収益性を示しました。最終テストは、2024年1月の履歴データを用いて実施し、すべてのモデルパラメータとインジケーター設定を維持しました。このアプローチにより、実際の市場環境にできるだけ近い条件でのモデル性能の客観的評価が可能になります。結果は以下の通りです。

テスト期間中、モデルは95回の取引を実行し、類似期間の以前のモデルのパフォーマンスを大きく上回りました。取引の42%以上が利益で決済されました。平均的な利益取引は、平均的な損失取引の1.5倍の規模であったため、モデル全体として利益が出ました。プロフィットファクターは1.09と記録されました。

興味深いことに、利益の大部分は月の前半に、価格が比較的狭い範囲で変動していた期間に発生しました。弱気トレンドが形成された際には、バランスラインは横ばいに推移し、いくつかのドローダウンが観察されました。

テスト期間の銘柄チャート

私の見解では、観察された挙動は市場分析モジュールと補助ツールモジュールのアルゴリズムに起因する可能性が高いです。この領域は、さらに詳細な調査が必要です。



結論

包括的な市場分析および履歴データ評価のための先進的ソリューションであるFinAgentフレームワークを検討しました。このフレームワークは、テキスト情報と視覚情報を統合することで、十分に情報に基づいた取引判断の可能性を大幅に拡張します。5つの主要なアーキテクチャコンポーネントを備えたFinAgentは、精度と高い適応性の両方を示しており、頻繁に変化する市場環境での取引において極めて重要です。

特筆すべきは、このフレームワークが単一の分析手法に限定されない点です。テキストデータとグラフィカルデータの両方で効果的に動作する幅広いツールを提供します。この多用途性により、モデルは複数の市場要因を考慮でき、市場動態に関するより深い理解を提供します。これらの特徴により、FinAgentは、変化する市場条件に適応し、わずかな市場変動も考慮できる取引戦略の開発に有望なツールとなります。

実践的な作業では、私たちはMQL5でフレームワークアプローチの独自の解釈を実装しました。これらのアプローチを統合してモデルを学習させ、実際の履歴データでテストしました。結果として、モデルは利益を生み出す能力を示しました。しかし、利益は市場状況に依存することが確認されました。また、動的に変化する市場環境に対するモデルの適応性を向上させるためのさらなる実験が必要です。 


参照文献


記事で使用されているプログラム

#名前種類説明
1Research.mq5EAサンプル収集用EA
2ResearchRealORL.mq5
EA
Real-ORL法を用いたサンプル収集用EA
3Study.mq5EAモデル訓練EA
4Test.mq5EAモデルテスト用EA
5Trajectory.mqhクラスライブラリシステム状態とモデルアーキテクチャ記述構造
6NeuroNet.mqhクラスライブラリニューラルネットワークを作成するためのクラスのライブラリ
7NeuroNet.clコードライブラリOpenCLプログラムコード

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/16867

添付されたファイル |
MQL5.zip (2327.64 KB)
取引におけるニューラルネットワーク:金融市場向けマルチモーダルツール拡張エージェント(FinAgent) 取引におけるニューラルネットワーク:金融市場向けマルチモーダルツール拡張エージェント(FinAgent)
FinAgentを紹介します。FinAgentは、マーケットの動向や過去の取引パターンを反映するさまざまなタイプのデータを分析できるマルチモーダル金融取引エージェントのフレームワークです。
多通貨エキスパートアドバイザーの開発(第21回):重要な実験の準備とコードの最適化 多通貨エキスパートアドバイザーの開発(第21回):重要な実験の準備とコードの最適化
さらなる前進のためには、自動最適化を定期的に再実行し、新しいエキスパートアドバイザー(EA)を生成することで結果を改善できるかどうかを検証することが有益でしょう。パラメータ最適化の利用を巡る多くの議論における最大の障害は、取得したパラメータを将来の期間において、収益性およびドローダウンを所定の水準に保ったまま、どれだけ長く取引に使用できるのかという点です。そして、そもそもそれは可能なのかという問題でもあります。
リスク管理(第1回):リスク管理クラス構築の基礎 リスク管理(第1回):リスク管理クラス構築の基礎
本記事では、取引におけるリスク管理の基礎を解説し、適切なロットサイズやストップロスを計算するための最初の関数の作成方法を学びます。さらに、これらの機能がどのように動作するのかを、各ステップを追いながら詳しく説明します。本記事の目的は、自動売買においてこれらの概念をどのように適用するかを明確に理解することです。最後に、インクルードファイルを使用したシンプルなスクリプトを作成し、すべてを実践に落とし込みます。
取引におけるニューラルネットワーク:層状メモリを持つエージェント(最終回) 取引におけるニューラルネットワーク:層状メモリを持つエージェント(最終回)
引き続き、FinMemフレームワークの構築に取り組みます。本フレームワークは、人間の認知プロセスを模した層状メモリアプローチを用いることで、複雑な金融データを効果的に処理できるだけでなく、新しいシグナルに適応することも可能にします。その結果、動的に変化する市場における投資判断の精度と有効性が大幅に向上します。