English Русский 中文 Español Deutsch Português
時系列の予測(第1部):経験的分解モード(EMD)法

時系列の予測(第1部):経験的分解モード(EMD)法

MetaTrader 5トレーディングシステム | 15 6月 2020, 09:27
1 880 0
Stanislav Korotky
Stanislav Korotky

はじめに

トレーダーが成功するかどうかは、主に、「将来を見据える」、つまり、一定期間後に価格がどのように変化するかを推測する能力に依存します。この問題を解決するには、基本的な市場特性の最新アップデートからテクニカル分析アルゴリズムまで、さまざまなツールと機能を用意することが重要です。それらのすべては、時系列予測の数学的方法、価格自体とテクニカル指標、ボラティリティ、マクロ経済指標、取引ポートフォリオのバランス、またはそのような時系列として機能する他の何かを使用して、多かれ少なかれ強化できます。

予測は非常に幅広いトピックであり、mql5.com Webサイトですでに何度も触れられています。最初の紹介記事でありながら重要な記事の1つである「金融時系列の予測」は、2008年に発行されました。コードベースにある他の多くの記事や出版物の中には、MetaTrader対応のツールなどがあります。

Webサイトの関連セクション(記事コードベース )を検索すると、完全なリストを取得できます。

この目的のために、利用可能な予測ツールのリストを2つの新しいツールで拡張します。1番目は「Empirical Mode Decompositionの紹介」稿ですでに検討されているEmpirical Mode Decomposition(EMD)法に基づいていますが、予測には適用されていません。EMDは最初の部分で考慮されます。

2番目のツールは、サポートベクターマシン(SVM)法(バージョン最小二乗サポートベクターマシン(LS-SVM))を使用します。 これについては、後半で取り上げます。


EMDベースの予測アルゴリズム

EMD技術の詳細な紹介については、「経験的モード分解メソッドのイントロダクション」を参照してください。EMD技術は時系列をストランド(いわゆる固有モード関数(IMF))について分解します。各フォームは、時系列の最大値と最小値のスプライン補間であり、最初に極値を最初の系列で検索します。次に、見つかったばかりのIMFがそこから差し引かれます。その後、変更されたシリーズの極値に対してスプライン補間が実行され、残りが指定されたノイズレベルよりも低くなるまで、いくつかのIMFの構築が続行されます。視覚的には、結果はフーリエ級数展開に似ています。ただし、後者とは異なり、典型的なEMDフォームは周波数を決定する調和振動ではありません。取得されるIMF拡張関数の数は、初期系列の滑らかさとアルゴリズム設定に依存します。

上記の記事では、EMDを計算するための準備ができたクラスが提示されていますが、分解結果を外部HTMLファイルのグラフとして取得することを提案しています。本稿では、これらのクラスに基づいて、アルゴリズムを予測的にするために必要な追加を記述します。

CEMDecomp.mqhとCEMD_2.mqhの2つのファイルが記事に添付されました。後者は前者を少し改良したバージョンなので、ここでは後者を使用し、EMD.mqhという新しい名前でコピーし、まだ変更を加えずにEMD.mq5指標に含めます。

  #include <EMD.mqh>

また、バッファ指標の配列IndArray.mqhの簡略化された宣言に特別なクラスを使用します(英語の説明はブログにあって、その現在のバージョンは記事に添付されています)。多くのバッファが必要ですが、それらは統一された方法で処理されます。

  #define BUF_NUM 18 // 16 IMF maximum (including input at 0-th index) + residue + reconstruction
  
  #property indicator_separate_window
  #property indicator_buffers BUF_NUM
  #property indicator_plots   BUF_NUM
  
  #include <IndArray.mqh>
  IndicatorArray buffers(BUF_NUM);
  IndicatorArrayGetter getter(buffers);

ご覧のとおり、指標は別のウィンドウに表示され、以下の18個のバッファが表示用に予約されています。

  • 最初のシリーズ
  • その分解の16のコンポーネント(おそらく、それらのすべてが使用されるわけではありません)
  • 残り( 「傾向」)
  • 再建

最も興味深いのは最後の項目です。IMF関数を取得した後は、それらの一部(すべてではない)を合計して、最初のシリーズの平滑化バージョンを取得できます。これは、後に現れるバーに対して外挿できる(スプライン外挿)既知のスプラインの合計であるため、予測のソースとして機能する平滑化された再構成です。ただし、IMFが取得された最新の既知のポイントから離れると、IMFは無関係になるため、予測の深さはいくつかのバーに制限する必要があります。

