記事"MQL5クックブック - スライディングウィンドウ内指標の高速計算のためのリングバッファの作成"についてのディスカッション

 

新しい記事 MQL5クックブック - スライディングウィンドウ内指標の高速計算のためのリングバッファの作成 はパブリッシュされました:

リングバッファは、スライディングウィンドウで計算を実行するときにデータを配置するのに最も簡単で効率的な方法です。本稿では、そのアルゴリズムを説明し、同アルゴリズムが如何にスライディングウィンドウでの計算を単純化して効率を向上させるかを示します。

指標のグラフィック表示は標準移動平均と同等です。

 

図1 リングバッファで計算された単純移動平均‌


作者: Vasiliy Sokolov

 

トレーディング、自動売買システム、トレーディング戦略のテストに関するフォーラム

MQL4で配列を宣言する - 変数の値を渡して要素数を設定できない(定数も)。

fxsaber, 2016.09.16 14:32

void OnStart()
{
  double Index = -345.23;
  double Size = -432.98;
  
  double Array[];
    
  // どんなサイズでも(整数でなくても)機能する。
  ArrayResize(Array, (int)Size < 0 ? (int)MathAbs(Size) : (int)Size);
  
// どのインデックスでも(整数でなくても)常に(ゼロを除いて - 配列サイズがゼロの場合は)エラーなしで実行される。
// こうして、配列は双方向でそれ自身の無限コピーとなる。
  Array[(int)Index < 0 ? ArraySize(Array) + ((int)Index % ArraySize(Array)) : (int)Index % ArraySize(Array)] = 1;
}
OOPは、このようなタスクのためにいくつかのオーバーキルと見られている。
 
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
//---
   bool calc = false;
   for(int i = prev_calculated; i < rates_total; i++)
   {
      Sma.AddValue(price[i]);
      buff[i] = Sma.SMA();
      calc = true;
   }
   if(!calc)
   {
      Sma.ChangeValue(MaPeriod-1, price[rates_total-1]);
      buff[rates_total-1] = Sma.SMA();
   }
   return(rates_total-1);
}
計算の状況が理解できない。もっと単純なように思えます。
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
//--- 
   if (prev_calculated < rates_total)
     for(int i = prev_calculated; i < rates_total; i++)
     {
        Sma.AddValue(price[i]);
        buff[i] = Sma.SMA();
     }
   else
   {
      Sma.ChangeValue(MaPeriod-1, price[rates_total-1]);
      buff[rates_total-1] = Sma.SMA();
   }
   return(rates_total-1);
}


ZY prev_calculatedがゼロの場合、エラーが発生します。

 

非常に重要なトピックに関する非常に良い記事だ。

リング・バッファーは 非常に重要なメカニズムだ。

この記事に加えて、リングバッファーの使い方をさらに発展させる方法について私の考えを付け加えたい:

リング・バッファにパラメーターの値を記録するのと並行して、このパラメーター用に2つ目のリング・バッファを作成し、1つ目のバッファの値の変化の間の時間を記録すれば、両方のリング・バッファのデータから、現在の期間における値の変化の曲線を構築することができる。さらに、数学的な操作の助けを借りて、パラメータ値の変化の特徴、すなわち現在のシグネチャーを抽出することが可能であり、特定の値ではなく、現在の期間全体のパラメータ変化のコンテキストを使用して作業することができます。


P.S. これを独立したメカニズムとして実装してくれる人がいたら最高です。試してみる?

 

記事をありがとう!いくつかの質問と辛辣なコメントがあります。)


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


ターミナルで生成されたH1時系列は、Expert Advisor内で生成された同じ時系列よりも多くのメモリを消費しますか?


しかし、それでも3GB近くのRAMが必要です。この数字を減らす他の方法はありますか?タイムフレーム数を最適化すれば可能です。テストコードを少し変更し、PERIOD_M1という21の代わりに1つのタイムフレームだけを使用してみましょう。インジケータの数は変わりませんが、重複するインジケータがあります:

内部計算モードでは、同じ504のインジケータが548MBのRAMを占有します。

この動きはまったく理解できません。21タイムフレームのインジケータの計算と1TFの計算をどうやって比較するのですか?計算結果は全く異なるものであり、メモリの使用量にどのような違いがあるのでしょうか?


リング・バッファの用途として、取引ほど適切なものを見つけるのは難しい。このデータ構築アルゴリズムがこれまでMQLコミュニティで取り上げられてこなかったことは、なおさら驚きである。

Konstantin Gruzdevは2012年に彼のクラスといくつかの例を投稿している。検索すれば見つかるだろう


一般的に、もちろんこのテクニックは優れています。欠点は、すべてのインジケーターを書き換える必要があることです。

 
Andrey Khatimlianskii:

記事をありがとう!いくつか質問と辛辣なコメントがあります。)

可能であれば、複数のタイムフレームからデータを取得するクエリは避けてください。代わりに、計算には1つの(最も小さい)タイムフレームを使用してください。例えば、M1とH1の2つのインディケータを計算する必要がある場合、M1のデータを取得し、H1に変換してから、このデータをH1のインディケータの計算に使用します。この方法はより複雑ですが、メモリを節約できます。


ターミナルで生成される H1 時系列は、Expert Advisor 内で生成される同 じ時系列よりも多くのメモリを消費しますか?

残念ながら、そうです。そしてはるかに多い。また、1つのバーを要求したか、利用可能な履歴全体を要求したかは関係ありません。指定した時間枠のすべてのデータが内部メモリにコピーされます。どのくらいコピーされるのか正確にはわかりませんが、私のメモリー測定によると、ほとんどすべてがコピーされるようです。

Andrey Khatimlianskii:


しかし、それでも3GB近くのRAMが必要でした。この数字を減らす方法はありますか?時間枠の数を最適化すれば可能です。テストコードを少し変更し、PERIOD_M1という21の代わりに1つのタイムフレームだけを使用してみましょう。インジケータの数は変わりませんが、重複するインジケータがあります:

内部計算モードでは、同じ504のインジケータが548MBのRAMを占有します。

この動きはまったく理解できません。21タイムフレームのインジケータの計算と1TFの計算をどうやって比較するのですか?計算結果は全く異なるものであり、メモリの使用量にどのような違いがあるのでしょうか?

最小のタイムフレームを1つだけ使って、異なるタイムフレームで複数の指標を計算すれば、メモリを節約できます。2つのインディケータがあり、1つはM1で、もう1つはH1で値をカウントするとします。M1とH1の気配値をロードすることができます。各インジケータの気配値をロードし、そこから値を取得することができます。しかし、H1がロードされるだけで、メモリ使用量は大幅に増加します。そこで、M1 を要求し、M1 を H1 に変換し、このデータを H1 のインジケータに送れば、メモリは大幅に節約されます。この節約は、MetaTraderの内部バッファがH1の気配値を保存するために、これらの気配値がExpert Advisorの内部に保存される場合よりもはるかに多くのメモリを割り当てるという事実によって達成されます。

記事には記載されていないが、もう一つ興味深い機能がある。ストラテジーテスターの メモリ割り当てモデルは異なり、より経済的です。複数のタイムフレームを使用する場合、メモリの割り当てが大幅に少なくなりますが、すべて正常に計算されます。

 
Andrey Khatimlianskii:

...


一般的に、この記事を書く目的は3つあった:

  • Expert Advisor内のインジケータの計算の ための高速アルゴリズムを作成すること(完了)。
  • リングバッファで計算するための便利なインターフェースを作成する(完了)。
  • メモリを節約する計算を作成する(未実装)。

最後の項目は失敗した。数十メガバイトのメモリーを節約することは可能だったが、10-20メガバイトのメモリーを節約することは無意味であり、どのようなクォート要求でも実際の端末メモリーの消費を100-200メガバイト増加させることになる。数十バーの深さで6シンボルの21タイムフレームをロードする場合、13Gバイト以上の内部端末メモリ(!)が必要になる。
 
Реter Konow:

非常に重要なトピックに関する非常に良い記事だ。

リング・バッファーは非常に重要なメカニズムだ。

この記事に加えて、リングバッファーの使い方をさらに発展させる方法について私の考えを付け加えたい:

リング・バッファにパラメーターの値を記録するのと並行して、このパラメーター用に2つ目のリング・バッファを作成し、1つ目のバッファの値の変化の間の時間を記録すれば、両方のリング・バッファのデータから、現在の期間における値の変化の曲線を構築することができる。さらに、数学的な操作の助けを借りて、パラメータ値の変化の特徴、すなわち現在のシグネチャーを抽出することが可能であり、特定の値ではなく、現在の期間全体のパラメータ変化のコンテキストを使用して作業することができます。


P.S. これを独立したメカニズムとして実装してくれる人がいたら最高です。試してみてください。


あなたが説明しているのは単なるインジケーターです。CTradeChangeと しましょう。その中に2つの同期リング・バッファを置きます。1つはN個の最終価格、もう1つはN個の最終時間値を格納します:

CTradeChange change;
...
change.Add(value, TimeCurrent());

次に、Addメソッドで現在と前回の時間値の差を計算します。

 
Vasiliy Sokolov:


一般的に、記事を書くにあたって3つの目標があった:

  • Expert Advisor内のインジケータの計算の ための高速アルゴリズムを作成する(完了)。
  • リングバッファで計算するための便利なインターフェースを作成する(完了)。
  • メモリを節約する計算を作成する(未実装)。

