English Deutsch
preview
GMDH (The Group Method of Data Handling):MQL5で多層反復アルゴリズムを実装する

GMDH (The Group Method of Data Handling):MQL5で多層反復アルゴリズムを実装する

MetaTrader 5 | 27 5月 2024, 10:51
71 0
Francis Dube
Francis Dube

はじめに

GMDH (Group Method of Data Handling)は、コンピュータベースのデータモデリングに使用される帰納的アルゴリズムのファミリーです。このアルゴリズムは、データから多項式ニューラルネットワークモデルを自動的に構築し最適化することで動作し、入力変数と出力変数の関係を明らかにするユニークなアプローチを提供します。従来、GMDHのフレームワークは、組み合わせアルゴリズム(COMBI)、組み合わせ選択アルゴリズム(MULTI)、多層反復アルゴリズム(MIA)、緩和反復アルゴリズム(RIA)の4つの主要アルゴリズムで構成されていました。この記事では、MQL5における多層反復アルゴリズムの実装を探求します。その内部構造について説明し、データセットから予測モデルを構築するために適用できる方法を示します。


GMDH (Group Method of Data Handling)について

Group Method of Data Handling(英語)は、データ分析と予測に使われるアルゴリズムの一種です。これは、与えられたデータセットを記述するのに最適な数学モデルを見つけることを目的とした機械学習技術です。GMDHは1960年代にソ連の数学者Alexey Ivakhnenkoによって開発されました。このアルゴリズムは、経験的データに基づく複雑なシステムのモデリングに伴う課題に対処するために開発されました。GMDHアルゴリズムは、先入観や理論的仮定ではなく、観測されたデータに基づいてモデルを生成改良する、データ駆動型のモデリングアプローチを採用しています。

GMDHの主な利点の1つは、モデル候補を繰り返し生成評価することで、モデル構築のプロセスを自動化できることです。最もパフォーマンスの高いモデルを選択し、データからのフィードバックに基づいて改良します。この自動化により、モデルの構築における手作業や専門知識の必要性が軽減されます。

GMDHの背後にある重要な考え方は、変数を反復的に選択し組み合わせることによって、複雑さと精度を増す一連のモデルを構築することです。このアルゴリズムは、単純なモデル(通常は線形モデル)の集合から始まり、変数や項を追加することで徐々に複雑さを増していきます。各ステップにおいて、アルゴリズムはモデルの性能を評価し、最も性能の良いものを選択し、次の反復の基礎とします。このプロセスは、満足のいくモデルが得られる、または停止基準が満たされるまで続けられます。

GMDHは、多数の入力変数とそれらの間の複雑な関係を持つデータセットのモデリングに特に適しています。GMDH技術は、入力を無限Volterra–Kolmogorov–Gabor(VKG)多項式で表現可能な出力に関連付けるモデルをもたらします。Volterra-Kolmogorov-Gabor (VKG)多項式は、非線形システムのモデリングや複雑なデータの近似に使用される特定のタイプの多項式です。VKG多項式は以下の形をとります。

VKG公式

ここで

  • Yn:システムの出力
  • Xi、Xj、Xk:それぞれ時刻i、j、kにおける入力変数
  • ai、aj、ak:多項式の係数

このような多項式は、多項式ニューラルネットワーク(PNN)と考えることができます。PNNは、ニューロン内で多項式活性化関数を使用する人工ニューラル ネットワーク(ANN)アーキテクチャの一種です。多項式ニューラルネットワークの構造は他のニューラルネットワークと似ており、入力ノード、隠れ層、出力ノードがあります。しかし、PNNのニューロンに適用される活性化関数は多項式関数です。パラメトリックGMDHアルゴリズムは、連続変数を扱うために特別に開発されました。このアルゴリズムでは、モデル化される対象が、その表現や定義に曖昧さがない特性によって特徴付けられています。多層反復アルゴリズムは、パラメトリックGMDHアルゴリズムの一例です。


多層反復アルゴリズム

MIAは、多項式ニューラルネットワークモデルを構築するためのGMDHフレームワークの変種です。その構造は、多層フィードフォワードニューラルネットワークとほぼ同じです。情報は入力層から中間層を経て最終出力へと流れます。各層はデータに対して特定の変換をおこないます。GMDHの一般的な手法と比較して、MIAの重要な差別化要因は、データを最もよく記述する最終多項式の最適な部分関数の選択にあります。つまり、訓練によって得られた情報の一部は、あらかじめ定義された基準に従って破棄されます。

MIAを使ってモデルを構築するには、まず調査したいデータセットを訓練セットとテストセットに分割します。基礎となるプロセスの特徴を適切に捉えるために、訓練セットにはできるだけ多くの種類を用意したいです。それが終わったら、層の構築を開始します。 


層の構築

多層フィードフォワードニューラルネットワークと同様に、予測変数または独立変数の集まりである入力層から始めます。これらの入力は一度に2つずつ取り込まれ、ネットワークの第1層に送られます。したがって、第1層は「2のM組合せ」ノードで構成されます。ここで、Mは予測子の数です。

MIA入力と第1層

上の図は、4つの入力(x1...x4と表記)を扱った場合の入力層と第1層の例を示しています。第1層では、訓練データセットを使ってノードの入力に基づいて部分モデルが構築され、得られた部分モデルがテストデータセットに対して評価されます。その後、層内のすべての部分モデルからの予測誤差が比較されます。最良のN個のモデルが記録され、次の層の入力を生成するために使用されます。ある層の上位N個のモデルの予測誤差は、何らかの方法で結合され、モデル生成の全体的な進歩を示す単一の尺度となります。これは前の層の数字と比較されます。前の層以下の場合は、新しい層が作成され、そのプロセスが繰り返されます。そうでなければ、改善が見られません。モデル生成は停止され、現在の層からのデータは破棄されます。つまり、モデルの訓練が完了することを示します。

新しい層


ノードと部分モデル

層の各ノードでは、前の層から出力された入力のペアが与えられた訓練データセットの観測を推定する多項式が計算されます。これはいわゆる部分モデルです。ノードの入力が与えられた訓練セットの出力をモデル化するために使用される方程式の例を以下に示します。

活性化関数

ここでvはフィッティングされた線形モデルの係数です。当てはまり度は、テストデータセットにおける実際の値に対する予測値の平均二乗誤差を求めることによってテストされます。これらの誤差は、平均を計算する、単純に平均二乗誤差が最小のノードを選択するなどの何らかの方法で組み合わされます。この最終的な指標は、近似値が他の層と比較して向上しているかどうかを示します。同時に、予測誤差が最も少ないベストNノードを記録します。そして、対応する係数は、次の層のための新しい入力のセットの値を生成するために使用されます。現在の層の近似値が、前の層の近似値よりも優れている(この場合は小さい)場合、新しい層が構築されます。

ネットワークが完成すると、各層で最も予測誤差が大きかったノードの係数のみが保持され、データを最もよく記述する最終モデルを定義するために使用されます。次のセクションでは、今説明した手順を実行するコードを掘り下げて行きます。このコードは、GitHubで公開されているGMDHのC++実装を参考にしています。


MQL5での実装

C++の実装はモデルを訓練し、後で使用するためにJSON形式でテキストファイルに保存します。マルチスレッドを活用して訓練を高速化し、BoostとEigenライブラリを使用して構築されています。MQL5の実装では、マルチスレッド訓練と、一次方程式を解くためのQR分解の代替オプションが利用可能であることを除けば、ほとんどの機能が引き継がれます。

実装は、3つのヘッダーファイルで構成されます。最初はgmdh_internal.mqhです。このファイルには、さまざまなカスタムデータ型の定義が含まれています。まず、3つの列挙を定義します。

  • PolynomialType:再度の訓練をおこなう前に、既存の変数を変換するために使用する多項式のタイプを指定します。
    //+---------------------------------------------------------------------------------------------------------+
    //|  Enumeration for specifying the polynomial type to be used to construct new variables from existing ones|
    //+---------------------------------------------------------------------------------------------------------+
    enum PolynomialType
      {
       linear,
       linear_cov,
       quadratic
      };
    PolynomialTypeでは、以下の多項式関数を表す3つのオプションが公開されています。ここで、x1とx2は関数f(x1,x2)の入力で、v0...vNは求める係数です。列挙は、解の集合が生成される方程式のタイプを表します。
オプション
関数 f(x1,x2)
linear
一次方程式: v0 + v1*x1 + v2*x2
linear_cov
共分散を持つ一次方程式: v0 + v1*x1 + v2*x2 + v3*x1*x2
quadratic
二次方程式: v0 + v1*x1 + v2*x2 + v3*x1*x2 + v4*x1^2 + v5*x2^2
  • Solver:連立一次方程式を解くために使用されるQR分解法を決定します。使用可能なオプションは1つだけです。C++バージョンは、Eigenライブラリを使ったQR分解にHouseholder法のバリエーションを採用しています。
    //+-----------------------------------------------------------------------------------------------+
    //|  Enum  for specifying the QR decomposition method for linear equations solving in models.     |
    //+-----------------------------------------------------------------------------------------------+
    enum Solver
      {
       fast,
       accurate,
       balanced
      };
  • CriterionType:候補モデルの評価基準として使用される特定の外部基準を選択できます。この列挙は、モデルを訓練するときの停止基準として使用できるオプションをキャプチャします。
    //+------------------------------------------------------------------+
    //|Enum for specifying the external criterion                        |
    //+------------------------------------------------------------------+
    enum CriterionType
      {
       reg,
       symReg,
       stab,
       symStab,
       unbiasedOut,
       symUnbiasedOut,
       unbiasedCoef,
       absoluteNoiseImmun,
       symAbsoluteNoiseImmun
      };
    利用可能なオプションについては、以下の表で詳しく説明します。
CriterionType
詳細
reg
regularity:テストデータセットの目標と、訓練データセットとテストデータセットの予測変数の組み合わせで計算された係数を用いた予測との差に基づく、正則化された二乗誤差和(SSE)を適用します。
symReg
symmetric regularity:テストデータセットの目標と、テストデータセットの予測子と組み合わせて訓練データセットを用いて計算された係数を用いた予測との間の差に基づくSSEと、訓練データセットの目標と、訓練データセットの予測子と組み合わせてテストデータセットを用いて計算された係数を用いた予測との間の差に基づくSSEとの和です。
stab
stability:すべての目標と、訓練データセットとすべての予測変数の組み合わせで計算された係数を用いた予測との間の差に基づくSSEを使用します。
symStab
symetric stabitlity:stability基準と同様に計算されたSSEと、テストデータセットを用いて計算された係数とデータセットのすべての目標とすべての予測変数の組み合わせを用いた予測との間の差に基づくSSEを組み合わせています。
unbiasedOut
unbiased outputs:訓練データセットを用いて計算された係数を用いた予測値と,テストデータセットの予測子を用いて計算された係数を用いた予測値の差に基づくSSEです。
symUnbiasedOut
symmetric unbiased outputs:unbiasedOutputs基準と同じ方法でSSEを計算しますが,今回はすべての予測変数を使用します。
unbiasedCoef
unbiased Coefficients:訓練データを使って計算された係数とテストデータを使って計算された係数の差の二乗和です。
absoluteNoiseImmun
absolute noise immunity:データセット全体で訓練したモデルの予測値から、テストデータセットに適用したときの訓練データセットで訓練したモデルの予測値を引いたものと、テストデータセットで訓練したモデルの予測値から、テストデータセットに適用したときの訓練データセットで訓練したモデルの予測値を引いたものとのドット積として計算されます。
symAbsoluteNoiseImmun
symmetric absolute noise immunity:データセット全体で訓練されたモデルの予測値から、学習データセットに適用されたときの訓練データセットで訓練されたモデルの予測値を引いたものと、データセット全体で学習されたモデルの予測値と、すべての観測に適用されたときのテストデータセットで訓練されたモデルの予測値のドット積です。