EMD.mqhファイルに戻ります。クラスCEMDが定義され、すべての作業を実行します。プロセスはdecompメソッドを呼び出すことによって起動されます。このメソッドには時系列カウント配列yが渡されます。適切な関数(IMFResult)の長さNを決定するのは、この配列のサイズです。arrayprepareメソッドは、バッキング配列を準備してそれらを計算します。

  class CEMD
  {
    private:
      int N;              // Input and output data size
      double IMFResult[]; // Result
      double X[];         // X-coordinate for the TimeSeries. X[]=0,1,2,...,N-1.
      ...
    
    public:
      int N;              // Input and output data size
      double Mean;        // Mean of input data
      ...
      
      int decomp(double &y[])
      {
        ...
        N = ArraySize(y);
        arrayprepare();
        for(i = 0; i < N; i++)
          X[i] = i;
        Mean = 0;
        for(i = 0; i < N; i++)
          Mean += (y[i] - Mean) / (i + 1.0); // Mean (average) of input data
        for(i = 0; i < N; i++)
        {
          a = y[i] - Mean;
          Imf[i] = a;
          IMFResult[i] = a;
        }
        // The loop of decomposition
          ...
          extrema(...);
          ...
        ...
      }
      

    private:
      int arrayprepare(void)
      {
        if(ArrayResize(IMFResult, N) != N) return (-1);
        ...
      }
  };

参照点の数を増やすために、新しいパラメータをdecompメソッドに追加し、外挿して、予測の深さを定義します。初期系列の実際の長さをローカル変数Nfに事前に保存して、外挿で要求されたカウントの数だけNを増やします(コードでは、追加と変更はそれぞれ「+」と「*」で示されています )。

      int decomp(const double &y[], const int extrapolate = 0) // *
      {
        ...
        N = ArraySize(y);
        int Nf = N;                            // + preserve actual number of input data points
        N += extrapolate;                      // + 
        arrayprepare();
        for(i = 0; i < N; i++)
          X[i] = i;
        Mean = 0;
        for(i = 0; i < Nf; i++)                // * was N
          Mean += (y[i] - Mean) / (i + 1.0);
        for(i = 0; i < N; i++)
        {
          a = y[MathMin(i, Nf - 1)] - Mean;    // * was y[i]
          Imf[i] = a;
          IMFResult[i] = a;
        }
        // The loop of decomposition
          ...
          extrema(...);
          ...
        for(i = 0; i < N; i++)
        {
          IMFResult[i + N * nIMF] = IMFResult[i];
          IMFResult[i] = y[MathMin(i, Nf - 1)] - Mean; // * was y[i]
        }
        
      }

予測するバーのIMFの構築は、時系列の最後の既知の値から始まります。

これらは、予測に必要なほとんどすべての変更です。取得したコードの完全なコードは、添付のEMDloose.mqhファイルに示されています。しかし、なぜEMD.mqhではなくEMDloose.mqhなのでしょうか。

問題は、この予測方法が正確ではないということです。オブジェクトのすべての配列のNサイズを増やしたので、極値を検索するために予測されるバーが含まれます。これは、メソッド極値で実行されます。技術的には、極値はありません。計算中に形成されるすべての極値は、スプライン外挿の合計の極値です(将来存在しない最初の系列なし)。その結果、スプライン関数は相互に調整を開始し、スタックをスムーズにしようとします。ある意味で、予測は自己バランスをとるので便利です。振動プロセスは時系列の値の近くに留まり、無限大にはなりません。ただし、そのような予測の値は最小限です。これは、初期の時系列をもはや特徴付けません。ただし、この方法は間違いなく使用でき、プロジェクトに正確にEMDloose.mqhを含めて、この方法を使用できます。

この問題を修正するために、さらに変更を加えて、EMD.mqhの最終版を入手します。2つの予測方法によって提供される効果を比較するために、指標がEMD.mqhおよびEMDloose.mqhでどのように機能するかを以下で確認します。

まあ、IMF関数を時系列の最後の実際のポイントのスプライン上で将来構築する必要があります。この場合、3次スプラインは再構築されない場合、無限になる傾向があるため、予測深度には物理的な(適用された)制約があります。予測の深さは最初の段階でバーによって制限されるため、これは重要ではありません。