最後の項目は失敗した。数十メガバイトのメモリーを節約することは可能だったが、10-20メガバイトのメモリーを節約することは無意味であり、どのようなクォート要求でも実際の端末メモリーの消費を100-200メガバイト増加させることになる。数十バーの深さで6シンボルの21タイムフレームをロードする場合、13Gバイト以上の内部端末メモリが必要になります(!)。

ご回答ありがとうございました。

記事で紹介したパネルを開発する際、チャートに表示するバーの数(5000本)を制限する必要性に迫られました。正確にはメモリの問題ですが...。

MT4ではもっと簡単でした:第一に、各TFは独立してロードされたので、M1をロードする必要がなかったこと、第二に、インジケータ・バッファは埋めた分だけスペースを取っていました(それ以上必要なければ100バーでも)。

5では、M1からTFが生成されるため、より普遍的で統合的なものになりましたが、メモリは難しいかもしれません。

 
Vasiliy Sokolov:


あなたが説明しているのは単なるインジケーターです。仮にCTradeChangeとします。その中に2つの同期リング・バッファを置きます。1つはN個の最後の価格を格納し、もう1つはN個の最後の時間値を格納します:

そしてAddメソッドで現在と前回の時間値の差を計算します。

あなたは奇妙な答えをしました。どうやらあなたはこの考え方をまったく理解していないようです。もう一度引用します:

「リング バッファに パラメータ値を記録するのと並行して、このパラメータ用に2つ目のリングバッファを作成し、1つ目のバッファの値の変化の間の時間を記録する。さらに、数学的演算を使えば、パラメータ値の変化の性質、つまり現在のシグネチャーを 抽出することが でき、特定の値ではなく、現在の期間全体にわたるパラメータ変化の文脈を扱うことができる。"


注:私は、2つの同期リング・バッファを構築する方法を尋ねたわけではなく、そのメカニズムは非常に単純であるため、リング・バッファの適用範囲を記事に記載されているよりもさらに発展させることを試みることを提案した。

記事で提案された応用範囲は、現在の期間内のインデックスによって特定の値を取得する ことである。

私が提案するこの分野の拡張は、現在の期間内の値の変化のサインを得る ことです。これは、2つのリングバッファからのデータに基づいて(グラフ上ではなく)曲線をプロットすることによって行うことができます:

1. 現在の期間の値を含むバッファ。

2. 最初のバッファの値の間の時間間隔のバッファ。


データを組み合わせることで、曲線を数学的にプロットし(グラフ上である必要はない)、プログラム内でアルゴリズム的に表現して研究することができる。そのためには、両方のバッファをスキャンし、パラメータ変化のシグネチャを考慮する必要があります。

この解決策は、ある期間内のパラメーターの特定の値だけでなく、全期間にわたるパラメーターの変化のシグネチャーを操作する可能性を開く。


例えば、プログラムに指定することが可能になる:

if(Характер_изменения_значения_параметра_за_период ==  BIG_WAVE)Лот  +=  10;

定数BIG_WAVEは、現在の期間における値の変化の特徴を表すシグネチャーである。

例えば、5パターンのシグネチャーを作ることができる:

flat、rising、big_wave、falling、small_waves。

これらの定数はそれぞれ、現在の期間内のパラメータ変化の特定の性格を表すパターンである。

シグネチャ記録のフォーマットを開発し、これらのテンプレートを作成する必要がある。次に、アルゴリズムは現在のシグネチャを読み取り、それをテンプレートと比較し、現在の変化の性質とテンプレートの1つとの間の最も近い一致(絶対的な一致はありえない)を見つける。

標準的な特定の値の使用に対するこのアプローチの利点は明らかである。

シグネチャを使用することで、データセットから抽出されたコンテキストに基づいて判断することが可能になり、単一の値からコンテキストを抽出しようとする必要がなくなる。


追伸:今回の私の言いたいことを理解していただけただろうか。

 
Реter Konow:

奇妙な答えだね。要点をまったく理解していないようだ。もう一度引用する:

...

私が提案するドメインの拡張は、現在の期間内の値の変化のサインを得る ことです。これは、2つのリングバッファからのデータに基づいて(グラフではなく)曲線をプロットすることによって行うことができます:

追伸:今回の私の指摘を理解していただけたでしょうか?

一度目もあなたの言いたいことは完璧に理解できました。

値変化シグネチャー」とは何ですか?ダイナミクスの中で変化する値のことだ。したがって、それは指標である。この目的のためにリングバッファを 開発する必要はなく、いくつかのリングインジケーターを基に、まさにあなたが言っている「変化の特徴」を計算するアルゴリズムを作るだけで十分です。