列挙の後には、4つのカスタム構造体が続きます。

  • BufferValues:係数と、テストデータセットと訓練データセットの両方を使用してさまざまな方法で計算された予測値を格納するために使用されるベクトルの構造体
    //+-------------------------------------------------------------------------------------+
    //| Structure for storing coefficients and predicted values calculated in different ways|
    //+--------------------------------------------------------------------------------------+
    struct BufferValues
      {
       vector            coeffsTrain; // Coefficients vector calculated using training data
       vector            coeffsTest; // Coefficients vector calculated using testing data
       vector            coeffsAll; // Coefficients vector calculated using learning data
       vector            yPredTrainByTrain; // Predicted values for *training* data calculated using coefficients vector calculated on *training* data
       vector            yPredTrainByTest; // Predicted values for *training* data calculated using coefficients vector calculated on *testing* data
       vector            yPredTestByTrain; // Predicted values for *testing* data calculated using coefficients vector calculated on *training* data
       vector            yPredTestByTest; //Predicted values for *testing* data calculated using coefficients vector calculated on *testing* data
    
                         BufferValues(void)
         {
    
         }
    
                         BufferValues(BufferValues &other)
         {
          coeffsTrain = other.coeffsTrain;
          coeffsTest =  other.coeffsTest;
          coeffsAll = other.coeffsAll;
          yPredTrainByTrain = other.yPredTrainByTrain;
          yPredTrainByTest = other.yPredTrainByTest;
          yPredTestByTrain = other.yPredTestByTrain;
          yPredTestByTest = other.yPredTestByTest;
         }
    
       BufferValues      operator=(BufferValues &other)
         {
          coeffsTrain = other.coeffsTrain;
          coeffsTest =  other.coeffsTest;
          coeffsAll = other.coeffsAll;
          yPredTrainByTrain = other.yPredTrainByTrain;
          yPredTrainByTest = other.yPredTrainByTest;
          yPredTestByTrain = other.yPredTestByTrain;
          yPredTestByTest = other.yPredTestByTest;
    
          return this;
         }
    
      };

  • PairDVXd:スカラーと対応するベクトルを組み合わせたデータ構造をカプセル化する
    //+------------------------------------------------------------------+
    //|  struct PairDV                                                   |
    //+------------------------------------------------------------------+
    struct PairDVXd
      {
       double            first;
       vector            second;
    
                         PairDVXd(void)
         {
          first = 0.0;
          second = vector::Zeros(10);
         }
    
                         PairDVXd(double &_f, vector &_s)
         {
          first = _f;
          second.Copy(_s);
         }
    
                         PairDVXd(PairDVXd &other)
         {
          first = other.first;
          second = other.second;
         }
    
       PairDVXd          operator=(PairDVXd& other)
         {
          first = other.first;
          second = other.second;
    
          return this;
         }
      };

  • PairMVXd:行列とベクトルを組み合わせた構造体。これらには入力とそれに対応する出力または目標値が格納されます。入力は行列に格納され、ベクトルは出力の集まりです。行列の各行はベクトルの値に対応します。
    //+------------------------------------------------------------------+
    //| structure PairMVXd                                               |
    //+------------------------------------------------------------------+
    struct PairMVXd
      {
       matrix            first;
       vector            second;
    
                         PairMVXd(void)
         {
          first = matrix::Zeros(10,10);
          second = vector::Zeros(10);
         }
    
                         PairMVXd(matrix &_f,  vector& _s)
         {
          first = _f;
          second = _s;
         }
    
                         PairMVXd(PairMVXd &other)
         {
          first = other.first;
          second = other.second;
         }
    
       PairMVXd          operator=(PairMVXd &other)
         {
          first = other.first;
          second = other.second;
    
          return this;
         }
      };
  • SplittedData:訓練用とテスト用に分割されたデータセットが格納される構造体
    //+------------------------------------------------------------------+
    //|  Structure for storing parts of a split dataset                  |
    //+------------------------------------------------------------------+
    struct SplittedData
      {
       matrix            xTrain;
       matrix            xTest;
       vector            yTrain;
       vector            yTest;
    
                         SplittedData(void)
         {
          xTrain = matrix::Zeros(10,10);
          xTest = matrix::Zeros(10,10);
          yTrain = vector::Zeros(10);
          yTest = vector::Zeros(10);
         }
    
                         SplittedData(SplittedData &other)
         {
          xTrain = other.xTrain;
          xTest =  other.xTest;
          yTrain = other.yTrain;
          yTest =  other.yTest;
         }
    
       SplittedData      operator=(SplittedData &other)
         {
          xTrain = other.xTrain;
          xTest =  other.xTest;
          yTrain = other.yTrain;
          yTest =  other.yTest;
    
          return this;
         }
      };