変更のポイントは、初期時系列の長さをdecompメソッドでローカルに保存するのではなく、オブジェクトの変数に保存することです。

  class CEMD
  {
    private:
      int N;       // Input and output data size
      int Nf;      // +
      
    public:
        int decomp(const double &y[], const int extrapolate = 0)
        {
          ...
          N = ArraySize(y);
          Nf = N;                            // + preserve actual number of input data points in the object
          N += extrapolate;                  // +
          ...
        }
  };

次に、関連する場所で増加したNをNfに置き換えて、極値メソッドでは変数Nfを使用できるので、したがって、最初の時系列に由来する実際の極値のみが考慮されます。ファイルEMD.mqhとEMDloose.mqhのコンテキストを比較してすべての変更を確認するのが最も簡単です。

実際、これで予測アルゴリズムが完成します。分解結果の取得に関しては、もう1つの小さなステップがあります。CEMDクラスgetIMFのメソッドはこのためのものです。最初に、宛先配列(x)、要求されたIMF高調波の数(nn)の2つのパラメータが渡されています。

  void CEMD::getIMF(double &x[], const int nn, const bool reverse = false) const
  {
    ...
    if(reverse) ArrayReverse(x); // +
  }

ここでは、オプションのパラメータreverseが追加されています。これを使用すると、配列を逆順に並び替えできて、時系列のようなインデックス付けが便利な指標バッファを確実に操作するために必要です(0番目の要素が最新です)。

これで、予測を目的としたCEMDクラスの拡張が完了したため、EMDベースの指標の実装に進むことができます。

指標EMD.mq5

デモのために、指標は相場で直接機能します。 ただし、このアプローチは完全な実際の取引には必ずしも適していません。外挿を使用した価格シリーズの予測では、少なくとも、予測期間に対する強い外部の影響を排除するニュースをフィルターすることが提案されます。時間枠が短い場合は、おそらくナイトフラットが最初の選択です。さらに、ノイズの影響を受けにくい長い時間枠といくつかの商品のバランスの取れた合成の使用をお勧めします。

指標の入力を定義しましょう。

  input int Length = 300;  // Length (bars, > 5)
  input int Offset = 0;    // Offset (0..P bars)
  input int Forecast = 0;  // Forecast (0..N bars)
  input int Reconstruction = 0; // Reconstruction (0..M IMFs)

OffsetおよびLengthパラメータは、分析する系列のオフセットとバーの数を設定します。履歴での予測の分析を容易にするために、Offsetパラメータは、チャート内インターフェイスでマウスでドラッグできる垂直破線によっても表示され、予測をインタラクティブに再計算できます(系列の長さと形状、およびプロセッサのパフォーマンスによっては計算にかなりの時間がかかる場合があります)。

Forecastパラメータは予測されるバーの数です。厳密なアルゴリズム(EMD.mqh)では5〜10を超える値を使用することはお勧めしません。簡略化されたアルゴリズム(EMDloose.mqh)では、より大きな値が許可されます。

Reconstructionパラメータは、時系列の再構築で省略できるIMF関数の数を定義し、他の関数が予測を形成するようにします。ここで0を指定すると、再構築は最初の系列と完全に一致し、予測は不可能になります(基本的に、定数(最終価格)に等しくなるので、意味がありません)。1に設定すると、一番小さい振動が除外されるため、再構成は滑らかになります。 2の場合、第1および第2高調波が除外されます。見つかったIMF関数の数と同じ数を入力すると、再構築は残りの部分と一致します(「傾向」)。これらすべてのケースで、平滑化シリーズには予測があります(IMFの数の組み合わせごとに独自のもの)。MFの数を超える数を設定すると、再構築と予測は不確定になります。このパラメータの推奨値は2です。

Reconstructionの値が小さいほど、移動性が高く、最初のシリーズに近くなりますが(短期間MA同様)、予測は非常に不安定になります。この値が大きいほど、再構成と予測はより滑らかで安定します(長期間MA同様)。

OnInitハンドラでは、予測の深さに応じてバッファのオフセットを設定します。

  int OnInit()
  {
    IndicatorSetString(INDICATOR_SHORTNAME, "EMD (" + (string)Length + ")");
    for(int i = 0; i < BUF_NUM; i++)
    {
      PlotIndexSetInteger(i, PLOT_DRAW_TYPE, DRAW_LINE);
      PlotIndexSetInteger(i, PLOT_SHIFT, Forecast);
    }
    return INIT_SUCCEEDED;
  }