構造体の次はクラス定義です。

  • Combinationクラスは候補モデルを表します。モデルの評価基準、入力の組み合わせ、計算された係数が格納されます。
    //+------------------------------------------------------------------+
    //| Сlass representing the candidate model of the GMDH algorithm     |
    //+------------------------------------------------------------------+
    class Combination
      {
       vector            _combination,_bestCoeffs;
       double            _evaluation;
    public:
                         Combination(void) { _combination = vector::Zeros(10); _bestCoeffs.Copy(_combination); _evaluation = DBL_MAX; }
                         Combination(vector &comb) : _combination(comb) { _bestCoeffs=vector::Zeros(_combination.Size()); _evaluation = DBL_MAX;}
                         Combination(vector &comb, vector &coeffs) : _combination(comb),_bestCoeffs(coeffs) { _evaluation = DBL_MAX; }
                         Combination(Combination &other) { _combination = other.combination(); _bestCoeffs=other.bestCoeffs(); _evaluation = other.evaluation();}
       vector            combination(void) { return _combination;}
       vector            bestCoeffs(void)  { return _bestCoeffs; }
       double            evaluation(void)  { return _evaluation; }
    
       void              setCombination(vector &combination) { _combination = combination; }
       void              setBestCoeffs(vector &bestcoeffs) { _bestCoeffs = bestcoeffs; }
       void              setEvaluation(double evaluation)  { _evaluation = evaluation; }
    
       bool              operator<(Combination &combi) { return _evaluation<combi.evaluation();}
       Combination       operator=(Combination &combi)
         {
          _combination = combi.combination();
          _bestCoeffs = combi.bestCoeffs();
          _evaluation = combi.evaluation();
    
          return this;
         }
      };
  • CVector:組み合わせインスタンスのコレクションを格納する,カスタムベクトルのようなコンテナを定義します。候補モデルのコンテナにします。
    //+------------------------------------------------------------------+
    //| collection of Combination instances                              |
    //+------------------------------------------------------------------+
    class CVector
      {
    protected:
       Combination       m_array[];
       int               m_size;
       int               m_reserve;
    public:
       //+------------------------------------------------------------------+
       //| default constructor                                              |
       //+------------------------------------------------------------------+
                         CVector(void) :m_size(0),m_reserve(1000) { }
       //+------------------------------------------------------------------+
       //| parametric constructor specifying initial size                   |
       //+------------------------------------------------------------------+
                         CVector(int size, int mem_reserve = 1000) :m_size(size),m_reserve(mem_reserve)
         {
          ArrayResize(m_array,m_size,m_reserve);
         }
       //+------------------------------------------------------------------+
       //| Copy constructor                                                 |
       //+------------------------------------------------------------------+
                         CVector(CVector &other)
         {
          m_size = other.size();
          m_reserve = other.reserve();
    
          ArrayResize(m_array,m_size,m_reserve);
    
          for(int i=0; i<m_size; ++i)
             m_array[i]=other[i];
         }
    
    
       //+------------------------------------------------------------------+
       //| destructor                                                       |
       //+------------------------------------------------------------------+
                        ~CVector(void)
         {
    
         }
       //+------------------------------------------------------------------+
       //| Add element to end of array                                      |
       //+------------------------------------------------------------------+
       bool              push_back(Combination &value)
         {
          ResetLastError();
    
          if(ArrayResize(m_array,int(m_array.Size()+1),m_reserve)<m_size+1)
            {
             Print(__FUNCTION__," Critical error: failed to resize underlying array ", GetLastError());
             return false;
            }
    
          m_array[m_size++]=value;
    
          return true;
         }
       //+------------------------------------------------------------------+
       //| set value at specified index                                     |
       //+------------------------------------------------------------------+
       bool              setAt(int index, Combination &value)
         {
          ResetLastError();
    
          if(index < 0 || index >= m_size)
            {
             Print(__FUNCTION__," index out of bounds ");
             return false;
            }
    
          m_array[index]=value;
    
          return true;
    
         }
       //+------------------------------------------------------------------+
       //|access by index                                                   |
       //+------------------------------------------------------------------+
    
       Combination*      operator[](int index)
         {
          return GetPointer(m_array[uint(index)]);
         }
    
       //+------------------------------------------------------------------+
       //|overload assignment operator                                      |
       //+------------------------------------------------------------------+
    
       CVector           operator=(CVector &other)
         {
          clear();
    
          m_size = other.size();
          m_reserve = other.reserve();
    
          ArrayResize(m_array,m_size,m_reserve);
    
          for(int i=0; i<m_size; ++i)
             m_array[i]= other[i];
    
    
          return this;
         }
       //+------------------------------------------------------------------+
       //|access last element                                               |
       //+------------------------------------------------------------------+
    
       Combination*      back(void)
         {
          return GetPointer(m_array[m_size-1]);
         }
       //+-------------------------------------------------------------------+
       //|access by first index                                             |
       //+------------------------------------------------------------------+
    
       Combination*      front(void)
         {
          return GetPointer(m_array[0]);
         }
       //+------------------------------------------------------------------+
       //| Get current size of collection ,the number of elements           |
       //+------------------------------------------------------------------+
    
       int               size(void)
         {
          return ArraySize(m_array);
         }
       //+------------------------------------------------------------------+
       //|Get the reserved memory size                                      |
       //+------------------------------------------------------------------+
       int               reserve(void)
         {
          return m_reserve;
         }
       //+------------------------------------------------------------------+
       //|set the reserved memory size                                      |
       //+------------------------------------------------------------------+
       void              reserve(int new_reserve)
         {
          if(new_reserve > 0)
             m_reserve = new_reserve;
         }
       //+------------------------------------------------------------------+
       //| clear                                                            |
       //+------------------------------------------------------------------+
       void              clear(void)
         {
          ArrayFree(m_array);
    
          m_size = 0;
         }
    
      };
  • CVector2d:別のカスタムベクトルのようななコンテナで, CVectorインスタンスのコレクションを保存します。
    //+------------------------------------------------------------------+
    //| Collection of CVector instances                                  |
    //+------------------------------------------------------------------+
    class CVector2d
      {
    protected:
       CVector           m_array[];
       int               m_size;
       int               m_reserve;
    public:
       //+------------------------------------------------------------------+
       //| default constructor                                              |
       //+------------------------------------------------------------------+
                         CVector2d(void) :m_size(0),m_reserve(1000) { }
       //+------------------------------------------------------------------+
       //| parametric constructor specifying initial size                   |
       //+------------------------------------------------------------------+
                         CVector2d(int size, int mem_reserve = 1000) :m_size(size),m_reserve(mem_reserve)
         {
          ArrayResize(m_array,m_size,m_reserve);
         }
       //+------------------------------------------------------------------+
       //| Copy constructor                                                 |
       //+------------------------------------------------------------------+
                         CVector2d(CVector2d &other)
         {
          m_size = other.size();
          m_reserve = other.reserve();
    
          ArrayResize(m_array,m_size,m_reserve);
    
          for(int i=0; i<m_size; ++i)
             m_array[i]= other[i];
         }
    
    
       //+------------------------------------------------------------------+
       //| destructor                                                       |
       //+------------------------------------------------------------------+
                        ~CVector2d(void)
         {
    
         }
       //+------------------------------------------------------------------+
       //| Add element to end of array                                      |
       //+------------------------------------------------------------------+
       bool              push_back(CVector &value)
         {
          ResetLastError();
    
          if(ArrayResize(m_array,int(m_array.Size()+1),m_reserve)<m_size+1)
            {
             Print(__FUNCTION__," Critical error: failed to resize underlying array ", GetLastError());
             return false;
            }
    
          m_array[m_size++]=value;
    
          return true;
         }
       //+------------------------------------------------------------------+
       //| set value at specified index                                     |
       //+------------------------------------------------------------------+
       bool              setAt(int index, CVector &value)
         {
          ResetLastError();
    
          if(index < 0 || index >= m_size)
            {
             Print(__FUNCTION__," index out of bounds ");
             return false;
            }
    
          m_array[index]=value;
    
          return true;
    
         }
       //+------------------------------------------------------------------+
       //|access by index                                                   |
       //+------------------------------------------------------------------+
    
       CVector*          operator[](int index)
         {
          return GetPointer(m_array[uint(index)]);
         }
    
       //+------------------------------------------------------------------+
       //|overload assignment operator                                      |
       //+------------------------------------------------------------------+
    
       CVector2d         operator=(CVector2d &other)
         {
          clear();
    
          m_size = other.size();
          m_reserve = other.reserve();
    
          ArrayResize(m_array,m_size,m_reserve);
    
          for(int i=0; i<m_size; ++i)
             m_array[i]= other[i];
    
          return this;
         }
       //+------------------------------------------------------------------+
       //|access last element                                               |
       //+------------------------------------------------------------------+
    
       CVector*          back(void)
         {
          return GetPointer(m_array[m_size-1]);
         }
       //+-------------------------------------------------------------------+
       //|access by first index                                             |
       //+------------------------------------------------------------------+
    
       CVector*          front(void)
         {
          return GetPointer(m_array[0]);
         }
       //+------------------------------------------------------------------+
       //| Get current size of collection ,the number of elements           |
       //+------------------------------------------------------------------+
    
       int               size(void)
         {
          return ArraySize(m_array);
         }
       //+------------------------------------------------------------------+
       //|Get the reserved memory size                                      |
       //+------------------------------------------------------------------+
       int               reserve(void)
         {
          return m_reserve;
         }
       //+------------------------------------------------------------------+
       //|set the reserved memory size                                      |
       //+------------------------------------------------------------------+
       void              reserve(int new_reserve)
         {
          if(new_reserve > 0)
             m_reserve = new_reserve;
         }
       //+------------------------------------------------------------------+
       //| clear                                                            |
       //+------------------------------------------------------------------+
       void              clear(void)
         {
    
          for(uint i = 0; i<m_array.Size(); i++)
             m_array[i].clear();
    
          ArrayFree(m_array);
    
          m_size = 0;
         }
    
      };
  • Criterion:このクラスは、選択された基準タイプに基づいて、さまざまな外部基準の計算を実装します。
    //+---------------------------------------------------------------------------------+
    //|Class that implements calculations of internal and individual external criterions|
    //+---------------------------------------------------------------------------------+
    class Criterion
      {
    protected:
       CriterionType     criterionType; // Selected CriterionType object
       Solver            solver; // Selected Solver object
    
    public:
       /**
       Implements the internal criterion calculation
       param xTrain Matrix of input variables that should be used to calculate the model coefficients
       param yTrain Target values vector for the corresponding xTrain parameter
       return Coefficients vector representing a solution of the linear equations system constructed from the parameters data
       */
       vector            findBestCoeffs(matrix& xTrain,  vector& yTrain)
         {
          vector solution;
    
          matrix q,r;
    
          xTrain.QR(q,r);
    
          matrix qT = q.Transpose();
    
          vector y = qT.MatMul(yTrain);
    
          solution = r.LstSq(y);
    
    
          return solution;
         }
    
       /**
        Calculate the value of the selected external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param _criterionType Selected external criterion type
       param bufferValues Temporary storage for calculated coefficients and target values
       return The value of external criterion and calculated model coefficients
        */
       PairDVXd          getResult(matrix& xTrain,  matrix& xTest,  vector& yTrain,  vector& yTest,
                                   CriterionType _criterionType, BufferValues& bufferValues)
         {
          switch(_criterionType)
            {
             case reg:
                return regularity(xTrain, xTest, yTrain, yTest, bufferValues);
             case symReg:
                return symRegularity(xTrain, xTest, yTrain, yTest, bufferValues);
             case stab:
                return stability(xTrain, xTest, yTrain, yTest, bufferValues);
             case symStab:
                return symStability(xTrain, xTest, yTrain, yTest, bufferValues);
             case unbiasedOut:
                return unbiasedOutputs(xTrain, xTest, yTrain, yTest, bufferValues);
             case symUnbiasedOut:
                return symUnbiasedOutputs(xTrain, xTest, yTrain, yTest, bufferValues);
             case unbiasedCoef:
                return unbiasedCoeffs(xTrain, xTest, yTrain, yTest, bufferValues);
             case absoluteNoiseImmun:
                return absoluteNoiseImmunity(xTrain, xTest, yTrain, yTest, bufferValues);
             case symAbsoluteNoiseImmun:
                return symAbsoluteNoiseImmunity(xTrain, xTest, yTrain, yTest, bufferValues);
            }
    
          PairDVXd pd;
          return pd;
         }
       /**
        Calculate the regularity external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param bufferValues Temporary storage for calculated coefficients and target values
       param inverseSplit True, if it is necessary to swap the roles of training and testing data, otherwise false
       return The value of the regularity external criterion and calculated model coefficients
        */
       PairDVXd          regularity(matrix& xTrain, matrix& xTest, vector &yTrain, vector& yTest,
                                    BufferValues& bufferValues, bool inverseSplit = false)
         {
          PairDVXd pdv;
          vector f;
          if(!inverseSplit)
            {
             if(bufferValues.coeffsTrain.Size() == 0)
                bufferValues.coeffsTrain = findBestCoeffs(xTrain, yTrain);
    
             if(bufferValues.yPredTestByTrain.Size() == 0)
                bufferValues.yPredTestByTrain = xTest.MatMul(bufferValues.coeffsTrain);
    
             f = MathPow((yTest - bufferValues.yPredTestByTrain),2.0);
             pdv.first = f.Sum();
             pdv.second = bufferValues.coeffsTrain;
            }
          else
            {
             if(bufferValues.coeffsTest.Size() == 0)
                bufferValues.coeffsTest = findBestCoeffs(xTest, yTest);
    
             if(bufferValues.yPredTrainByTest.Size() == 0)
                bufferValues.yPredTrainByTest = xTrain.MatMul(bufferValues.coeffsTest);
    
             f = MathPow((yTrain - bufferValues.yPredTrainByTest),2.0);
             pdv.first = f.Sum();
             pdv.second = bufferValues.coeffsTest;
            }
    
          return pdv;
         }
       /**
        Calculate the symmetric regularity external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param bufferValues Temporary storage for calculated coefficients and target values
       return The value of the symmertic regularity external criterion and calculated model coefficients
        */
       PairDVXd          symRegularity(matrix& xTrain, matrix& xTest, vector& yTrain, vector& yTest,
                                       BufferValues& bufferValues)
         {
          PairDVXd pdv1,pdv2,pdsum;
    
          pdv1 = regularity(xTrain,xTest,yTrain,yTest,bufferValues);
          pdv2 = regularity(xTrain,xTest,yTrain,yTest,bufferValues,true);
    
          pdsum.first = pdv1.first+pdv2.first;
          pdsum.second = pdv1.second;
    
          return pdsum;
         }
    
       /**
        Calculate the stability external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param bufferValues Temporary storage for calculated coefficients and target values
       param inverseSplit True, if it is necessary to swap the roles of training and testing data, otherwise false
       return The value of the stability external criterion and calculated model coefficients
        */
       PairDVXd          stability(matrix& xTrain,  matrix& xTest,  vector& yTrain,  vector& yTest,
                                   BufferValues& bufferValues, bool inverseSplit = false)
         {
          PairDVXd pdv;
          vector f1,f2;
          if(!inverseSplit)
            {
             if(bufferValues.coeffsTrain.Size() == 0)
                bufferValues.coeffsTrain = findBestCoeffs(xTrain, yTrain);
    
             if(bufferValues.yPredTrainByTrain.Size() == 0)
                bufferValues.yPredTrainByTrain = xTrain.MatMul(bufferValues.coeffsTrain);
    
             if(bufferValues.yPredTestByTrain.Size() == 0)
                bufferValues.yPredTestByTrain = xTest.MatMul(bufferValues.coeffsTrain);
    
             f1 = MathPow((yTrain - bufferValues.yPredTrainByTrain),2.0);
             f2 = MathPow((yTest - bufferValues.yPredTestByTrain),2.0);
    
             pdv.first = f1.Sum()+f2.Sum();
             pdv.second = bufferValues.coeffsTrain;
            }
          else
            {
             if(bufferValues.coeffsTest.Size() == 0)
                bufferValues.coeffsTest = findBestCoeffs(xTest, yTest);
    
             if(bufferValues.yPredTrainByTest.Size() == 0)
                bufferValues.yPredTrainByTest = xTrain.MatMul(bufferValues.coeffsTest);
    
             if(bufferValues.yPredTestByTest.Size() == 0)
                bufferValues.yPredTestByTest = xTest.MatMul(bufferValues.coeffsTest);
    
             f1 = MathPow((yTrain - bufferValues.yPredTrainByTest),2.0);
             f2 = MathPow((yTest - bufferValues.yPredTestByTest),2.0);
             pdv.first = f1.Sum() + f2.Sum();
             pdv.second = bufferValues.coeffsTest;
            }
    
          return pdv;
         }
    
       /**
        Calculate the symmetric stability external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param bufferValues Temporary storage for calculated coefficients and target values
       return The value of the symmertic stability external criterion and calculated model coefficients
        */
       PairDVXd          symStability(matrix& xTrain,  matrix& xTest,  vector& yTrain,  vector& yTest,
                                      BufferValues& bufferValues)
         {
          PairDVXd pdv1,pdv2,pdsum;
    
          pdv1 = stability(xTrain, xTest, yTrain, yTest, bufferValues);
          pdv2 = stability(xTrain, xTest, yTrain, yTest, bufferValues, true);
    
          pdsum.first=pdv1.first+pdv2.first;
          pdsum.second = pdv1.second;
    
          return pdsum;
         }
    
       /**
        Calculate the unbiased outputs external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param bufferValues Temporary storage for calculated coefficients and target values
       return The value of the unbiased outputs external criterion and calculated model coefficients
        */
       PairDVXd          unbiasedOutputs(matrix& xTrain,  matrix& xTest,  vector& yTrain,  vector& yTest,
                                         BufferValues& bufferValues)
         {
          PairDVXd pdv;
          vector f;
    
          if(bufferValues.coeffsTrain.Size() == 0)
             bufferValues.coeffsTrain = findBestCoeffs(xTrain, yTrain);
    
          if(bufferValues.coeffsTest.Size() == 0)
             bufferValues.coeffsTest = findBestCoeffs(xTest, yTest);
    
          if(bufferValues.yPredTestByTrain.Size() == 0)
             bufferValues.yPredTestByTrain = xTest.MatMul(bufferValues.coeffsTrain);
    
          if(bufferValues.yPredTestByTest.Size() == 0)
             bufferValues.yPredTestByTest = xTest.MatMul(bufferValues.coeffsTest);
    
          f = MathPow((bufferValues.yPredTestByTrain - bufferValues.yPredTestByTest),2.0);
          pdv.first = f.Sum();
          pdv.second = bufferValues.coeffsTrain;
    
          return pdv;
         }
    
       /**
        Calculate the symmetric unbiased outputs external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param bufferValues Temporary storage for calculated coefficients and target values
       return The value of the symmetric unbiased outputs external criterion and calculated model coefficients
        */
       PairDVXd          symUnbiasedOutputs(matrix &xTrain,  matrix &xTest,  vector &yTrain,  vector& yTest,BufferValues& bufferValues)
         {
          PairDVXd pdv;
          vector f1,f2;
    
          if(bufferValues.coeffsTrain.Size() == 0)
             bufferValues.coeffsTrain = findBestCoeffs(xTrain, yTrain);
          if(bufferValues.coeffsTest.Size() == 0)
             bufferValues.coeffsTest = findBestCoeffs(xTest, yTest);
          if(bufferValues.yPredTrainByTrain.Size() == 0)
             bufferValues.yPredTrainByTrain = xTrain.MatMul(bufferValues.coeffsTrain);
          if(bufferValues.yPredTrainByTest.Size() == 0)
             bufferValues.yPredTrainByTest = xTrain.MatMul(bufferValues.coeffsTest);
          if(bufferValues.yPredTestByTrain.Size() == 0)
             bufferValues.yPredTestByTrain = xTest.MatMul(bufferValues.coeffsTrain);
          if(bufferValues.yPredTestByTest.Size() == 0)
             bufferValues.yPredTestByTest = xTest.MatMul(bufferValues.coeffsTest);
    
          f1 = MathPow((bufferValues.yPredTrainByTrain - bufferValues.yPredTrainByTest),2.0);
          f2 = MathPow((bufferValues.yPredTestByTrain - bufferValues.yPredTestByTest),2.0);
          pdv.first = f1.Sum() + f2.Sum();
          pdv.second = bufferValues.coeffsTrain;
    
          return pdv;
         }
    
       /**
        Calculate the unbiased coefficients external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param bufferValues Temporary storage for calculated coefficients and target values
       return The value of the unbiased coefficients external criterion and calculated model coefficients
        */
       PairDVXd          unbiasedCoeffs(matrix& xTrain,  matrix& xTest,  vector& yTrain,  vector& yTest,BufferValues& bufferValues)
         {
          PairDVXd pdv;
          vector f1;
    
          if(bufferValues.coeffsTrain.Size() == 0)
             bufferValues.coeffsTrain = findBestCoeffs(xTrain, yTrain);
    
          if(bufferValues.coeffsTest.Size() == 0)
             bufferValues.coeffsTest = findBestCoeffs(xTest, yTest);
    
          f1 = MathPow((bufferValues.coeffsTrain - bufferValues.coeffsTest),2.0);
          pdv.first = f1.Sum();
          pdv.second = bufferValues.coeffsTrain;
    
          return pdv;
         }
    
       /**
        Calculate the absolute noise immunity external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param bufferValues Temporary storage for calculated coefficients and target values
       return The value of the absolute noise immunity external criterion and calculated model coefficients
        */
       PairDVXd          absoluteNoiseImmunity(matrix& xTrain,  matrix& xTest,  vector& yTrain,  vector& yTest,BufferValues& bufferValues)
         {
          vector yPredTestByAll,f1,f2;
          PairDVXd pdv;
    
          if(bufferValues.coeffsTrain.Size() == 0)
             bufferValues.coeffsTrain = findBestCoeffs(xTrain, yTrain);
    
          if(bufferValues.coeffsTest.Size() == 0)
             bufferValues.coeffsTest = findBestCoeffs(xTest, yTest);
    
          if(bufferValues.coeffsAll.Size() == 0)
            {
             matrix dataX(xTrain.Rows() + xTest.Rows(), xTrain.Cols());
    
             for(ulong i = 0; i<xTrain.Rows(); i++)
                dataX.Row(xTrain.Row(i),i);
    
             for(ulong i = 0; i<xTest.Rows(); i++)
                dataX.Row(xTest.Row(i),i+xTrain.Rows());
    
             vector dataY(yTrain.Size() + yTest.Size());
    
             for(ulong i=0; i<yTrain.Size(); i++)
                dataY[i] = yTrain[i];
    
             for(ulong i=0; i<yTest.Size(); i++)
                dataY[i+yTrain.Size()] = yTest[i];
    
             bufferValues.coeffsAll = findBestCoeffs(dataX, dataY);
            }
    
          if(bufferValues.yPredTestByTrain.Size() == 0)
             bufferValues.yPredTestByTrain = xTest.MatMul(bufferValues.coeffsTrain);
    
          if(bufferValues.yPredTestByTest.Size() == 0)
             bufferValues.yPredTestByTest = xTest.MatMul(bufferValues.coeffsTest);
    
          yPredTestByAll = xTest.MatMul(bufferValues.coeffsAll);
    
          f1 =  yPredTestByAll - bufferValues.yPredTestByTrain;
          f2 = bufferValues.yPredTestByTest - yPredTestByAll;
    
          pdv.first = f1.Dot(f2);
          pdv.second = bufferValues.coeffsTrain;
    
          return pdv;
         }
    
       /**
        Calculate the symmetric absolute noise immunity external criterion for the given data
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       param bufferValues Temporary storage for calculated coefficients and target values
       return The value of the symmetric absolute noise immunity external criterion and calculated model coefficients
        */
       PairDVXd          symAbsoluteNoiseImmunity(matrix& xTrain,  matrix& xTest,  vector& yTrain,  vector& yTest,BufferValues& bufferValues)
         {
          PairDVXd pdv;
          vector yPredAllByTrain, yPredAllByTest, yPredAllByAll,f1,f2;
          matrix dataX(xTrain.Rows() + xTest.Rows(), xTrain.Cols());
    
          for(ulong i = 0; i<xTrain.Rows(); i++)
             dataX.Row(xTrain.Row(i),i);
    
          for(ulong i = 0; i<xTest.Rows(); i++)
             dataX.Row(xTest.Row(i),i+xTrain.Rows());
    
          vector dataY(yTrain.Size() + yTest.Size());
    
          for(ulong i=0; i<yTrain.Size(); i++)
             dataY[i] = yTrain[i];
    
          for(ulong i=0; i<yTest.Size(); i++)
             dataY[i+yTrain.Size()] = yTest[i];
    
          if(bufferValues.coeffsTrain.Size() == 0)
             bufferValues.coeffsTrain = findBestCoeffs(xTrain, yTrain);
    
          if(bufferValues.coeffsTest.Size() == 0)
             bufferValues.coeffsTest = findBestCoeffs(xTest, yTest);
    
          if(bufferValues.coeffsAll.Size() == 0)
             bufferValues.coeffsAll = findBestCoeffs(dataX, dataY);
    
          yPredAllByTrain = dataX.MatMul(bufferValues.coeffsTrain);
          yPredAllByTest = dataX.MatMul(bufferValues.coeffsTest);
          yPredAllByAll = dataX.MatMul(bufferValues.coeffsAll);
    
          f1 = yPredAllByAll - yPredAllByTrain;
          f2 = yPredAllByTest - yPredAllByAll;
    
          pdv.first = f1.Dot(f2);
          pdv.second = bufferValues.coeffsTrain;
    
          return pdv;
    
         }
    
       /**
        Get k models from the given ones with the best values of the external criterion
       param combinations Vector of the trained models
       param data Object containing parts of a split dataset used in model training. Parameter is used in sequential criterion
       param func Function returning the new X train and X test data constructed from the original data using given combination of input variables column indexes. Parameter is used in sequential criterion
       param k Number of best models
       return Vector containing k best models
        */
       virtual void      getBestCombinations(CVector &combinations, CVector &bestCombo,SplittedData& data, MatFunc func, int k)
         {
          double proxys[];
          int best[];
    
          ArrayResize(best,combinations.size());
          ArrayResize(proxys,combinations.size());
    
          for(int i = 0 ; i<combinations.size(); i++)
            {
             proxys[i] = combinations[i].evaluation();
             best[i] = i;
            }
    
          MathQuickSortAscending(proxys,best,0,combinations.size()-1);
    
          for(int i = 0; i<int(MathMin(MathAbs(k),combinations.size())); i++)
             bestCombo.push_back(combinations[best[i]]);
    
         }
       /**
        Calculate the value of the selected external criterion for the given data.
        For the individual criterion this method only calls the getResult() method
       param xTrain Input variables matrix of the training data
       param xTest Input variables matrix of the testing data
       param yTrain Target values vector of the training data
       param yTest Target values vector of the testing data
       return The value of the external criterion and calculated model coefficients
        */
       virtual PairDVXd  calculate(matrix& xTrain,  matrix& xTest,
                                   vector& yTrain,  vector& yTest)
         {
          BufferValues tempValues;
          return getResult(xTrain, xTest, yTrain, yTest, criterionType, tempValues);
         }
    
    public:
       ///  Construct a new Criterion object
                         Criterion() {};
    
       /**
        Construct a new Criterion object
       param _criterionType Selected external criterion type
       param _solver Selected method for linear equations solving
        */
                         Criterion(CriterionType _criterionType)
         {
          criterionType = _criterionType;
          solver = balanced;
         }
    
      };
    

最後に、gmdh_internal.mqhの最後を飾る2つの関数があります。

validateInputData():クラスメソッドやその他のスタンドアロン関数に渡される値が正しく指定されていることを確認するために使用します。

**
 *  Validate input parameters values
 *
 * param testSize Fraction of the input data that should be placed into the second part
 * param pAverage The number of best models based of which the external criterion for each level will be calculated
 * param threads The number of threads used for calculations. Set -1 to use max possible threads
 * param verbose 1 if the printing detailed infomation about training process is needed, otherwise 0
 * param limit The minimum value by which the external criterion should be improved in order to continue training
 * param kBest The number of best models based of which new models of the next level will be constructed
 * return Method exit status
 */
int validateInputData(double testSize=0.0, int pAverage=0, double limit=0.0, int kBest=0)
  {
   int errorCode = 0;
//
   if(testSize <= 0 || testSize >= 1)
     {
      Print("testsize value must be in the (0, 1) range");
      errorCode |= 1;
     }
   if(pAverage && pAverage < 1)
     {
      Print("p_average value must be a positive integer");
      errorCode |= 4;
     }
   if(limit && limit < 0)
     {
      Print("limit value must be non-negative");
      errorCode |= 8;
     }
   if(kBest && kBest < 1)
     {
      Print("k_best value must be a positive integer");
      errorCode |= 16;
     }

   return errorCode;
  }

timeSeriesTransformation():ベクトル内の系列を入力とし、選択されたラグ数に従って入力と目標のデータ構造に変換するユーティリティ関数です。

/**
 *  Convert the time series vector to the 2D matrix format required to work with GMDH algorithms
 *
 * param timeSeries Vector of time series data
 * param lags The lags (length) of subsets of time series into which the original time series should be divided
 * return Transformed time series data
 */
PairMVXd timeSeriesTransformation(vector& timeSeries, int lags)
  {
   PairMVXd p;

   string errorMsg = "";
   if(timeSeries.Size() == 0)
      errorMsg = "time_series value is empty";
   else
      if(lags <= 0)
         errorMsg = "lags value must be a positive integer";
      else
         if(lags >= int(timeSeries.Size()))
            errorMsg = "lags value can't be greater than  time_series  size";
   if(errorMsg != "")
      return p;

   ulong last = timeSeries.Size() - ulong(lags);
   vector yTimeSeries(last,slice,timeSeries,ulong(lags));
   matrix xTimeSeries(last, ulong(lags));
   vector vect;
   for(ulong i = 0; i < last; ++i)
     {
      vect.Init(ulong(lags),slice,timeSeries,i,i+ulong(lags-1));
      xTimeSeries.Row(vect,i);
     }

   p.first = xTimeSeries;
   p.second = yTimeSeries;

   return p;
  }

ここでラグとは、後続項を計算するための予測値として使用される直前の系列値の数を指します。

これでgmdh_internal.mqhの説明は終わりです。2つ目のヘッダーファイル、gmdh.mqhに移ります。

これは、splitData()関数の定義から始まります。

/**
 *  Divide the input data into 2 parts
 *
 * param x Matrix of input data containing predictive variables
 * param y Vector of the taget values for the corresponding x data
 * param testSize Fraction of the input data that should be placed into the second part
 * param shuffle True if data should be shuffled before splitting into 2 parts, otherwise false
 * param randomSeed Seed number for the random generator to get the same division every time
 * return SplittedData object containing 4 elements of data: train x, train y, test x, test y
 */
SplittedData splitData(matrix& x,  vector& y, double testSize = 0.2, bool shuffle = false, int randomSeed = 0)
  {
   SplittedData data;

   if(validateInputData(testSize))
      return data;
   
   string errorMsg = "";
   if(x.Rows() != y.Size())
      errorMsg = " x rows number and y size must be equal";
   else
      if(round(x.Rows() * testSize) == 0 || round(x.Rows() * testSize) == x.Rows())
         errorMsg = "Result contains an empty array. Change the arrays size or the  value for correct splitting";
   if(errorMsg != "")
     {
      Print(__FUNCTION__," ",errorMsg);
      return data;
     }


   if(!shuffle)
      data = GmdhModel::internalSplitData(x, y, testSize);
   else
     {
      if(randomSeed == 0)
         randomSeed = int(GetTickCount64());
      MathSrand(uint(randomSeed));

      int shuffled_rows_indexes[],shuffled[];
      MathSequence(0,int(x.Rows()-1),1,shuffled_rows_indexes);
      MathSample(shuffled_rows_indexes,int(shuffled_rows_indexes.Size()),shuffled);

      int testItemsNumber = (int)round(x.Rows() * testSize);


      matrix Train,Test;
      vector train,test;

      Train.Resize(x.Rows()-ulong(testItemsNumber),x.Cols());
      Test.Resize(ulong(testItemsNumber),x.Cols());

      train.Resize(x.Rows()-ulong(testItemsNumber));
      test.Resize(ulong(testItemsNumber));

      for(ulong i = 0; i<Train.Rows(); i++)
        {
         Train.Row(x.Row(shuffled[i]),i);
         train[i] = y[shuffled[i]];
        }

      for(ulong i = 0; i<Test.Rows(); i++)
        {
         Test.Row(x.Row(shuffled[Train.Rows()+i]),i);
         test[i] = y[shuffled[Train.Rows()+i]];
        }

      data.xTrain = Train;
      data.xTest = Test;
      data.yTrain = train;
      data.yTest = test;
     }

   return data;
  }  