指標は、バーごとのモードで始値で計算されます。OnCalculateハンドラの重要なポイントは次のとおりです。

ローカル変数を定義し、タイマーシリーズとして使用されるOpenとTimeのインデックスを設定します。

  int OnCalculate(const int rates_total,
                  const int prev_calculated,
                  const datetime& Time[],
                  const double& Open[],
                  const double& High[],
                  const double& Low[],
                  const double& Close[],
                  const long& Tick_volume[],
                  const long& Volume[],
                  const int& Spread[])
  {

    int i, ret;
    
    ArraySetAsSeries(Time, true);
    ArraySetAsSeries(Open, true);

バーごとのモードを確保します。

    static datetime lastBar = 0;
    static int barCount = 0;
    
    if(Time[0] == lastBar && barCount == rates_total && prev_calculated != 0) return rates_total;
    lastBar = Time[0];
    barCount = rates_total;

十分な量のデータを待機します。

    if(rates_total < Length || ArraySize(Time) < Length) return prev_calculated;
    if(rates_total - 1 < Offset || ArraySize(Time) - 1 < Offset) return prev_calculated;

指標バッファを初期化します。

    for(int k = 0; k < BUF_NUM; k++)
    {
      buffers[k].empty();
    }

ローカル配列(yy)を配布して、最初のシリーズをオブジェクトに渡し、結果を取得します。

    double yy[];
    int n = Length;
    ArrayResize(yy, n, n + Forecast);

分析する時系列を配列に入力します。

    for(i = 0; i < n; i++)
    {
      yy[i] = Open[n - i + Offset - 1]; // we need to reverse for extrapolation
    }

適切なオブジェクトを使用してEMDアルゴリズムを開始します。

    CEMD emd;
    ret = emd.decomp(yy, Forecast);
    
    if(ret < 0) return prev_calculated;

成功した場合は、取得したデータ(主に、IMF関数の数と平均値)を読み取ります。

    const int N = emd.getN();
    const double mean = emd.getMean();

配列yyを展開し、そこに将来のバーの各関数のポイントを書き込みます。

    n += Forecast;
    ArrayResize(yy, n);

視覚化の設定: 最初のシリーズ、再構成、および予測は太い線で表示され、他のすべてのIMFは細い線で表示されます。IMFの数は動的に変化するため(最初のシリーズの形状に応じて)、この設定をOnInitで一度実行することはできません。

    for(i = 0; i < BUF_NUM; i++)
    {
      PlotIndexSetInteger(i, PLOT_SHOW_DATA, i <= N + 1);
      PlotIndexSetInteger(i, PLOT_LINE_WIDTH, i == N + 1 ?2 : 1);
      PlotIndexSetInteger(i, PLOT_LINE_STYLE, STYLE_SOLID);
    }

最後のバッファの最初の時系列を表示します(実際にはEAコードなどから必要ないため、転送されたデータのみを制御します)。

    emd.getIMF(yy, 0, true);
    if(Forecast > 0)
    {
      for(i = 0; i < Forecast; i++) yy[i] = EMPTY_VALUE;
    }
    buffers[N + 1].set(Offset, yy);

再構成用の配列合計(IMFの合計)を配布します。ループでは、再構成に関与するすべてのIMFを検索し、この配列のカウントを合計します。同時に、各IMFを独自のバッファに入れます。

    double sum[];
    ArrayResize(sum, n);
    ArrayInitialize(sum, 0);
  
    for(i = 1; i < N; i++)
    {
      emd.getIMF(yy, i, true);
      buffers[i].set(Offset, yy);
      if(i > Reconstruction)
      {
        for(int j = 0; j < n; j++)
        {
          sum[j] += yy[j];
        }
      }
    }

最後から2番目のバッファが残りを取り、点線で表示します。

    PlotIndexSetInteger(N, PLOT_LINE_STYLE, STYLE_DOT);
    emd.getIMF(yy, N, true);
    buffers[N].set(Offset, yy);

実際、最初のバッファから最後から2番目のバッファには、すべての分解高調波が昇順で含まれています(最初に小さいものから、次に「傾向」までの大きいもの)。

最後に、配列合計のカウントによるコンポーネントの合計を完了し、最終的な再構成を取得します。

    for(int j = 0; j < n; j++)
    {
      sum[j] += yy[j];
      if(j < Forecast && (Reconstruction == 0 || Reconstruction > N - 1)) // completely fitted curve can not be forecasted (gives a constant)
      {
        sum[j] = EMPTY_VALUE;
      }
    }
    buffers[0].set(Offset, sum);
    
    return rates_total;
  }