testSizeパラメータは,テストセットとして使用するデータセットの割合を定義します。shuffleパラメータは,データセットのランダムシャッフルを有効にし,randomSeedパラメータは,シャッフル処理に使用する乱数生成器のシードを指定します。

次に、GMDHアルゴリズムの一般的なロジックを定義するGmdhModelクラスがあります。

//+------------------------------------------------------------------+
//| Class implementing the general logic of GMDH algorithms          |
//+------------------------------------------------------------------+

class  GmdhModel
  {
protected:

   string            modelName; // model name
   int               level; // Current number of the algorithm training level
   int               inputColsNumber; // The number of predictive variables in the original data
   double            lastLevelEvaluation; // The external criterion value of the previous training level
   double            currentLevelEvaluation; // The external criterion value of the current training level
   bool              training_complete; // flag indicator successful completion of model training
   CVector2d         bestCombinations; // Storage for the best models of previous levels

   /**
    *struct for generating vector sequence
    */
   struct unique
     {
   private:
      int            current;

      int            run(void)
        {
         return ++current;
        }

   public:
                     unique(void)
        {
         current = -1;
        }

      vector         generate(ulong t)
        {
         ulong s=0;
         vector ret(t);

         while(s<t)
            ret[s++] = run();

         return ret;
        }
     };

   /**
    *  Find all combinations of k elements from n
    *
    * param n Number of all elements
    * param k Number of required elements
    * return Vector of all combinations of k elements from n
    */
   void              nChooseK(int n, int k, vector &combos[])
     {
      if(n<=0 || k<=0 || n<k)
        {
         Print(__FUNCTION__," invalid parameters for n and or k", "n ",n , " k ", k);
         return;
        }

      unique q;

      vector comb = q.generate(ulong(k));

      ArrayResize(combos,combos.Size()+1,100);

      long first, last;

      first = 0;
      last = long(k);
      combos[combos.Size()-1]=comb;

      while(comb[first]!= double(n - k))
        {
         long mt = last;
         while(comb[--mt] == double(n - (last - mt)));
         comb[mt]++;
         while(++mt != last)
            comb[mt] = comb[mt-1]+double(1);
         ArrayResize(combos,combos.Size()+1,100);
         combos[combos.Size()-1]=comb;
        }

      for(uint i = 0; i<combos.Size(); i++)
        {
         combos[i].Resize(combos[i].Size()+1);
         combos[i][combos[i].Size()-1] = n;
        }

      return;
     }

   /**
    *  Get the mean value of extrnal criterion of the k best models
    *
    * param sortedCombinations Sorted vector of current level models
    * param k The numebr of the best models
    * return Calculated mean value of extrnal criterion of the k best models
    */
   double            getMeanCriterionValue(CVector &sortedCombinations, int k)
     {
      k = MathMin(k, sortedCombinations.size());

      double crreval=0;

      for(int i = 0; i<k; i++)
         crreval +=sortedCombinations[i].evaluation();
      if(k)
         return crreval/double(k);
      else
        {
         Print(__FUNCTION__, " Zero divide error ");
         return 0.0;
        }
     }

   /**
    *  Get the sign of the polynomial variable coefficient
    *
    * param coeff Selected coefficient
    * param isFirstCoeff True if the selected coefficient will be the first in the polynomial representation, otherwise false
    * return String containing the sign of the coefficient
    */
   string            getPolynomialCoeffSign(double coeff, bool isFirstCoeff)
     {
      return ((coeff >= 0) ? ((isFirstCoeff) ? " " : " + ") : " - ");
     }

   /**
    *  Get the rounded value of the polynomial variable coefficient without sign
    *
    * param coeff Selected coefficient
    * param isLastCoeff True if the selected coefficient will be the last one in the polynomial representation, otherwise false
    * return String containing the rounded value of the coefficient without sign
    */
   string            getPolynomialCoeffValue(double coeff, bool isLastCoeff)
     {
      string stringCoeff = StringFormat("%e",MathAbs(coeff));
      return ((stringCoeff != "1" || isLastCoeff) ? stringCoeff : "");
     }

   /**
    *  Train given subset of models and calculate external criterion for them
    *
    * param data Data used for training and evaulating models
    * param criterion Selected external criterion
    * param beginCoeffsVec Iterator indicating the beginning of a subset of models
    * param endCoeffsVec Iterator indicating the end of a subset of models
    * param leftTasks The number of remaining untrained models at the entire level
    * param verbose 1 if the printing detailed infomation about training process is needed, otherwise 0
    */
   bool              polynomialsEvaluation(SplittedData& data,  Criterion& criterion,  CVector &combos, uint beginCoeffsVec,
                                           uint endCoeffsVec)
     {
      vector cmb,ytrain,ytest;
      matrix x1,x2;
      for(uint i = beginCoeffsVec; i<endCoeffsVec; i++)
        {
         cmb = combos[i].combination();
         x1 = xDataForCombination(data.xTrain,cmb);
         x2 = xDataForCombination(data.xTest,cmb);
         ytrain = data.yTrain;
         ytest = data.yTest;
         PairDVXd pd = criterion.calculate(x1,x2,ytrain,ytest);

         if(pd.second.HasNan()>0)
            {
             Print(__FUNCTION__," No solution found for coefficient at ", i, "\n xTrain \n", x1, "\n xTest \n", x2, "\n yTrain \n", ytrain, "\n yTest \n", ytest);
             combos[i].setEvaluation(DBL_MAX);
             combos[i].setBestCoeffs(vector::Ones(3));
            }
         else
            {
             combos[i].setEvaluation(pd.first);
             combos[i].setBestCoeffs(pd.second);
            } 
        }

      return true;
     }

   /**
   *  Determine the need to continue training and prepare the algorithm for the next level
   *
   * param kBest The number of best models based of which new models of the next level will be constructed
   * param pAverage The number of best models based of which the external criterion for each level will be calculated
   * param combinations Trained models of the current level
   * param criterion Selected external criterion
   * param data Data used for training and evaulating models
   * param limit The minimum value by which the external criterion should be improved in order to continue training
   * return True if the algorithm needs to continue training, otherwise fasle
   */
   bool              nextLevelCondition(int kBest, int pAverage, CVector &combinations,
                                        Criterion& criterion, SplittedData& data, double limit)
     {
      MatFunc fun = NULL;
      CVector bestcombinations;
      criterion.getBestCombinations(combinations,bestcombinations,data, fun, kBest);
      currentLevelEvaluation = getMeanCriterionValue(bestcombinations, pAverage);

      if(lastLevelEvaluation - currentLevelEvaluation > limit)
        {
         lastLevelEvaluation = currentLevelEvaluation;
         if(preparations(data,bestcombinations))
           {
            ++level;
            return true;
           }
        }
      removeExtraCombinations();
      return false;

     }

   /**
    *  Fit the algorithm to find the best solution
    *
    * param x Matrix of input data containing predictive variables
    * param y Vector of the taget values for the corresponding x data
    * param criterion Selected external criterion
    * param kBest The number of best models based of which new models of the next level will be constructed
    * param testSize Fraction of the input data that should be used to evaluate models at each level
    * param pAverage The number of best models based of which the external criterion for each level will be calculated
    * param limit The minimum value by which the external criterion should be improved in order to continue training
    * return A pointer to the algorithm object for which the training was performed
    */
   bool              gmdhFit(matrix& x,  vector& y,  Criterion& criterion, int kBest,
                             double testSize, int pAverage, double limit)
     {
      if(x.Rows() != y.Size())
        {
         Print("X rows number and y size must be equal");
         return false;
        }

      level = 1; // reset last training
      inputColsNumber = int(x.Cols());
      lastLevelEvaluation = DBL_MAX;

      SplittedData data = internalSplitData(x, y, testSize, true) ;
      training_complete = false;
      bool goToTheNextLevel;
      CVector evaluationCoeffsVec;
      do
        {
         vector combinations[];
         generateCombinations(int(data.xTrain.Cols() - 1),combinations);
         
         if(combinations.Size()<1)
           {
            Print(__FUNCTION__," Training aborted");
            return training_complete;
           }  

         evaluationCoeffsVec.clear();

         int currLevelEvaluation = 0;
         for(int it = 0; it < int(combinations.Size()); ++it, ++currLevelEvaluation)
           {
            Combination ncomb(combinations[it]);
            evaluationCoeffsVec.push_back(ncomb);
           }

         if(!polynomialsEvaluation(data,criterion,evaluationCoeffsVec,0,uint(currLevelEvaluation)))
           {
            Print(__FUNCTION__," Training aborted");
            return training_complete;
           }

         goToTheNextLevel = nextLevelCondition(kBest, pAverage, evaluationCoeffsVec, criterion, data, limit); // checking the results of the current level for improvement
        }
      while(goToTheNextLevel);

      training_complete = true;

      return true;
     }

   /**
    *  Get new model structures for the new level of training
    *
    * param n_cols The number of existing predictive variables at the current training level
    * return Vector of new model structures
    */
   virtual void      generateCombinations(int n_cols,vector &out[])
     {
      return;
     }


   ///  Removed the saved models that are no longer needed
   virtual void      removeExtraCombinations(void)
     {
      return;
     }

   /**
    *  Prepare data for the next training level
    *
    * param data Data used for training and evaulating models at the current level
    * param _bestCombinations Vector of the k best models of the current level
    * return True if the training process can be continued, otherwise false
    */
   virtual bool      preparations(SplittedData& data, CVector &_bestCombinations)
     {
      return false;
     }

   /**
    *  Get the data constructed according to the model structure from the original data
    *
    * param x Training data at the current level
    * param comb Vector containing the indexes of the x matrix columns that should be used in the model
    * return Constructed data
    */
   virtual matrix    xDataForCombination(matrix& x,  vector& comb)
     {
      return matrix::Zeros(10,10);
     }

   /**
    *  Get the designation of polynomial equation
    *
    * param levelIndex The number of the level counting from 0
    * param combIndex The number of polynomial in the level counting from 0
    * return The designation of polynomial equation
    */
   virtual string    getPolynomialPrefix(int levelIndex, int combIndex)
     {
      return NULL;
     }

   /**
    *  Get the string representation of the polynomial variable
    *
    * param levelIndex The number of the level counting from 0
    * param coeffIndex The number of the coefficient related to the selected variable in the polynomial counting from 0
    * param coeffsNumber The number of coefficients in the polynomial
    * param bestColsIndexes Indexes of the data columns used to construct polynomial of the model
    * return The string representation of the polynomial variable
    */
   virtual string    getPolynomialVariable(int levelIndex, int coeffIndex, int coeffsNumber,
                                           vector& bestColsIndexes)
     {
      return NULL;
     }

   /*
    *  Transform model data to JSON format for further saving
    *
    * return JSON value of model data
    */
   virtual CJAVal    toJSON(void)
     {
      CJAVal json_obj_model;

      json_obj_model["modelName"] = getModelName();
      json_obj_model["inputColsNumber"] = inputColsNumber;
      json_obj_model["bestCombinations"] = CJAVal(jtARRAY,"");


      for(int i = 0; i<bestCombinations.size(); i++)
        {

         CJAVal Array(jtARRAY,"");

         for(int k = 0; k<bestCombinations[i].size(); k++)
           {
            CJAVal collection;
            collection["combination"] = CJAVal(jtARRAY,"");
            collection["bestCoeffs"] = CJAVal(jtARRAY,"");
            vector combination = bestCombinations[i][k].combination();
            vector bestcoeff = bestCombinations[i][k].bestCoeffs();
            for(ulong j=0; j<combination.Size(); j++)
               collection["combination"].Add(int(combination[j]));
            for(ulong j=0; j<bestcoeff.Size(); j++)
               collection["bestCoeffs"].Add(bestcoeff[j],-15);
            Array.Add(collection);
           }

         json_obj_model["bestCombinations"].Add(Array);

        }

      return json_obj_model;

     }

   /**
    *  Set up model from JSON format model data
    *
    * param jsonModel Model data in JSON format
    * return Method exit status
    */
   virtual bool      fromJSON(CJAVal &jsonModel)
     {
      modelName = jsonModel["modelName"].ToStr();
      bestCombinations.clear();
      inputColsNumber = int(jsonModel["inputColsNumber"].ToInt());

      for(int i = 0; i<jsonModel["bestCombinations"].Size(); i++)
        {
         CVector member;
         for(int j = 0; j<jsonModel["bestCombinations"][i].Size(); j++)
           {
            Combination cb;
            vector c(ulong(jsonModel["bestCombinations"][i][j]["combination"].Size()));
            vector cf(ulong(jsonModel["bestCombinations"][i][j]["bestCoeffs"].Size()));
            for(int k = 0; k<jsonModel["bestCombinations"][i][j]["combination"].Size(); k++)
               c[k] = jsonModel["bestCombinations"][i][j]["combination"][k].ToDbl();
            for(int k = 0; k<jsonModel["bestCombinations"][i][j]["bestCoeffs"].Size(); k++)
               cf[k] = jsonModel["bestCombinations"][i][j]["bestCoeffs"][k].ToDbl();
            cb.setBestCoeffs(cf);
            cb.setCombination(c);
            member.push_back(cb);
           }
         bestCombinations.push_back(member);
        }
      return true;
     }



   /**
    *  Compare the number of required and actual columns of the input matrix
    *
    * param x Given matrix of input data
    */
   bool              checkMatrixColsNumber(matrix& x)
     {
      if(ulong(inputColsNumber) != x.Cols())
        {
         Print("Matrix  must have " + string(inputColsNumber) + " columns because there were " + string(inputColsNumber) + " columns in the training  matrix");
         return false;
        }

      return true;
     }
     
     

public:
   ///  Construct a new Gmdh Model object
                     GmdhModel() : level(1), lastLevelEvaluation(0) {}

   /**
   *  Get full class name
   *
   * return String containing the name of the model class
   */
   string            getModelName(void)
     {
      return modelName;
     }
   /**
     *Get number of inputs required for model
     */
    int getNumInputs(void)
     {
      return inputColsNumber;
     }

   /**
    *  Save model data into regular file
    *
    * param path Path to regular file
    */
   bool              save(string file_name)
     {

      CFileTxt modelFile;

      if(modelFile.Open(file_name,FILE_WRITE|FILE_COMMON,0)==INVALID_HANDLE)
        {
         Print("failed to open file ",file_name," .Error - ",::GetLastError());
         return false;
        }
      else
        {
         CJAVal js=toJSON();
         if(modelFile.WriteString(js.Serialize())==0)
           {
            Print("failed write to ",file_name,". Error -",::GetLastError());
            return false;
           }
        }

      return true;
     }

   /**
    *  Load model data from regular file
    *
    * param path Path to regular file
    */
   bool               load(string file_name)
     {
      training_complete = false;
      CFileTxt modelFile;
      CJAVal js;

      if(modelFile.Open(file_name,FILE_READ|FILE_COMMON,0)==INVALID_HANDLE)
        {
         Print("failed to open file ",file_name," .Error - ",::GetLastError());
         return false;
        }
      else
        {
         if(!js.Deserialize(modelFile.ReadString()))
           {
            Print("failed to read from ",file_name,".Error -",::GetLastError());
            return false;
           }
         training_complete = fromJSON(js);
        }
      return training_complete;
     }
   /**
    *  Divide the input data into 2 parts without shuffling
    *
    * param x Matrix of input data containing predictive variables
    * param y Vector of the taget values for the corresponding x data
    * param testSize Fraction of the input data that should be placed into the second part
    * param addOnesCol True if it is needed to add a column of ones to the x data, otherwise false
    * return SplittedData object containing 4 elements of data: train x, train y, test x, test y
    */
   static SplittedData internalSplitData(matrix& x,  vector& y, double testSize, bool addOnesCol = false)
     {
      SplittedData data;
      ulong testItemsNumber = ulong(round(double(x.Rows()) * testSize));
      matrix Train,Test;
      vector train,test;

      if(addOnesCol)
        {
         Train.Resize(x.Rows() - testItemsNumber, x.Cols() + 1);
         Test.Resize(testItemsNumber, x.Cols() + 1);

         for(ulong i = 0; i<Train.Rows(); i++)
            Train.Row(x.Row(i),i);

         Train.Col(vector::Ones(Train.Rows()),x.Cols());

         for(ulong i = 0; i<Test.Rows(); i++)
            Test.Row(x.Row(Train.Rows()+i),i);

         Test.Col(vector::Ones(Test.Rows()),x.Cols());

        }
      else
        {
         Train.Resize(x.Rows() - testItemsNumber, x.Cols());
         Test.Resize(testItemsNumber, x.Cols());

         for(ulong i = 0; i<Train.Rows(); i++)
            Train.Row(x.Row(i),i);

         for(ulong i = 0; i<Test.Rows(); i++)
            Test.Row(x.Row(Train.Rows()+i),i);
        }

      train.Init(y.Size() - testItemsNumber,slice,y,0,y.Size() - testItemsNumber - 1);
      test.Init(testItemsNumber,slice,y,y.Size() - testItemsNumber);

      data.yTrain = train;
      data.yTest = test;

      data.xTrain = Train;
      data.xTest = Test;

      return data;
     }

   /**
    *  Get long-term forecast for the time series
    *
    * param x One row of the test time series data
    * param lags The number of lags (steps) to make a forecast for
    * return Vector containing long-term forecast
    */
   virtual vector    predict(vector& x, int lags)
     {
      return vector::Zeros(1);
     }

   /**
    *  Get the String representation of the best polynomial
    *
    * return String representation of the best polynomial
    */
   string            getBestPolynomial(void)
     {
      string polynomialStr = "";
      int ind = 0;
      for(int i = 0; i < bestCombinations.size(); ++i)
        {
         for(int j = 0; j < bestCombinations[i].size(); ++j)
           {
            vector bestColsIndexes = bestCombinations[i][j].combination();
            vector bestCoeffs = bestCombinations[i][j].bestCoeffs();
            polynomialStr += getPolynomialPrefix(i, j);
            bool isFirstCoeff = true;
            for(int k = 0; k < int(bestCoeffs.Size()); ++k)
              {
               if(bestCoeffs[k])
                 {
                  polynomialStr += getPolynomialCoeffSign(bestCoeffs[k], isFirstCoeff);
                  string coeffValuelStr = getPolynomialCoeffValue(bestCoeffs[k], (k == (bestCoeffs.Size() - 1)));
                  polynomialStr += coeffValuelStr;
                  if(coeffValuelStr != "" && k != bestCoeffs.Size() - 1)
                     polynomialStr += "*";
                  polynomialStr += getPolynomialVariable(i, k, int(bestCoeffs.Size()), bestColsIndexes);
                  isFirstCoeff = false;
                 }
              }
            if(i < bestCombinations.size() - 1 || j < (bestCombinations[i].size() - 1))
               polynomialStr += "\n";
           }//j
         if(i < bestCombinations.size() - 1 && bestCombinations[i].size() > 1)
            polynomialStr += "\n";
        }//i
      return polynomialStr;
     }

                    ~GmdhModel()
     {
      for(int i = 0; i<bestCombinations.size(); i++)
         bestCombinations[i].clear();

      bestCombinations.clear();
     }
  };


//+------------------------------------------------------------------+

これは、他のGMDH型が派生する基本クラスです。モデルを訓練または構築し、その後それを使用して予測をおこなううためのメソッドを提供します。saveメソッドとloadメソッドにより、モデルを保存し、後で使用するためにファイルから読み込むことができます。モデルはJSON形式ですべてのMetaTrader端末共通のディレクトリにあるテキストファイルに保存されます。

最後のヘッダーファイルmia.mqhには、MIAクラスの定義が含まれています。

//+------------------------------------------------------------------+
//| Class implementing multilayered iterative algorithm MIA          |
//+------------------------------------------------------------------+
class MIA : public GmdhModel
  {
protected:
   PolynomialType    polynomialType; // Selected polynomial type

   void              generateCombinations(int n_cols,vector &out[])  override
     {
      GmdhModel::nChooseK(n_cols,2,out);
      return;
     }
   /**
   *  Get predictions for the input data
   *
   * param x Test data of the regression task or one-step time series forecast
   * return Vector containing prediction values
   */
   virtual vector    calculatePrediction(vector& x)
     {
      if(x.Size()<ulong(inputColsNumber))
         return vector::Zeros(ulong(inputColsNumber));

      matrix modifiedX(1,x.Size()+ 1);

      modifiedX.Row(x,0);

      modifiedX[0][x.Size()] = 1.0;

      for(int i = 0; i < bestCombinations.size(); ++i)
        {
         matrix xNew(1, ulong(bestCombinations[i].size()) + 1);
         for(int j = 0; j < bestCombinations[i].size(); ++j)
           {
            vector comb = bestCombinations[i][j].combination();
            matrix xx(1,comb.Size());
            for(ulong i = 0; i<xx.Cols(); ++i)
               xx[0][i] = modifiedX[0][ulong(comb[i])];
            matrix ply = getPolynomialX(xx);
            vector c,b;
            c = bestCombinations[i][j].bestCoeffs();
            b = ply.MatMul(c);
            xNew.Col(b,ulong(j));
           }
         vector n  = vector::Ones(xNew.Rows());
         xNew.Col(n,xNew.Cols() - 1);
         modifiedX = xNew;
        }

      return modifiedX.Col(0);

     }

   /**
    *  Construct vector of the new variable values according to the selected polynomial type
    *
    * param x Matrix of input variables values for the selected polynomial type
    * return Construct vector of the new variable values
    */
   matrix            getPolynomialX(matrix& x)
     {
      matrix polyX = x;
      if((polynomialType == linear_cov))
        {
         polyX.Resize(x.Rows(), 4);
         polyX.Col(x.Col(0)*x.Col(1),2);
         polyX.Col(x.Col(2),3);
        }
      else
         if((polynomialType == quadratic))
           {
            polyX.Resize(x.Rows(), 6);
            polyX.Col(x.Col(0)*x.Col(1),2) ;
            polyX.Col(x.Col(0)*x.Col(0),3);
            polyX.Col(x.Col(1)*x.Col(1),4);
            polyX.Col(x.Col(2),5) ;
           }

      return polyX;
     }

   /**
    *  Transform data in the current training level by constructing new variables using selected polynomial type
    *
    * param data Data used to train models at the current level
    * param bestCombinations Vector of the k best models of the current level
    */
   virtual void      transformDataForNextLevel(SplittedData& data,  CVector &bestCombs)
     {
      matrix xTrainNew(data.xTrain.Rows(), ulong(bestCombs.size()) + 1);
      matrix xTestNew(data.xTest.Rows(), ulong(bestCombs.size()) + 1);

      for(int i = 0; i < bestCombs.size(); ++i)
        {
         vector comb = bestCombs[i].combination();

         matrix train(xTrainNew.Rows(),comb.Size()),test(xTrainNew.Rows(),comb.Size());

         for(ulong k = 0; k<comb.Size(); k++)
           {
            train.Col(data.xTrain.Col(ulong(comb[k])),k);
            test.Col(data.xTest.Col(ulong(comb[k])),k);
           }

         matrix polyTest,polyTrain;
         vector bcoeff = bestCombs[i].bestCoeffs();
         polyTest = getPolynomialX(test);
         polyTrain = getPolynomialX(train);

         xTrainNew.Col(polyTrain.MatMul(bcoeff),i);
         xTestNew.Col(polyTest.MatMul(bcoeff),i);
        }

      xTrainNew.Col(vector::Ones(xTrainNew.Rows()),xTrainNew.Cols() - 1);
      xTestNew.Col(vector::Ones(xTestNew.Rows()),xTestNew.Cols() - 1);

      data.xTrain = xTrainNew;
      data.xTest =  xTestNew;
     }

   virtual void      removeExtraCombinations(void) override
     {

      CVector2d realBestCombinations(bestCombinations.size());
      CVector n;
      n.push_back(bestCombinations[level-2][0]);
      realBestCombinations.setAt(realBestCombinations.size() - 1,n);

      vector comb(1);
      for(int i = realBestCombinations.size() - 1; i > 0; --i)
        {
         double usedCombinationsIndexes[],unique[];
         int indexs[];
         int prevsize = 0;
         for(int j = 0; j < realBestCombinations[i].size(); ++j)
           {
            comb = realBestCombinations[i][j].combination();
            ArrayResize(usedCombinationsIndexes,prevsize+int(comb.Size()-1),100);
            for(ulong k = 0; k < comb.Size() - 1; ++k)
               usedCombinationsIndexes[ulong(prevsize)+k] = comb[k];
            prevsize = int(usedCombinationsIndexes.Size());
           }
         MathUnique(usedCombinationsIndexes,unique);
         ArraySort(unique);

         for(uint it = 0; it<unique.Size(); ++it)
            realBestCombinations[i - 1].push_back(bestCombinations[i - 1][int(unique[it])]);

         for(int j = 0; j < realBestCombinations[i].size(); ++j)
           {
            comb = realBestCombinations[i][j].combination();
            for(ulong k = 0; k < comb.Size() - 1; ++k)
               comb[k] = ArrayBsearch(unique,comb[k]);
            comb[comb.Size() - 1] = double(unique.Size());
            realBestCombinations[i][j].setCombination(comb);
           }

         ZeroMemory(usedCombinationsIndexes);
         ZeroMemory(unique);
         ZeroMemory(indexs);
        }

      bestCombinations = realBestCombinations;
     }
   virtual bool      preparations(SplittedData& data, CVector &_bestCombinations) override
     {
      bestCombinations.push_back(_bestCombinations);
      transformDataForNextLevel(data, bestCombinations[level - 1]);
      return true;
     }
   virtual matrix    xDataForCombination(matrix& x,  vector& comb)  override
     {
      matrix xx(x.Rows(),comb.Size());

      for(ulong i = 0; i<xx.Cols(); ++i)
         xx.Col(x.Col(ulong(comb[i])),i);

      return getPolynomialX(xx);
     }

   string            getPolynomialPrefix(int levelIndex, int combIndex)  override
     {
      return ((levelIndex < bestCombinations.size() - 1) ?
              "f" + string(levelIndex + 1) + "_" + string(combIndex + 1) : "y") + " =";
     }
   string            getPolynomialVariable(int levelIndex, int coeffIndex, int coeffsNumber,
                                           vector &bestColsIndexes)  override
     {
      if(levelIndex == 0)
        {
         if(coeffIndex < 2)
            return "x" + string(int(bestColsIndexes[coeffIndex]) + 1);
         else
            if(coeffIndex == 2 && coeffsNumber > 3)
               return "x" + string(int(bestColsIndexes[0]) + 1) + "*x" + string(int(bestColsIndexes[1]) + 1);
            else
               if(coeffIndex < 5 && coeffsNumber > 4)
                  return "x" + string(int(bestColsIndexes[coeffIndex - 3]) + 1) + "^2";
        }
      else
        {
         if(coeffIndex < 2)
            return "f" + string(levelIndex) + "_" + string(int(bestColsIndexes[coeffIndex]) + 1);
         else
            if(coeffIndex == 2 && coeffsNumber > 3)
               return "f" + string(levelIndex) + "_" + string(int(bestColsIndexes[0]) + 1) +
                      "*f" + string(levelIndex) + "_" + string(int(bestColsIndexes[1]) + 1);
            else
               if(coeffIndex < 5 && coeffsNumber > 4)
                  return "f" + string(levelIndex) + "_" + string(int(bestColsIndexes[coeffIndex - 3]) + 1) + "^2";
        }
      return "";
     }


   CJAVal            toJSON(void)  override
     {
      CJAVal json_obj_model = GmdhModel::toJSON();

      json_obj_model["polynomialType"] = int(polynomialType);
      return json_obj_model;

     }

   bool              fromJSON(CJAVal &jsonModel) override
     {
      bool parsed = GmdhModel::fromJSON(jsonModel);

      if(!parsed)
         return false;

      polynomialType = PolynomialType(jsonModel["polynomialType"].ToInt());

      return true;
     }

public:
   //+------------------------------------------------------------------+
   //| Constructor                                                      |
   //+------------------------------------------------------------------+

                     MIA(void)
     {
      modelName = "MIA";
     }

   //+------------------------------------------------------------------+
   //| model a time series                                              |
   //+------------------------------------------------------------------+

   virtual bool      fit(vector &time_series,int lags,double testsize=0.5,PolynomialType _polynomialType=linear_cov,CriterionType criterion=stab,int kBest = 10,int pAverage = 1,double limit = 0.0)
     {

      if(lags < 3)
        {
         Print(__FUNCTION__," lags must be >= 3");
         return false;
        }

      PairMVXd transformed = timeSeriesTransformation(time_series,lags);

      SplittedData splited = splitData(transformed.first,transformed.second,testsize);

      Criterion criter(criterion);

      if(kBest < 3)
        {
         Print(__FUNCTION__," kBest value must be an integer >= 3");
         return false;
        }

      if(validateInputData(testsize, pAverage, limit, kBest))
         return false;

      polynomialType = _polynomialType;

      return GmdhModel::gmdhFit(splited.xTrain, splited.yTrain, criter, kBest, testsize, pAverage, limit);
     }

   //+------------------------------------------------------------------+
   //| model a multivariable data set  of inputs and targets            |
   //+------------------------------------------------------------------+

   virtual bool      fit(matrix &vars,vector &targets,double testsize=0.5,PolynomialType _polynomialType=linear_cov,CriterionType criterion=stab,int kBest = 10,int pAverage = 1,double limit = 0.0)
     {

      if(vars.Cols() < 3)
        {
         Print(__FUNCTION__," columns in vars must be >= 3");
         return false;
        }

      if(vars.Rows() != targets.Size())
        {
         Print(__FUNCTION__, " vars dimensions donot correspond with targets");
         return false;
        }

      SplittedData splited = splitData(vars,targets,testsize);

      Criterion criter(criterion);

      if(kBest < 3)
        {
         Print(__FUNCTION__," kBest value must be an integer >= 3");
         return false;
        }

      if(validateInputData(testsize, pAverage, limit, kBest))
         return false;

      polynomialType = _polynomialType;

      return GmdhModel::gmdhFit(splited.xTrain, splited.yTrain, criter, kBest, testsize, pAverage, limit);
     }

   virtual vector     predict(vector& x, int lags)  override
     {
      if(lags <= 0)
        {
         Print(__FUNCTION__," lags value must be a positive integer");
         return vector::Zeros(1);
        }

      if(!training_complete)
        {
         Print(__FUNCTION__," model was not successfully trained");
         return vector::Zeros(1);
        }

      vector expandedX = vector::Zeros(x.Size() + ulong(lags));
      for(ulong i = 0; i<x.Size(); i++)
         expandedX[i]=x[i];

      for(int i = 0; i < lags; ++i)
        {
         vector vect(x.Size(),slice,expandedX,ulong(i),x.Size()+ulong(i)-1);
         vector res = calculatePrediction(vect);
         expandedX[x.Size() + i] = res[0];
        }

      vector vect(ulong(lags),slice,expandedX,x.Size());
      return vect;
     }



  };