合計を予測と一緒にゼロバッファに表示します。ゼロインデックスは、EAからの読み取りを容易にするために選択されます。関係するIMFとバッファの数は通常、新しいバーが入ると変化するため、バッファの他のインデックスは可変です。

ラベルをフォーマットし、履歴オフセットラインをインタラクティブに操作する方法については、一部のニュアンスが省略されています。完全なソースコードは本稿末尾に添付されています。

唯一の注目すべきニュアンスは、垂直線を使用してオフセットパラメータのオフセットを変更するときに、指標がChartSetSymbolPeriodを呼び出すことによってチャートの更新を要求することに関連しています。この関数はMetaTrader 5に実装されており、現在の銘柄のすべての時間枠のキャッシュをリセットして、それらを再構築します。チャートでのバーの数やコンピュータのパフォーマンス、選択された設定によっては、このプロセスにかなりの時間がかかる場合があります(例えば、数百万のバーを持つM1グラフがある場合、場合によっては数十秒)。残念ながら、MQL APIは個々の指標を再構築する効率的な方法を提供していません。これに関連して、この問題が発生した場合は、指標のプロパティダイアログでオフセットを変更するか、チャートに表示されるバーの数を減らすことをお勧めします(ターミナルを再起動する必要があります)。垂直カーソル線が追加され、予想されるデータサンプルの最初の位置に簡単かつ正確に配置できるようになります。

厳密モードと同じ設定の簡易モードで指標がどのように機能するかを確認してみましょう(メインモードではないため、EMDloose.mqhファイルで再コンパイルすることで簡易モードが取得されることを思い出してください)。 EURUSD D1の場合、次の設定を使用します。

  • Length = 250;
  • Offset = 0;
  • Forecast = 10;
  • Reconstruction = 2;

短期予測、指標EMD、EURUSD D1

短期予測、指標EMD、EURUSD D1

上のスクリーンショットには、2つの指標バージョンが示されています。上部に厳格なバージョンがあり、下部に簡略化されたバージョンがあります。厳密なバージョンでは、一部の高調波は上下に異なる方向に「逃げる」傾向があることに注意してください。これが、最初の指標のスケールでさえ、2番目の指標のスケールよりも小さくなっている理由です(再スケーリングは、予測の深さが不十分であることを視覚的に警告しています)。簡易モードでは、すべての分解コンポーネントがゼロ付近でホバリングを続けます。これは、例えばForecastパラメータに100の値を設定することで、より長期の予測を取得するために使用できます。見た目はいいですが、通常は現実からかけ離れています。そのような予測の唯一のアプリケーションは、内向きのバウンスまたはブレイクアウトで取引を試みることができる将来の価格変動範囲の推定であるようです。

Long forecast, indicators EMD, EURUSD D1長期予測、指標EMD、EURUSD D1

Long forecast, indicators EMD, EURUSD D1長期予測、指標EMD、EURUSD D1

厳密なバージョンでは、これにより、多項式の端が無限大に分岐しているのを見ることができますが、グラフの有益な部分はゼロ付近で「崩壊」しています。

予測期間が増加した場合、指標の見出しに違いが見られます。最初に、どちらの場合も、6つの独自の関数が見つかりました(括弧内の2番目の数、分析するバーの数の後に)、次に簡略化バージョンでは、7個のバーを使用しています。その場合、予測に要求された100個のバーが極値の計算に参加するためです。10個のバーでの予測では、このような効果はありません(この時系列の場合)。Forecast = 10が許容される最大の長さですが、推奨される長さではありません。推奨される長さは2〜4バーです。

再構築された初期時系列と予測を視覚的に参照するために、価格チャートに直接表示される同様の指標(EMDPrice)を簡単に作成できます。その内部構造は、考慮されるEMD指標の構造に完全に従いますが、バッファは1つしかありません(一部のIMFは計算に含まれますが、チャートが込みすぎるのを避けるために表示されません)。

EMDPriceでは、OnCalculateハンドラの短い形式を使用します。これにより、例えば、典型的な価格など、計算の価格タイプを選択できます。ただし、始値のタイプについては、指標はバーが開くときに計算されるため、最後に形成されたバー1(つまり、すべての価格タイプを持つバー)であることを考慮する必要があります。つまり、始値の場合、Offsetは0にしかできませんが、それ以外の場合は1以上でなければなりません。

以下のスクリーンショットでは、EMDPrice指標が過去のオフセットと15バーでどのように機能するかを確認できます。

価格チャートEURUSD D1でのEMDPrice指標の予測

価格チャートEURUSD D1でのEMDPrice指標の予測 - 履歴での相殺

EMD指標の予測能力をテストするために、特別なEAを開発します。

EMDベースのテストエキスパートアドバイザー

EMD指標のインスタンスを作成し、その予測に基づいて取引する簡単なEA(TestEMD)を作成してみましょう。指標は始値を使用して予測するため、EAはバーが開くときに動作します。

以下は、EAの基本的な入力です。

  • Length — 指標に渡される時系列の長さ
  • Forecast — 指標に渡される予測バーの数
  • Reconstruction - 予測を再構築する際に省略され、指標に渡される小さな高調波の数。
  • SignalBar - 予測値が指標バッファから要求されるバー番号

取引シグナルとして、SignalBar(このパラメータは、未来を予測するためには負である必要があります)と現在のゼロバーの指標値の差を取ります。正の差は買いシグナル、負の差は売りシグナルです。

指標EMDは将来の予測を作成するため、SignalBarのバー番号は通常負であり、絶対値がForecastの値と等しくなります(基本的に、シグナルはより離れたバーから取得することもできますが、その場合、より多くのバーの予測を計算する理由は不明です)。これは、取引操作を実行するときの通常の作業モードの場合です。このモードでは、指標EMDが呼び出されると、履歴の予測を調査しないため、そのオフセットパラメータは常にゼロになります。

ただし、EAは、最後の予測バーでの仮想トランザクションの収益性の理論的計算により、最適化を迅速に実行できる別の特別な非取引モードもサポートします。計算は、選択された日付範囲内の新しいバーごとに順次実行され、予測に実際の価格変動を乗算する利益係数の形式である一般統計がOnTesterから返されます。テスターでは、最適化価格としてカスタム最適化基準を選択する必要があります。このモードをSignalBarパラメータに含めるには、0を入力します。同時に、EA自体が自動的にOffsetをForecastに等しく設定します。これはまさに、EAが最後のForecastバーの予測と価格変化を比較できる理由です。

もちろん、EAは通常の操作モードでも最適化し、取引操作を実行し、組み込みの最適化インデックスを選択することもできます。費用効果の高い非取引モードはかなり粗いため(特に、スプレッドは考慮されません)、これは特に本当です。ただし、両方のフィットネス関数の最大値と最小値はほぼ同じでなければなりません。

予測は数バー先で行うことができ、関連する方向性のあるポジションが同じ期間開かれるため、反対方向の方向性のあるポジションが同時に存在する可能性があります。例えば、Forecastが3の場合、各ポジションは3つのバーの市場内で保有され、3つのポジションがそれぞれのタイプで開かれます。この点で、ヘッジ勘定が必要です。

EAの完全なソースコードは記事に添付されており、ここでは詳しく説明しません。取引の部分は、取引関数の呼び出しを容易にする MT4Ordersライブラリに基づいています。EAでは、マジックナンバー、厳密なエラー処理、スリッページ、StopLosses、TakeProfitsの設定を使用した注文の「敵味方」制御はありません。固定ロットサイズはロット入力パラメータで設定され、成行注文と取引されます。動作中のEAでEMDを使用する場合は、必要に応じて、関連する機能を使用してこのテストEAを拡張するか、既存のEAと同様の方法でEMD指標で動作するパーツを挿入できます。固定ロットサイズはロット入力パラメータで設定され、成行注文と取引されます。

最適化の設定例は、TestEMD.setファイルとして記事に添付されています。加速モードで2018年のEURUSD D1を最適化すると、次の最適な「セット」が提供されます。

  • Length=110
  • Forecast=4
  • Reconstruction=2

したがって、SignalBarはマイナス予測、つまり-4と等しくなければなりません。

2018年の初めから2020年2月までの期間、つまり2019年と2020年の初めに転送を行うこれらの設定を使用した単一のテストは下のようになります。

EURUSD D1、2018~2020のTestEMDレポート

EURUSD D1、2018~2020のTestEMDレポート

ご覧のとおり、システムにはメリットがありますが、インデックスは改善の余地があることを示しています。特に、ステップバイステップモードでより頻繁に再最適化し、ステップサイズを検索すると、ロボットのパフォーマンスが向上すると想定するのが合理的です。