//+------------------------------------------------------------------+

GmdhModelを継承し、多層反復アルゴリズムを実装しています。  MIAには、与えられたデータセットをモデル化するために呼び出すことができる2つのfit()オーバーロードがあります。これらのメソッドは、第1パラメータと第2パラメータによって区別されます。過去の値のみを使用して時系列をモデル化する場合は、以下のfit()を使用します。

fit(vector &time_series,int lags,double testsize=0.5,PolynomialType _polynomialType=linear_cov,CriterionType criterion=stab,int kBest = 10,int pAverage = 1,double limit = 0.0)

もう一方は、従属変数と独立変数のデータセットをモデル化するときに有用です。両メソッドのパラメータは次の表で説明します。

データ型
パラメータ名
詳細
vector
 time_series  ベクトルに含まれる時系列を表す
integer  lags
 モデルで予測値として使用するラグ値の数を定義する
matrix
 vars
予測変数を含む入力データの行列
vector
 targets
varsの対応する行メンバーの目標値のベクトル
CriterionType
 criterion
モデル構築プロセスの外部基準を指定する列挙変数
integer
 kBest
後続の層の新しい入力を構築するための、最適な部分モデルの数を定義
PolynomialType
 _polynomialType
訓練中に既存の変数から新しい変数を構成するために使用する多項式のタイプ
double
 testSize
モデルの評価に使用すべき入力データの割合
int
 pAverage
停止基準の計算で考慮される最良の部分モデルの数
double  limit  訓練を継続するために、外部基準を改善すべき最小値

一旦モデルが訓練されると、predict()を呼び出すことで、予測に使用することができます。この方法は、入力のベクトルと、希望する予測数を指定する整数値を必要とします。実行に成功すると、このメソッドは計算された予測値を含むベクトルを返します。そうでない場合は、0のベクトルを返します。  以下のセクションでは、今説明したコードの使い方をよりよく理解するために、いくつかの簡単な例を見てみましょう。


スクリプトとして実装された3つの例について説明します。さまざまなシナリオでMIAがどのように適用できるかを網羅します。1つ目では、時系列のモデルを構築します。ここで、ある数の直列の前の値は、後続の項を決定するために使用することができます。この例はMIA_Test.mq5スクリプトに含まれており、そのコードを以下に示します。

//+------------------------------------------------------------------+
//|                                                     MIA_Test.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs
#include <GMDH\mia.mqh>

input int NumLags = 3;
input int NumPredictions = 6;
input CriterionType critType = stab;
input PolynomialType polyType = linear_cov;
input double DataSplitSize = 0.33;
input int NumBest = 10;
input int pAverge = 1;
input double critLimit = 0;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---time series we want to model
   vector tms = {1,2,3,4,5,6,7,8,9,10,11,12};
//---
   if(NumPredictions<1)
     {
      Alert("Invalid setting for NumPredictions, has to be larger than 0");
      return;
     }
//---instantiate MIA object
   MIA mia;
//---fit the series according to user defined hyper parameters
   if(!mia.fit(tms,NumLags,DataSplitSize,polyType,critType,NumBest,pAverge,critLimit))
      return;
//---generate filename based on user defined parameter settings
   string modelname = mia.getModelName()+"_"+EnumToString(critType)+"_"+string(DataSplitSize)+"_"+string(pAverge)+"_"+string(critLimit);
//---save the trained model
   mia.save(modelname+".json");
//---inputs from original series to be used for making predictions
   vector in(ulong(NumLags),slice,tms,tms.Size()-ulong(NumLags));
//---predictions made from the model
   vector out = mia.predict(in,NumPredictions);
//---output result of prediction
   Print(modelname, " predictions ", out);
//---output the polynomial that defines the model
   Print(mia.getBestPolynomial());
  }
//+------------------------------------------------------------------+

スクリプトを実行すると、ユーザーはモデルのさまざまな側面を変更できます。NumLagsは、次の項を計算するための直前の系列値の数を指定します。NumPredictionsは、指定されたシリーズを超えておこなわれる予測の数を示します。残りのユーザー調整可能なパラメータは、メソッド fit()に渡される引数に対応します。モデルが正常に構築されると、ファイルに保存されます。そして予測がおこなわれ、モデルを表す最終的な多項式とともに端末の[エキスパート]タブに出力されます。デフォルト設定でスクリプトを実行した結果を以下に示します。表示されている多項式は、与えられた時系列を記述するのに最適な数学モデルを示しています。シリーズのシンプルさを考えれば、明らかに不必要に複雑化しすぎています。しかし、予測結果を考慮すると、このモデルは依然として系列の一般的な傾向を捉えています。

PS      0       22:37:31.246    MIA_Test (USDCHF,D1)    MIA_stab_0.33_1_0.0 predictions [13.00000000000001,14.00000000000002,15.00000000000004,16.00000000000005,17.0000000000001,18.0000000000001]
OG      0       22:37:31.246    MIA_Test (USDCHF,D1)    y = - 9.340179e-01*x1 + 1.934018e+00*x2 + 3.865363e-16*x1*x2 + 1.065982e+00