基本的に、EMDアルゴリズムは、より長い時間枠でファンダメンタルな、ある意味では相場の勢いの変動を識別し、それに基づいて収益性の高い取引システムを作成できると言えます。

ここで検討するのはEMDだけではありませんが、第2部に進む前に、時系列を学習するためにいくつかの数学を「リフレッシュ」する必要があります。

MQLにおける時系列の主な特性の分析— 指標TSA

mql5.comのWebサイトには、同様のタイトルの記事(時系列の主要特性分析)がすでに公開されています。これは、平均、中央値、分散、歪度、尖度係数、分布ヒストグラム、自己相関関数、部分自己相関などの値を計算するための詳細な考慮事項を提供します。これらすべては、TSAnalysis.mqhファイルのTSAnalysisクラスに収集され、TSAexample.mq5スクリプトでデモ目的に使用されます。残念ながら、クラスのパフォーマンスを視覚化するために、ブラウザで分析する必要のある外部HTMLファイルを生成するアプローチが適用されました。同時に、MetaTrader 5はデータ配列を表示するためのさまざまなグラフィックツールを提供します。最も重要なのは指標バッファです。クラスを少し修正して、指標に対してより「フレンドリー」にします。その後、ターミナルで直接相場を分析できる指標を実装します。

新しいファイルにTSAnalysisMod.mqhクラスの名前を付けます。主な操作原理は同じです。Calcメソッドを使用して、時系列がオブジェクトに渡されます。この時系列では、インデックスのセット全体が処理中に計算されます。これらはすべて、スカラーと配列の2つのタイプに分類されます。その後、呼び出し元のコードは任意の特性を読み取ることができます。

TSStatMeasuresの単一の構造体にスカラー特性をまとめます。

  struct TSStatMeasures
  {
    double MinTS;      // Minimum time series value
    double MaxTS;      // Maximum time series value
    double Median;     // Median
    double Mean;       // Mean (average)
    double Var;        // Variance
    double uVar;       // Unbiased variance
    double StDev;      // Standard deviation
    double uStDev;     // Unbiaced standard deviation
    double Skew;       // Skewness
    double Kurt;       // Kurtosis
    double ExKurt;     // Excess Kurtosis
    double JBTest;     // Jarque-Bera test
    double JBpVal;     // JB test p-value
    double AJBTest;    // Adjusted Jarque-Bera test
    double AJBpVal;    // AJB test p-values
    double maxOut;     // Sequence Plot. Border of outliers
    double minOut;     // Sequence Plot. Border of outliers
    double UPLim;      // ACF. Upper limit (5% significance level)
    double LOLim;      // ACF. Lower limit (5% significance level)
    int NLags;         // Number of lags for ACF and PACF Plot
    int IP;            // Autoregressive model order
  };

配列はTSA_TYPEの列挙子で示します。

  enum TSA_TYPE
  {
    tsa_TimeSeries,
    tsa_TimeSeriesSorted,
    tsa_TimeSeriesCentered,
    tsa_HistogramX,
    tsa_HistogramY,
    tsa_NormalProbabilityX,
    tsa_ACF,
    tsa_ACFConfidenceBandUpper,
    tsa_ACFConfidenceBandLower,
    tsa_ACFSpectrumY,
    tsa_PACF,
    tsa_ARSpectrumY,
    tsa_Size //  
  };        //  ^ non-breaking space (to hide aux element tsa_Size name)

作業結果を含むTSStatMeasuresの完全な構造体を取得するためには、getStatMeasuresメソッドが提供されています。マクロを使用して配列を取得するには、getARRAYNAMEとして表示される同じタイプのメソッドを生成します。ARRAYNAMEはTSA_TYPEのいずれかの列挙子のサフィックスに対応します。例えば、並べ替えられた時系列を読み取るには、getTimeSeriesSortedメソッドを呼び出す必要があります。このようなメソッドにはすべて署名があります。

  int getARRAYNAME(double &result[]) const;

渡された配列に入力し、要素の数を返します。

さらに、配列を読み取るためには汎用メソッドがあります。

  int getResult(const TSA_TYPE type, double &result[]) const

show仮想メソッドは、役に立たないため、元のクラスから完全に削除されています。すべてのインターフェイス関連タスクの完全な制御は、呼び出し元のコードに与えられます。

特別な指標(TSA.mq5)からTSAnalysisクラスを使用してコードを処理すると便利です。その主な目的は、配列を表す特性を視覚化することです。必要に応じて、スカラー値を表示するオプションを追加できます(これらは、今ログに出力されます)。

一部の配列は論理的に3つに相互接続されているため(例えば、自動相関関数には95%の信頼区間の上限と下限があります)、3つのバッファが指標で予約されています。バッファの表示スタイルは、要求されたデータの意味に応じて動的に調整されます。

以下は、指標入力パラメータです。

  • Type — 要求された配列のタイプ(TSA_TYPE列挙子)
  • Length — 分析された時系列のバーの長さ
  • Offset — 時系列の初期オフセット(0 - 開始点)
  • Differencing — 相場をそのまま読み取るか1階差分を取るかを定義する差分モード
  • Smoothing — 平均化の期間
  • Method — 平均化法
  • Price — 価格の種類(デフォルトでは始値)

指標はバーによって計算されます。

これは、部分自動相関関数が500バーでEURUSD D1を探す方法の例です。

指標TSD、EURUSD D1

指標TSD、EURUSD D1

1階差分をとることで、系列の定常性(および予測可能性)を高めることができます。基本的に、2階差分はさらに定常的で、3階差分はさらに安定します。ただし、これにはマイナス面があります。これについては、後で説明します(第2部)。

また、部分的な自己相関関数を選択したのは偶然ではありません。これは、次の段階で別の予測方法に進むときに必要になります。かなりの量の資料を調査する必要があるため、この準備の章を使用してこの記事を作成しました。さらに、時系列の統計分析は普遍的な値を表し、MQLの他のカスタム開発でも使用できます。


終わりに

この記事では、経験的モード分解アルゴリズムの特別な側面を検討しました。これにより、時系列の短期予測の領域にその適用性を拡大できます。MQLに実装されたクラス、指標、およびEAにより、自動取引システムの一部としてだけでなく、取引決定を行う際の追加の要素としてEMD予測を使用できるようになります。さらに、時系列の統計分析を実行するようにツールキットを更新しました。これは、次の記事でLS-SVM法による予測を検討するために必要になります。

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

添付されたファイル |
MQL5EMD.zip (45.93 KB)
連続ウォークフォワード最適化(パート5):自動オプティマイザプロジェクトの概要とGUIの作成 連続ウォークフォワード最適化(パート5):自動オプティマイザプロジェクトの概要とGUIの作成
この記事では、MetaTrader5 ターミナルでのウォークフォワード最適化の詳細を説明します。 以前の記事では、最適化レポートを生成およびフィルタリングする方法を検討し、最適化プロセスを担当するアプリケーションの内部構造の分析を開始しました。 自動オプティマイザは C# アプリケーションとして実装され、独自のグラフィカル インターフェイスを備えています。 5番目となるこの記事では、このグラフィカルインタフェースの作成に専念します。
このプロジェクトは、収益性の高いトレーディングロボットを作成する手助けになります! 少なくとも、そうなるでしょう。 このプロジェクトは、収益性の高いトレーディングロボットを作成する手助けになります! 少なくとも、そうなるでしょう。
大きなプログラムは小さなファイルから始まり、関数やオブジェクトを追加し続けるにつれてサイズが大きくなります。 ほとんどのトレードロボット開発者は、この問題を処理するためにインクルードファイルを利用しています。 しかし、より良い解決策があります。:それは、プロジェクト内の任意のトレードアプリケーションの開発を開始することです。 そうする理由はたくさんあります。
DoEasyライブラリの時系列(第36部): すべての使用銘柄期間の時系列オブジェクト DoEasyライブラリの時系列(第36部): すべての使用銘柄期間の時系列オブジェクト
本稿では、使用された各銘柄期間のバーオブジェクトのリストを単一の銘柄時系列オブジェクトに結合することを検討します。使用されるすべての銘柄時系列期間のリストを格納するオブジェクトが各銘柄に備わることになります。
連続ウォークフォワード最適化(パート4):最適化マネージャ(オートオプティマイザ) 連続ウォークフォワード最適化(パート4):最適化マネージャ(オートオプティマイザ)
この記事の主な目的は、アプリケーションとその機能を操作するメカニズムについて説明することです。 したがって、この記事は、アプリケーションの使用方法に関する説明書としても使うことができます。 アプリケーションの使用法においてありがちな落とし穴と詳細を扱っています。