スクリプトの2回目の実行で。NumLagsを4に増加します。これがモデルにどんな影響を与えるか見てみましょう。

スクリプトの2回目の実行の設定

余分な予測変数を追加することで、どれだけモデルに複雑さがもたらされるかに注目してください。これが予測に与える影響も同様です。多項式は数行にわたるようになりましたが、モデル予測に目立った改善は見られませんでした。

22:37:42.921    MIA_Test (USDCHF,D1)    MIA_stab_0.33_1_0.0 predictions [13.00000000000001,14.00000000000002,15.00000000000005,16.00000000000007,17.00000000000011,18.00000000000015]
ML      0       22:37:42.921    MIA_Test (USDCHF,D1)    f1_1 = - 1.666667e-01*x2 + 1.166667e+00*x4 + 8.797938e-16*x2*x4 + 6.666667e-01
CO      0       22:37:42.921    MIA_Test (USDCHF,D1)    f1_2 = - 6.916614e-15*x3 + 1.000000e+00*x4 + 1.006270e-15*x3*x4 + 1.000000e+00
NN      0       22:37:42.921    MIA_Test (USDCHF,D1)    f1_3 = - 5.000000e-01*x1 + 1.500000e+00*x3 + 1.001110e-15*x1*x3 + 1.000000e+00
QR      0       22:37:42.921    MIA_Test (USDCHF,D1)    f2_1 = 5.000000e-01*f1_1 + 5.000000e-01*f1_3 - 5.518760e-16*f1_1*f1_3 - 1.729874e-14
HR      0       22:37:42.921    MIA_Test (USDCHF,D1)    f2_2 = 5.000000e-01*f1_1 + 5.000000e-01*f1_2 - 1.838023e-16*f1_1*f1_2 - 8.624525e-15
JK      0       22:37:42.921    MIA_Test (USDCHF,D1)    y = 5.000000e-01*f2_1 + 5.000000e-01*f2_2 - 2.963544e-16*f2_1*f2_2 - 1.003117e-14

最後の例では、独立変数によって定義された出力をモデル化するという、別のシナリオを考えてみます。この例では、3つの入力を足し合わせることをモデルに教えようとしています。この例のコードはMIA_Multivariable_test.mq5にあります。

//+------------------------------------------------------------------+
//|                                       MIA_miavariable_test.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs
#include <GMDH\mia.mqh>

input CriterionType critType = stab;
input PolynomialType polyType = linear_cov;
input double DataSplitSize = 0.33;
input int NumBest = 10;
input int pAverge = 1;
input double critLimit = 0;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---simple independent and dependent data sets we want to model
   matrix independent = {{1,2,3},{3,2,1},{1,4,2},{1,1,3},{5,3,1},{3,1,9}};
   vector dependent = {6,6,7,5,9,13};
//---declare MIA object     
   MIA mia;   
//---train the model based on chosen hyper parameters
   if(!mia.fit(independent,dependent,DataSplitSize,polyType,critType,NumBest,pAverge,critLimit))
      return;
//---construct filename for generated model
   string modelname = mia.getModelName()+"_"+EnumToString(critType)+"_"+string(DataSplitSize)+"_"+string(pAverge)+"_"+string(critLimit)+"_multivars";
//---save the model
   mia.save(modelname+".json");
//---input data to be used as input for making predictions
   matrix unseen = {{1,2,4},{1,5,3},{9,1,3}};
//---make predictions and output to the terminal
  for(ulong row = 0; row<unseen.Rows(); row++)
     {
       vector in = unseen.Row(row);
       Print("inputs ", in , " prediction ", mia.predict(in,1));
     }  
//---output the polynomial that defines the model
   Print(mia.getBestPolynomial()); 
  }
//+------------------------------------------------------------------+

予測変数は行列varsにあります。各行は、ベクトルtargets内の目標に対応しています。前の例と同じように、モデルの訓練ハイパーパラメータの様々な側面を設定するオプションがあります。デフォルト設定での訓練結果は、以下のように非常に悪くなります。

RE      0       22:38:57.445    MIA_Multivariable_test (USDCHF,D1)      inputs [1,2,4] prediction [5.999999999999997]
JQ      0       22:38:57.445    MIA_Multivariable_test (USDCHF,D1)      inputs [1,5,3] prediction [7.5]
QI      0       22:38:57.445    MIA_Multivariable_test (USDCHF,D1)      inputs [9,1,3] prediction [13.1]
QK      0       22:38:57.445    MIA_Multivariable_test (USDCHF,D1)      y = 1.900000e+00*x1 + 1.450000e+00*x2 - 9.500000e-01*x1*x2 + 3.100000e+00

訓練パラメータを調整することで、モデルを改善することができます。最良の結果が得られたのは、以下に示すような設定でした。

モデル設定の改善

これらの設定を用いることで、モデルは最終的に「未知の」入力変数のセットに対して正確な予測をおこなうことができます。たとえ最初の例と同じように、生成される多項式が複雑すぎるとしてもです。

DM      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      inputs [1,2,4] prediction [6.999999999999998]
JI      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      inputs [1,5,3] prediction [8.999999999999998]
CD      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      inputs [9,1,3] prediction [13.00000000000001]
OO      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      f1_1 = 1.071429e-01*x1 + 6.428571e-01*x2 + 4.392857e+00
IQ      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      f1_2 = 6.086957e-01*x2 - 8.695652e-02*x3 + 4.826087e+00
PS      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      f1_3 = - 1.250000e+00*x1 - 1.500000e+00*x3 + 1.125000e+01
LO      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      f2_1 = 1.555556e+00*f1_1 - 6.666667e-01*f1_3 + 6.666667e-01
HN      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      f2_2 = 1.620805e+00*f1_2 - 7.382550e-01*f1_3 + 7.046980e-01
PP      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      f2_3 = 3.019608e+00*f1_1 - 2.029412e+00*f1_2 + 5.882353e-02
JM      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      f3_1 = 1.000000e+00*f2_1 - 3.731079e-15*f2_3 + 1.155175e-14
NO      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      f3_2 = 8.342665e-01*f2_2 + 1.713326e-01*f2_3 - 3.359462e-02
FD      0       22:44:25.269    MIA_Multivariable_test (USDCHF,D1)      y = 1.000000e+00*f3_1 + 3.122149e-16*f3_2 - 1.899249e-15

ここで観察した単純な例から明らかなように、多層反復アルゴリズムは、初歩的なデータセットに対してはやり過ぎかもしれません。生成される多項式は猛烈に複雑になる可能性があります。このようなモデルには、訓練データを過剰適合させる危険性があります。アルゴリズムがデータ中のノイズや外れ値を取り込んでしまう可能性があり、未見のサンプルに対する汎化性能が低下します。一般にMIAとGMDHアルゴリズムの性能は、入力データの質と特性に大きく依存します。ノイズの多いデータや不完全なデータは、モデルの精度と安定性に悪影響を及ぼし、信頼できない予測につながる可能性があります。最後に、訓練プロセスはかなり単純ですが、最良の結果を得るためには、ある程度のハイパーパラメータの調整が必要です。完全には自動化されていません。

最後のデモンストレーションでは、ファイルからモデルを読み込み、それを使って予測をおこなうスクリプトを用意しました。この例はLoadModelFromFile.mq5に示されています。

//+------------------------------------------------------------------+
//|                                            LoadModelFromFile.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs
#include <GMDH\mia.mqh>
//--- input parameters
input string   JsonFileName="";

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---declaration of MIA instance
    MIA mia;
//---load the model from file  
    if(!mia.load(JsonFileName))
      return;
//---get the number of required inputs for the loaded model     
    int numlags = mia.getNumInputs();
//---generate arbitrary inputs to make a prediction with  
    vector inputs(ulong(numlags),arange,21.0,1.0);
//---make prediction and output results to terminal    
    Print(JsonFileName," input ", inputs," prediction ", mia.predict(inputs,1));
//---output the model's polynomial    
    Print(mia.getBestPolynomial()); 
  }
//+------------------------------------------------------------------+

次の図は、スクリプトがどのように動作し、正常に実行された結果を示しています。

ファイルからモデルを読み込む


結論

MQL5にGMDH多層反復アルゴリズムが実装されたことで、トレーダーはその概念を戦略に応用する機会を得ました。ダイナミックなフレームワークを提供するこのアルゴリズムは、市場分析を継続的に適応させ、改良する能力をユーザーに与えます。しかし、その将来性とは裏腹に、実務家にとっては、その限界を慎重に乗り越えることが不可欠です。ユーザーは、GMDHアルゴリズムに固有の計算負荷、特に大規模なデータセットや高次元のデータセットを扱う場合に注意する必要があります。アルゴリズムの反復的な性質上、最適なモデル構造を確認するために何度も計算をおこなう必要があり、その過程で多大な時間とリソースを消費します。

これらのことを考慮すると、実務家は、GMDH多層反復アルゴリズムの長所と限界を微妙に理解した上で、GMDH多層反復アルゴリズムの使用に取り組むことが望まれます。ダイナミックな市場分析のための強力なツールを提供する一方で、その複雑さは、その潜在能力を効果的に最大限に引き出すために慎重なナビゲーションを必要とします。トレーダーは、GMDHアルゴリズムを熟考して適用し、その複雑さを考慮することで、取引戦略を充実させ、市場データから貴重な洞察を得ることができます。

すべてのMQL5のコードは記事の最後に添付されています。

ファイル
詳細
Mql5\include\VectorMatrixTools.mqh
ベクトルや行列の操作に使われる関数定義のヘッダーファイル
Mql5\include\JAson.mqh
JSONオブジェクトの解析と生成に使用されるカスタム型の定義
Mql5\include\GMDH\gmdh_internal.mqh
gmdhライブラリで使用されるカスタム型の定義を含むヘッダファイル
Mql5\include\GMDH\gmdh.mqh
基本クラスGmdhModelの定義を含むインクルードファイル
Mql5\include\GMDH\mia.mqh
多層反復アルゴリズムを実装したクラスMIA
Mql5\script\MIA_Test.mq5
単純な時系列のモデルを構築することでMIAクラスの使い方を示すスクリプト
Mql5\script\MIA_Multivarible_test.mq5
多変数データセットのモデルを構築するためのMIAクラスの使用を示す別のスクリプト
Mql5\script\LoadModelFromFile.mq5  jsonファイルからモデルを読み込む方法を示すスクリプト


MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/14454

添付されたファイル |
MIA_Test.mq5 (2.14 KB)
JAson.mqh (33.43 KB)
gmdh.mqh (23.86 KB)
gmdh_internal.mqh (82.09 KB)
mia.mqh (12.1 KB)
Mql5.zip (24.57 KB)
ニュース取引が簡単に(第1回):データベースの作成 ニュース取引が簡単に(第1回):データベースの作成
ニュース取引は複雑で圧倒されるかもしれませんが、この記事ではニュースデータを入手する手順を説明し、さらに、MQL5経済指標カレンダーとその特徴についても学びます。
どんな市場でも優位性を得る方法 どんな市場でも優位性を得る方法
現在の技術レベルに関係なく、取引したいどのような市場でも先んじることができる方法を学びましょう。
知っておくべきMQL5ウィザードのテクニック(第15回):ニュートンの多項式を用いたサポートベクトルマシン 知っておくべきMQL5ウィザードのテクニック(第15回):ニュートンの多項式を用いたサポートベクトルマシン
サポートベクトルマシンは、データの次元を増やす効果を調べることで、あらかじめ定義されたクラスに基づいてデータを分類します。これは教師あり学習法で、多次元のデータを扱う可能性を考えるとかなり複雑です。この記事では、2次元データの非常に基本的な実装であるニュートンの多項式が、価格とアクションを分類する際にどのように効率的に実行できるかを検討します。
知っておくべきMQL5ウィザードのテクニック(第14回):STFによる多目的時系列予測 知っておくべきMQL5ウィザードのテクニック(第14回):STFによる多目的時系列予測
データのモデリングに「空間」と「時間」の両方の測定基準を使用する空間的時間的融合は、主にリモートセンシングや、私たちの周囲をよりよく理解するための他の多くの視覚ベースの活動で有用です。発表された論文のおかげで、トレーダーへの可能性を検証することで、その活用に斬新なアプローチを取ります。